SQL Server将多行组合为一行,具有多个列

时间:2017-01-04 13:41:49

标签: sql sql-server tsql

我有一个数据库,其中我有以下行:

  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

我想要的是什么:

  • 每人一排
  • 每行多个开始和结束日期
  • 时间顺序顺序


我能找到的大多数东西都希望它在同一列中,或者不希望它按时间顺序排序,所以不幸的是这些情况并不适用于我。

我现在真的知道如何解决这个问题。

5 个答案:

答案 0 :(得分:4)

如果您只有三个日期,那么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语句以提前进行计数。
  • 手动计算自己并添加相应的列。

答案 1 :(得分:2)

戈登的条件聚合将是我的首选。但是,如果你需要去DYNAMIC

laravel 5.3

返回

enter image description here

答案 2 :(得分:0)

假设您有一个静态的日期范围,您可以使用窗口函数实现此目的...

;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;

答案 3 :(得分:0)

我不确定你为什么要这样做,因为这个结构不太好。

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

答案 4 :(得分:0)

您可以使用pivot来转置和查询,如下所示:

;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

如果您有动态列,则可以使用stuff创建动态查询以创建列列表并将此查询作为动态查询运行以获取所有列列表