2013-04-24 56 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