使用连接插入缺少的行

时间:2013-04-24 21:52:25

标签: sql sql-server tsql

我有一个返回此派生表的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 / YYYY(03 / 2012,66 / 2012,11 / 2012等)。

我想用猫与猫填写失踪的MM / YYYY得分为0(零)。

我试图加入一个包含查询将运行范围的所有MM / YYYY的表,但是这只会返回第一次出现的缺失行,而不会为每个Cat重复(应该知道的是)。

所以我的问题是这个,我可以使用连接执行此操作,还是必须在临时表中执行此操作,然后输出数据。

AHIGA, LarryR ...

4 个答案:

答案 0 :(得分: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
        )

虽然,在我看来,如果你有一个填充空白的查询,插入数据有点浪费时间。

答案 1 :(得分: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

答案 2 :(得分:0)

从“完整表”到“不完整表”进行左连接并设置where语句以检查“不完整”表的日期列。因此,您只会在选择查询中获得缺失的结果。之后,只需在之前设置“insert into tablename”。

在第一次运行中,它会找到两行,这些行尚未包含在不完整的表中。所以它将被insert into语句插入,受影响的两行。在第二次运行中,select语句中的结果有0行,所以没有任何反应。受影响的零行: - )

示例:http://sqlfiddle.com/#!2/895fe/6 (只需标记select语句;插入到语句不需要只看到,连接如何工作)

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

答案 3 :(得分: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