簡體   English   中英

SQL Server將多行合並為具有多列的一行

[英]SQL Server Combine multiple rows to one row with multiple columns

我有一個數據庫,其中包含以下行:

  ID  |  Date start  |  Date end
----------------------------------
  a   |  01-01-1950  |  30-01-1951
  a   |  01-01-1948  |  31-12-1949
  a   |  31-01-1951  |  01-06-2000
  b   |  01-01-1980  |  01-08-2010
  c   |  01-01-1990  |  31-12-2017
  c   |  31-01-1985  |  31-12-1989

我得到了什么

  • 每人多排
  • 每行一個開始和結束日期
  • 按時間順序


選擇要返回以下內容的查詢:

  ID  |  Date start 1 |  Date end 1  |  Date start 2 |  Date end 2  |  Date start 3 |  Date end 3
---------------------------------------------------------------------------------------------------
  a   |  01-01-1948   |  31-12-1949  |  01-01-1950   |  30-01-1951  |  31-01-1951   |  01-06-2000
  b   |  01-01-1980   |  01-08-2010
  c   |  31-01-1985   |  31-12-1989  |  01-01-1990   |  31-12-2017

我想要的是:

  • 每人一行
  • 每行有多個開始和結束日期
  • 按時間順序


我能找到的大多數東西都希望在同一列中進行,或者不希望按時間順序進行排序,因此不幸的是,這些情況不適用於我。

我現在真的知道如何解決這個問題。

如果只有三個日期,則pivot /條件聚合應該可以:

select id,
       max(case when seqnum = 1 then dstart end) as start_1,
       max(case when seqnum = 1 then dend end) as end_1,
       max(case when seqnum = 2 then dstart end) as start_2,
       max(case when seqnum = 2 then dend end) as end_2,
       max(case when seqnum = 3 then dstart end) as start_3,
       max(case when seqnum = 3 then dend end) as end_3
from (select t.*,
             row_number() over (partition by id order by dstart) as seqnum
      from t
     ) t
group by id;

注意:您必須指定輸出中的列數。 如果您不知道有多少個,可以:

  • 生成動態SQL語句以預先進行計數。
  • 手動計算自己並添加適當的列。

戈登的條件聚合將是我的首選。 但是,如果您需要動態

Declare @SQL varchar(max) = Stuff((Select Distinct ',' + QuoteName(concat('Date Start ',RN)) +',' + QuoteName(concat('Date End ',RN)) 
                                    From (Select Distinct RN=Row_Number() over (Partition By ID Order By [Date Start]) 
                                            From YourTable) A  
                                    Order by 1 For XML Path('')),1,1,'') 

Select  @SQL = '
Select [ID],' + @SQL + '
From (
        Select ID,B.*
         From (
                Select *,RN=Row_Number() over (Partition By ID Order By [Date Start]) From YourTable
              ) A
         Cross Apply (Values (concat(''Date Start '',A.RN),A.[Date Start])
                            ,(concat(''Date End '',A.RN),A.[Date End]) ) B (Col,Value)
     ) A
 Pivot (max(Value) For [Col] in (' + @SQL + ') ) p'
Exec(@SQL);

退貨

在此處輸入圖片說明

假設您有固定數量的日期范圍,則可以使用如下窗口函數來實現此目的...

;WITH cteData
    (
    ID,
    DateStart,
    DateEnd
    )
    AS
    (
    SELECT 'a', CONVERT(DATE, '01-01-1950'), CONVERT(DATE, '30-01-1951')
    UNION ALL SELECT 'a', '01-01-1948', '31-12-1949'
    UNION ALL SELECT 'a', '31-01-1951', '01-06-2000'
    UNION ALL SELECT 'b', '01-01-1980', '01-08-2010'
    UNION ALL SELECT 'c', '01-01-1990', '31-12-2017'
    UNION ALL SELECT 'c', '31-01-1985', '31-12-1989'
    ),
    cteDefineColumns
    AS
    (
    SELECT RangeColumnID = ROW_NUMBER() OVER (PARTITION BY ID ORDER BY DateStart),
        *
        FROM cteData
    )
    SELECT col1.ID,
        [Date start 1] = col1.DateStart,
        [Date end 1] = col1.DateEnd,
        [Date start 2] = col2.DateStart,
        [Date end 2] = col2.DateEnd,
        [Date start 3] = col3.DateStart,
        [Date end 3] = col3.DateEnd
        FROM cteDefineColumns AS col1
            LEFT OUTER JOIN cteDefineColumns AS col2
                ON col1.ID = col2.ID
                AND col2.RangeColumnID = 2
            LEFT OUTER JOIN cteDefineColumns AS col3
                ON col1.ID = col3.ID
                AND col3.RangeColumnID = 3
        WHERE col1.RangeColumnID = 1
        ORDER BY col1.ID,
            col1.DateStart,
            col1.DateEnd;

我不確定為什么要取消它,因為這種結構不太好。

if object_Id('tempdb..#TmpRankedTable') is not null drop table #TmpRankedTable
select Id, strt_dt, end_dt, row_number() over(partition by Id order by strt_dt) OrbyCol
into #TmpRankedTable
from dbo.YourTable

if object_Id('tempdb..#TmpStarts') is not null drop table #TmpStarts
select *
into #TmpStarts
from (
    select Id, strt_dt, OrbyCol
    from #TmpRankedTable) t
pivot (min(strt_dt) for OrbyCol in ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10])) p

if object_Id('tempdb..#TmpEnds') is not null drop table #TmpEnds
select *
into #TmpEnds
from (
    select Id, end_dt, OrbyCol
    from #TmpRankedTable) t
pivot (min(end_dt) for OrbyCol in ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10])) p

select 
    s.Id, 
    s.[1] [Start 1], 
    e.[1] [End 1], 
    s.[2] [Start 2], 
    e.[2] [End 2],
    s.[3] [Start 3],
    e.[3] [End 3], 
    s.[4] [Start 4],
    e.[4] [End 4],
    s.[5] [Start 5],
    e.[5] [End 5],
    s.[6] [Start 6], 
    e.[6] [End 6],
    s.[7] [Start 7], 
    e.[7] [End 7],
    s.[8] [Start 8], 
    e.[8] [End 8],
    s.[9] [Start 9], 
    e.[9] [End 9],
    s.[10] [Start 10], 
    e.[10] [End 10]
from #TmpStarts s
inner join #TmpEnds e on s.Id = e.Id

您可以按以下方式使用數據透視進行轉置和查詢:

;with cte as (
select *, RowN = row_number() over(partition by id order by datestart) from #temp ) 
, cte2 as (
    select id, [1] as [datestart1], [2] as [datestart2], [3] as datestart3 from 
    (select id, datestart, RowN from cte) sourcetable
    pivot (max(datestart) for RowN in ([1],[2],[3]) ) p
) 
, cte3 as (
    select id, [1] as [dateend1], [2] as [dateend2], [3] as dateend3 from 
    (select id, dateend, RowN from cte) sourcetable
    pivot (max(dateend) for RowN in ([1],[2],[3]) ) p
) select c2.id, c2.datestart1,c3.dateend1,c2.datestart2,c3.dateend2,c2.datestart3,c3.dateend3 
    from cte2 c2 left join cte3 c3 on c2.id = c3.id

如果您有動態列,則可以使用東西創建動態查詢以創建列列表,並將此查詢作為動態查詢運行以獲取所有列列表

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM