GROUP BY条件

时间:2014-10-22 09:51:11

标签: sql sql-server sql-server-2008-r2

例如,我有一张订单表。该表有一个键(Order.No),一个打开订单的日期(Order.Open)和一个订单关闭的日期(Order.Close)。

No          Open        Close
---------   ----------  ----------
2013-1208   2013-03-11  2013-03-26
2013-1272   2013-03-11  2013-03-11
2013-1273   2013-03-11  2013-03-11
2013-1274   2013-03-11  2013-03-11
2013-1275   2013-03-11  2013-03-11
2013-1280   2013-03-11  2013-06-26
2013-1281   2013-03-11  2013-04-18
2013-1282   2013-03-11  2013-03-14
2013-1287   2013-03-12  2013-04-18
2013-1291   2013-03-12  2013-03-12

现在我想进行查询,在那里我可以找到每月有多少订单在本月的最后一天仍然开放。

例如,我想了解1月最后一天仍有多少订单仍在营业:

订单在2月的第一天或之后关闭,订单在2月的第一天之前开放:

SELECT COUNT(Order.No) 'Open', '1' 'Month', '2013' 'Year' FROM Orders
WHERE (Orders.Open < '2013-02-01') AND (Orders.Close >= '2013-02-01')

现在,如果我想获得每个月的这些信息,我将不得不这样做:

SELECT COUNT(Order.No) 'Open', '1' 'Month', '2013' 'Year'  FROM Orders
WHERE (Orders.Open < '2013-02-01') AND (Orders.Close >= '2013-02-01')

UNION

SELECT COUNT(Order.No) Open, '2' Month, '2013' 'Year'  FROM Orders
WHERE (Orders.Open < '2013-03-01') AND (Orders.Close >= '2013-03-01')

UNION

SELECT COUNT(Order.No) 'Open', '3' 'Month', '2013' 'Year' FROM Orders
WHERE (Orders.Open < '2013-04-01') AND (Orders.Close >= '2013-04-01')

UNION

SELECT COUNT(Order.No) 'Open', '4' Month, '2013' Year FROM Orders
WHERE (Orders.Open < '2013-05-01') AND (Orders.Close >= '2013-05-01')

我可以以某种方式简化此查询,因此我不必每个月和每年都写这个吗?

所需的输出类似于:

Open    Month   Year
----    -----   ----
684     1       2013
683     2       2013
760     3       2013
659     4       2013

7 个答案:

答案 0 :(得分:3)

首先生成您希望在结果中看到的所有月份。然后每个月加入未结订单并按月计算。

with months as
(
  select 
    datefromparts(year(min(opened)), month(min(opened)), 1) as startdate
  from orders
  union all
  select
    dateadd(month, 1, startdate)
  from months
  where dateadd(month, 1, startdate) <= cast(getdate() as date)
)
select 
  year(months.startdate) as yr, month(months.startdate) as mon, count(orders.opened) as cnt
from months
left join orders 
  on orders.opened <= eomonth(months.startdate)
  and coalesce(orders.closed, '2999-12-31') > eomonth(months.startdate)
group by year(months.startdate), month(months.startdate)
order by year(months.startdate), month(months.startdate);

这是SQL小提琴:http://sqlfiddle.com/#!6/12ee7/3

在SQL Server 2008中,缺少函数DATEFROMPARTS和EOMONTH。替换为

cast( cast(year(min(opened)) as varchar) + '-' + cast(month(min(opened)) as varchar) + '-01' as date) as startdate

dateadd(day, -1, dateadd(month, 1, months.startdate))

以下是SQL Server 2008的SQL小提琴:http://sqlfiddle.com/#!3/12ee7/5

答案 1 :(得分:2)

您只需输入所需的月份/年份列表并加入其中,例如

SELECT  [Open] = COUNT(o.No),
        [Month] = DATEPART(MONTH, d.[Date]),
        [Year] = DATEPART(YEAR, d.[Date])
FROM    (VALUES
            ('2013-01-01'),
            ('2013-02-01'),
            ('2013-03-01'),
            ('2013-04-01'),
            ('2013-05-01'),
            ('2013-06-01')
        ) d (Date)
        LEFT JOIN Orders AS o
            ON o.[Open] < d.[Date]
            AND o.[Close] >= d.[Date]
GROUP BY d.Date;

如果您有calendar table,则可以使用此代码而不是硬编码日期列表。

如果您不想对所需日期进行硬编码,那么您可以相当轻松地生成列表,首先生成一个数字列表:

