涉及以下表格:
Table Product:
product_id
merged_product_id
product_name
Table Company_Product:
product_id
company_id
(Company_Product在product_id和company_id列上都有一个主键)
我现在想要在Company_Product上运行更新,将product_id列设置为merged_ product_id。此更新可能会导致重复操作,从而触发主键冲突,因此我在where子句中添加了“not exists”检查,我的查询如下所示:
update cp
set cp.product_id = p.merged_product_id
from Company_Product cp
join Product p on p.product_id = cp.product_id
where p.merged_product_id is not null
and not exists
(select * from Company_Product cp2
where cp2.company_id = cp.company_id and
cp2.product_id = p.merged_product_id)
但是这个查询因主键违规而失败。
我认为可能会发生因为Product表包含多个具有相同merged_product_id的行,它将成功用于第一个产品,但是当使用相同的merged_product_id转到下一个产品时,它将失败,因为'不存在'子查询没有看到第一个更改,因为查询尚未完成并已提交。
我是否正确地思考这个问题,以及如何更改查询以使其正常工作?
[编辑] 一些数据示例:
Product:
product_id merged_product_id
23 35
24 35
25 12
26 35
27 NULL
Company_Product:
product_id company_id
23 2
24 2
25 2
26 3
27 4
[编辑2] 最后我使用了此解决方案,该解决方案使用临时表进行更新,然后将更新的数据插入到原始的Company_Product表中:
create table #Company_Product
(product_id int, company_id int)
insert #Company_Product select * from Company_Product
update cp
set cp.product_id = p.merged_product_id
from #Company_Product cp
join Product p on p.product_id = cp.product_id
where p.merged_product_id is not null
delete from Company_Product
insert Company_Product select distinct * from #Company_Product
drop table #Company_Product
答案 0 :(得分:2)
主键应该是三件事:
通过更改部分主键,您违反了要求#3。
我认为你最好创建一个新表,填充它,然后删除约束,删除原始表,并将新表重命名为所需的名称(当然,重新应用原始约束) 。根据我的经验,这使您有机会在“新”数据生效前检查它们。
分享并享受。
答案 1 :(得分:1)
如果您至少使用SQL 2008,则可以使用MERGE。
否则,您将不得不选择一个标准来建立您想要的 merged_product_id以及您遗漏哪一个:
update cp
set cp.product_id = p.merged_product_id
from Company_Product cp
cross apply (
select top(1) merged_product_id
from Product
where product_id = cp.product_id
and p.merged_product_id is not null
and not exists (
select * from Company_Product cp2
where cp2.company_id = cp.company_id and
cp2.product_id = merged_product_id)
order by <insert diferentiating criteria here>) as p
请注意,如果多个并发请求正在运行合并逻辑,则这不安全。
答案 2 :(得分:1)
我不能完全看看你的结构是如何工作的,或者这个更新试图实现的目标。您似乎正在更新Company_Product
并在显然具有不同product_id
的现有行上设置(新)product_id
;例如,将行从一个产品更改为另一个产品。这似乎是一个奇怪的用例,我希望你能插入一个新的独特行。所以我想我错过了什么。
如果你转换 Company_Product
使用一组新的产品ID而不是旧的产品ID(名称“merged_product_id”让我推测这个),你确定那里吗?新旧之间没有重叠? 那会导致像你所描述的那样的问题。
答案 3 :(得分:0)
如果没有看到您的数据,我相信您的分析是正确的 - 整个集合已更新,然后提交失败,因为它会导致违反约束。在某些UPDATE的“部分提交”之后,永远不会重新评估EXISTS。
我认为您需要更准确地定义有关尝试根据merged_product_id将多个产品更改为同一产品的规则,然后在查询中明确说明这些规则。例如,您可以排除任何属于该类别的产品,并使用适当的查询进一步提供NOT EXISTS。
答案 4 :(得分:0)
我认为你对更新失败的原因是正确的。要解决此问题,请在company_product表上运行删除查询,以删除将应用相同merged_prduct_id的额外product_ids。
这里是查询可能是什么
delete company_product
where product_id not in (
select min(product_id)
from product
group by merged_product_id
)
and product_id not in (
select product_id
from product
where merged_product_id is null
)
- 在评论中添加了解释 -
这样做的目的是删除更新后将重复的行。由于您的产品具有多个合并ID,因此在完成后您实际上只需要表中的一个产品(针对每个公司)。因此,我的查询(如果有效......)将保留每个合并产品ID的最小原始产品ID - 然后您的更新将有效。
所以,假设您有3个产品ID,它们将映射到2个合并的ID:1 - &gt; 10,2 - &gt; 20,3 - &gt; 20.您有以下company_product数据:
product_id company_id
1 A
2 A
3 A
如果针对此运行更新,它将尝试将第二行和第三行都更改为产品ID 20,它将失败。如果你运行我建议的删除,它将删除第三行。删除和更新后,表格如下所示:
product_id company_id
10 A
20 A
答案 5 :(得分:0)
试试这个:
create table #Company_Product
(product_id int, company_id int)
create table #Product (product_id int,merged_product_id int)
insert into #Company_Product
select 23, 2
union all select 24, 2
union all select 25, 2
union all select 26, 3
union all select 27, 4
insert into #product
Select 23, 35
union all select 24, 35
union all select 25, 12
union all select 26, 35
union all select 27, NULL
update cp
set product_id = merged_product_id
from #company_product cp
join
(
select min(product_id) as product_id, merged_product_id
from #product where merged_product_id is not null
group by merged_product_id
) a on a.product_id = cp.product_id
delete cp
--select *
from #company_product cp
join #product p on cp.product_id = p.product_id
where cp.product_id <> p.merged_product_id
and p.merged_product_id is not null