2012-05-15 38 views
1

我想在SELECT聲明中填寫缺失的月份,所以我打算將我的表與另一個包含所有月份的表結合起來。我怎樣才能以輕量級的方式生成一個月表?例如,在兩個日期之間生成月份?

CREATE TABLE #TEMP(Timewhen DATETIME, Value INT) 

INSERT INTO #TEMP VALUES('2012-02-04', 4) 
INSERT INTO #TEMP VALUES('2012-02-06', 4) 
INSERT INTO #TEMP VALUES('2012-02-10', 4) 
INSERT INTO #TEMP VALUES('2012-04-08', 4) 
INSERT INTO #TEMP VALUES('2012-04-12', 4) 

SELECT YEAR(Timewhen) EventYear, MONTH(Timewhen) EventMonth, SUM(Value) Total 
FROM #TEMP 
GROUP BY YEAR(Timewhen), MONTH(Timewhen) 

DROP TABLE #TEMP 

給我:

EventYear EventMonth Total 
2012    2 12 
2012    4 8 

但我需要:

EventYear EventMonth Total 
2012  2   12 
2012  3   0 
2012  4   8 
+1

你爲什麼關心如何生成的數據,即什麼是「輕量級」是什麼意思?希望你打算創建一個永久性的日曆表或數字表,你可以在所有查詢中使用,而不僅僅是這一個?儘管所有的數據庫都應該有這兩個表格,因爲它們有很多用途。 – Pondlife

回答

2
DECLARE @months  INT, 
     @firstmonth DATE; 

SELECT 
    @months  = DATEDIFF(MONTH, MIN(Timewhen), MAX(Timewhen)) + 1, 
    @firstmonth = DATEADD(DAY, 1-DAY(MIN(Timewhen)), MIN(Timewhen)) 
FROM #temp; 

;WITH m(rn) AS 
(
    SELECT TOP (@months) rn = ROW_NUMBER() OVER (ORDER BY [object_id]) 
    FROM sys.objects ORDER BY rn 
), 
x(d) AS 
(
    SELECT DATEADD(MONTH, rn-1, @firstmonth) FROM m 
) 
SELECT YEAR(x.d), MONTH(x.d), Total = SUM(COALESCE(t.Value, 0)) 
    FROM x 
    LEFT OUTER JOIN #temp AS t 
    ON t.Timewhen >= x.d AND t.Timewhen < DATEADD(MONTH, 1, x.d) 
    GROUP BY YEAR(x.d), MONTH(x.d); 

或者一個稍微不那麼詳細的版本:使用日曆表

DECLARE @months  INT, 
     @firstmonth DATE; 

SELECT 
    @months  = DATEDIFF(MONTH, MIN(Timewhen), MAX(Timewhen)) + 1, 
    @firstmonth = DATEADD(DAY, 1-DAY(MIN(Timewhen)), MIN(Timewhen)) 
FROM #temp; 

;WITH x(y, m, s, e) AS 
(
    SELECT YEAR(dt), MONTH(dt), dt, DATEADD(MONTH, 1, dt) FROM 
    (SELECT dt = DATEADD(MONTH, rn-1, @firstmonth) FROM 
    (SELECT TOP (@months) rn = ROW_NUMBER() OVER (ORDER BY [object_id]) 
     FROM sys.objects ORDER BY rn 
    ) AS z 
) AS y 
) 
SELECT EventYear = x.y, EventMonth = x.m, Total = SUM(COALESCE(t.Value, 0)) 
    FROM x LEFT OUTER JOIN #temp AS t 
    ON t.Timewhen >= x.s AND t.Timewhen < x.e 
    GROUP BY x.y, x.m; 

替代的解決方案:

here創建使用指令的日曆表。

-- Script to create a calendar table 

DROP TABLE dbo.Numbers 
DROP TABLE dbo.Calendar 
GO 

-- Use this to determine the number in the next query 
DECLARE @NUMDAYS int 
SELECT @NUMDAYS = DATEDIFF(DAY, '20000101', '20291231') 

CREATE TABLE dbo.Numbers 
( 
    Number INT IDENTITY(1,1) PRIMARY KEY CLUSTERED 
) 

WHILE COALESCE(SCOPE_IDENTITY(), 0) <= @NUMDAYS 
BEGIN 
    INSERT dbo.Numbers DEFAULT VALUES 
END 
GO 

