如何使用基于多个重复列(T-SQL)的序列自动为重复行编号

时间:2015-09-21 14:12:07

标签: sql tsql ssis

尝试使用序列对象创建自动编号行的脚本,其中列[姓氏,出生日期,性别]中具有相同值的行被分类为重复项,并且每个重复项分别被分配相同的“外部ID”由序列对象分配........ 当新行通过脚本运行时,如果它无法根据选择列找到匹配项,它应该增加例如R5到R6但如果它可以在表中找到匹配项,它应该分配“先前分配的”[Ext ID]预先存在的匹配,而不是冗余地增加新的[Ext ID]'

Ref Surname Firstname   Birthdate   Sex ExternalSource  Ext ID
1   AAA     AA          1/1/2000    M   Alpha           Null
2   BBB     BB          1/1/2001    F   Beta            Null
3   AAA     AA          1/1/2000    M   Beta            Null
4   CCC     CC          1/1/2003    M   Alpha           Null
5   BBB     BB          1/1/2001    F   Gamma           Null
6   DDD     DD          1/1/2004    M   Beta            Null
7   CCC     CC          1/1/2003    M   Alpha           Null
8   AAA     AA          1/1/2000    M   Gamma           Null

这样脚本相应地填充[Ext ID]列,如下表

Ref Surname Firstname   Birthdate   Sex ExternalSource  Ext ID
1   AAA     AA          1/1/2000    M   Alpha           R1
2   BBB     BB          1/1/2001    F   Beta            R2
3   AAA     AA          1/1/2000    M   Beta            R1
4   CCC     CC          1/1/2003    M   Alpha           R3
5   BBB     BB          1/1/2001    F   Gamma           R2
6   DDD     DD          1/1/2004    M   Beta            R4
7   CCC     CC          1/1/2003    M   Alpha           R3
8   AAA     AA          1/1/2000    M   Gamma           R1

业务场景>该表表示在单独的业务应用程序中具有相同姓氏的行的所有客户记录的联合,生日和&在这些不同的业务应用程序中,性别被认为是相同的“客户”,因此分配相同的[外部ID]值有助于将这些相似的行分类在一起,以便[外部ID]可以在外部用于查询&检索这些值相同的所有记录

进一步澄清 假设'Desired'脚本填充了数据库中加载的'FIRST'基础表的[EXT ID],请有人创建脚本以在另一个表中填充[EXT ID],该表包含新的一组新行,例如基于相同的选择列[姓氏,出生日期,性别],如果在此“新”表和“FIRST”基础表之间找到匹配项,

Ref Surname Firstname   Birthdate   Sex ExternalSource  Ext ID
9   AAA     AA          1/1/2000    M   Alpha           Null
10  EEE     EE          1/1/2001    F   Beta            Null
11  AAA     AA          1/1/2000    M   Beta            Null
12  CCC     CC          1/1/2003    M   Alpha           Null
13  EEE     EE          1/1/2001    F   Gamma           Null
14  FFF     FF          1/1/2004    M   Beta            Null
15  CCC     CC          1/1/2003    M   Alpha           Null
16  AAA     AA          1/1/2000    M   Gamma           Null

从'FIRST'表中的[EXT ID]中检索“NEW”表中指定的[EXT ID],但如果没有匹配,则“NEW”表中指定的[EXT ID]应继续从'FIRST'基础表中最后一个[EXT ID]的末尾开始,例如,如果'FIRST'表中的[EXT ID]是R12,'NEW'表中的[EXT ID]应该是R13

Ref Surname Firstname   Birthdate   Sex ExternalSource  Ext ID
9   AAA     AA          1/1/2000    M   Alpha           R1
10  EEE     EE          1/1/2001    F   Beta            R5
11  AAA     AA          1/1/2000    M   Beta            R1
12  CCC     CC          1/1/2003    M   Alpha           R3
13  EEE     EE          1/1/2001    F   Gamma           R5
14  FFF     FF          1/1/2004    M   Beta            R6
15  CCC     CC          1/1/2003    M   Alpha           R3
16  AAA     AA          1/1/2000    M   Gamma           R1

