Merge 在sql server中合并查询中的行和列

Merge 在sql server中合并查询中的行和列,merge,sql-server-2008-r2,Merge,Sql Server 2008 R2,我有一个简单的查询,返回以下行: 当前行: Empl ECode DCode LCode Earn Dedn Liab ==== ==== ===== ===== ==== ==== ==== 123 PerHr Null Null 13 0 0 123 Null Union Null 0

我有一个简单的查询,返回以下行:

当前行:

Empl    ECode   DCode       LCode       Earn    Dedn    Liab
====    ====    =====       =====       ====    ====    ====
123     PerHr   Null        Null        13      0       0
123     Null    Union       Null        0       10      0
123     Null    Per         Null        0       20      0
123     Null    Null        MyHealth    0       0       5
123     Null    Null        401         0       0       10
123     Null    Null        Train       0       0       15
123     Null    Null        CAFTA       0       0       20
但是,我需要看到上面的行,如下所示:

Empl    ECode   DCode   LCode       Earn    Dedn    Liab
====    ====    =====   =====       ====    ====    ====
123     PerHr   Union   MyHealth    13      10      5
123     Null    Per     401         0       20      10
123     Null    Null    Train       0       0       15
123     Null    Null    CAFTA       0       0       20
这更像是在
EarnCode
DednCode
&
LiabCode
遇到空值时,将后续行合并到前面的行中。实际上,我想看到的是把所有的东西都卷到前面几行

在Oracle中,我们可以使用这个
LAST_VALUE
函数,但在这种情况下,我根本不知道如何处理它

在上面的示例中,
ECode
的和值列是
Earn
DCode
Dedn
LCode
Liab
;请注意,每当
ECode
DCode
LCode
中的任何一个不为空时,
Earn
Dedn
Liab
列中都有相应的值

顺便说一下,我们在工作中使用的是SQL Server 2008 R2


希望得到您的建议,谢谢。

不确定这是否是您需要的,但您试过了吗

SELECT Name, Class, Color, ProductNumber,
COALESCE(Class, Color, ProductNumber) AS FirstNotNull
FROM Production.Product ;

我有一个解决方案,但它是非常笨拙的。如果有人有更好的,那就太好了

然而,一种算法:

1) 获取列中每个不同值列表的行号
2) 根据行数联接所有列

例如:

select Distinct ECode   into #Ecode     from source_table   order by rowid;
select Distinct DCode   into #Dcode     from source_table   order by rowid;
select Distinct LCode   into #Lcode     from source_table   order by rowid;
select Distinct Earn    into #Earn  from source_table   order by rowid;
select Distinct Dedn    into #Dedn  from source_table   order by rowid;
select Distinct Liab    into #Liab  from source_table   order by rowid;

select b.ECode, c.DCode, d.LCode, e.Earn, f.Dedn, g.Liab
from source_table   a -- Note:  a source for row numbers that will be >= the below
left outer join #Ecode b on a.rowid = b.rowid
left outer join #DCode c on a.rowid = c.rowid
left outer join #LCode d on a.rowid = d.rowid
left outer join #Earn  e on a.rowid = e.rowid
left outer join #Dedn  f on a.rowid = f.rowid
left outer join #Liab  g on a.rowid = g.rowid
where 
    b.ecode is not null or
    c.dcode is not null or
    d.lcode is not null or
    e.earn is not null or
    f.dedn is not null or
    g.liab is not null;
我没有包括EMP,因为我不知道你希望它扮演什么角色。如果这对一个给定的员工来说都是真的,那么你可以添加它,加入它,并完成它

我一点也不喜欢这个解决方案,所以希望其他人能想出更优雅的解决方案

最好的,
David

