我试图编写一个存储过程,根据月份对行进行分组,如果存在,则返回所有项目的总和,如果不存在则返回0。
对于查询的日期部分,我想要获得的是今天的日期 - 提取月份并返回5个月以收集任何数据(如果存在)。
在这个阶段,查询运行正常,但我想知道是否有任何方法可以优化它,因为看起来我一遍又一遍地运行同一组数据并且它在某种程度上也是硬编码的。
我想要实现的数据集如下:
Month TotalAmount TotalCount
-----------------------------------
2017-11 0 0
2017-12 200.00 2
2018-01 300.00 3
2018-02 0 0
2018-03 300.00 3
2018-04 100.00 1
使用下面的查询,我能够实现我想要的效果,但正如您所看到的,它在过去的5个月内难以编码,所以如果我想回到12个月,我会这样做必须添加更多代码。
DECLARE @5MonthAgo date = CAST(DATEADD(MONTH, -5, GETDATE()) + 1 - DATEPART(DAY, DATEADD(MONTH, -5, GETDATE())) AS DATE)
DECLARE @4MonthAgo date = CAST(DATEADD(MONTH, -4, GETDATE()) + 1 - DATEPART(DAY, DATEADD(MONTH, -4, GETDATE())) AS DATE)
DECLARE @3MonthAgo date = CAST(DATEADD(MONTH, -3, GETDATE()) + 1 - DATEPART(DAY, DATEADD(MONTH, -3, GETDATE())) AS DATE)
DECLARE @2MonthAgo date = CAST(DATEADD(MONTH, -2, GETDATE()) + 1 - DATEPART(DAY, DATEADD(MONTH, -2, GETDATE())) AS DATE)
DECLARE @1MonthAgo date = CAST(DATEADD(MONTH, -1, GETDATE()) + 1 - DATEPART(DAY, DATEADD(MONTH, -1, GETDATE())) AS DATE)
DECLARE @CurrentMonth date = CAST(GETDATE() + 1 - DATEPART(DAY, GETDATE()) AS DATE)
-- Table to return grouped and sum data
DECLARE @StatsTable TABLE ([Month] DATE,
[Total Amount] DECIMAL(18,2),
[Total Count] INT
)
-- Temporary table to hold onto data batch - so table isn't used later on
DECLARE @TempGenTable TABLE ([Id] INT,
[Date] DATETIME,
[Lines] INT NULL,
[Amount] DECIMAL(18, 2) NULL
)
INSERT INTO @TempGenTable
SELECT
Id, Date, Lines, Amount
FROM
TallyTable
WHERE
Date >= @5MonthAgo
INSERT INTO @StatsTable
SELECT
@5MonthAgo,
COALESCE((SELECT SUM(Amount)
FROM @TempGenTable
WHERE Date >= @5MonthAgo AND Date < @4MonthAgo
GROUP BY DATEADD(MONTH, DATEDIFF(MONTH, 0, Date), 0)), 0),
COALESCE((SELECT COUNT(Id)
FROM @TempGenTable
WHERE Date >= @5MonthAgo AND Date < @4MonthAgo
GROUP BY DATEADD(MONTH, DATEDIFF(MONTH, 0, Date), 0)), 0)
UNION
SELECT
@4MonthAgo,
COALESCE((SELECT SUM(Amount)
FROM @TempGenTable
WHERE Date >= @4MonthAgo AND Date < @3MonthAgo
GROUP BY DATEADD(MONTH, DATEDIFF(MONTH, 0, Date), 0)), 0),
COALESCE((SELECT COUNT(Id)
FROM @TempGenTable
WHERE Date >= @4MonthAgo AND Date < @3MonthAgo
GROUP BY DATEADD(MONTH, DATEDIFF(MONTH, 0, Date), 0)), 0)
...
是否有更简单的方法可以在几个月内更灵活地获取上述数据?
最好让查询在一个月份变量中传递,它只检查当前月份,并在控制器中有一个循环返回x个月?
答案 0 :(得分:0)
我会使用递归CTE生成数据,然后使用left join
:
with months as (
select datefromparts(year(getdate()), month(getdate()), 1) as month_start, 5 as n
union all
select dateadd(month, -1, month_start), n - 1
from months
where n > 0
)
select m.month_start, count(s.id), sum(s.amount)
from months m left join
@StatsTable s
on m.month_start = s.month
group by m.month_start
order by m.month_start;
您尚未提供样本数据,因此我不确定s.month
的样子。您可能希望连接条件为:
on s.month >= m.month_start and s.month < dateadd(month, 1, m.month_start)
答案 1 :(得分:0)
以下是基于集合的方法,用于生成所需的月度期间:
--sample data
CREATE TABLE dbo.TallyTable (
Id int
, Date datetime
, Lines int
, Amount decimal(18, 2)
);
INSERT INTO dbo.TallyTable
VALUES
(1, '2017-12-05', 1, 50.00)
,(2, '2017-12-06', 1, 150.00)
,(3, '2018-01-10', 1, 100.00)
,(4, '2018-01-11', 1, 100.00)
,(5, '2018-01-12', 1, 100.00)
,(6, '2018-03-15', 1, 225.00)
,(7, '2018-03-15', 1, 25.00)
,(8, '2018-03-15', 1, 50.00)
,(9, '2018-04-20', 1, 100.00);
GO
DECLARE @Months int = 5; --number of historical months
WITH
t10 AS (SELECT n FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) t(n))
,t100 AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0)) - 1 AS num FROM t10 AS a CROSS JOIN t10 AS b)
, periods AS (SELECT
CONVERT(varchar(7), DATEADD(month, DATEDIFF(month, '', GETDATE()) - num, ''),121) AS Month
, DATEADD(month, DATEDIFF(month, '', CAST(GETDATE() AS date)) - num, '') AS PeriodStart
, DATEADD(month, DATEDIFF(month, '', CAST(GETDATE() AS date)) - num + 1, '') AS NextPeriodStart
FROM t100
WHERE num <= @Months
)
SELECT periods.Month, COALESCE(SUM(Amount), 0) AS TotalAmount, COALESCE(COUNT(ID), 0) AS TotalCount
FROM periods
LEFT JOIN dbo.TallyTable ON
TallyTable.Date >= PeriodStart
AND TallyTable.Date < NextPeriodStart
GROUP BY periods.Month
ORDER BY periods.Month;