原因是实际上,新的记录将定期从这些业务应用程序中汇总而来,因为本列永久引用“新”表中的“首页”基础表中的[EXT ID]是理想的作为源业务应用程序的外部参考密钥

3 个答案:

答案 0 :(得分:3)

您可以使用DENSE_RANK()SurnameBirthDateSex的每个唯一组合提供唯一编号,然后将其放入更新语句中进行更新你的专栏:

UPDATE  t
SET     ExtID = NewExtID
FROM    (   SELECT  ExtID,
                    NewExtID = 'R' + CAST(DENSE_RANK() 
                                            OVER(ORDER BY Surname, Birthdate, Sex) 
                                        AS VARCHAR(10))
            FROM    dbo.YourTableName
        ) AS t;

完整的工作示例

IF OBJECT_ID(N'tempdb..#T', 'U') IS NOT NULL
    DROP TABLE #T;

CREATE TABLE #T
(   Ref INT, 
    Surname VARCHAR(50), 
    Firstname VARCHAR(50), 
    Birthdate DATE, 
    Sex CHAR(1), 
    ExternalSource VARCHAR(50), 
    ExtID VARCHAR(11)
);

INSERT #T (Ref, Surname, Firstname, Birthdate, Sex, ExternalSource)
VALUES
    (1, 'AAA', 'AA', '2000-01-01', 'M', 'Alpha'),
    (2, 'BBB', 'BB', '2001-01-01', 'F', 'Beta'),
    (3, 'AAA', 'AA', '2000-01-01', 'M', 'Beta'),
    (4, 'CCC', 'CC', '2003-01-01', 'M', 'Alpha'),
    (5, 'BBB', 'BB', '2001-01-01', 'F', 'Gamma'),
    (6, 'DDD', 'DD', '2004-01-01', 'M', 'Beta'),
    (7, 'CCC', 'CC', '2003-01-01', 'M', 'Alpha'),
    (8, 'AAA', 'AA', '2000-01-01', 'M', 'Gamma');

UPDATE  t
SET     ExtID = NewExtID
FROM    (   SELECT  ExtID,
                    NewExtID = 'R' + CAST(DENSE_RANK() 
                                            OVER(ORDER BY Surname, Birthdate, Sex) 
                                        AS VARCHAR(10))
            FROM    #T
        ) AS t;

SELECT  *
FROM    #T
ORDER BY Ref;       

<强>附录

为了保持这一点,我建议采用略有不同的方法,并有一个单独的表来维护你的ExtID,这将允许你利用一个标识列:

CREATE TABLE dbo.Ext 
(
        ID INT IDENTITY(1, 1) NOT NULL,
        Surname VARCHAR(50) NOT NULL,
        BirthDate DATE NOT NULL,
        Sex CHAR(1) NOT NULL,
        ExtID AS 'R' + CAST(ExtIntID AS VARCHAR(10)),
    CONSTRAINT PK_Ext__ID PRIMARY KEY (ID),
);
CREATE UNIQUE NONCLUSTERED INDEX UQ_Ext__Surname_Birthdate_Sex ON dbo.Ext (Surname, Birthdate, Sex);

实际上,你的基表上有一个类似的索引,你可能不需要这个ExtID列,你可以加入上面的表来获得ExtID并没有太大的性能影响,但是你很可能确实需要更新你可以使用的ExtID列:

MERGE dbo.Ext AS e WITH (HOLDLOCK)
USING 
(   SELECT  DISTINCT Surname, Birthdate, Sex
    FROM    dbo.YourTable
) AS t
    ON t.Surname = e.Surname
    AND t.Birthdate = e.Birthdate
    AND t.Sex = e.Sex
WHEN NOT MATCHED THEN 
    INSERT (Surname, Birthdate, Sex)
    VALUES (t.Surname, t.Birthdate, t.Sex);

UPDATE  t
SET     ExtID = r.ExtID
FROM    db.YourTable AS t
        INNER JOIN dbo.Ext AS e
            ON e.Surname = t.Surname
            AND e.Birthdate = t.Birthdate
            AND e.Sex = t.Sex
WHERE   t.ExtID IS NULL;

