2012-08-11 52 views
0

任何人都可以帮助一个SQL问题我有我需要合并n个行到一个记录。个人记录可能有也可能没有其他人填写的字段。智能SQL合并 - n行,合并

基本上我有一个问题,在SQL中创建重复记录。有些包含其他人的信息。我需要合并它们(我可以对它们进行排名),如果该值不存在于前一记录中(从最高排名第一位开始),则更新一个字段。

例如,如果我有两个用户记录,一个姓氏填充,另一个具有名字。这些都是重复的,需要合并成一条记录,如合并。但是,这是n行。

它实质上是一个多记录到一个转置,其中只有当排名较低的重复记录填充该字段并且该字段不存在于排名较高的排中时才更新字段。

这是一个非常简化版本的问题。如您所见,使用SQL Fiddle脚本会创建6条记录。这些记录应合并为2条记录并填写所有字段。

问题是,可能有x行数。由于行数不同,我无法使用coalesce语句。

希望有道理吗?

CREATE TABLE [dbo].[Employee]([EmployeeId] varchar(10) NULL, 
[First Name] [varchar](30) NULL, 
[Middle Name] [varchar](30) NOT NULL, 
[Last Name] [varchar](30) NOT NULL, 
[E-Mail] [varchar](80) NOT NULL) 


insert into Employee(EmployeeId,[First Name],[Middle Name],[Last Name],[E-Mail]) 
values('BOB1','Bob','','','[email protected]'); 

insert into Employee(EmployeeId,[First Name],[Middle Name],[Last Name],[E-Mail]) 
values('BOB1','','John','','[email protected]'); 

insert into Employee(EmployeeId,[First Name],[Middle Name],[Last Name],[E-Mail]) 
values('BOB1','','','Smith','[email protected]'); 

insert into Employee(EmployeeId,[First Name],[Middle Name],[Last Name],[E-Mail]) 
values('MARK1','','Peter','','[email protected]'); 

insert into Employee(EmployeeId,[First Name],[Middle Name],[Last Name],[E-Mail]) 
values('MARK1','Mark','','','[email protected]'); 

insert into Employee(EmployeeId,[First Name],[Middle Name],[Last Name],[E-Mail]) 
values('MARK1','','','Davis','[email protected]'); 


select * from [Employee] 

希望是有道理的。

感谢

+3

如果您有解释代码,您的问题可能会更清楚 – codingbiz 2012-08-11 05:39:32

+1

看起来您的问题与此类似:http://stackoverflow.com/q/11785805/10192 – GilM 2012-08-11 06:17:08

+0

-1 ...很多努力回答你的问题;你还没有评论任何答案或标记为正确的! – whytheq 2012-08-27 09:20:58

回答

0

如果性能是重要的,足以证明一对夫妇的编码和小时你被允许使用SQLCLR,就可以计算出所有值在一个表定义Aggregare多参数用户扫描。

这里有一个返回聚集的例子最低排名非NULL字符串:

using System; 
using System.Data; 
using System.Data.SqlClient; 
using System.Data.SqlTypes; 
using System.IO; 
using Microsoft.SqlServer.Server; 

[Serializable] 
[SqlUserDefinedAggregate(Format.UserDefined, MaxByteSize = -1, IsNullIfEmpty = true)] 
public struct LowestRankString : IBinarySerialize 
{ 
    public int currentRank; 
    public SqlString currentValue; 

    public void Init() 
    { 
     currentRank = int.MaxValue; 
     currentValue = SqlString.Null; 
    } 

    public void Accumulate(int Rank, SqlString Value) 
    { 
     if (!Value.IsNull) 
     { 
      if (Rank <= currentRank) 
      { 
       currentRank = Rank; 
       currentValue = Value; 
      } 
     } 
    } 

    public void Merge(LowestRankString Group) 
    { 
     Accumulate(Group.currentRank, Group.currentValue); 
    } 

    public SqlString Terminate() 
    { 
     return currentValue; 
    } 

    public void Read(BinaryReader r) 
    { 
     currentRank = r.ReadInt32(); 
     bool hasValue = r.ReadBoolean(); 
     if (hasValue) 
     { 
      currentValue = new SqlString(r.ReadString()); 
     } 
     else 
     { 
      currentValue = SqlString.Null; 
     } 
    } 

    public void Write(BinaryWriter w) 
    { 
     w.Write(currentRank); 

     bool hasValue = !currentValue.IsNull; 
     w.Write(hasValue); 
     if (hasValue) 
     { 
      w.Write(currentValue.Value); 
     } 
    } 
} 

假设你的表看起来是这样的:

CREATE TABLE TopNonNullRank( ID INT NOT NULL, UserId NVARCHAR(32)NOT NULL, Value1 NVARCHAR(128)NULL, Value2 NVARCHAR(128)NULL, Value3 NVARCHAR(128)NULL, Value4 NVARCHAR(128)NULL, PRIMARY KEY CLUSTERED(Id ASC) );

