Каков наиболее эффективный способ создания сводки количества заказов по часам, дням и месяцам в SQL Server 2005? - PullRequest
3 голосов
/ 24 июня 2009

С учетом таблицы:

create table #orders (
    orderid int,
    orderdatetime datetime
)

Как лучше всего написать sql для вывода отчета, содержащего количество заказов за текущий и предыдущие 24 часа, общее количество заказов за текущий день и предыдущие 7 дней, общее количество заказов за текущую неделю и предыдущие 4 недели, и общее количество заказов за месяц и предыдущие 6 месяцев?

Мне интересно, может ли это быть эффективно свернуто в один SQL с помощью аналитических функций, или 4 оператора SQL, генерирующие 4 группы данных, являются единственным (или лучшим) способом.

Кроме того, учитывая группировку по часам / дням / неделям, как это можно сделать на сервере sql? Мне кажется, что даты - это боль в заднице каждый раз, когда мне приходится что-то делать с ними ...

Идеи? Поместить в куб SSAS и сделать это оттуда, может быть?

Ответы [ 6 ]

6 голосов
/ 24 июня 2009
SELECT  DATEPART(month, orderdatetime), DATEPART(week, orderdatetime), DATEPART(day, orderdatetime), COUNT(*)
FROM    #orders
GROUP BY
        DATEPART(month, orderdatetime), DATEPART(week, orderdatetime), DATEPART(day, orderdatetime) WITH ROLLUP

Это сгруппирует COUNT по дням, неделям и месяцам в одном запросе.

Недельные сводки будут иметь столбец NULL в DATEPART(day, orderdatetime), месячные сводки будут иметь NULL в столбцах DATEPART(day, orderdatetime) и DATEPART(week, orderdatetime).

Чтобы сделать это для каждого часа, дня, недели или месяца из текущего без пробелов, используйте CTE s:

WITH    q_hours AS
        (
        SELECT  0 AS col_hour
        UNION ALL
        SELECT  col_hour + 1
        FROM    q_hours
        WHERE   col_hour < 22
        ),
        q_days AS
        (
        SELECT  0 AS col_day
        UNION ALL
        SELECT  col_day + 1
        FROM    q_days
        WHERE   col_day < 31
        ),
        q_months AS
        (
        SELECT  0 AS col_month
        UNION ALL
        SELECT  col_month + 1
        FROM    q_months
        WHERE   col_month < 12
        )
SELECT  col_month, col_day, col_hour, COUNT(orderid)
FROM    q_hours
CROSS JOIN
        q_days
CROSS JOIN
        q_months
LEFT JOIN
        #orders
ON      DATEDIFF(month, orderdatetime, GETDATE()) = col_month
        AND DATEDIFF(day, orderdatetime, GETDATE()) % 31 = col_day
        AND DATEDIFF(hour, orderdatetime, GETDATE()) % 24 = col_hour
GROUP BY
        col_month, col_day, col_hour WITH ROLLUP
HAVING  (
        col_month = 0
        AND col_day = 0
        AND col_hour IS NOT NULL
        ) -- all hours within 24 hours from now
        OR
        (
        col_month = 0
        AND col_day <= 7
        AND col_hour IS NULL
        ) -- all days within 7 days from now
        OR
        (
        col_month <= 6
        AND col_day IS NULL
        AND col_hour IS NULL
        ) -- all months within 6 months from now
0 голосов
/ 24 июня 2009

Для результатов в одном ряду, что-то вроде этого:

select
  orders_day   = sum(case when datediff(hour,orderdatetime,getdate())  < 24 then 1 else 0 end)
, orders_week  = sum(case when datediff(day,orderdatetime,getdate())   < 7  then 1 else 0 end)
, orders_month = sum(case when datediff(week,orderdatetime,getdate())  < 4  then 1 else 0 end)
, orders_half  = sum(case when datediff(month,orderdatetime,getdate()) < 6  then 1 else 0 end)
from #orders

Возможно, вы захотите настроить критерии даты, чтобы получить соответствующее поведение.

Для нескольких строк возьмите приведенные выше результаты и транспонируйте их с помощью UNPIVOT или CASE. CROSS JOIN.

0 голосов
/ 24 июня 2009
SELECT     
    sum(case when orderdatetime between GetDate() - 1 and GetDate() then 1 else 0 end) as Current24Hours,
    sum(case when orderdatetime between GetDate() - 2 and GetDate() - 1 then 1 else 0 end) as Previous24Hours,
    sum(case when orderdatetime between GetDate() - 7 and GetDate() then 1 else 0 end) as Current7Days,
    sum(case when orderdatetime between GetDate() - 14 and GetDate() - 7 then 1 else 0 end) as Previous7Days,
    sum(case when DATEDIFF (m, OrderDate, @now) <= 1 then 1 else 0 end) as PreviousMonth,
    sum(case when DATEDIFF (m, OrderDate, @now) <= 6 then 1 else 0 end) as PreviousSixMonths
FROM orders
0 голосов
/ 24 июня 2009

Я думаю, вы хотите группировать наборы. Я понимаю, что SQL Server поддерживает группировку наборов.

EDIT1: Я читал, что SQL Server 2005 не поддерживает наборы группировки, но SQL Server 2008 поддерживает. Здесь интересно прочесть предполагаемое, но не существующее различие между mapreduce и rdbms, такими как Oracle и Sql Server. Пожалуйста, прочитайте комментарии тоже! http://www.data -miners.com / blog / 2008/01 / mapreduce-and-sql-aggregations.html

0 голосов
/ 24 июня 2009

Поскольку вам нужны разные сроки для каждого типа даты, использование одного запроса со сверткой, вероятно, не даст вам того, что вы хотите. Я бы подумал просто объединить их вместе, как-то так ...

SELECT  DatePartValue = DATEPART(HH, orderdatetime), 
        Type = 'Hourly',
        COUNT(*)
FROM    #orders
WHERE   orderdatetime > DATEADD(HH, -25, GETDATE())
GROUP BY DATEPART(HH, orderdatetime)
UNION 
SELECT  DATEPART(DD, orderdatetime), 
        Type = 'Daily',
        COUNT(*)
FROM    #orders
WHERE   orderdatetime > DATEADD(DD, -8, GETDATE())
GROUP BY DATEPART(DD, orderdatetime)
UNION 
SELECT  DATEPART(WEEK, orderdatetime), 
        Type = 'Weekly',
        COUNT(*)
FROM    #orders
WHERE   orderdatetime > DATEADD(WEEK, -5, GETDATE())
GROUP BY DATEPART(WEEK, orderdatetime)
ORDER BY Type, DatePartValue
UNION 
SELECT  DATEPART(MM, orderdatetime), 
        Type = 'Monthly',
        COUNT(*)
FROM    #orders
WHERE   orderdatetime > DATEADD(MM, -7, GETDATE())
GROUP BY DATEPART(MM, orderdatetime)
ORDER BY Type, DatePartValue
0 голосов
/ 24 июня 2009

Вы можете запустить четыре выбора из «фиктивной таблицы» или «идентификационной» таблицы, состоящей из одной строки.

Вы можете иметь:

SELECT
    (<query count of orders current/prev 24 hours>) as <name1>,
    (<total orders current + 7 days>) as <name2>,
    (<total orders current week + 4 weeks>) as <name3>,
    (<total orders month + 6 months>) as <name4>
FROM
<IDENTITY table>;
...