【问题标题】:SQL Server: Primary Key ConflictSQL Server:主键冲突
【发布时间】:2018-07-19 16:22:27
【问题描述】:

首先,我想为您即将遇到的混乱代码道歉,但这对我的观点很重要。我已经稍微简化了它,但这里通常是我正在使用的代码:

create table [TableExample](
    ID varchar(50) primary key not null,
);

with CTE as (
    select case when ltrim(rtrim(S1.ID)) is null
            then convert(varchar(50),newid())
            else ltrim(rtrim(S1.ID))
        end as 'ID',
    row_number()over(partition by ltrim(rtrim(S1.ID)) order by newid()) as 'duplicates' 
    from [DB2].[dbo].[Source1] as S1
    full outer join [DB2].[dbo].[Source2] as S2
        on S2.[ID]=S1.[ID]
    full outer join [DB2].[dbo].[Source3] as S3
        on S3.[ID]=S1.[ID])

insert into [dbo].[TableExample] (
    [ID]
)

select  distinct case 
        when [duplicates] > 1
            then convert(varchar(50),newid())
            else CTE.[ID]
        end as 'ID'
from [DB2].[dbo].[Source1] as S1
full outer join [DB2].[dbo].[Source2] as S2
    on ltrim(rtrim(S2.ID))=ltrim(rtrim(S1.ID))
full outer join [DB2].[dbo].[Source3] as S3
    on ltrim(rtrim(S3.ID))=ltrim(rtrim(S1.ID))
full outer join CTE on CTE.ID=ltrim(rtrim(S1.ID))
where (S2.ID is not null or S3.ID is not null or S1.ID is not null)
    and [duplicates] = 1

如您所见,我正在运行多个非常冗余的检查,以确保主键字段 [ID] 没有收到任何重复项。我选择 distinct,使用 CTE 标记任何重复的键然后拒绝它们,最后如果仍然通过,我将重复项更改为

convert(varchar(50),newid())

然而,每次我运行它时都会遇到重复键错误。

如果您想知道,是的,我每次都会删除表格,以确保没有保留任何可能导致错误的内容。

有超过 100,000 行数据需要输入,我对这件事束手无策。任何建议将不胜感激!

【问题讨论】:

  • 您能否提供表 Source1、Source2 和 Source3 的示例数据,并说明您要查找哪些重复项?令人困惑的部分是,在 CTE 中,Source2 和 Source3 已连接,但未使用。重复项是否分布在这些表中?您可能需要联合而不是联接。
  • 错误消息标识重复值。所以删除插入语句,只需对提到的 ID 值运行选择查询。而且你的逻辑很密集,正如所写的那样,没有什么意义。您是否假设每个表中的 ID 都是唯一的? CTE 和实际 select 语句中完全连接的重复很难理解 - 不一致的连接逻辑使情况变得更糟。很少有人需要修剪字符串以进行连接,但如果你这样做了,那你为什么在 cte 中修剪呢?

标签: sql sql-server key


【解决方案1】:

您似乎正在尝试合并来自 Source1、Source2 和 Source3 的 ID,修剪它们并查找重复项。如果有重复的行,保留一个,并用新的唯一标识符替换其他行。这正是实现了这一点:

CREATE TABLE TableExample ( ID VARCHAR(50) PRIMARY KEY NOT NULL)
CREATE TABLE Source1 ( ID VARCHAR(50) PRIMARY KEY NOT NULL)
CREATE TABLE Source2 ( ID VARCHAR(50) PRIMARY KEY NOT NULL)
CREATE TABLE Source3 ( ID VARCHAR(50) PRIMARY KEY NOT NULL)

INSERT INTO Source1 VALUES ('6C0240E7-847B-41F3-9DCA-D2EA54FB0E96'), (' 6C0240E7-847B-41F3-9DCA-D2EA54FB0E96 '), ('007171B1-10DB-4DD2-832F-026C968AC19A'), ('A28976BC-443F-44E6-9AED-B41DAFBBB651')
INSERT INTO Source2 VALUES ('6C0240E7-847B-41F3-9DCA-D2EA54FB0E96 '), ('007171B1-10DB-4DD2-832F-026C968AC19A'), ('80373FA7-5A61-41AB-B7DA-1F807A0E442B'), ('A38C6C43-A2B1-4C5B-8699-601054FB7968')
INSERT INTO Source3 VALUES ('  6C0240E7-847B-41F3-9DCA-D2EA54FB0E96 '), ('1A366E5C-0B6C-4B3D-B3E4-126035D641F1'), ('1183D160-AF00-43C1-8EA1-E953B51918E4'), (' 80373FA7-5A61-41AB-B7DA-1F807A0E442B ')

;WITH CTE AS
(
    SELECT RTRIM(LTRIM(ID)) ID
    FROM Source1 
    UNION ALL
    SELECT RTRIM(LTRIM(ID)) ID
    FROM Source2
    UNION ALL
    SELECT RTRIM(LTRIM(ID)) ID
    FROM Source3
),
CTE2 AS
(
    SELECT ID, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY NEWID()) AS RN
    FROM CTE
)
INSERT INTO TableExample (ID)
SELECT CASE WHEN RN > 1 THEN CAST(NEWID() AS VARCHAR(50)) ELSE ID END AS ID
FROM CTE2

SELECT * FROM TableExample

输出:

ID
007171B1-10DB-4DD2-832F-026C968AC19A
0841CEF6-B70E-442F-90CD-B79C235107F9
0AE6C8EC-31CF-49EA-B992-09E0CDC32B61
1183D160-AF00-43C1-8EA1-E953B51918E4
16EE8FD4-E5C9-480E-98DB-1410DCB0CFF5
1A366E5C-0B6C-4B3D-B3E4-126035D641F1
2C5F8AE0-CE70-406C-96C4-FE5144C16634
6C0240E7-847B-41F3-9DCA-D2EA54FB0E96
80373FA7-5A61-41AB-B7DA-1F807A0E442B
A28976BC-443F-44E6-9AED-B41DAFBBB651
A38C6C43-A2B1-4C5B-8699-601054FB7968
CB426799-E85E-4D13-AC6A-16ACC571333A

【讨论】:

  • 我正在尝试这个建议的变体。我提供的代码显然有点过于简化了。除了 ID 之外,还有其他字段,它们与每个源不同,因此它确实需要是联接而不是联合。话虽如此,即使进行了更改,它看起来也比我以前的要干净得多。我会再次发布结果。
  • 是的!修改后的代码效果很好,谢谢。我已对您的评论表示赞成,但由于
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多