HRS*_*SO1 5 sql-server sql-server-2012 sql-server-2019
有 3 个表:客户、发票和付款。带有发票和付款表的客户链接。问题是,需要检索报表明智的借方、贷方和余额。主要问题是未从日期范围获得期初余额。我提供带有查询的示例数据。
客户表:
(插入新客户时,如果他们有未分配余额与期初余额,否则保留空白(空))
CREATE TABLE BASE_Customer
(
CustomerId INT IDENTITY(1,1),
CustomerName VARCHAR(45),
SalesOpeningBalance MONEY NULL,
PRIMARY KEY(CustomerId)
)
INSERT INTO BASE_Customer (CustomerName, SalesOpeningBalance) VAlUES ('Ricky', 2500) -- with opening balance.
INSERT INTO BASE_Customer (CustomerName) VAlUES ('Smith') -- without opening balance.
Run Code Online (Sandbox Code Playgroud)
发票表:
CREATE TABLE BASE_Invoice
(
InvoiceId INT IDENTITY(1,1),
InvoiceDate DATE,
CustomerId INT,
Total MONEY,
PRIMARY KEY(InvoiceId)
)
INSERT INTO BASE_Invoice (InvoiceDate, CustomerId, Total) VALUES ('2022-01-01', 1, 500) -- Ricky
INSERT INTO BASE_Invoice (InvoiceDate, CustomerId, Total) VALUES ('2022-01-02', 2, 250) -- Smith
INSERT INTO BASE_Invoice (InvoiceDate, CustomerId, Total) VALUES ('2022-01-02', 1, 100) -- Ricky
INSERT INTO BASE_Invoice (InvoiceDate, CustomerId, Total) VALUES ('2022-01-03', 1, 400) -- Ricky
INSERT INTO BASE_Invoice (InvoiceDate, CustomerId, Total) VALUES ('2022-01-03', 2, 500) -- Smith
INSERT INTO BASE_Invoice (InvoiceDate, CustomerId, Total) VALUES ('2022-01-04', 1, 200) -- Ricky
INSERT INTO BASE_Invoice (InvoiceDate, CustomerId, Total) VALUES ('2022-01-04', 2, 800) -- Smith
INSERT INTO BASE_Invoice (InvoiceDate, CustomerId, Total) VALUES ('2022-01-05', 1, 100) -- Ricky
INSERT INTO BASE_Invoice (InvoiceDate, CustomerId, Total) VALUES ('2022-01-06', 1, 200) -- Ricky
INSERT INTO BASE_Invoice (InvoiceDate, CustomerId, Total) VALUES ('2022-01-07', 1, 500) -- Ricky
INSERT INTO BASE_Invoice (InvoiceDate, CustomerId, Total) VALUES ('2022-01-08', 1, 300) -- Ricky
INSERT INTO BASE_Invoice (InvoiceDate, CustomerId, Total) VALUES ('2022-01-08', 2, 400) -- Smith
INSERT INTO BASE_Invoice (InvoiceDate, CustomerId, Total) VALUES ('2022-01-09', 1, 600) -- Ricky
Run Code Online (Sandbox Code Playgroud)
付款表:
CREATE TABLE BASE_Payment
(
PaymentId INT IDENTITY(1,1),
PaymentDate DATE,
CustomerId INT,
Total MONEY,
PRIMARY KEY(PaymentId)
)
INSERT INTO BASE_Payment (PaymentDate, CustomerId, Total) VALUES ('2022-01-03', 1, 400) -- Ricky
INSERT INTO BASE_Payment (PaymentDate, CustomerId, Total) VALUES ('2022-01-05', 1, 500) -- Ricky
INSERT INTO BASE_Payment (PaymentDate, CustomerId, Total) VALUES ('2022-01-05', 2, 300) -- Smith
INSERT INTO BASE_Payment (PaymentDate, CustomerId, Total) VALUES ('2022-01-07', 1, 600) -- Ricky
INSERT INTO BASE_Payment (PaymentDate, CustomerId, Total) VALUES ('2022-01-08', 2, 200) -- Smith
Run Code Online (Sandbox Code Playgroud)
如何按客户从上述 3 个表记录中检索报表视图(完整报表和日期范围)
笔记
插入新客户及其期初余额时,“期初余额”应出现在第一条记录上
如果从所选客户的日期范围中检索,“以前的记录期末余额”应作为期初余额
如何检索输出应在屏幕截图下方,
图01:客户(Ricky)完整声明
注意:创建此客户时给出期初余额
图02:客户(Ricky)日期范围声明
注意:日期范围期初余额应为“以前的记录期末余额”,而不是创建该客户时给定输出图片上方的期初余额 01 2,500
图03:客户(史密斯)完整声明
注意:创建此客户时未给出期初余额,因此不需要出现期初余额
图04:客户(史密斯)日期范围声明
注:日期范围期初余额应为以前记录的期末余额 750(以上完整报表第 1 条记录总计)
我尝试使用下面的查询,但没有得到像上面 4 个屏幕截图那样的预期结果,主要问题是检索日期范围时的期初余额。
--YYYY-MM-DD (Date Format)
DECLARE @CustomerId INT = 2;
SELECT
[Date],
Particulars,
ABS(Debit) AS Debit,
ABS(Credit) AS Credit,
SUM(CASE WHEN Credit = 0 THEN Debit ELSE Credit END) OVER (ORDER BY [Date], [Particulars]) AS Balance
FROM
(
SELECT
NULL AS [Date],
'OPENING BALANCE' AS [Particulars],
ISNULL(SalesOpeningBalance, 0) AS [Debit],
0 AS [Credit],
ISNULL(SalesOpeningBalance, 0) AS [Balance]
FROM BASE_Customer
WHERE (CustomerId = @CustomerId)
UNION ALL
SELECT
BASE_Invoice.InvoiceDate AS [Date],
'INVOICE' AS [Particulars],
BASE_Invoice.Total AS [Debit],
0 AS [Credit],
ISNULL(BASE_Customer.SalesOpeningBalance, 0) AS [Balance]
FROM BASE_Customer INNER JOIN BASE_Invoice ON BASE_Customer.CustomerId = BASE_Invoice.CustomerId
WHERE (BASE_Customer.CustomerId = @CustomerId) --AND (BASE_Invoice.InvoiceDate BETWEEN '2022-01-04' AND '2022-01-05')
UNION ALL
SELECT
BASE_Payment.PaymentDate AS [Date],
'PAYMENT' AS [Particulars],
0 AS [Debit],
-1.0 * BASE_Payment.Total AS [Credit],
ISNULL(BASE_Customer.SalesOpeningBalance, 0) AS [Balance]
FROM BASE_Customer INNER JOIN BASE_Payment ON BASE_Customer.CustomerId = BASE_Payment.CustomerId
WHERE (BASE_Customer.CustomerId = @CustomerId) --AND (BASE_Payment.PaymentDate BETWEEN '2022-01-04' AND '2022-01-05')
) u
ORDER BY [Date]
Run Code Online (Sandbox Code Playgroud)
我稍微压缩了您的示例 DDL/DML:
DECLARE @BASE_Customer TABLE (CustomerId INT IDENTITY(1,1) PRIMARY KEY, CustomerName VARCHAR(45), SalesOpeningBalance DECIMAL(10,2) NULL);
INSERT INTO @BASE_Customer (CustomerName, SalesOpeningBalance) VAlUES
('Ricky', 2500), ('Smith', NULL);
DECLARE @BASE_Invoice TABLE (InvoiceId INT IDENTITY(1,1) PRIMARY KEY, InvoiceDate DATE, CustomerId INT, Total DECIMAL(10,2));
INSERT INTO @BASE_Invoice (InvoiceDate, CustomerId, Total) VALUES
('2022-01-01', 1, 500), ('2022-01-02', 2, 250), ('2022-01-02', 1, 100), ('2022-01-03', 1, 400),
('2022-01-03', 2, 500), ('2022-01-04', 1, 200), ('2022-01-04', 2, 800), ('2022-01-05', 1, 100),
('2022-01-06', 1, 200), ('2022-01-07', 1, 500), ('2022-01-08', 1, 300), ('2022-01-08', 2, 400),
('2022-01-09', 1, 600);
DECLARE @BASE_Payment TABLE (PaymentId INT IDENTITY(1,1) PRIMARY KEY, PaymentDate DATE, CustomerId INT, Total DECIMAL(10,2));
INSERT INTO @BASE_Payment (PaymentDate, CustomerId, Total) VALUES
('2022-01-03', 1, 400), ('2020-01-05', 1, 500), ('2020-01-05', 2, 300), ('2020-01-07', 1, 600),
('2020-01-08', 2, 200);
Run Code Online (Sandbox Code Playgroud)
DECLARE @CustomerID INT, @DateFrom DATE, @DateTo DATE
SET @CustomerID = 1
SET @DateFrom = '2022-01-03'
SET @DateTo = '2022-01-08'
SELECT a.Date, a.Particulars, a.Debit, a.Credit, a.Balance
FROM (
SELECT CustomerID, Date, Particulars, Debit, Credit, SUM(Balance) OVER (PARTITION BY CustomerID ORDER BY Date, a.Particulars) AS Balance
FROM (
SELECT CAST(NULL AS DATE) AS Date, 'OPENING BALANCE' AS Particulars, CAST(NULL AS DECIMAL(10,2)) AS Debit, CAST(NULL AS DECIMAL(10,2)) AS Credit, SalesOpeningBalance AS Balance, CustomerID
FROM @BASE_Customer
WHERE SalesOpeningBalance IS NOT NULL
UNION ALL
SELECT TOP 100 PERCENT a.Date, a.Particulars, a.Debit, a.Credit, Balance, CustomerID
FROM (
SELECT InvoiceDate AS Date, 'INVOICE' AS Particulars, Total AS Debit, CAST(NULL AS DECIMAL(10,2)) AS Credit, -1*Total AS Balance, CustomerID
FROM @BASE_Invoice
UNION ALL
SELECT PaymentDate AS Date, 'PAYMENT' AS Particulars, CAST(NULL AS DECIMAL(10,2)) AS Debit, Total AS Credit, Total AS Balance, CustomerID
FROM @BASE_Payment
) a
ORDER BY CustomerID, Date
) a
) a
WHERE (
@CustomerID = a.CustomerId
OR @CustomerID IS NULL
)
AND (
Date BETWEEN @DateFrom AND @DateTo
OR @DateTo IS NULL
OR @DateFrom IS NULL
)
Run Code Online (Sandbox Code Playgroud)
这是一种可以获得所需输出的方法。首先,我们将三组数据以统一的格式合并在一起。请注意,我将借方和贷方放入余额列中,并将借方乘以负 1,一旦我们有了一组统一的数据,我们就可以使用窗口求和函数来计算运行余额。
我在获取所有数据后应用了参数,因为我们仍然需要预先确定请求的开始日期的任何内容,以了解从哪里开始运行余额。我还必须决定如何打破联系,因为我们只使用日期,所以我首先订购了发票。
使用 @CustomerID = 1、@DateFrom = 2022-01-03 和 @DateTo = 2022-01-08:
| 日期 | 细节 | 借方 | 信用 | 平衡 |
|---|---|---|---|---|
| 2022-01-03 | 发票 | 400.00 | 无效的 | 2600.00 |
| 2022-01-03 | 支付 | 无效的 | 400.00 | 3000.00 |
| 2022-01-04 | 发票 | 200.00 | 无效的 | 2800.00 |
| 2022-01-05 | 发票 | 100.00 | 无效的 | 2700.00 |
| 2022-01-06 | 发票 | 200.00 | 无效的 | 2500.00 |
| 2022-01-07 | 发票 | 500.00 | 无效的 | 2000.00 |
| 2022-01-08 | 发票 | 300.00 | 无效的 | 1700.00 |
使用 @CustomerID = 1 和 NULL 日期:
| 日期 | 细节 | 借方 | 信用 | 平衡 |
|---|---|---|---|---|
| 无效的 | 期初余额 | 无效的 | 无效的 | 2500.00 |
| 2020-01-05 | 支付 | 无效的 | 500.00 | 3000.00 |
| 2020-01-07 | 支付 | 无效的 | 600.00 | 3600.00 |
| 2022-01-01 | 发票 | 500.00 | 无效的 | 3100.00 |
| 2022-01-02 | 发票 | 100.00 | 无效的 | 3000.00 |
| 2022-01-03 | 发票 | 400.00 | 无效的 | 2600.00 |
| 2022-01-03 | 支付 | 无效的 | 400.00 | 3000.00 |
| 2022-01-04 | 发票 | 200.00 | 无效的 | 2800.00 |
| 2022-01-05 | 发票 | 100.00 | 无效的 | 2700.00 |
| 2022-01-06 | 发票 | 200.00 | 无效的 | 2500.00 |
| 2022-01-07 | 发票 | 500.00 | 无效的 | 2000.00 |
| 2022-01-08 | 发票 | 300.00 | 无效的 | 1700.00 |
| 2022-01-09 | 发票 | 600.00 | 无效的 | 1100.00 |
需要注意的事项:您可能应该使用 DATETIME 列而不是日期,这样两个事务捆绑的机会就会大大减少。如果 DATETIME2 可用,您可以进一步减少此值。最好以 UTC 格式存储 DATETIME 以防止时区混淆,并按此方式标记列(例如 TransactionDateTimeUTC)。对于货币栏来说,MONEY 是一个糟糕的选择,因为在四舍五入方面存在潜在的问题。您可以在单个 INSERT .. VALUES 语句中插入最多 1000 行。我使用表变量,因为它们不需要任何清理,但这只是个人喜好。
编辑:
事实证明,提供的数据使用的是 YYYY-DD-MM 日期格式,而不是 ISO 标准 YYYY-MM-DD。此编辑提供了使用不同月份而不是日期的示例数据。此编辑还考虑了有关“期初余额”添加的评论。
修改后的演示数据:
DECLARE @BASE_Customer TABLE (CustomerId INT IDENTITY(1,1) PRIMARY KEY, CustomerName VARCHAR(45), SalesOpeningBalance DECIMAL(10,2) NULL);
INSERT INTO @BASE_Customer (CustomerName, SalesOpeningBalance) VAlUES
('Ricky', 2500), ('Smith', NULL);
DECLARE @BASE_Invoice TABLE (InvoiceId INT IDENTITY(1,1) PRIMARY KEY, InvoiceDate DATE, CustomerId INT, Total DECIMAL(10,2));
INSERT INTO @BASE_Invoice (InvoiceDate, CustomerId, Total) VALUES
('2022-01-01', 1, 500), ('2022-02-01', 2, 250), ('2022-02-01', 1, 100), ('2022-03-01', 1, 400),
('2022-03-01', 2, 500), ('2022-04-01', 1, 200), ('2022-04-01', 2, 800), ('2022-05-01', 1, 100),
('2022-06-01', 1, 200), ('2022-07-01', 1, 500), ('2022-08-01', 1, 300), ('2022-08-01', 2, 400),
('2022-09-01', 1, 600);
DECLARE @BASE_Payment TABLE (PaymentId INT IDENTITY(1,1) PRIMARY KEY, PaymentDate DATE, CustomerId INT, Total DECIMAL(10,2));
INSERT INTO @BASE_Payment (PaymentDate, CustomerId, Total) VALUES
('2022-03-01', 1, 400), ('2022-05-01', 1, 500), ('2022-05-01', 2, 300), ('2022-07-01', 1, 600),
('2022-08-01', 2, 200);
Run Code Online (Sandbox Code Playgroud)
修改后的查询:
DECLARE @CustomerID INT, @DateFrom DATE, @DateTo DATE
SET @CustomerID = 1
SET @DateFrom = '2022-04-01'
SET @DateTo = '2022-05-01'
;WITH allItems AS (
SELECT CustomerID, Date, Particulars, Debit, Credit, SUM(Balance) OVER (PARTITION BY CustomerID ORDER BY Date, a.Particulars) AS Balance
FROM (
SELECT CAST(NULL AS DATE) AS Date, 'OPENING BALANCE' AS Particulars, CAST(NULL AS DECIMAL(10,2)) AS Debit, CAST(NULL AS DECIMAL(10,2)) AS Credit, SalesOpeningBalance AS Balance, CustomerID
FROM @BASE_Customer
WHERE SalesOpeningBalance IS NOT NULL
UNION ALL
SELECT a.Date, a.Particulars, a.Debit, a.Credit, Balance, CustomerID
FROM (
SELECT InvoiceDate AS Date, 'INVOICE' AS Particulars, Total AS Debit, CAST(NULL AS DECIMAL(10,2)) AS Credit, -1*Total AS Balance, CustomerID
FROM @BASE_Invoice
UNION ALL
SELECT PaymentDate AS Date, 'PAYMENT' AS Particulars, CAST(NULL AS DECIMAL(10,2)) AS Debit, Total AS Credit, Total AS Balance, CustomerID
FROM @BASE_Payment
) a
) a
)
SELECT a.Date, a.Particulars, a.Debit, a.Credit, a.Balance
FROM (
SELECT TOP 1 CustomerID, Date, 'OPENING BALANCE' AS Particulars, NULL AS Debit, NULL AS Credit, Balance
FROM allItems a
WHERE Date < @DateFrom
ORDER BY Date DESC
UNION ALL
SELECT CustomerID, Date, Particulars, Debit, Credit, Balance
FROM allItems a
WHERE (
@CustomerID = a.CustomerId
OR @CustomerID IS NULL
)
AND (
Date BETWEEN @DateFrom AND @DateTo
OR @DateTo IS NULL
OR @DateFrom IS NULL
)
) a
Run Code Online (Sandbox Code Playgroud)
修订结果(2022年4月1日至2022年5月1日)
| 日期 | 细节 | 借方 | 信用 | 平衡 |
|---|---|---|---|---|
| 2022-03-01 | 期初余额 | 无效的 | 无效的 | 1500.00 |
| 2022-04-01 | 发票 | 200.00 | 无效的 | 1700.00 |
| 2022-05-01 | 发票 | 100.00 | 无效的 | 1600.00 |
| 2022-05-01 | 支付 | 无效的 | 500.00 | 2100.00 |
修正结果(2022年5月1日至2022年7月1日)(图02)
| 日期 | 细节 | 借方 | 信用 | 平衡 |
|---|---|---|---|---|
| 2022-04-01 | 期初余额 | 无效的 | 无效的 | 1700.00 |
| 2022-05-01 | 发票 | 100.00 | 无效的 | 1600.00 |
| 2022-05-01 | 支付 | 无效的 | 500.00 | 2100.00 |
| 2022-06-01 | 发票 | 200.00 | 无效的 | 1900.00 |
| 2022-07-01 | 发票 | 500.00 | 无效的 | 1400.00 |
| 2022-07-01 | 支付 | 无效的 | 600.00 | 2000.00 |
| 归档时间: |
|
| 查看次数: |
1292 次 |
| 最近记录: |