2013-04-24 51 views
3

我有一個返回這個派生表的SQL腳本。用連接插入缺失的行

MM/YYYY Cat Score 
01/2012 Test1 17 
02/2012 Test1 19 
04/2012 Test1 15 
05/2012 Test1 16 
07/2012 Test1 14 
08/2012 Test1 15 
09/2012 Test1 15 
12/2012 Test1 11 
01/2013 Test2 10 
02/2013 Test2 15 
03/2013 Test2 13 
05/2013 Test2 18 
06/2013 Test2 14 
08/2013 Test2 15 
09/2013 Test2 14 
12/2013 Test2 10 

正如您所見,我錯過了一些MM/YYYYs(03/2012,06/2012,11/2012等)。

我想用Cat &填寫缺失的MM/YYYY,得到0(零)。

我試圖加入一個包含所有MM/YYYY的查詢將運行的範圍的表,但這隻會返回第一次出現的缺失行,它不會爲每個Cat重複(應該已知那)。

所以我的問題是這樣的,我可以使用連接來做到這一點,或者我必須在臨時表中執行此操作,然後輸出數據。

AHIGA, LarryR ...

回答

0

嘗試這一個 -

DECLARE @temp TABLE (FDOM DATETIME, Cat NVARCHAR(50), Score INT) 

INSERT INTO @temp (FDOM, Cat, Score) 
VALUES 
    ('20120101', 'Test1', 17),('20120201', 'Test1', 19), 
    ('20120401', 'Test1', 15),('20120501', 'Test1', 16), 
    ('20120701', 'Test1', 14),('20120801', 'Test1', 15), 
    ('20120901', 'Test1', 15),('20121001', 'Test1', 13), 
    ('20121201', 'Test1', 11),('20130101', 'Test1', 10), 
    ('20130201', 'Test1', 15),('20130301', 'Test1', 13), 
    ('20130501', 'Test1', 18),('20130601', 'Test1', 14), 
    ('20130801', 'Test1', 15),('20130901', 'Test1', 14), 
    ('20131201', 'Test1', 10),('20120601', 'Test2', 10) 

;WITH enum AS 
(
    SELECT Cat, StartDate = MIN(FDOM), EndDate = MAX(FDOM) 
    FROM @temp 
    GROUP BY Cat 

    UNION ALL 

    SELECT Cat, DATEADD(MONTH, 1, StartDate), EndDate 
    FROM enum 
    WHERE StartDate < EndDate 
) 
SELECT e.StartDate, t.Cat, Score = ISNULL(t.Score, 0) 
FROM enum e 
LEFT JOIN @temp t ON e.StartDate = t.FDOM AND e.Cat = t.Cat 
ORDER BY e.StartDate, t.Cat 
+1

雖然範圍很小,但性能差異並不明顯,但一般情況下,使用遞歸CTE是生成順序列表的不好方法。 [這篇關於SQL性能的文章](http://www.sqlperformance.com/2013/01/t-sql-queries/generate-a-set-2)顯示了可用於在SQL中生成順序列表的各種方法的測試-服務器。 – GarethD 2013-04-25 08:01:11

+0

這也失敗了,因爲如果在'@ temp'表中添加了另一行('20120601','Test2',10)',那麼結果將不再考慮test1的'20120601'丟失,即使那裏在2012年6月沒有測試記錄。 – GarethD 2013-04-25 08:04:56

+0

@GarethD,謝謝你的評論。不過,最初的問題是解決這個任務。我同意你的看法,即在某些情況下使用CTE並不是最佳選擇。但是,對於這個特定的任務,我們不會展望太多,以便生成一個超過50,000個值的序列。從這個角度來看,我們甚至可能看不到查詢生產力的下降。 – Devart 2013-04-25 09:16:28

0

爲了得到你想要的東西,開始一個驅動表,然後使用left outer join。結果是這樣的:

select driver.cat, driver.MMYYYY, coalesce(t.score, 0) as score 
from (select cat, MMYYYY 
     from (select distinct cat from t) c cross join 
      themonths -- use where to get a date range 
    ) driver left outer join 
    t 
    on t.cat = driver.cat and t.MMMYYYY = driver.MMYYYY 
7

你需要交叉加入你的類別和範圍內的所有日期列表。既然你已經張貼沒有表結構我要在你輕微的結構猜測,但假設你有一個日曆表,你可以使用這樣的事情:

SELECT calendar.Date, 
     Category.Cat, 
     Score = ISNULL(Scores.Score, 0) 
FROM Calendar 
     CROSS JOIN Catogory 
     LEFT JOIN Scores 
      ON Scores.Cat = Category.Cat 
      AND Scores.Date = Calendar.Date 
WHERE Calendar.DayOfMonth = 1; 

如果你沒有一個日曆表,你可以生成使用該系統表Master..spt_values日期列表:

SELECT Date = DATEADD(MONTH, Number, '20120101') 
    FROM Master..spt_values 
    WHERE Type = 'P'; 

凡硬編碼的日期「20120101」是在你的範圍內的第一個日期。

附錄

如果您需要實際插入缺少的行,而不是隻具有在空白填補查詢您可以使用此:

INSERT Scores (Date, Cat, Score) 
SELECT calendar.Date, 
     Category.Cat, 
     Score = 0 
FROM Calendar 
     CROSS JOIN Catogory 
WHERE Calendar.DayOfMonth = 1 
AND  NOT EXISTS 
     ( SELECT 1 
      FROM Scores 
      WHERE Scores.Cat = Category.Cat 
      AND  Scores.Date = Calendar.Date 
     ) 

雖然,在如果我的觀點你有一個填充空白的查詢插入數據是有點浪費時間。

+0

GarethD,感謝您的輸入,但是這是一個表從SP返回和我沒有改變它的選項。同樣,像使用spt_values一樣,對我來說也是新的。 – larryr 2013-04-25 20:35:33

+0

完美的答案,謝謝! – Craig 2014-01-16 16:20:45

0

執行從「完整表」到「不完整表」的左連接,並設置where語句以檢查「不完整」表的日期列。所以你只會在你的選擇查詢中得到缺失的結果。之後,只需設置一個「insert into tablename」即可。

在第一次運行中,它會找到兩行,這些行不在不完整的表中。所以它會被insert into聲明插入,兩行受到影響。在第二次運行中,select語句中的結果有0行,所以沒有任何反應。受影響的:-)零行

樣品:http://sqlfiddle.com/#!2/895fe/6 (只標出select語句; INSERT INTO語句不需要只是看看,如何參加工作)

Insert Into supportContacts 


Select * FROM 

(

Select 
'01/2012' as DDate, 'Test1' as Cat, 17 as Score 
UNION 
Select 
'02/2012' as DDate, 'Test1' as Cat, 17 as Score 
UNION 
Select 
'03/2012' as DDate, 'Test1' as Cat, 17 as Score 
UNION 
Select 
'04/2012' as DDate, 'Test1' as Cat, 17 as Score 
UNION 
Select 
'05/2012' as DDate, 'Test1' as Cat, 17 as Score 

) CompleteTable 

LEFT JOIN 

(

Select 
'01/2012' as DDate, 'Test1' as Cat, 17 as Score 
UNION 
Select 
'02/2012' as DDate, 'Test1' as Cat, 17 as Score 
UNION 
Select 
'03/2012' as DDate, 'Test1' as Cat, 17 as Score 


) InCompleteTable 

ON CompleteTable.DDate = IncompleteTable.DDate 

WHERE IncompleteTable.DDate is null