如何使用基于多个重复列的序列自动编号重复行 (T-SQL)
How to Auto-Number Duplicate Rows Using Sequence Based on Multiple Duplicate Columns (T-SQL)
尝试使用序列对象为行自动编号创建脚本,其中 [姓氏、出生日期、性别] 列中具有相同值的行被归类为重复项,并且这些重复项中的每一个都分别分配相同的 'Ext ID' 由序列对象赋值.......
当新行通过脚本 运行 时,如果它不能根据 select 列找到匹配项,它应该递增,例如 R5 到 R6,但如果它可以在 table 中找到匹配项,它应该分配预先存在的匹配项的 'Previously assigned' [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],如下所示 table
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
业务场景> table 表示不同业务应用程序中所有客户记录的并集,并且具有相同姓氏、出生日期和性别的行被认为是相同的 'Customer' 在这些不同的业务应用程序,因此分配相同的 [Ext ID] 值有助于将这些相似的行分类在一起,这样 [Ext ID] 可以在外部用于查询和检索这些值相同的所有记录
进一步说明
假设 'Desired' 脚本填充数据库中加载的 'FIRST' 基础 Table 的 [EXT ID],请有人可以创建用于在另一个包含新鲜的 table 中填充 [EXT ID] 的脚本一组新行,如果在 'NEW' table 和 'FIRST' 基金会 table,
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
'NEW' Table 中分配的 [EXT ID] 是从 'FIRST' Table 中的 [EXT ID] 中检索的,但如果没有匹配项, 'NEW' Table 中分配的 [EXT ID] 应该从 'FIRST' 基础 Table 中最后分配的 [EXT ID] 的末尾继续,例如,如果 [EXT ID] 'FIRST' Table 中的 Table 是 R12,'NEW' Table 中的 [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
原因是在现实中,新的新鲜记录会定期从这些业务应用程序中聚合,其中最好总是在 'FIRST' 基础 table 中引用 [EXT ID] 'NEW' tables 因为此列永久用作源业务应用程序的外部参考键
使用 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
你可以看看DENSE_RANK()
https://msdn.microsoft.com/en-us/library/ms173825.aspx
这样使用:
select 'R' + convert(varchar(100), dense_rank() over (order by Surname, Birthday, Sex)), ...
您可以使用 DENSE_RANK()
为 Surname
、BirthDate
和 Sex
的每个唯一组合赋予一个唯一数字,然后只需将其放入更新语句中更新您的专栏:
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;
附录
为了维护它,我会建议一种稍微不同的方法,并有一个单独的 table 来维护您的 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);
实际上,在你的基础 tables 上有一个类似的索引,你可能不需要这个 ExtID 列,你可以加入上面的 table 来获得 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)
,因为这是我所知道的满足竞争条件并违反唯一约束的最不容易受到攻击的方法。
如果所有这些都不是 suitable,那么我仍然会像上面那样建议(如果可能的话)从标识符中删除 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))
);
这只会使获得最大值更容易,并且可能也会使其他用途更容易。
那么,您的更新语句非常相似:
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;
尝试使用序列对象为行自动编号创建脚本,其中 [姓氏、出生日期、性别] 列中具有相同值的行被归类为重复项,并且这些重复项中的每一个都分别分配相同的 'Ext ID' 由序列对象赋值....... 当新行通过脚本 运行 时,如果它不能根据 select 列找到匹配项,它应该递增,例如 R5 到 R6,但如果它可以在 table 中找到匹配项,它应该分配预先存在的匹配项的 'Previously assigned' [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],如下所示 table
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
业务场景> table 表示不同业务应用程序中所有客户记录的并集,并且具有相同姓氏、出生日期和性别的行被认为是相同的 'Customer' 在这些不同的业务应用程序,因此分配相同的 [Ext ID] 值有助于将这些相似的行分类在一起,这样 [Ext ID] 可以在外部用于查询和检索这些值相同的所有记录
进一步说明 假设 'Desired' 脚本填充数据库中加载的 'FIRST' 基础 Table 的 [EXT ID],请有人可以创建用于在另一个包含新鲜的 table 中填充 [EXT ID] 的脚本一组新行,如果在 'NEW' table 和 'FIRST' 基金会 table,
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
'NEW' Table 中分配的 [EXT ID] 是从 'FIRST' Table 中的 [EXT ID] 中检索的,但如果没有匹配项, 'NEW' Table 中分配的 [EXT ID] 应该从 'FIRST' 基础 Table 中最后分配的 [EXT ID] 的末尾继续,例如,如果 [EXT ID] 'FIRST' Table 中的 Table 是 R12,'NEW' Table 中的 [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
原因是在现实中,新的新鲜记录会定期从这些业务应用程序中聚合,其中最好总是在 'FIRST' 基础 table 中引用 [EXT ID] 'NEW' tables 因为此列永久用作源业务应用程序的外部参考键
使用 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
你可以看看DENSE_RANK()
https://msdn.microsoft.com/en-us/library/ms173825.aspx
这样使用:
select 'R' + convert(varchar(100), dense_rank() over (order by Surname, Birthday, Sex)), ...
您可以使用 DENSE_RANK()
为 Surname
、BirthDate
和 Sex
的每个唯一组合赋予一个唯一数字,然后只需将其放入更新语句中更新您的专栏:
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;
附录
为了维护它,我会建议一种稍微不同的方法,并有一个单独的 table 来维护您的 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);
实际上,在你的基础 tables 上有一个类似的索引,你可能不需要这个 ExtID 列,你可以加入上面的 table 来获得 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)
,因为这是我所知道的满足竞争条件并违反唯一约束的最不容易受到攻击的方法。
如果所有这些都不是 suitable,那么我仍然会像上面那样建议(如果可能的话)从标识符中删除 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))
);
这只会使获得最大值更容易,并且可能也会使其他用途更容易。
那么,您的更新语句非常相似:
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;