我需要一些帮助来生成与所需的阶梯输出匹配的MS SQL 2012查询。行按一个日期范围(帐户提交日期月份)汇总数据,列按另一个日期范围(付款日期月份)汇总
表1:帐户跟踪为集合设置的帐户。
CREATE TABLE [dbo].[Accounts](
[AccountID] [nchar](10) NOT NULL,
[SubmissionDate] [date] NOT NULL,
[Amount] [money] NOT NULL,
CONSTRAINT [PK_Accounts] PRIMARY KEY CLUSTERED (AccountID ASC))
INSERT INTO [dbo].[Accounts] VALUES ('1000', '2012-01-01', 1999.00)
INSERT INTO [dbo].[Accounts] VALUES ('1001', '2012-01-02', 100.00)
INSERT INTO [dbo].[Accounts] VALUES ('1002', '2012-02-05', 350.00)
INSERT INTO [dbo].[Accounts] VALUES ('1003', '2012-03-01', 625.00)
INSERT INTO [dbo].[Accounts] VALUES ('1004', '2012-03-10', 50.00)
INSERT INTO [dbo].[Accounts] VALUES ('1005', '2012-03-10', 10.00)
表2: Trans 跟踪付款
CREATE TABLE [dbo].[Trans](
[TranID] [int] IDENTITY(1,1) NOT NULL,
[AccountID] [nchar](10) NOT NULL,
[TranDate] [date] NOT NULL,
[TranAmount] [money] NOT NULL,
CONSTRAINT [PK_Trans] PRIMARY KEY CLUSTERED (TranID ASC))
INSERT INTO [dbo].[Trans] VALUES (1000, '2012-01-15', 300.00)
INSERT INTO [dbo].[Trans] VALUES (1000, '2012-02-15', 300.00)
INSERT INTO [dbo].[Trans] VALUES (1000, '2012-03-15', 300.00)
INSERT INTO [dbo].[Trans] VALUES (1002, '2012-02-20', 325.00)
INSERT INTO [dbo].[Trans] VALUES (1002, '2012-04-20', 25.00)
INSERT INTO [dbo].[Trans] VALUES (1003, '2012-03-24', 625.00)
INSERT INTO [dbo].[Trans] VALUES (1004, '2012-03-28', 31.00)
INSERT INTO [dbo].[Trans] VALUES (1004, '2012-04-12', 5.00)
INSERT INTO [dbo].[Trans] VALUES (1005, '2012-04-08', 7.00)
INSERT INTO [dbo].[Trans] VALUES (1005, '2012-04-28', 3.00)
这是所需的输出应该是什么样的
*Total Payments in Each Month*
SubmissionYearMonth TotalAmount | 2012-01 2012-02 2012-03 2012-04
--------------------------------------------------------------------
2012-01 2099.00 | 300.00 300.00 300.00 0.00
2012-02 350.00 | 325.00 0.00 25.00
2012-03 685.00 | 656.00 15.00
前两列按月分组Account.Amount分组。
对于在当前行的给定月份中放置的帐户,最后4列按月累计Tran.TranAmount。
我一直在使用的查询感觉很亲密。我只是没有正确的滞后。 这是我到目前为止所使用的查询:
Select SubmissionYearMonth,
TotalAmount,
pt.[0] AS MonthOld0,
pt.[1] AS MonthOld1,
pt.[2] AS MonthOld2,
pt.[3] AS MonthOld3,
pt.[4] AS MonthOld4,
pt.[5] AS MonthOld5,
pt.[6] AS MonthOld6,
pt.[7] AS MonthOld7,
pt.[8] AS MonthOld8,
pt.[9] AS MonthOld9,
pt.[10] AS MonthOld10,
pt.[11] AS MonthOld11,
pt.[12] AS MonthOld12,
pt.[13] AS MonthOld13
From (
SELECT Convert(Char(4),Year(SubmissionDate)) + '-' + Right('00' + Convert(VarChar(2), DatePart(Month, SubmissionDate)),2) AS SubmissionYearMonth,
SUM(Amount) AS TotalAmount
FROM Accounts
GROUP BY Convert(Char(4),Year(SubmissionDate)) + '-' + Right('00' + Convert(VarChar(2), DatePart(Month, SubmissionDate)),2)
)
AS AccountSummary
OUTER APPLY
(
SELECT *
FROM (
SELECT CASE WHEN DATEDIFF(Month, SubmissionDate, TranDate) < 13
THEN DATEDIFF(Month, SubmissionDate, TranDate)
ELSE 13
END AS PaymentMonthAge,
TranAmount
FROM Trans INNER JOIN Accounts ON Trans.AccountID = Accounts.AccountID
Where Convert(Char(4),Year(TranDate)) + '-' + Right('00' + Convert(VarChar(2), DatePart(Month, TranDate)),2)
= AccountSummary.SubmissionYearMonth
) as TransTemp
PIVOT (SUM(TranAmount)
FOR PaymentMonthAge IN ([0],
[1],
[2],
[3],
[4],
[5],
[6],
[7],
[8],
[9],
[10],
[11],
[12],
[13])) as TransPivot
) as pt
它产生以下输出:
SubmissionYearMonth TotalAmount MonthOld0 MonthOld1 MonthOld2 MonthOld3 ...
2012-01 2099.00 300.00 NULL NULL NULL ...
2012-02 350.00 325.00 300.00 NULL NULL ...
2012-03 685.00 656.00 NULL 300.00 NULL ...
列日期标题。我不确定这里最好的选择是什么。我可以添加一组额外的列并创建一个我可以在结果报告中使用的计算值。
答案 0 :(得分:5)
由于您使用的是SQL Server 2012,我们可以使用Format函数使日期非常漂亮。没有必要按字符串分组。相反,我发现尽可能长时间使用正确的数据类型并且仅在显示器上使用Format或Convert(或者根本不使用中间层处理显示器)是有用的。
在这个解决方案中,我随意地假设最早的TransDate并从中提取,即该月的第一天。但是,可以使用所需开始日期的静态值轻松替换该表达式,此解决方案将采用该期限和接下来的12个月。
With SubmissionMonths As
(
Select DateAdd(d, -Day(A.SubmissionDate) + 1, A.SubmissionDate) As SubmissionMonth
, A.Amount
From dbo.Accounts As A
)
, TranMonths As
(
Select DateAdd(d, -Day(Min( T.TranDate )) + 1, Min( T.TranDate )) As TranMonth
, 1 As MonthNum
From dbo.Accounts As A
Join dbo.Trans As T
On T.AccountId = A.AccountId
Join SubmissionMonths As M
On A.SubmissionDate >= M.SubmissionMonth
And A.SubmissionDate < DateAdd(m,1,SubmissionMonth)
Union All
Select DateAdd(m, 1, TranMonth), MonthNum + 1
From TranMonths
Where MonthNum < 12
)
, TotalBySubmissionMonth As
(
Select M.SubmissionMonth, Sum( M.Amount ) As Total
From SubmissionMonths As M
Group By M.SubmissionMonth
)
Select Format(SMT.SubmissionMonth,'yyyy-MM') As SubmissionMonth, SMT.Total
, Sum( Case When TM.MonthNum = 1 Then T.TranAmount End ) As Month1
, Sum( Case When TM.MonthNum = 2 Then T.TranAmount End ) As Month2
, Sum( Case When TM.MonthNum = 3 Then T.TranAmount End ) As Month3
, Sum( Case When TM.MonthNum = 4 Then T.TranAmount End ) As Month4
, Sum( Case When TM.MonthNum = 5 Then T.TranAmount End ) As Month5
, Sum( Case When TM.MonthNum = 6 Then T.TranAmount End ) As Month6
, Sum( Case When TM.MonthNum = 7 Then T.TranAmount End ) As Month7
, Sum( Case When TM.MonthNum = 8 Then T.TranAmount End ) As Month8
, Sum( Case When TM.MonthNum = 9 Then T.TranAmount End ) As Month9
, Sum( Case When TM.MonthNum = 10 Then T.TranAmount End ) As Month10
, Sum( Case When TM.MonthNum = 11 Then T.TranAmount End ) As Month11
, Sum( Case When TM.MonthNum = 12 Then T.TranAmount End ) As Month12
From TotalBySubmissionMonth As SMT
Join dbo.Accounts As A
On A.SubmissionDate >= SMT.SubmissionMonth
And A.SubmissionDate < DateAdd(m,1,SMT.SubmissionMonth)
Join dbo.Trans As T
On T.AccountId = A.AccountId
Join TranMonths As TM
On T.TranDate >= TM.TranMonth
And T.TranDate < DateAdd(m,1,TM.TranMonth)
Group By SMT.SubmissionMonth, SMT.Total
答案 1 :(得分:1)
以下查询几乎可以返回您想要的内容。您需要单独执行操作。我只是一起加入结果:
select a.yyyymm, a.Amount,
t201201, t201202, t201203, t201204
from (select LEFT(convert(varchar(255), a.submissiondate, 121), 7) as yyyymm,
SUM(a.Amount) as amount
from Accounts a
group by LEFT(convert(varchar(255), a.submissiondate, 121), 7)
) a left outer join
(select LEFT(convert(varchar(255), a.submissiondate, 121), 7) as yyyymm,
sum(case when trans_yyyymm = '2012-01' then tranamount end) as t201201,
sum(case when trans_yyyymm = '2012-02' then tranamount end) as t201202,
sum(case when trans_yyyymm = '2012-03' then tranamount end) as t201203,
sum(case when trans_yyyymm = '2012-04' then tranamount end) as t201204
from Accounts a join
(select t.*, LEFT(convert(varchar(255), t.trandate, 121), 7) as trans_yyyymm
from trans t
) t
on a.accountid = t.accountid
group by LEFT(convert(varchar(255), a.submissiondate, 121), 7)
) t
on a.yyyymm = t.yyyymm
order by 1
我得到一个NULL,你在两个单元格中有0.00。
答案 2 :(得分:1)
Thomas,我使用您的回复作为我最终使用的以下解决方案的灵感。
我首先创建一个SubmissionDate,TranDate交叉连接骨架日期矩阵,我后来用它来加入AccountSummary和TranSummary数据。
根据TranDate月,生成的查询输出未按列格式化。而是我在SQL Server Reporting Services矩阵中使用输出,并使用基于TranSummaryMonthNum列的列分组来获得所需的格式化输出。
;
WITH
--Generate a list of Dates, from the first SubmissionDate, through today.
--Note: Requires the use of: 'OPTION (MAXRECURSION 0)' to generate a list with more than 100 dates.
CTE_AutoDates AS
( Select Min(SubmissionDate) as FiscalDate
From Accounts
UNION ALL
SELECT DATEADD(Day, 1, FiscalDate)
FROM CTE_AutoDates
WHERE DATEADD(Day, 1, FiscalDate) <= GetDate()
),
FiscalDates As
( SELECT FiscalDate,
DATEFROMPARTS(Year(FiscalDate), Month(FiscalDate), 1) as FiscalMonthStartDate
FROM CTE_AutoDates
--Optionaly filter Fiscal Dates by the last known Math.Max(SubmissionDate, TranDate)
Where FiscalDate <= (Select Max(MaxDate)
From (Select Max(SubmissionDate) as MaxDate From Accounts
Union All
Select Max(TranDate) as MaxDate From Trans
) as MaxDates
)
),
FiscalMonths as
( SELECT Distinct FiscalMonthStartDate
FROM FiscalDates
),
--Matrix to store the reporting date groupings for the Account submission and payment periods.
SubmissionAndTranMonths AS
( Select AM.FiscalMonthStartDate as SubmissionMonthStartDate,
TM.FiscalMonthStartDate as TransMonthStartDate,
DateDiff(Month, (Select Min(FiscalMonthStartDate) From FiscalMonths), TM.FiscalMonthStartDate) as TranSummaryMonthNum
From FiscalMonths AS AM
Join FiscalMonths AS TM
ON TM.FiscalMonthStartDate >= AM.FiscalMonthStartDate
),
AccountData as
( Select A.AccountID,
A.Amount,
FD.FiscalMonthStartDate as SubmissionMonthStartDate
From Accounts as A
Inner Join FiscalDates as FD
ON A.SubmissionDate = FD.FiscalDate
),
TranData as
( Select T.AccountID,
T.TranAmount,
AD.SubmissionMonthStartDate,
FD.FiscalMonthStartDate as TranMonthStartDate
From Trans as T
Inner Join AccountData as AD
ON T.AccountID = AD.AccountID
Inner Join FiscalDates AS FD
ON T.TranDate = FD.FiscalDate
),
AccountSummaryByMonth As
( Select ASM.FiscalMonthStartDate,
Sum(AD.Amount) as TotalSubmissionAmount
From FiscalMonths as ASM
Inner Join AccountData as AD
ON ASM.FiscalMonthStartDate = AD.SubmissionMonthStartDate
Group By
ASM.FiscalMonthStartDate
),
TranSummaryByMonth As
( Select STM.SubmissionMonthStartDate,
STM.TransMonthStartDate,
STM.TranSummaryMonthNum,
Sum(TD.TranAmount) as TotalTranAmount
From SubmissionAndTranMonths as STM
Inner Join TranData as TD
ON STM.SubmissionMonthStartDate = TD.SubmissionMonthStartDate
AND STM.TransMonthStartDate = TD.TranMonthStartDate
Group By
STM.SubmissionMonthStartDate,
STM.TransMonthStartDate,
STM.TranSummaryMonthNum
)
--#Inspect 1
--Select * From SubmissionAndTranMonths
--OPTION (MAXRECURSION 0)
--#Inspect 1 Results
--SubmissionMonthStartDate TransMonthStartDate TranSummaryMonthNum
--2012-01-01 2012-01-01 0
--2012-01-01 2012-02-01 1
--2012-01-01 2012-03-01 2
--2012-01-01 2012-04-01 3
--2012-02-01 2012-02-01 1
--2012-02-01 2012-03-01 2
--2012-02-01 2012-04-01 3
--2012-03-01 2012-03-01 2
--2012-03-01 2012-04-01 3
--2012-04-01 2012-04-01 3
--#Inspect 2
--Select * From AccountSummaryByMonth
--OPTION (MAXRECURSION 0)
--#Inspect 2 Results
--FiscalMonthStartDate TotalSubmissionAmount
--2012-01-01 2099.00
--2012-02-01 350.00
--2012-03-01 685.00
--#Inspect 3
--Select * From TranSummaryByMonth
--OPTION (MAXRECURSION 0)
--#Inspect 3 Results
--SubmissionMonthStartDate TransMonthStartDate TranSummaryMonthNum TotalTranAmount
--2012-01-01 2012-01-01 0 300.00
--2012-01-01 2012-02-01 1 300.00
--2012-01-01 2012-03-01 2 300.00
--2012-02-01 2012-02-01 1 325.00
--2012-02-01 2012-04-01 3 25.00
--2012-03-01 2012-03-01 2 656.00
--2012-03-01 2012-04-01 3 15.00
Select STM.SubmissionMonthStartDate,
ASM.TotalSubmissionAmount,
STM.TransMonthStartDate,
STM.TranSummaryMonthNum,
TSM.TotalTranAmount
From SubmissionAndTranMonths as STM
Inner Join AccountSummaryByMonth as ASM
ON STM.SubmissionMonthStartDate = ASM.FiscalMonthStartDate
Left Join TranSummaryByMonth AS TSM
ON STM.SubmissionMonthStartDate = TSM.SubmissionMonthStartDate
AND STM.TransMonthStartDate = TSM.TransMonthStartDate
Order By STM.SubmissionMonthStartDate, STM.TranSummaryMonthNum
OPTION (MAXRECURSION 0)
--#Results
--SubmissionMonthStartDate TotalSubmissionAmount TransMonthStartDate TranSummaryMonthNum TotalTranAmount
--2012-01-01 2099.00 2012-01-01 0 300.00
--2012-01-01 2099.00 2012-02-01 1 300.00
--2012-01-01 2099.00 2012-03-01 2 300.00
--2012-01-01 2099.00 2012-04-01 3 NULL
--2012-02-01 350.00 2012-02-01 1 325.00
--2012-02-01 350.00 2012-03-01 2 NULL
--2012-02-01 350.00 2012-04-01 3 25.00
--2012-03-01 685.00 2012-03-01 2 656.00
--2012-03-01 685.00 2012-04-01 3 15.00
答案 3 :(得分:1)
以下查询完全复制your final query in your own answer的结果,但不超过CPU的1/30(或更好),加上更简单。
如果我有时间&amp;能量我相信我能找到更多的改进......我的直觉告诉我,我可能不必多次点击Accounts
表。但无论如何,这是一个巨大的进步,即使对于非常大的结果集也应该表现得非常好。
WITH L0 AS (SELECT 1 N UNION ALL SELECT 1),
L1 AS (SELECT 1 N FROM L0, L0 B),
L2 AS (SELECT 1 N FROM L1, L1 B),
L3 AS (SELECT 1 N FROM L2, L2 B),
L4 AS (SELECT 1 N FROM L3, L2 B),
Nums AS (SELECT N = Row_Number() OVER (ORDER BY (SELECT 1)) FROM L4),
Anchor AS (
SELECT MinDate = DateAdd(month, DateDiff(month, '20000101', Min(SubmissionDate)), '20000101')
FROM dbo.Accounts
),
MNums AS (
SELECT N
FROM Nums
WHERE
N <= DateDiff(month,
(SELECT MinDate FROM Anchor),
(SELECT Max(TranDate) FROM dbo.Trans)
) + 1
),
A AS (
SELECT
AM.AccountMo,
Amount = Sum(A.Amount)
FROM
dbo.Accounts A
CROSS APPLY (
SELECT DateAdd(month, DateDiff(month, '20000101', A.SubmissionDate), '20000101')
) AM (AccountMo)
GROUP BY
AM.AccountMo
), T AS (
SELECT
AM.AccountMo,
TM.TranMo,
TotalTranAmount = Sum(T.TranAmount)
FROM
dbo.Accounts A
CROSS APPLY (
SELECT DateAdd(month, DateDiff(month, '20000101', A.SubmissionDate), '20000101')
) AM (AccountMo)
INNER JOIN dbo.Trans T
ON A.AccountID = T.AccountID
CROSS APPLY (
SELECT DateAdd(month, DateDiff(month, '20000101', T.TranDate), '20000101')
) TM (TranMo)
GROUP BY
AM.AccountMo,
TM.TranMo
)
SELECT
SubmissionStartMonth = A.AccountMo,
TotalSubmissionAmount = A.Amount,
M.TransMonth,
TransMonthNum = N.N - 1,
T.TotalTranAmount
FROM
A
INNER JOIN MNums N
ON N.N >= DateDiff(month, (SELECT MinDate FROM Anchor), A.AccountMo) + 1
CROSS APPLY (
SELECT TransMonth = DateAdd(month, N.N - 1, (SELECT MinDate FROM Anchor))
) M
LEFT JOIN T
ON A.AccountMo = T.AccountMo
AND M.TransMonth = T.TranMo
ORDER BY
A.AccountMo,
M.TransMonth;