我使用过MERGE WITH (HOLDLOCK),因为这是我所知道的最不容易遇到的竞争条件,并且会遇到唯一的约束违规行为。

如果所有这些都不合适,那么我仍然建议如上所述(如果可能)从标识符中删除R,并使其只是一个整数。如果需要,您可以将文本列创建为计算列:

CREATE TABLE #T
(   Ref INT, 
    Surname VARCHAR(50), 
    Firstname VARCHAR(50), 
    Birthdate DATE, 
    Sex CHAR(1), 
    ExternalSource VARCHAR(50), 
    ExtIntID INT,
    ExtID AS 'R' + CAST(ExtIntID AS VARCHAR(10))
);

这样可以使maximim变得更容易,也可能使其他用途更容易。

然后,您的更新语句非常相似:

UPDATE  t
SET     ExtIntID = NewExtID
FROM    (   SELECT  t.ExtIntID,
                    NewExtID = CASE WHEN e.ExtIntID IS NOT NULL THEN e.ExtIntID
                                ELSE
                                    ISNULL(m.MaxID, 0) + 
                                    DENSE_RANK() OVER(PARTITION BY e.ExtIntID
                                                    ORDER BY t.Surname, t.Birthdate, t.Sex) 
                                END
            FROM    #T AS t
                    LEFT JOIN
                    (   SELECT  Surname, Birthdate, Sex, ExtIntID = MAX(ExtIntID)
                        FROM     #T
                        GROUP BY Surname, Birthdate, Sex
                    ) AS e
                        ON e.Surname = t.Surname
                        AND e.Birthdate = t.Birthdate
                        AND e.Sex = t.Sex
                    OUTER APPLY (SELECT MAX(ExtIntID) FROM #T) AS m (MaxID)
            WHERE   t.ExtIntID IS NULL              
        ) AS t;

如果您无法创建INT列,则更新非常相似,您只需要更多地格式化:

UPDATE  t
SET     ExtID = NewExtID
FROM    (   SELECT  t.ExtID,
                    NewExtID = CASE WHEN e.ExtID IS NOT NULL THEN e.ExtID
                                ELSE
                                    'R' + 
                                    CAST(ISNULL(m.MaxID, 0) + 
                                        DENSE_RANK() OVER(PARTITION BY e.ExtID
                                                            ORDER BY t.Surname, t.Birthdate, t.Sex) 
                                        AS VARCHAR(10))
                                END
            FROM    #T AS t
                    LEFT JOIN
                    (   SELECT  Surname, Birthdate, Sex, ExtID = MAX(ExtID)
                        FROM     #T
                        GROUP BY Surname, Birthdate, Sex
                    ) AS e
                        ON e.Surname = t.Surname
                        AND e.Birthdate = t.Birthdate
                        AND e.Sex = t.Sex
                    OUTER APPLY (SELECT MAX(CONVERT(INT, SUBSTRING(ExtID, 2, LEN(ExtID)))) FROM #T) AS m (MaxID)
            WHERE   t.ExtID IS NULL             
        ) AS t;

答案 1 :(得分:1)

使用SELECT DISTINCT仅为每个人获取一次,然后使用ROW_NUMBER()创建您的ID。

    SELECT DISTINCT Surname, Birthdate, Sex, ROW_NUMBER() OVER (ORDER by
 Surname,Birthdate, Sex) as RowNum
    FROM mytable

然后您可以使用它来使用UPDATE语句分配这些值:

   UPDATE mytable
    SET [Ext ID] = 'R'+cast(RowNum as varchar)
    FROM
    mytable
    INNER JOIN
    (SELECT DISTINCT Surname, Birthdate, Sex, ROW_NUMBER() OVER (
ORDER by Surname,Birthdate, Sex) as RowNum
    FROM mytable) AS generateIds
    ON generateIds.Surname=mytable.Surname
    AND generateIds.Birthdate=mytable.Birthdate
    NAD generateIds.Sex=mytable.Sex

答案 2 :(得分:0)

您可以查看DENSE_RANK() https://msdn.microsoft.com/en-us/library/ms173825.aspx

像这样使用:

select 'R' + convert(varchar(100), dense_rank() over (order by Surname, Birthday, Sex)), ...