【问题标题】:T-SQL Multiple groupingT-SQL 多重分组
【发布时间】:2012-12-24 11:45:38
【问题描述】:

我有以下数据:

Product Price   StartDate                   EndDate
Apples  4.9     2010-03-01 00:00:00.000     2010-03-01 00:00:00.000
Apples  4.9     2010-03-02 00:00:00.000     2010-03-02 00:00:00.000
Apples  2.5     2010-03-03 00:00:00.000     2010-03-03 00:00:00.000
Apples  4.9     2010-03-05 00:00:00.000     2010-03-05 00:00:00.000
Apples  4.9     2010-03-06 00:00:00.000     2010-03-06 00:00:00.000
Apples  4.9     2010-03-09 00:00:00.000     2010-03-09 00:00:00.000
Apples  2.5     2010-03-10 00:00:00.000     2010-03-10 00:00:00.000
Apples  4.9     2010-03-11 00:00:00.000     2010-03-11 00:00:00.000
Apples  4.9     2010-03-12 00:00:00.000     2010-03-12 00:00:00.000
Apples  4.9     2010-03-13 00:00:00.000     2010-03-13 00:00:00.000
Apples  4.9     2010-03-15 00:00:00.000     2010-03-15 00:00:00.000
Apples  4.9     2010-03-16 00:00:00.000     2010-03-16 00:00:00.000

想像product, price, min(startdate), max(startdate) 一样分组,但也应该在开始日期和结束日期分组............如下所示

想要的结果

Apples  4.9     2010-03-01 00:00:00.000     2010-03-02 00:00:00.000
Apples  2.5     2010-03-03 00:00:00.000     2010-03-03 00:00:00.000
Apples  4.9     2010-03-05 00:00:00.000     2010-03-09 00:00:00.000
Apples  2.5     2010-03-10 00:00:00.000     2010-03-10 00:00:00.000
Apples  4.9     2010-03-11 00:00:00.000     2010-03-16 00:00:00.000

【问题讨论】:

  • 欢迎使用 StackOverflow:如果您发布代码、XML 或数据示例,在文本编辑器中突出显示这些行并单击“代码示例”按钮 ({ } ) 在编辑器工具栏上以很好地格式化和语法突出显示它!
  • EndDate 列的用途是什么?它似乎总是等于StartDate。这个假设是真的吗?如果是这样,请从您的示例数据中删除 EndDate 列。如果不是这样,我希望您提供最“棘手”的数据而不是最统一/无聊的数据,以便提供答案的人可以确定正确的查询以始终提供正确的结果。
  • 为了清楚起见:即使您没有显示 2010-03-14 的数据,您也希望看到 Apples 的最后一行跨越 2010-03-1120100316?跨度>

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


【解决方案1】:

我的方法。

数据:

create table t ( producte varchar(50), 
                 price money, 
                 start_date date,
                 end_date date);

insert into t values
( 'apple', 4.9, '2012-01-01', '2012-01-01' ),
( 'apple', 4.9, '2012-01-02', '2012-01-02' ),
( 'apple', 8, '2012-01-04', '2012-01-04' ),
( 'cat', 5, '2012-01-01', '2012-01-01' ),
( 'cat', 6, '2012-01-02', '2012-01-02' ),
( 'cat', 6, '2012-01-03', '2012-01-03' );

查询:

with start_dates as (
  select 
    t.producte, t.price, t.start_date, t.end_date, t.start_date as gr_date    
  from 
    t left outer join 
    t t1 on 
        t.price = t1.price and                         --new
        t.producte = t1.producte and
        t.start_date = dateadd(day,1, t1.end_date )
  where t1.producte is null
  union all
  select 
      t.producte, t.price, t.start_date,t. end_date, gr_date
  from
      t inner join 
      start_dates t1 on  
        t.price = t1.price and                         --new
        t.producte = t1.producte and
        t.start_date = dateadd(day,1, t1.end_date )
)
select t.producte, t.price , min( t.start_date ), max( t.end_date )
from start_dates t
group by  t.producte, gr_date  ,t.price

Results:

| PRODUCTE | PRICE |   COLUMN_2 |   COLUMN_3 |
----------------------------------------------
|    apple |   4.9 | 2012-01-01 | 2012-01-02 |
|    apple |     8 | 2012-01-04 | 2012-01-04 |
|      cat |     5 | 2012-01-01 | 2012-01-01 |
|      cat |     6 | 2012-01-02 | 2012-01-03 |

说明

这是一个递归 CTE 表达式。基本查询获取每组价格的初始日期。递归查询查找具有此价格的最后一条数据。

【讨论】:

  • @user1926569,我接受后更新了查询。审查。
  • 请从您的回答中删除任何不正确的查询。编辑的目的不是提供历史记录,而是提供最佳答案。有不正确的查询和答案如何随时间变化的证据并不是最好的答案。
  • @ErikE,感谢您的评论。固定的。现在是对的吗?问候。
【解决方案2】:
SELECT  product, price, MIN(start_date), MAX(end_date)
FROM    (
        SELECT  product, price, start_date, end_date,
                ROW_NUMBER() OVER (PARTITION BY product ORDER BY startDate) rn1,
                ROW_NUMBER() OVER (PARTITION BY product, price ORDER BY startDate) rn2
        FROM    mytable
        ) q
GROUP BY
        product, price, rn2 - rn1
ORDER BY
        product, MIN(start_date), price

【讨论】:

  • 它给了我同样的结果
  • {select product, price, product, price, MIN(start_date), MAX(end_date) from mytable GROUP BY product, price } 会给出
  • @user1926569:当然,我的错。请尝试更新后的查询:sqlfiddle.com/#!3/cf7ad/16
  • 页面上的最佳答案! Quassnoi,你有没有机会看到this post of mine?您知道这种用于查找变化组的减法 Row_Number() 技术有多久了?
  • @ErikE:不,没有看到你的帖子。 stackoverflow.com/questions/5662545/… 我的博客上也有很多关于这个问题的帖子。
【解决方案3】:

这是SQLFiddle demo

with t2 as 
(
select t1.*,
(select count(Price) 
  from t 
  where startdate<t1.startdate 
        and Price<>t1.price
        and Product=t1.Product
)
rng  
from t as t1
)
select Product,Price,min(startDate),max(EndDate)  
from t2 group by Product,Price,RNG
order by 3

【讨论】:

    【解决方案4】:

    这里有一个建议:对于每一行,您必须找到价格不同的最大上一个日期,然后您就可以分组。例如,对于 2010-03-11 和 2010-03-16 之间的任何行,您必须检索日期 2010-03-10,因为这是价格不同的最大上一个日期(2.5 对 4.9)。第一行将返回一个空日期,但这应该不是问题。

    但是,对于一个很长的表,这种查询可能会变得很慢。因此,如果您有一些速度问题,您应该考虑添加一列并使用光标增量填充它的可能性:您按日期循环遍历它,每次看到新价格时,您都会更改它的值。最终的分组就很简单了。

    这里有一些东西:

    Select Product, Price, Min(StartDate) as StartDate, PreviousDate from (
        Select product, price, StartDate, (Select max (StartDate) from table_2 t3 where t3.price <> t2.price and t3.StartDate < t2.StartDate and t3.Product = t2.Product) as previousDate
        from table_2 t2) SQ
    
    Group by Product, Price, PreviousDate
    Order by PreviousDate
    

    【讨论】:

      【解决方案5】:

      我相信这是迄今为止效果最好的解决方案:

      WITH Calc AS (
         SELECT *,
            Grp = DateAdd(day, -Row_Number()
               OVER (PARTITION BY Product, Price ORDER BY StartDate), StartDate
            )
         FROM dbo.PriceHistory
      )
      SELECT Product, Price, FromDate = Min(StartDate), ToDate = Max(StartDate)
      FROM Calc
      GROUP BY Product, Price, Grp
      ORDER BY FromDate;
      

      Try this out yourself

      【讨论】:

      • 它不返回@op 要求的内容。
      • 你是对的,Quassnoi。我错过了 OP 想要跳过空白。我很快就会回到这个。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多