这基本上与Tango_Guy使用的技术相同,但是没有临时表,并且排序是显式的。因为每个雇员的行数就是规则的确切数量,你什么时候合并,什么时候不合并?我也想看看你对此的要求。实际上,唯一的要求是尽可能“压缩”所有代码以占据一行,以及它的汇总值。在上面的示例中,ECode PerHr是第一行,DCode和LCode都没有;但是,在第二行中,DCode有一个联合代码,该联合代码将与其Dedn和一起上移,以占用空DCode。LCode MyHealth将占用第一排的空LCode。所以,基本上,压缩所有代码以占据前面的行,只要ECode、DCode和LCode有空值;它仍然将代码(Ecode、Dcode、LCode)分离到它自己的单独行中。我以前做过这件事,唯一要做的是向源添加一个顺序,该顺序是一致的,列不为NULL,然后在列上重新联接。不过会很难看的。实际上,您的列值与它们所在的行几乎没有关系,即它们与键无关。这是一个示例,从这里可以看出,Coalesce水平地(跨列)组合了内容。。。他想跨行这样做;这需要跨行执行,如示例所示,通过向上推所有较低的行来占用前面行中的任何空ECode、DCode和LCode;我结合Cade的即兴算法定制了您的代码,并使用表变量将其放入存储过程中。它运行得非常好。谢谢它工作得很好!只是做了一些小的调整,使其适应我们当前的存储过程,并使我们的工资单报表正常工作。也许其他人会在遇到类似情况时发现这些代码作为基础。感谢Cade和Tango_Guy提供的算法!!!
CREATE TABLE data(ID INT IDENTITY NOT NULL,
                  Empl VARCHAR(3), 
                  ECode VARCHAR(8), 
                  DCode VARCHAR(8), 
                  LCode VARCHAR(8),
                  Earn INT NOT NULL,
                  Dedn INT NOT NULL,
                  Liab INT NOT NULL ) ;

INSERT INTO data (Empl, ECode, DCode, LCode, Earn, Dedn, Liab)
VALUES ('123', 'PerHr', NULL, NULL, 13, 0, 0),
        ('123', NULL, 'Union', NULL, 0, 10, 0),
        ('123', NULL, 'Per', NULL, 0, 20, 0),
        ('123', NULL, NULL, 'MyHealth', 0, 0, 5),
        ('123', NULL, NULL, '401', 0, 0, 10),
        ('123', NULL, NULL, 'Train', 0, 0, 15),
        ('123', NULL, NULL, 'CAFTA', 0, 0, 20);

WITH basedata AS (
    SELECT *, ROW_NUMBER () OVER(ORDER BY ID) AS OrigSort, ROW_NUMBER () OVER(PARTITION BY Empl ORDER BY ID) AS EmplSort
    FROM data
),
E AS (
  SELECT Empl, ECode, Earn, ROW_NUMBER () OVER(PARTITION BY Empl ORDER BY OrigSort) AS EmplSort
  FROM basedata
  WHERE ECode IS NOT NULL
),
D AS (
  SELECT Empl, DCode, Dedn, ROW_NUMBER () OVER(PARTITION BY Empl ORDER BY OrigSort) AS EmplSort
  FROM basedata
  WHERE DCode IS NOT NULL
),
L AS (
  SELECT Empl, LCode, Liab, ROW_NUMBER () OVER(PARTITION BY Empl ORDER BY OrigSort) AS EmplSort
  FROM basedata
  WHERE LCode IS NOT NULL
)
SELECT basedata.Empl, E.ECode, D.Dcode, L.LCode, E.Earn, D.Dedn, L.Liab
FROM basedata
LEFT JOIN E
    ON E.Empl = basedata.Empl AND E.EmplSort = basedata.EmplSort
LEFT JOIN D
    ON D.Empl = basedata.Empl AND D.EmplSort = basedata.EmplSort
LEFT JOIN L
    ON L.Empl = basedata.Empl AND L.EmplSort = basedata.EmplSort
WHERE E.ECode IS NOT NULL OR D.DCode IS NOT NULL OR L.LCode IS NOT NULL
ORDER BY basedata.Empl, basedata.EmplSort