客户需要一种工具来显示几个月前客户每月的不成功付款。开始月份和返回的月份数应由用户定义 所有付款都记录在同一张表中。 同一客户在过去几个月中可能有也可能没有付款。 这是一张表格,每笔付款都有多行,所以我需要使用分组依据。
我正在尝试找出查询信息的最佳方法。
我想它应该是动态构建的(对吗?可以在没有动态sql的情况下完成吗?),并且在搜索网络时看到了不同的方法:
使用案例,例如以下查询,在select子句中动态添加行:SQL query to compare product sales by month
将每个月要比较的UNIONS动态添加到同一张表中
将每个月要比较的左联接动态添加到同一张表中
方法1的概念代码:
DECLARE @month int, @monthNum int
SET @month = 103
SET @monthNum = 1
SELECT
name,ID,Property
,MAX(CASE WHEN month = @month THEN month ELSE 0 END) AS month
,SUM(CASE WHEN month = @month THEN paymentSum ELSE 0 END) AS paymentSum
,MAX(CASE WHEN month = @month THEN PaymentFailReason ELSE 0 END) AS PaymentFailReason
,MAX(CASE WHEN month = @month -1 THEN month ELSE 0 END) AS monthMinusOne
,SUM(CASE WHEN month = @month -1 THEN paymentSum ELSE 0 END) AS paymentSumMinusOne
,MAX(CASE WHEN month = @month -1 THEN PaymentFailReason ELSE 0 END) AS PaymentFailReasonMinusOne
FROM
payments
WHERE
month BETWEEN @month -@monthNum AND @month AND status = 2
GROUP BY
name,ID,Property
状态= 2表示付款失败。
在这种情况下是否有首选的解决方案?
一种方法比其他方法更有效吗?
样本数据:
month ID name Property paymentSum PaymentFailReason Status
100 1 Aron A 100 Has No money 2
100 2 Burt B 100 Has No money 2
100 3 Carl C 50 Has No money 2
101 1 Aron A 50 Has No money 2
101 2 Burt B 50 Has No money 2
101 3 Carl C 50 Has No money 2
102 1 Aron A 100 Has No money 2
102 2 Burt B 100 1
102 3 Carl C 100 Has No money 2
103 1 Aron A 102 Has No money 2
103 2 Burt B 102 Has No money 2
103 3 Carl C 102 1
查询上述第103个月和1个月前的数据的结果应为:
month ID name Property paymentSum PaymentFailReason Status monthMinusOne paymentSumMinusOne PaymentFailReasonMinusOne
103 1 Aron A 102 Has No money 2 102 100 Has No money
103 2 Burt B 102 Has No money 0 102 0 0
卡尔成功支付了103英镑,因此他不在结果中。
Burt在102日成功付款,因此该月的数据无关紧要(全为零)。
编辑: 我创建了一个动态SQL查询,该查询循环循环创建用户定义的月份数的案例。需要一段时间...我想念什么?左联接或工会会更好吗?
答案 0 :(得分:0)
自己弄清楚。希望这对其他人有帮助...
选项1显然效果很好。由于某种原因,当我第一次在SSMS中运行它时,花了20个月才花了10秒钟以上的时间-但这只是第一次运行。
这很奇怪。 (也许服务器在第一次运行后就适应了?)
从那时起,它运行起来非常快,几个月后又不到一秒钟。
这是我所做的: (改进和样式说明将不胜感激!)
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[CompareFailedPayments](@month int , @NumOfMonths int)
AS
BEGIN
DECLARE @SqlHead nvarchar(max), @Sql nvarchar(max)='', @Crlf nvarchar(max), @counter int
SET @counter = 0
SET @Crlf = CHAR(13)+CHAR(10)
SET @SqlHead = 'DECLARE @month int = '+ CAST(@month AS VARCHAR) + @Crlf +
'SELECT name,IDnumber,Property, ID, month, paymentsum, PaymentFailReason, EncryptedCardNum '
SET @Sql = 'SELECT name,IDnumber,Property,Payments.ID as Id, Payments.Property as p'+ @Crlf +
',MAX(CASE WHEN month = @month THEN month ELSE NULL END) AS month '+ @Crlf +
',SUM(CASE WHEN month = @month THEN paymentsum ELSE NULL END) AS paymentsum '+ @Crlf +
',MAX(CASE WHEN month = @month THEN PaymentFailReason ELSE NULL END) AS PaymentFailReason '+ @Crlf +
',MAX(CASE WHEN month = @month THEN RIGHT(EncryptedCardNum,4) ELSE NULL END) AS EncryptedCardNum '+ @Crlf +
'FROM Payments '+ @Crlf +
'INNER JOIN'+ @Crlf +
'client ON Payments.ID = client.ID'+ @Crlf +
'WHERE (Payments.month = '+ CAST(@month AS VARCHAR) +') AND (Payments.status = 2)'+ @Crlf +
'GROUP BY client.name, client.IDnumber, Payments.Property,Payments.ID, Payments.Property '+ @Crlf +
') AS Base'+ @Crlf +
'LEFT OUTER JOIN'+ @Crlf +
'('+ @Crlf +
'SELECT Payments.ID as Id1, Payments.Property as p1'+ @Crlf+ @Crlf
WHILE @counter < @NumOfMonths
BEGIN
SET @counter = @counter+1
SET @Sql = @Sql+',MAX(CASE WHEN month = @month-'+CAST(@counter AS VARCHAR)+' THEN month'+' ELSE NULL END) AS month'+CAST(@counter AS VARCHAR)+ @Crlf +
',SUM(CASE WHEN month = @month-'+CAST(@counter AS VARCHAR)+' THEN paymentsum ELSE NULL END) AS paymentsum'+CAST(@counter AS VARCHAR)+ @Crlf +
',MAX(CASE WHEN month = @month-'+CAST(@counter AS VARCHAR)+' THEN PaymentFailReason ELSE NULL END) AS PaymentFailReason'+CAST(@counter AS VARCHAR)+ @Crlf +
',MAX(CASE WHEN month = @month-'+CAST(@counter AS VARCHAR)+' THEN RIGHT(EncryptedCardNum,4) ELSE NULL END) AS EncryptedCardNum'+CAST(@counter AS VARCHAR)+ @Crlf + @Crlf
SET @SqlHead = @SqlHead +', month'+CAST(@counter AS VARCHAR)+', paymentsum'+CAST(@counter AS VARCHAR)+', PaymentFailReason'+CAST(@counter AS VARCHAR)+', EncryptedCardNum'+CAST(@counter AS VARCHAR)+''
END
SET @SqlHead = @SqlHead +' FROM(' + @Crlf
SET @Sql = @Sql+
'FROM Payments'+ @Crlf +
'WHERE (Payments.month BETWEEN '+CAST(@month-@NumOfMonths AS VARCHAR)+' AND ' +CAST(@month-1 AS VARCHAR)+') AND (Payments.status = 2)'+ @Crlf +
'GROUP BY Payments.ID, Payments.Property'+ @Crlf +
') AS Back '+ @Crlf +
'ON Base.ID = Back.m1 and Base.h = Back.h1'
--PRINT(@sqlHead+@Sql)
EXEC(@sqlHead+@Sql)