這是另一種利用計數表(又名數字表)創建日期表的方法。注意我的意見。
-- Build the source data table.
declare @t table(Name nvarchar(10), InsertDate date, Size int);
insert into @t values
('john','20150630',1 )
,('john','20160110',10 )
,('john','20160112',100)
,('john','20160305',1000)
,('doe' ,'20160101',1 );
-- A year is fine, don't need a date data type
declare @year smallint = 2016;
WITH -- dummy rows for a tally table:
E AS (SELECT E FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) t(e)),
dateRange(totalDays, mn, mx) AS -- Get the range and number of months to create
(
SELECT DATEDIFF(MONTH, MIN(InsertDate), MAX(InsertDate)), MIN(InsertDate), MAX(InsertDate)
FROM @t
),
iTally(N) AS -- Tally Oh! Create an inline Tally (aka numbers) table starting with 0
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1))-1
FROM E a CROSS JOIN E b CROSS JOIN E c CROSS JOIN E d
),
RunningTotal AS -- perform a running total by year/month for each person (Name)
(
SELECT
yr = YEAR(DATEADD(MONTH, n, mn)),
mo = MONTH(DATEADD(MONTH, n, mn)),
Name,
Size = SUM(Size) OVER
(PARTITION BY Name ORDER BY YEAR(DATEADD(MONTH, n, mn)), MONTH(DATEADD(MONTH, n, mn)))
FROM iTally
CROSS JOIN dateRange
LEFT JOIN @t ON MONTH(InsertDate) = MONTH(DATEADD(MONTH, n, mn))
WHERE N <= totalDays
) -- Final output will only return rows where the year matches @year:
SELECT
name = ISNULL(name, LAG(Name, 1) OVER (ORDER BY yr, mo)),
yr, mo,
size = ISNULL(Size, LAG(Size, 1) OVER (ORDER BY yr, mo))
FROM RunningTotal
WHERE yr = @year
GROUP BY yr, mo, name, size;
結果:
name yr mo size
---------- ----------- ----------- -----------
doe 2016 1 1
john 2016 1 111
john 2016 2 111
john 2016 3 1111
你需要,你可以使用外加入日曆表。請參見[this](https://www.mssqltips.com/sqlservertip/4054/creating-a-date-dimension-or-calendar-table-in-sql-server/)或[this](https:// sqlperformance的.com/2013/01/T-SQL查詢/生成-A-設置-3)。 –
其實我想提供一些靜態值(幾個月)作爲左外連接表,但無法找到它的方式。 – sotn
什麼是靜態值?另請注意,如果您的客戶在過去的2年中要求您提供查詢,那麼您的查詢應該可以正常工作,因此您需要將其變爲動態幷包含年份。一旦你有日曆表,這並不難。您只需按年份+月份進行分組,並通過子查詢選擇「MIN(InsertDate)'和'MAX(InsertDate)'來獲取開始日期和結束日期。 –