【问题标题】:Transposing rows into columns based on a condition sql根据条件 sql 将行转置为列
【发布时间】:2015-04-26 02:33:34
【问题描述】:

我有一个下表(一个简化的示例,实际上该表包含多个 ID,日期数量可变,每个日期的事件数量可变):

IDs   Date                  Event
102   1996-10-16 00:00:00   A
102   1996-10-23 00:00:00   A
102   1996-10-23 00:00:00   B
102   1997-01-14 00:00:00   A
103   1997-01-14 00:00:00   D
103   1997-01-15 00:00:00   A
103   1997-01-16 00:00:00   A
103   1997-01-16 00:00:00   B
103   1997-01-16 00:00:00   C

我正在尝试获取一个表,其中我将具有不同的 ID/日期对,其中已重新编码的多个事件的行被转置到列中。所以,我正在寻找一个表格,对于这个例子来说,它看起来像这样:

IDs   Date                  Event   Event2   Event3
102   1996-10-16 00:00:00   A       NULL     NULL
102   1996-10-23 00:00:00   A       B        NULL
102   1997-01-14 00:00:00   A       NULL     NULL
103   1997-01-14 00:00:00   D       NULL     NULL
103   1997-01-15 00:00:00   A       NULL     NULL
103   1997-01-16 00:00:00   A       B        C

很抱歉没有发布任何代码,但坦率地说,我什至不知道如何开始。

【问题讨论】:

    标签: sql duplicates transpose


    【解决方案1】:

    关于PIVOT方法的详细信息。

    还有有用的答案:

    Using PIVOT in SQL Server 2008

    MSSQL dynamic pivot column values to column header

    试试这个代码:

    -- Temporary table...
    create table ##myTable (
            IDs int
            ,[Date] datetime
            ,[Event] varchar(1)
            )
    
    -- ... with sample data
    insert ##myTable
            select 102, '2010-01-01', 'A'
    union   select 102, '2010-01-01', 'B'
    union   select 102, '2010-01-01', 'C'
    union   select 102, '2010-01-01', 'E'
    union   select 103, '2010-01-01', 'A'
    union   select 104, '2010-01-01', 'B'
    union   select 104, '2010-01-01', 'C'
    union   select 105, '2010-01-01', 'F'
    
    -- Variables
    DECLARE @cols   AS NVARCHAR(MAX)
            ,@query AS NVARCHAR(MAX)
    
    -- Build column name for our result.
    -- The ROW_NUMBER() operator gives us the rank of the event for
    -- the combination of IDs and Date. With that, event B for IDs 104
    -- will have rank 1, and then will appear in the 1st column.
    SELECT  @cols = STUFF(
                           (SELECT  DISTINCT
                            ',' + QUOTENAME('Event' + LTRIM(STR(
                                        ROW_NUMBER() OVER (
                                            PARTITION BY IDs, [Date]
                                            ORDER BY IDs, [Date]
                                        )
                                    )))
                            FROM    ##myTable
                            FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)')
                        , 1, 1, '')
    
    set @query = '
        SELECT  IDs, [Date], ' + @cols + '
        FROM    (
                SELECT  IDs
                        ,[Date]
                        ,[Event]
                        ,''Event'' + LTRIM(STR(
                                ROW_NUMBER() OVER (
                                    PARTITION BY IDs, [Date]
                                    ORDER BY IDs, [Date]
                                )
                        )) as [EventNo]
                FROM    ##myTable
                ) x
        PIVOT
                (
                MAX([Event])
                FOR [EventNo] IN (' + @cols + ')
                ) p'
    
    execute sp_executesql @query
    
    -- Remove temporary table
    drop table ##myTable
    

    结果:

    【讨论】:

    • 感谢您的建议。我承认这远远超出了我目前的 sql 知识水平......但我确实有一个问题。它为每个可能的事件生成一个列。因此,如果我在整个数据集中有 100 个不同的事件,它将产生 100 列,大多数动物只有几个条目。是否不可能有一个类似的表,而不是将特定的事件类型作为列,而是有“Event1”、“Event2”等?这将大大节省空间!
    • 好吧,也许你能用其他例子来完善你的问题。您想在不同行的同一列“事件 1”中查看事件 A 和事件 B(某种方式将结果减少为有用的列),还是想要规范化的列名(或两者兼而有之)?
    • 我现在已经编辑了这个问题,希望它能更好地显示我所追求的。事件列的总数应该是给定 ID/日期对记录的最大事件数,而不是数据中重新编码的不同事件数。所以,在这个例子中,我记录了 4 个不同的事件,但同一个人在同一天只记录了 3 个事件,所以我所追求的列数是 3。
    • 我根据您上次的精度更新了查询。
    • 这正是我想要的!谢谢!只是一个额外的问题,或两个。首先,列的顺序是随机的,即我得到 Event14、Event2、Event10 等......这是一个装饰性的东西,但我只是想检查它是否表明存在问题?其次,我从来没有使用过这样的语法,当我想保存表格时,我有点困惑应该将“into”语句放在哪里......
    【解决方案2】:

    如果你只有两个事件,你可以用min()max()和一些额外的逻辑来做到这一点:

    select ids, date, min(event) as event,
           (case when min(event) <> max(event) then max(event) end) as event2
    from table t
    group by ids, date;
    

    这是标准 SQL,因此它应该适用于任何数据库。

    【讨论】:

    • 不幸的是,事件的数量会有很大差异。大多数 ID/日期只有 1 个,但可能有一些 ID/日期对最多有 10 个事件。
    【解决方案3】:

    首先尝试这个选择 ids,然后根据上面的 id 获取所有列 ....

    <?php
        $query = $database->getRows("SELECT DISTINCT ids FROM table");  
        ?>
    
        <table>
        <?php foreach($query as $row){ ?>
                <tr>
    
                    <td><?php echo $row['ids']; ?></td>
    
                <?php
                $id= $row["ids"];
                $sub_inner = $database->getRows("SELECT date,Event,Event2 FROM table where ids= :ids",
                array(':ids'=>$id));                        
                ?>
    
    
        <td><?php foreach($sub_inner as $list){ ?><?php echo $list['date']; ?></td><td><?php echo $list['Event']; ?><?php } ?></td> 
        <td><?php echo $list['Event2']; ?></td> 
    
        <?php } ?>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-11-10
      • 2017-05-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多