WITH E1 AS -- 10 ROWS
(   SELECT  N = 1
    FROM    (VALUES (1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) n (N)
), E2 AS -- 10 X 10 = 100 ROWS
(   SELECT  N = 1
    FROM    E1 CROSS JOIN E1 AS E2
), E3 AS -- 100 x 100 = 10,000 ROWS
(   SELECT  N = 1
    FROM    E2 CROSS JOIN E2 AS E3
)
SELECT  N = ROW_NUMBER() OVER(ORDER BY N)
FROM    E3;

这只是一个例子,但是会产生10,000个连续数字,实际上你可能不需要报告10,000个月,但要证明它并没有什么坏处。然后,您可以将此数字列表转换为日期列表:

WITH E1 (N) AS 
(   SELECT 1 
    FROM (VALUES (1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) n (N)
), 
E2 (N) AS (SELECT 1 FROM E1 CROSS JOIN E1 AS E2), 
E3 (N) AS (SELECT 1 FROM E2 CROSS JOIN E2 AS E3)
SELECT  [Date] = DATEADD(MONTH, ROW_NUMBER() OVER(ORDER BY N) - 1, '19000101')
FROM    E3;

然后你可以将它用作主表并左键加入订单:

WITH E1 (N) AS 
(   SELECT 1 
    FROM (VALUES (1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) n (N)
), 
E2 (N) AS (SELECT 1 FROM E1 CROSS JOIN E1 AS E2), 
E3 (N) AS (SELECT 1 FROM E2 CROSS JOIN E2 AS E3),
Dates AS 
(   SELECT  [Date] = DATEADD(MONTH, ROW_NUMBER() OVER(ORDER BY N) - 1, '19000101')
    FROM    E3
)
SELECT  [Open] = COUNT(o.No),
        [Month] = DATEPART(MONTH, d.[Date]),
        [Year] = DATEPART(YEAR, d.[Date])
FROM    Dates AS d
        LEFT JOIN Orders AS o
            ON o.[Open] < d.[Date]
            AND o.[Close] >= d.[Date]
WHERE   d.Date >= '20130101' -- OR WHATEVER DATE YOU LIKE
AND     d.Date < GETDATE();

关于生成和使用数字/日期表的一些进一步阅读,无论是静态还是动态,请看一下这个系列:

答案 2 :(得分:2)

也试试这个。 您可以更改条件以获得更好的结果

SELECT COUNT(`no`) Open, 
       DATE_FORMAT(`open`,'%Y-%m') as period, 
       month(`open`) Month, 
       year(`open`) Year
  FROM `orders`
WHERE DATE_FORMAT(`close`, '%Y-%m') <> period
GROUP BY period

答案 3 :(得分:1)

SELECT COUNT(Order.No) Open, '1' Month, '2013' Year FROM Orders
group by Orders.Open, Orders.Close

答案 4 :(得分:1)

试试这个

SELECT DISTINCT COUNT(no) OVER (PARTITION BY DATEPART(mm,Open)) AS [Open]
                DATEPART(mm,Open) AS [Month], 
                DATEPART(mm,Open) AS [Year]
FROM dbo.tble

答案 5 :(得分:1)

首先,创建一个月份系列,以便您的结果集中没有任何空白。然后根据所需条件将其加入您的订单表。

按月份系列对结果进行分组并完成。

;with months as (
    select cast('2013-01-01' as datetime) as dt union all
    select cast('2013-02-01' as datetime) union all
    select cast('2013-03-01' as datetime) union all
    select cast('2013-04-01' as datetime) union all
    select cast('2013-05-01' as datetime) union all
    select cast('2013-06-01' as datetime) union all
    select cast('2013-07-01' as datetime) union all
    select cast('2013-08-01' as datetime) union all
    select cast('2013-09-01' as datetime) union all
    select cast('2013-10-01' as datetime) union all
    select cast('2013-11-01' as datetime) union all
    select cast('2013-12-01' as datetime)
)
select count(o.No), datepart(month, m.dt) month, datepart(year, m.dt) year
from months m
left join Orders o 
    on o.Open < dateadd(month, m.dt, 1)
    and o.Close >= dateadd(month, m.dt, 1)
group by m.dt

答案 6 :(得分:0)

Jt,请看,这是否有效。

@Order成为表格。

DECLARE @Order TABLE (No VARCHAR(20),[OPEN] Date,[Close] Date)

SELECT      YEAR([OPEN]),
            MONTH([OPEN]),
            SUM(IIF([Close] > DATEADD(DAY, 1,EOMONTH([OPEN])),1,0)) 
FROM        @Order 
GROUP BY    YEAR([OPEN]),MONTH([OPEN])