获取每列

时间:2017-10-26 17:24:10

标签: sql sql-server group-by

假设我的SQL Server(2012)数据库中有以下“值”表:

表1:

Id   Col1   Col2   Col3   Col4

我想创建第二个“覆盖”表,它将存储值以覆盖原始值,以防用户需要这样做。因此,根据上表,覆盖表将如下所示:

覆盖:

FK_Id   Col1   Col2   Col3   Col4   When_Inserted

Overrides.FK_IdTable1.Id作为外键引用。

因此,例如,假设我的Overrides表中包含以下行,并在Table1中使用Id=1覆盖行:

FK_Id:     Col1:             Col2:             Col3:              Col4:       When_Inserted:
1          Val1_1            Val2_1            Expected_Val3      NULL        1-Jan
1          NULL              Val2_2            NULL               NULL        2-Jan
1          NULL              Expected_Val2     NULL               NULL        3-Jan
1          Expected_Val1     NULL              NULL               NULL        4-Jan

然后,基于When_Inserted列 - 希望最新的插入优先,我希望覆盖如下:

FK_Id:     Col1:             Col2:             Col3:              Col4:
1          Expected_Val1     Expected_Val2     Expected_Val3      NULL 

我正在尝试一种智能的方法来创建这个SQL,并提出了一个相当丑陋的解决方案:

SELECT
     FK_Id
    ,(
        SELECT TOP 1
            Col1
        FROM
            Overrides O1
        WHERE
            Col1 IS NOT NULL
            AND O1.FK_Id = O.FK_Id
        ORDER BY
            O1.When_Inserted DESC
      ) Col1

      ....  <same for each of the other columns>  ....

FROM
    Overrides O
GROUP BY
    FK_Id

我确信必须有更好的方式,更清洁,更有效率。

3 个答案:

答案 0 :(得分:4)

使用common table expression row_number()(最新的第一个),cross apply()取消对列的拆分,过滤每列的最新内容(rn = 1),最后{{ 3}}回到相同的形式:

;with cte as (
select o.fk_id, v.Col, v.Value, o.When_Inserted
  , rn = row_number() over (partition by o.fk_id, v.col order by o.when_inserted desc)
from overrides o
  cross apply (values('Col1',Col1),('Col2',Col2),('Col3',Col3),('Col4',Col4)
    ) v (Col,Value)
where v.value is not null
)
select fk_id, col1, col2, col3, col4
from (
  select fk_id, col, value
  from cte 
  where rn = 1
  ) s
pivot (max(Value) for Col in (col1,col2,col3,col4)) p

rextester演示:pivot()

返回:

+-------+---------------+---------------+---------------+------+
| fk_id |     col1      |     col2      |     col3      | col4 |
+-------+---------------+---------------+---------------+------+
|     1 | Expected_Val1 | Expected_Val2 | Expected_Val3 | NULL |
+-------+---------------+---------------+---------------+------+

http://rextester.com/KGM96394

查看样本的io统计数据:

unpivot / pivot版本:

Table 'Worktable'. Scan count 0, logical reads 0
Table 'overrides'. Scan count 1, logical reads 1

first_value over()版本:

Table 'Worktable'. Scan count 20, logical reads 100
Table 'overrides'. Scan count 1, logical reads 1

select top 1子查询版本:

Table 'overrides'. Scan count 5, logical reads 5
Table 'Worktable'. Scan count 0, logical reads 0

答案 1 :(得分:1)

您可以使用first_value()

select distinct fkid,
       first_value(col1) over (partition by fkid
                               order by (case when col1 is not null then 1 else 2 end),
                                        when_inserted desc
                              ) as col1,
       first_value(col2) over (partition by fkid
                               order by (case when col2 is not null then 1 else 2 end),
                                        when_inserted desc
                              ) as col2,
       . . .
from t;

select distinct是因为SQL Server没有与聚合函数相同的功能。

答案 2 :(得分:0)

看我的解决方案是完全不同的。

IMHO,我的脚本performance会更好,只要它在所有correct output中提供sample data

我在我的脚本中使用了自动生成的ID,但如果您没有身份ID,则可以使用ROW_NUMBER。我的剧本很容易理解。

declare @t table(id int identity(1,1),FK_Id int,Col1 varchar(50),Col2 varchar(50)
,Col3 varchar(50),Col4 varchar(50),When_Inserted date)
insert into @t VALUES
 (1 ,'Val1_1'  ,'Val2_1' ,'Expected_Val3',  NULL ,  '2017-01-1')
,(1 ,NULL     ,'Val2_2' , NULL ,  NULL,       '2017-01-2')
,(1 ,NULL     ,'Expected_Val2', NULL ,         NULL, '2017-01-3')
,(1 ,'Expected_Val1' , NULL  , NULL ,         NULL,  '2017-01-4')



;

WITH CTE
AS (
    SELECT *
        ,CASE 
            WHEN col1 IS NULL
                THEN NULL
            ELSE CONCAT (
                    cast(id AS VARCHAR(10))
                    ,'_'
                    ,col1
                    )
            END col1Code
        ,CASE 
            WHEN col2 IS NULL
                THEN NULL
            ELSE CONCAT (
                    cast(id AS VARCHAR(10))
                    ,'_'
                    ,col2
                    )
            END col2Code
        ,CASE 
            WHEN col3 IS NULL
                THEN NULL
            ELSE CONCAT (
                    cast(id AS VARCHAR(10))
                    ,'_'
                    ,col3
                    )
            END col3Code
        ,CASE 
            WHEN col4 IS NULL
                THEN NULL
            ELSE CONCAT (
                    cast(id AS VARCHAR(10))
                    ,'_'
                    ,col4
                    )
            END col4Code
    FROM @t
    )
    ,CTE1
AS (
    SELECT FK_Id
        ,max(col1Code) col1Code
        ,max(col2Code) col2Code
        ,max(col3Code) col3Code
        ,max(col4Code) col4Code
    FROM cte
    GROUP BY FK_Id
    )
SELECT FK_Id
    ,SUBSTRING(col1Code, charindex('_', col1Code) + 1, len(col1Code)) col1Code
    ,SUBSTRING(col2Code, charindex('_', col2Code) + 1, len(col2Code)) col2Code
    ,SUBSTRING(col3Code, charindex('_', col3Code) + 1, len(col2Code)) col3Code
    ,SUBSTRING(col4Code, charindex('_', col4Code) + 1, len(col4Code)) col4Code
FROM cte1 c1