INSERT INTO TopNonNullRank (Id, UserId, Value1, Value2, Value3, Value4) VALUES 
    (1, N'Ada', NULL, N'Top value 2 for A', N'Top value 3 for A', NULL), 
    (2, N'Ada', N'Top value 1 for A', NULL, N'Other value 3', N'Top value 4 for A'), 
    (3, N'Ada', N'Other value 1 for A', N'Other value 2 for A', N'Other value 3 for A', NULL), 
    (4, N'Bob', N'Top value 1 for B', NULL, NULL, NULL), 
    (5, N'Bob', NULL, NULL, NULL, N'Top value 4 for B'), 
    (6, N'Bob', N'Other value 1 for B', N'Top value 2 for B', NULL, N'Other value 4'); 

以下简单查询返回每个列的顶端非NULL值。

SELECT 
    UserId, 
    dbo.LowestRankString(Id, Value1) AS TopValue1, 
    dbo.LowestRankString(Id, Value2) AS TopValue2, 
    dbo.LowestRankString(Id, Value3) AS TopValue3, 
    dbo.LowestRankString(Id, Value4) AS TopValue4 
FROM TopNonNullRank 
GROUP BY UserId 

唯一剩下的就是将结果合并回原始表。最简单的方法是这样的:

WITH TopValuesPerUser AS 
(
    SELECT 
     UserId, 
     dbo.LowestRankString(Id, Value1) AS TopValue1, 
     dbo.LowestRankString(Id, Value2) AS TopValue2, 
     dbo.LowestRankString(Id, Value3) AS TopValue3, 
     dbo.LowestRankString(Id, Value4) AS TopValue4 
    FROM TopNonNullRank 
    GROUP BY UserId 
) 
UPDATE TopNonNullRank 
SET 
    Value1 = TopValue1, 
    Value2 = TopValue2, 
    Value3 = TopValue3, 
    Value4 = TopValue4 
FROM TopNonNullRank AS OriginalTable 
LEFT JOIN TopValuesPerUser ON TopValuesPerUser.UserId = OriginalTable.UserId; 

请注意,此更新还留给你重复行,你需要摆脱他们。

您也可以获得更多花式并将此查询的结果存储到临时表中,然后使用MERGE语句将它们应用于原始表。

另一种选择是将结果存储在新表中,然后使用sp_rename存储过程将其与原始表交换。

0

没有你正在处理其数据的一个例子是很难精确地解释这个问题。您可以在here中创建一个实例来供我们玩。

因此,没有一个例子,如果我想有数字字段在两个表MYTABLE1和myTable2 [N]和[M]那么为什么不直接使用COALESCE与FULL OUTER JOIN ..

SELECT 
    fieldx = COALESCE(a.fieldx, b.fieldx), 
    fieldy = COALESCE(a.fieldy, b.fieldy), 
    fieldz = COALESCE(a.fieldz, b.fieldz), 
    [N]=SUM(ISNULL(a.[N],0.0)), 
    [M]=SUM(ISNULL(b.[M],0.0)) 
FROM 
    myTable1 a 
    FULL OUTER JOIN myTable2 b 
    ON a.fieldx = b.fieldx 
     a.fieldy = b.fieldy 
     a.fieldz = b.fieldz 
GROUP BY 
    COALESCE(a.fieldx, b.fieldx), 
    COALESCE(a.fieldy, b.fieldy), 
    COALESCE(a.fieldz, b.fieldz) 
0

这应该将特定字段的每行的值设置为该组重复行的排名最高的非空值。

GroupingKey是一个字段或逻辑,用于确定一组需要合并到一个行中的行。排名是您的排名功能。

UPDATE SomeTable SET 
    Field1 = 
    (
     SELECT TOP 1 t2.Field1 
     FROM SomeTable t2 
     WHERE 
      t2.Field1 IS NOT NULL 
      AND 
      t2.GroupingKey = SomeTable.GroupingKey 
     ORDER BY Ranking DESC 
    ) 
FROM SomeTable 

您可以重复这一过程,每次需要更新或只更新查询来设置所有这些,只是复制粘贴场。一旦更新了所有字段,唯一剩下的就是删除重复的记录,并且可以使用相同的GroupingKey和Ranking来删除除特定GroupingKey的排名最高的行外的所有行。

DELETE SomeTable 
FROM SomeTable 
INNER JOIN 
(
    SELECT 
     GroupingKey, 
     MAX(Ranking) as MaxRanking 
    FROM SomeTable 
) t2 ON 
    SomeTable.GroupingKey = t2.GroupingKey 
    AND 
    SomeTable.Ranking < t2.MaxRanking