2012-08-11 67 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