CREATE TABLE dbo.Calendar 
( 
    dt SMALLDATETIME NOT NULL 
     PRIMARY KEY CLUSTERED, 

    isWeekday BIT, 
    isHoliday BIT, 
    Y SMALLINT, 
    FY SMALLINT, 
    Q TINYINT, 
    M TINYINT, 
    D TINYINT, 
    DW TINYINT, 
    monthname VARCHAR(9), 
    dayname VARCHAR(9), 
    W TINYINT , 
    HolidayDescription VARCHAR(32) 
) 
GO 



INSERT Calendar(dt) 
SELECT DATEADD(DAY, Number, '20000101') 
FROM dbo.Numbers 
--WHERE Number <= @NUMDAYS 
ORDER BY Number 

GO 

--SELECT * FROM Calendar 

UPDATE dbo.Calendar SET 

    isWeekday = CASE 
     WHEN DATEPART(DW, dt) IN (1,7) 
     THEN 0 
     ELSE 1 END, 

    isHoliday = 0, 

    Y = YEAR(dt), 

    FY = YEAR(dt), 

    /* 
    -- if our fiscal year 
    -- starts on May 1st: 

    FY = CASE 
     WHEN MONTH(dt) < 5 
     THEN YEAR(dt)-1 
     ELSE YEAR(dt) END, 
    */ 

    Q = CASE 
     WHEN MONTH(dt) <= 3 THEN 1 
     WHEN MONTH(dt) <= 6 THEN 2 
     WHEN MONTH(dt) <= 9 THEN 3 
     ELSE 4 END, 

    M = MONTH(dt), 

    D = DAY(dt), 

    DW = DATEPART(DW, dt), 

    monthname = DATENAME(MONTH, dt), 

    dayname = DATENAME(DW, dt), 

    W = DATEPART(WK, dt) 

GO 

創建Calendar表後,可以使用以下方法來實現這一目標:

CREATE TABLE #TEMP(Timewhen DATETIME, Value INT) 

INSERT INTO #TEMP VALUES('2012-02-04', 4) 
INSERT INTO #TEMP VALUES('2012-02-06', 4) 
INSERT INTO #TEMP VALUES('2012-02-10', 4) 
INSERT INTO #TEMP VALUES('2012-04-08', 4) 
INSERT INTO #TEMP VALUES('2012-04-12', 4) 

SELECT Y EventYear, M EventMonth, SUM(Value) Total 
FROM #TEMP RIGHT OUTER JOIN (SELECT DISTINCT Y,M FROM dbo.dateRange('20120204', '20120412')) X 
ON YEAR(Timewhen) = X.Y AND MONTH(Timewhen) = X.M 
GROUP BY Y,M 

DROP TABLE #TEMP 
+0

+1謝謝你的時間。我接受這個,但我真的很喜歡你作爲評論添加到我的問題的鏈接,所以我最終使用它。 – Legend

+0

@Legend感謝,如果你可以更具體地瞭解哪個鏈接和哪個代碼示例,我可以將它添加到我的答案中,以防萬一這個答案超過了該網站(我不再控制它,所以我不知道它有多長時間將保持並保持完好)。 –

+0

是的。肯定。這是它:http:// sqlserver2000。databases.aspfaq.com/why-should-i-consider-using-an-auxiliary-calendar-table.html創建表後,我做了一個'RIGHT OUTER JOIN'來獲得必要的結果。 – Legend

3

使用SQL Server中,我會做這樣的事情:

select dateadd(month, num, firstdate) as mon 
from (select row_number() over (partition by NULL order by (select NULL)) as num 
     from Information_Schema.Columns c 
    ) n cross join 
    (select cast('2001-01-01' as date) firstdate) const 

這將創建從第一次約會起一堆月。我只是使用列表來讓我生成一個序列。

+0

由於其速度和簡單性,我非常喜歡這個答案,但是我得到以下錯誤'給'日期'列添加值導致溢出' - 我無法完全找到一種方法讓它停止在給定日期。 – Morvael

+0

@Morvael。 。 。只需添加一個最大數量的「where」子句。 。 。 「那裏的日期<1200',說。 –

2

你可以聲明對端點構築起sys.messages表中的年/月列表:

DECLARE @EventStart datetime='1/1/2001'; 
DECLARE @EventEnd datetime='12/31/2012'; 

SELECT TOP (DATEDIFF(MONTH,@EventStart,@EventEnd)+1) 
EventYear=DATEPART(YEAR,DATEADD(MONTH,ROW_NUMBER()OVER(ORDER BY message_id)-1,@EventStart)) 
, EventMonth=DATEPART(MONTH,DATEADD(MONTH,ROW_NUMBER()OVER(ORDER BY message_id)-1,@EventStart)) 
FROM sys.messages m; 

