복붙노트

[SQL] 그룹화 순서 열 값의 변화에 ​​의해 데이터 그룹

SQL

그룹화 순서 열 값의 변화에 ​​의해 데이터 그룹

다음과 같은 데이터로

create table #ph (product int, [date] date, price int)
insert into #ph select 1, '20120101', 1
insert into #ph select 1, '20120102', 1
insert into #ph select 1, '20120103', 1
insert into #ph select 1, '20120104', 1
insert into #ph select 1, '20120105', 2
insert into #ph select 1, '20120106', 2
insert into #ph select 1, '20120107', 2
insert into #ph select 1, '20120108', 2
insert into #ph select 1, '20120109', 1
insert into #ph select 1, '20120110', 1
insert into #ph select 1, '20120111', 1
insert into #ph select 1, '20120112', 1

나는 다음과 같은 출력을 생성하고 싶습니다 :

product | date_from | date_to  | price
  1     | 20120101  | 20120105 |   1
  1     | 20120105  | 20120109 |   2
  1     | 20120109  | 20120112 |   1

I 가격 그룹은 최대 및 최소 날짜를 보여 주면 나는 내가 원하는되지 않은 것을 다음 (날짜의 래핑을 통해 참조) 얻을 것이다.

product | date_from | date_to  | price
  1     | 20120101  | 20120112 |   1
  1     | 20120105  | 20120108 |   2

그래서 기본적으로 내가 뭘 찾고있어 그룹 열 제품과 가격에 따라 데이터의 단계 변화에 의해 그룹입니다.

이를 달성하기 위해 가장 깨끗한 방법은 무엇입니까?

