动态压缩相同 SQL table 中的不同行
Dynamically compre different rows in same SQL table
客户需要一种工具来显示几个月前客户未成功的每月付款。起始月份和回溯的月份数应由用户定义
所有付款都记录在同一个 table 中。
同一客户在前几个月可能有也可能没有付款。
这是一个 table,每次付款都有多行,所以我需要使用分组依据。
我正在尝试找出查询信息的最佳方式。
我想它应该是动态构建的(我说得对吗?可以不用动态构建吗?sql),在网上搜索时看到了不同的方法:
在 select 子句中动态添加行,使用案例,如此查询:SQL query to compare product sales by month
为要比较的每个月将 UNIONS 动态添加到相同的 table
为要比较的每个月动态添加 LEFT JOINS 到相同的 table
方法 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
Status = 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 查询,该查询循环创建用户定义的几个月前的案例。需要很长时间...我错过了什么? LEFT JOIN 或 UNIONS 会更好吗?
我自己想出来了。希望这对其他人有帮助...
选项 1 显然效果很好。出于某种原因,当我第一次 运行 它在 SSMS 中时,20 个月前花了 10 秒以上 - 但那只是在第一个 运行 上。
那是圣运行ge。 (也许服务器在第一个 运行 之后适应?)
从那以后它运行得非常快,几个月前不到一秒。
这是我所做的:
(改进和风格评论将不胜感激!)
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)
客户需要一种工具来显示几个月前客户未成功的每月付款。起始月份和回溯的月份数应由用户定义 所有付款都记录在同一个 table 中。 同一客户在前几个月可能有也可能没有付款。 这是一个 table,每次付款都有多行,所以我需要使用分组依据。
我正在尝试找出查询信息的最佳方式。
我想它应该是动态构建的(我说得对吗?可以不用动态构建吗?sql),在网上搜索时看到了不同的方法:
在 select 子句中动态添加行,使用案例,如此查询:SQL query to compare product sales by month
为要比较的每个月将 UNIONS 动态添加到相同的 table
为要比较的每个月动态添加 LEFT JOINS 到相同的 table
方法 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
Status = 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 查询,该查询循环创建用户定义的几个月前的案例。需要很长时间...我错过了什么? LEFT JOIN 或 UNIONS 会更好吗?
我自己想出来了。希望这对其他人有帮助...
选项 1 显然效果很好。出于某种原因,当我第一次 运行 它在 SSMS 中时,20 个月前花了 10 秒以上 - 但那只是在第一个 运行 上。
那是圣运行ge。 (也许服务器在第一个 运行 之后适应?)
从那以后它运行得非常快,几个月前不到一秒。
这是我所做的: (改进和风格评论将不胜感激!)
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)