結果:

EventYear EventMonth 
----------- ----------- 
2001  1 
2001  2 
2001  3 
2001  4 
2001  5 
. 
. 
. 
2012  7 
2012  8 
2012  9 
2012  10 
2012  11 
2012  12 
0

---如果您正在使用ssrs報告或創建性能指標儀表板,請創建

---當前或上一個日曆年的月份下拉,這裏是您的T-SQL腳本。

DROP TABLE #Numbers 
DROP TABLE #Calendar 
------------------------------------------------------------------------------- 
-- Use this to determine the number in the next query 
DECLARE @NUMDAYS int 
DECLARE @start_date DATETIME, @end_date DATETIME 
DECLARE @year VARCHAR(50) 
declare @year_num AS VARCHAR(50) 
SET @year_num = 'current' ----'previous' 
-------------------------------------------------------------------------------  
    SET @start_date = CASE @year_num 
    WHEN 'current' THEN DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0) 
    WHEN 'previous' THEN DATEADD(yy, -1, DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0)) 
    ELSE DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0) 
    END 
    SET @end_date = CASE @year_num 
    WHEN 'current' THEN GETDATE() ----DATEADD(yy, DATEDIFF(yy, -1, GETDATE()), -1) 
    WHEN 'previous' THEN DATEADD(YY, -1, DATEADD(yy, DATEDIFF(yy, -1, GETDATE()), -1)) 
    ELSE DATEADD(yy, DATEDIFF(yy, -1, GETDATE()), -1) 
    END 

    SET @year = CASE @year_num 
    WHEN 'current' THEN YEAR(GETDATE()) 
    WHEN 'previous' THEN YEAR(DATEADD(yy, -1, GETDATE())) 
    ELSE YEAR(GETDATE()) 
    END 

SELECT @NUMDAYS = DATEDIFF(DAY, @start_date, @end_date) 

CREATE TABLE #Numbers 
( 
Number INT IDENTITY(1,1) PRIMARY KEY CLUSTERED 
) 

WHILE COALESCE(SCOPE_IDENTITY(), 0) <= @NUMDAYS 
BEGIN 
    INSERT #Numbers DEFAULT VALUES 
END 

CREATE TABLE #Calendar 
( 
dt SMALLDATETIME NOT NULL 
    PRIMARY KEY CLUSTERED, 

Y SMALLINT, 
Q TINYINT, 
M TINYINT, 
monthname VARCHAR(9) 
) 

INSERT #Calendar(dt) 
SELECT DATEADD(DAY, Number, @start_date) 
FROM #Numbers 
WHERE Number <= @NUMDAYS 
ORDER BY Number 

UPDATE #Calendar 
SET Y = YEAR(dt), 
    Q = CASE WHEN MONTH(dt) <= 3 THEN 1 
      WHEN MONTH(dt) <= 6 THEN 2 
      WHEN MONTH(dt) <= 9 THEN 3 
      ELSE 4 END, 
    M = MONTH(dt), 
    monthname = convert(varchar(3),datename(month,dt))----DATENAME(MONTH, dt) 

select distinct Y,M,monthname 
from #Calendar 
WHERE Y = @year 
order by M 
0
SELECT MIN (to_date((TO_CHAR (Actual_Date, 'DD-MM-RRRR')),'dd-mm-rrrr')) F_DATE, 
     MAX (to_date((TO_CHAR (Actual_Date, 'DD-MM-RRRR')),'dd-mm-rrrr')) T_DATE, 
     TO_CHAR (Actual_Date, 'MM-RRRR') MONTH 
    FROM ( SELECT TRUNC (TO_DATE (:P_FDATE, 'dd-mm-rrrr')) + LEVEL - 1 
         Actual_Date 
       FROM (SELECT TRUNC (TO_DATE (:P_FDATE, 'dd-mm-rrrr'), 'MM') - 1 
           AS dt 
         FROM DUAL) 
      CONNECT BY LEVEL <= 
         ( TO_DATE (:P_TDATE, 'dd-mm-rrrr') 
         - TRUNC (TO_DATE (:P_FDATE, 'dd-mm-rrrr')) 
         + 1)) 
GROUP BY TO_CHAR (Actual_Date, 'MM-RRRR') 
ORDER BY 1; 
相關問題