SQL Server按日期范围分组

时间:2015-09-04 15:06:19

标签: sql sql-server

我正在使用SQL Server 2012,并且具有一系列由一组业务规则定义的日期。

CREATE TABLE #end_of_week_range
    ([id] int, [year] int, [week] int, [first_day] datetime, [last_day] datetime);

INSERT INTO #end_of_week_range
    ([id], [year], [week], [first_day], [last_day])
VALUES
    (1, 2014, 1, '2013-12-05 00:00:00', '2014-01-05 00:00:00'),
    (2, 2015, 1, '2014-12-04 00:00:00', '2015-01-04 00:00:00'),
    (3, 2014, 10, '2014-02-09 00:00:00', '2014-03-09 00:00:00'),
    (4, 2015, 10, '2015-02-08 00:00:00', '2015-03-08 00:00:00'),
    (5, 2014, 11, '2014-02-16 00:00:00', '2014-03-16 00:00:00'),
    (6, 2015, 11, '2015-02-15 00:00:00', '2015-03-15 00:00:00'),
    (7, 2014, 12, '2014-02-23 00:00:00', '2014-03-23 00:00:00'),
    (8, 2015, 12, '2015-02-22 00:00:00', '2015-03-22 00:00:00'),
    (9, 2014, 13, '2014-02-28 00:00:00', '2014-03-28 00:00:00'),
    (10, 2015, 13, '2015-02-28 00:00:00', '2015-03-28 00:00:00');
-- Many more, see SQLFiddle

带有日期的销售记录。

CREATE TABLE #sales (id INT, saledate DATETIME);

INSERT INTO #sales (id, saledate)
VALUES 
    (1, '2014-02-08'),
    (2, '2015-02-08'),
    (3, '2015-02-08'),
    (4, '2015-02-08'),
    (5, '2015-02-08'),
    (6, '2014-02-08'),
    (7, '2014-02-08'),
    (8, '2014-02-08'),
    (9, '2015-02-08'),
    (10, '2015-02-08'),
    (11, '2015-07-08');

我想计算每个范围内的销售记录数,并获得此预期产出。

year    week    cnt
2014    6   4
2015    6   0
2014    7   4
2015    7   6
2014    8   4
2015    8   6
2014    9   4
2015    9   6
-- More rows, see SQLFiddle

我现在正在做的是循环遍历每个范围并过滤日期范围中的第一天和最后一天。

CREATE TABLE #sales_trended (
    [year] INT
    ,[week] INT
    ,[cnt] INT
    )

DECLARE @id INT

WHILE EXISTS (
        SELECT *
        FROM #end_of_week_range
        )
BEGIN
    SELECT TOP 1 @id = id
    FROM #end_of_week_range

    INSERT INTO #sales_trended
    SELECT (
            SELECT year
            FROM #end_of_week_range
            WHERE id = @id
            ) AS year
        ,(
            SELECT week
            FROM #end_of_week_range
            WHERE id = @id
            ) AS week
        ,count(*) AS cnt
    FROM #sales s
    WHERE (
            s.saledate >= (
                SELECT first_day
                FROM #end_of_week_range
                WHERE id = @id
                )
            AND s.saledate < (
                SELECT last_day
                FROM #end_of_week_range
                WHERE id = @id
                )
            )

    DELETE #end_of_week_range
    WHERE id = @id
END

SELECT *
FROM #sales_trended

我可以改为加入销售和日期范围表以及按日期范围分组而不是循环显示每个日期范围吗?我目前的方法看起来非常缓慢。

http://sqlfiddle.com/#!6/aaec8/2

2 个答案:

答案 0 :(得分:2)

不确定我是否正确理解了问题,但是您不能加入具有日期范围的表格,如下所示:

select e.year, e.week, count(s.id)
from end_of_week_range e
left outer join sales s on 
  s.saledate >= e.first_day and 
  s.saledate < e.last_day
group by e.year, e.week
order by e.year, e.week

SQL Fiddle

编辑:糟糕,计数当然必须来自销售

答案 1 :(得分:2)

如果你正在使用SQL并且你认为你需要使用循环,那么你几乎总是在想错。

您可以通过简单的连接来完成此操作:

SELECT r.year,
    r.week,
    COUNT(s.id) as cnt
FROM end_of_week_range r
LEFT JOIN sales s
    ON  s.saledate >= r.first_day
    AND s.saledate <  r.last_day
GROUP BY r.year,
    r.week
ORDER BY r.year, 
    r.week