해결법

  1. ==============================

    1.) (이 같은 전화를 두 ROW_NUMBER와 관련된 이러한 문제를 해결하는 (다소) 알려진 기술이있다 :

    ) (이 같은 전화를 두 ROW_NUMBER와 관련된 이러한 문제를 해결하는 (다소) 알려진 기술이있다 :

    WITH marked AS (
      SELECT
        *,
        grp = ROW_NUMBER() OVER (PARTITION BY product        ORDER BY date)
            - ROW_NUMBER() OVER (PARTITION BY product, price ORDER BY date)
      FROM #ph
    )
    SELECT
      product,
      date_from = MIN(date),
      date_to   = MAX(date),
      price
    FROM marked
    GROUP BY
      product,
      price,
      grp
    ORDER BY
      product,
      MIN(date)
    

    산출:

    product  date_from   date_to        price 
    -------  ----------  -------------  ----- 
    1        2012-01-01  2012-01-04     1     
    1        2012-01-05  2012-01-08     2     
    1        2012-01-09  2012-01-12     1     
    
  2. ==============================

    2.나는이 포럼에 새로 온 사람은 나의 기여 도움이되기를 바랍니다.

    나는이 포럼에 새로 온 사람은 나의 기여 도움이되기를 바랍니다.

    당신이 정말로 (아마 가장 좋은 방법을 먹으 렴 생각하지만)을 CTE를 사용하지 않을 경우 당신은 세트 기반 코드를 사용하여 솔루션을 얻을 수 있습니다. 이 코드의 성능을 테스트해야합니다!

    나는 각 레코드에 대한 고유 식별자를 사용할 수 있도록 나는 여분의 임시 테이블에 추가 한하지만 난 당신이 이미 소스 테이블이 열을해야합니다 생각한다. 그래서 임시 테이블을 heres.

        If Exists (SELECT Name FROM tempdb.sys.tables WHERE name LIKE '#phwithId%')
            DROP TABLE #phwithId    
    
        CREATE TABLE #phwithId
        (
            SaleId INT
            , ProductID INT
            , Price Money
            , SaleDate Date 
        )
        INSERT INTO #phwithId SELECT row_number() over(partition by product order by [date] asc) as SalesId, Product, Price, Date FROM ph 
    

    선택 문 이제 본체

        SELECT 
            productId 
            , date_from
            , date_to
            , Price
        FROM
            (   
                SELECT 
                    dfr.ProductId
                    , ROW_NUMBER() OVER (PARTITION BY ProductId ORDER BY ChangeDate) AS rowno1          
                    , ChangeDate AS date_from
                    , dfr.Price
                FROM
                    (       
                        SELECT
                            sl1.ProductId AS ProductId
                            , sl1.SaleDate AS ChangeDate
                            , sl1.price
                        FROM
                            #phwithId sl1
                        LEFT JOIN
                            #phwithId sl2
                            ON sl1.SaleId = sl2.SaleId + 1
                        WHERE
                            sl1.Price <> sl2.Price OR sl2.Price IS NULL
                    ) dfr
            ) da1
        LEFT JOIN
            (   
                SELECT 
                    ROW_NUMBER() OVER (PARTITION BY ProductId ORDER BY ChangeDate) AS rowno2
                    , ChangeDate AS date_to     
                FROM
                    (   
                        SELECT 
                            sl1.ProductId
                            , sl1.SaleDate AS ChangeDate
                        FROM
                            #phwithId sl1
                        LEFT JOIN
                            #phwithId sl3
                            ON sl1.SaleId = sl3.SaleId - 1  
                        WHERE
                            sl1.Price <> sl3.Price OR sl3.Price IS NULL         
                    ) dto
    
            ) da2 
            ON da1.rowno1 = da2.rowno2  
    

    가격 버킷이 변경 될 때 한 기록에 의해 상쇄 데이터 소스를 결합하여 (+ 또는 -) 우리는 식별 할 수있는 다음 버킷의 시작 및 종료 날짜를 받고 자사의 단지 문제는 단일 레코드로 백업 할 수 있습니다.

    모두는 가로장 설치 등등 비트와 나는 그것의 더 나은 성능을 제공 할 것 확실하지 않다 그러나 나는 도전을 즐겼다.

  3. ==============================

    3.

    WITH marked AS (
      SELECT
        *,
      case
       when (lag(price,1,'') over (partition by product order by date_from)) = price
       then 0 else 1
      end is_price_change
      FROM #ph
    ),
    marked_as_group AS
    ( SELECT m.*,
           SUM(is_price_change) over (PARTITION BY product order by date_from ROWS 
          BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS price_change_group
      FROM marked m
    ),
    SELECT
      product,
      date_from = MIN(date_from),
      date_to   = MAX(date_to),
      price = MIN(price)
    FROM marked_as_group 
    GROUP BY
      product,
      price_change_group
    ORDER BY
      product,
      date_to
    
  4. ==============================

    4.내가있는쪽으로 와서 하나 개의 솔루션은 상대적으로 "깨끗한"입니다 :

    내가있는쪽으로 와서 하나 개의 솔루션은 상대적으로 "깨끗한"입니다 :

    ;with cte_sort (product, [date], price, [row])
    as
        (select product, [date], price, row_number() over(partition by product order by [date] asc) as row
         from #ph)
    
    select a.product, a.[date] as date_from, c.[date] as date_to, a.price 
    from cte_sort a
    left outer join cte_sort b on a.product = b.product and (a.row+1) = b.row and a.price = b.price
    outer apply (select top 1 [date] from cte_sort z where z.product = a.product and z.row > a.row order by z.row) c
    where b.row is null
    order by a.[date] 
    

    당신이 다음 DATEADD 같은 기능을 사용하는 경우 어떤 날짜가 누락 여부에 대해 걱정할 필요가 없습니다 때문에 ROW_NUMBER와 CTE를 사용했다. 당신은 분명히 단지 당신이 (내가)이 DATE_TO 열이하고 싶은 경우에 적용되는 외부가 필요합니다.

    이 솔루션은 내가 그러나 내가 5 백만 행 내 테이블에 원하는대로 빨리 수행하기 위해 점점 약간의 문제가 발생하고, 내 문제를 해결 않습니다.

  5. ==============================

    5.

    Create function [dbo].[AF_TableColumns](@table_name nvarchar(55))
    returns nvarchar(4000) as
    begin
    declare @str nvarchar(4000)
        select @str = cast(rtrim(ltrim(column_name)) as nvarchar(500)) + coalesce('         ' + @str , '            ') 
        from information_schema.columns
        where table_name = @table_name
        group by table_name, column_name, ordinal_position 
        order by ordinal_position DESC
    return @str
    end
    
    --select dbo.AF_TableColumns('YourTable') Select * from YourTable
    
  6. from https://stackoverflow.com/questions/10110026/group-data-by-the-change-of-grouping-column-value-in-order by cc-by-sa and MIT license