SQL:Where子句

5
SELECT DISTINCT Campaign_id 
FROM Impressions 
WHERE Date BETWEEN '2015-03-01' AND '2015-03-31' ;

上面的查询给出了在2015年3月1日至2015年3月31日期间任何一天活动过的Campaign_id的结果。
我希望结果集包含那些在2015年3月1日至2015年3月31日期间所有日期都活动过的campaign_id。
我该如何做?
2个回答

6
假设 DATE 是一个不带时间部分的日期数据类型。
DECLARE @Start DATE = '2015-03-01',
        @End   DATE = '2015-03-31'

SELECT Campaign_id
FROM   Impressions
WHERE  Date BETWEEN @Start AND @End
GROUP  BY Campaign_id
HAVING COUNT(DISTINCT Date) = 1 + DATEDIFF(DAY, @Start, @End); 

或者一个不带变量的版本

SELECT Campaign_id
FROM   Impressions
       CROSS APPLY (VALUES ({ d '2015-03-01' },
                            { d '2015-03-31' })) V([Start], [End])
WHERE  [Date] BETWEEN [Start] AND [End]
GROUP  BY Campaign_id, [Start], [End]
HAVING COUNT(DISTINCT Date) = 1 + DATEDIFF(DAY, [Start], [End]); 

运行良好,为我提供了想要的结果。如果您能告诉我最后一行是如何工作的就太好了,因为我没完全明白。 - Tauseef Hussain
2
如果他们在三月的每一天都活跃,那意味着他们在那个月里活跃了31个不同的日子,DATEDIFF只是避免了硬编码这个数字。 - Martin Smith
1
我的错!抱歉!那是一个错误,扇自己一巴掌!!不过感谢你指出来 :) - Tauseef Hussain

4

在使用 COUNT(DISTINCT) 时,可以结合 HAVING 子句:

SELECT Campaign_id 
FROM Impressions
WHERE Date between '2015-03-01' and '2015-03-31' 
GROUP BY Campaign_id
HAVING COUNT(DISTINCT Date) = 31;

您还应该查看Aaron Betrand的博客文章,了解为什么在日期查询中使用BETWEEN是一个坏主意。
您可以通过以下方式安排查询,只提及日期一次:
WITH params as (
      SELECT CAST('2015-03-01' as DATE) as date1, CAST('2015-03-31' as DATE) date2
     )
SELECT i.Campaign_id
FROM params CROSS JOIN
     Impressions i
WHERE i.Date >= params.Date1 and i.Date < DATEADD(day, 1, params.Date2)
GROUP BY i.Campaign_id, params.date1, params.date2
HAVING COUNT(DISTINCT i.Date) = 1 + DATEDIFF(day, params.date1, params.date2);

注意:有人会更喜欢在这种情况下使用 JOIN 而不是 CROSS JOIN。出于习惯,我总是在使用 CROSS JOIN 的查询中放置一个参数 CTE。

2
使用 BETWEEN 进行日期查询没有问题,问题只存在于使用日期时间(datetimes)时。 - t-clausen.dk
非常完美,谢谢。 - Tauseef Hussain

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接