使用 SQL Server 从多个表中获取期初余额

Posted

技术标签:

【中文标题】使用 SQL Server 从多个表中获取期初余额【英文标题】:Get Opening balance From Multiple Tables Using SQL Server 【发布时间】:2018-09-16 11:34:49 【问题描述】:

我已经尝试了很多天并搜索了整个互联网,但找不到我的问题的解决方案,所以这在任何情况下都不是重复的或可能的重复。 好吧,我有三张桌子;

    客户(Cus_Id、姓名、OpeningBalance) 销售额(S_Id、Cus_Id、日期、发票编号、项目、总计、已收到、余额) 现金簿(Cb_Id、Acc_Id、日期、描述、PaidAmount、ReceivedAmount)

更新: 用户可以在添加新客户时在客户表中输入期初余额,以便我们可以跟踪客户开户时的期初余额。因此,为此目的保留了一列。

现在,我想获取一个客户分类帐,其中显示了所选日期之间的交易记录。我的问题是如何获取分别存储在客户表和所有其他表中的期初余额。 我正在共享我使用的存储过程,它为我提供了来自销售和现金簿的正确数据,但我无法获得期初余额以及在客户表中添加新客户时存储的期初余额如何?

让我们假设以下场景: 客户表的期初余额在客户表中为 $1000.00 最初创建时。他来了,买了几件有价值的东西 200.00 美元,只付了 50.00 美元,一天后他回来付了 300 美元 偿还部分加入现金的贷方金额 由用户预订。现在让我们看看它在分类帐中应该是什么样子。

S.No  Date      Description         Dr         Cr        Balance
-----------------------------------------------------------------
                Opening Balance    1000.00    0.00       1000.00
1  16/09/2018   Sales Invoice#1     200.00    0.00       1200.00
2  16/09/2018   Cash Received 
                Against S.Inv#1.    0.00      50.00      1150.00

3  17/09/2018   Cash Book Entry
                Received Cash.      0.00      300.00     850.00
=================================================================

所以从这个例子中我希望它清楚我想要实现的目标。请注意这一点,如果我想查看 2018 年 9 月 17 日的分类帐,那么期初余额应该是 1150.00 而不是 1000.00(这就是如何根据日期从客户表和其他表中获取期初余额的诀窍选择。)

这是存储过程:

DECLARE @GeneralLedger TABLE
        (
            Id int,
            TransactionDate DATETIME,
            TransactionDescription NVARCHAR(350),
            Dr DECIMAL(18,2),
            Cr DECIMAL(18,2)
        )

        INSERT INTO @GeneralLedger

        SELECT [Acc_Id], [Date], [Description], [PaidAmount], [ReceivedAmount] FROM [CashBook]

        UNION 
        SELECT [Cus_Id], [Date], '(SALES) Invoice# ' + [InvoiceNumber], [Total], CAST(' ' AS INT) FROM [Sales]

        UNION 
        SELECT [Cus_Id], [Date], 'Cash Received Against Sales Invoice# ' + [InvoiceNumber], CAST(' ' AS INT), [Recieved] FROM [Sales]
        Where [Recieved] > 0

        SELECT 
        TransactionDate as 'date',
        TransactionDescription as 'Description',
        Dr as 'Debit',
        Cr as 'Credit',
        SUM(coalesce(Dr, 0) - coalesce(Cr, 0)) OVER (ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS Balance

        FROM @GeneralLedger
        WHERE  TransactionDate >= @FromDate AND TransactionDate <= @ToDate AND id = @AccountTitleId
        GROUP BY
        id,
        TransactionDate,
        TransactionDescription,
        Dr,
        Cr

此存储过程仅适用于计算 dr/cr/balance 但我想在顶部显示期初余额,如果在选定日期之前没有以前的条目,则它应该显示客户表期初余额在顶部(如果余额为正,则应在 dr else cr 下显示)否则应返回上一行余额作为顶部的期初余额。

【问题讨论】:

。 .请编辑您的问题并提供示例数据和所需的结果。此外,这个问题令人困惑。您说您想要期初余额,但这显然是其中一个表中的一列。为什么不使用它?请把逻辑解释清楚。 我无法确定这个问题是关于如何编写SELECT 查询或如何正确计算期初余额或两者兼而有之?那么它是哪一个,SQL 还是金融? @GordonLinoff 我已经更新了问题,请看一下。 @JoakimDanielson 我已经在问题中进一步解释了。我需要一个 storedProcedure 来获取包含正确交易记录的分类帐,具体取决于所选日期。 听起来您需要做的就是加入客户表并添加期初余额。当前的存储过程是否有效?只是缺少期初余额吗? 【参考方案1】:

你有一个有趣的问题。 这是我对您的问题的解决方案:

    ALTER PROCEDURE x1_getCusBalance
        @AccountTitleId int,
        @FromDate smalldatetime,
        @ToDate smalldatetime
    AS
    BEGIN
        declare @GeneralLedger TABLE
            (
                Id int,
                TransactionDate DATETIME,
                TransactionDescription VARCHAR(350),
                Dr DECIMAL(18,2),
                Cr DECIMAL(18,2),
                GroupOrder int
            )

        declare @FirstBalance money = 0
        declare @FirstBalanceDr money = 0
        declare @FirstBalanceCr money = 0
        declare @SalesBalanceDr money = 0
        declare @CashBookBalanceDr money = 0
        declare @SalesBalanceCr money = 0
        declare @CashBookBalanceCr money = 0
        declare @PriorTransBalance money = 0

        -- Calculate Transaction Opening Balance

        SELECT @FirstBalance=OpeningBalance FROM CUSTOMER c
        WHERE
        Cus_id=@AccountTitleId

        SET @FirstBalance=ISNULL(@FirstBalance,0)
        IF @FirstBalance>=0 SET @FirstBalanceDr=@FirstBalance
        IF @FirstBalance<0 SET @FirstBalanceCr=@FirstBalance

        SELECT 
            @SalesBalanceCr=ISNULL(SUM(s.received),0)  
            ,@SalesBalanceDr=ISNULL(SUM(s.total),0)  
        FROM  
            SALES s 
        WHERE 
            Cus_id= @AccountTitleId 
            AND s.[date] <  @FromDate

        SELECT 
            @CashBookBalanceCr=ISNULL(SUM(b.receivedAmount ),0) 
            ,@CashBookBalanceDr=ISNULL(SUM( b.paidAmount),0) 
        FROM  
            CASHBOOK b    
        WHERE 
            Acc_id= @AccountTitleId 
            AND b.[date]  <  @FromDate

        SET @PriorTransBalance=ISNULL(@FirstBalance,0)
        + ABS(ISNULL(@SalesBalanceDr,0)-ISNULL(@SalesBalanceCr,0))
        - ABS(ISNULL(@CashBookBalanceDr,0)-ISNULL(@CashBookBalanceCr,0)) 

        -- Populate temp table

        INSERT INTO @GeneralLedger

        SELECT @AccountTitleId, @FromDate, 'Opening Balance',@PriorTransBalance,0  ,10  

        UNION

        SELECT [Acc_Id], [Date], [Description], [PaidAmount], [ReceivedAmount],20   FROM [CashBook]
        WHERE  
            [Date] BETWEEN  @FromDate AND @ToDate
            AND [Acc_Id] = @AccountTitleId

        UNION 

        SELECT [Cus_Id], [Date], '(SALES) Invoice# ' + [InvoiceNumber], [Total], 0,30 FROM [Sales]
            WHERE  
            [Date] BETWEEN  @FromDate AND @ToDate
            AND [Cus_Id] = @AccountTitleId

        UNION 

        SELECT [Cus_Id], [Date], 'Cash Received Against Sales Invoice# ' + [InvoiceNumber], 0, [Received],40 FROM [Sales]
        WHERE  
            [Date] BETWEEN  @FromDate AND @ToDate
            AND [Cus_Id] = @AccountTitleId
            AND [Received] > 0

        -- Produce Final Result Set 

        SELECT 
            TransactionDate as 'date',
            TransactionDescription as 'Description',
            Dr as 'Debit',
            Cr as 'Credit',
            SUM(coalesce(Dr, 0) - coalesce(Cr, 0)) OVER 
            (ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS Balance
        FROM @GeneralLedger

        GROUP BY
            id,
            TransactionDate,
            GroupOrder,
            TransactionDescription,
            Dr,
            Cr
    END

    /* UnComment and put the following codes in your Select Union statement 
       replacing the 'Opening Balance' Select statement, if you want to see the detail summary 
       of previous transactions balance */
    /*
            SELECT @AccountTitleId, @FromDate, 'Opening Balance Customer'
                ,isnull(@FirstBalanceDr,0)
                ,isnull(@FirstBalanceCr,0)
                ,10  

            UNION
            SELECT @AccountTitleId, @FromDate, 'Prior Sales Transactions'
            , @SalesBalanceDr
            , @SalesBalanceCr
            ,11 

            UNION

            SELECT @AccountTitleId, @FromDate, 'Prior CashBook Transactions'
            ,@CashBookBalanceDr
            ,@CashBookBalanceCr
            ,12  

            UNION
    */
    GO

测试结果:

exec x1_getCusBalance 1,'2018-09-16', '2018-09-20 23:59:59'

    Result :

    date                     Description                              Debit     Credit  Balance
    ------------------------------------------------------------------------------------------- 
    2018-09-16 00:00:00.000  Opening Balance                          1000.00     0.00  1000.00
    2018-09-16 12:00:00.000  (SALES) Invoice# 1                        200.00     0.00  1200.00
    2018-09-16 12:00:00.000  Cash Received Against Sales Invoice# 1      0.00    50.00  1150.00
    2018-09-17 12:00:00.000  Cash Book Entry Recived Cash                0.00   300.00   850.00
    2018-09-18 12:00:00.000  (SALES) Invoice# 2                        150.00     0.00  1000.00

exec x1_getCusBalance 1,'2018-09-17', '2018-09-20 23:59:59'

    Result :

    date                    Description                  Debit   Credit Balance
    ---------------------------------------------------------------------------
    2018-09-17 00:00:00.000 Opening Balance              1150.00   0.00 1150.00
    2018-09-17 12:00:00.000 Cash Book Entry Recived Cash    0.00 300.00  850.00
    2018-09-18 12:00:00.000 (SALES) Invoice# 2            150.00   0.00 1000.00

exec x1_getCusBalance 1,'2018-09-19', '2018-09-20 23:59:59'

    Result :

    date                    Description     Debit   Credit Balance
    --------------------------------------------------------------
    2018-09-19 00:00:00.000 Opening Balance 1000.00   0.00 1000.00

使用的数据:

    CUSTOMER table data : 
       Cus_id Name   OpeningBalance
       ------ ------ ---------------------
       1      Andy   1000,00

    SALES table data :
       S_id  Cus_id Date                InvoiceNumber Item    Total  Received Balance
       ----  ------ ------------------- ------------- ------- ------ -------- -------
       1     1      2018-09-16 12:00:00 1             abc     200,00 50,00    NULL
       2     1      2018-09-18 12:00:00 2             def     150,00 NULL     NULL

    CASHBOOK table data:
       Cb_id Acc_id Date                Description                   PaidAmount ReceivedAmount
       ----- ------ ------------------- ----------------------------- ---------- --------------
       1     1      2018-09-17 12:00:00 Cash Book Entry Received Cash 0,00       300,00

注意:

我在 @GeneralLedger 临时表中添加了 GroupOrder 列,以确保期初余额列始终显示在最终结果集的顶部。 确保传递不包括时间部分的 @FromDate 参数或使用 00:00:00(例如 '2018-09-16 00:00:00)和 @ToDate 并将时间设置为 23:59:59。 我已将最终结果集查询中的 WHERE 子句重新定位到 UNION 查询中的相应 SELECT 语句。这只是为了查询效率。 我认为您还必须保存日期字段的时间部分才能正确排序结果集 出于测试目的,我在 Sales 表(Sales #2)中添加了一个数据行。 我假设您在 Customer 表中的 OpeningBalance 始终为正(借方),如果它可能是负值(贷方),则必须在代码中进行一些修改,如果您遇到困难,请告诉我.

希望此解决方案对您有用...

【讨论】:

以上是关于使用 SQL Server 从多个表中获取期初余额的主要内容,如果未能解决你的问题,请参考以下文章

SSIS - 计算期初和期末余额

如何编写一个查询以从SQL Server中包含类似名称的多个表中获取数据

如何从 SQL Server 中特定数据库的表中获取所有列名?

nc65期初建账重分类

如何在 SQL Server 中使用 INNER JOIN 从多个表中删除

如何从SQL表中选择特定行并在SQL Server中连接多个表?