我尝试将Tomcat应用程序从使用Postgres 9.5迁移到SQL Server 2016,并且我遇到了一个问题陈述,我似乎无法复制。
它基本上是一个upsert,但其中一个复杂因素是请求提供了更新的参数,但是当存在冲突时,我需要使用冲突行中的一些现有值来插入/更新。 表中的主键有时会导致冲突,这需要更新行并删除旧行。
MS SQL中的表模式如下所示:
CREATE TABLE [dbo].[signup](
[site_key] [varchar](32) NOT NULL,
[list_id] [bigint] NOT NULL,
[email_address] [varchar](256) NOT NULL,
[customer_id] [bigint] NULL,
[attribute1] [varchar](64) NULL,
[date1] [datetime] NOT NULL,
[date2] [datetime] NULL,
CONSTRAINT [pk_signup] PRIMARY KEY CLUSTERED
(
[site_key] ASC,
[list_id] ASC,
[email_address] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
旧的Postgres SQL看起来像这样:
WITH updated_rows AS (
INSERT INTO signup
(site_key, list_id, email_address, customer_id, attribute1, date1, date2)
SELECT site_key, list_id, :emailAddress, customer_id, attribute1, date1, date2
FROM signup WHERE customer_id = :customerId and email_address <> :emailAddress
ON CONFLICT (site_key, list_id, email_address) DO UPDATE SET customer_id = excluded.customer_id
RETURNING site_key, customer_id, email_address, list_id
)
DELETE FROM signup AS signup_delete USING updated_rows
WHERE
signup_delete.site_key = updated_rows.site_key
AND signup_delete.customer_id = updated_rows.customer_id
AND signup_delete.list_id = updated_rows.list_id
AND signup_delete.email_address <> :emailAddress;
提供了两个参数,客户ID和电子邮件地址,此处显示为Spring NamedParameterJdbcTemplate值:customerId 和:emailAddress
它尝试将客户ID的电子邮件地址更改为提供的电子邮件地址,但有时提供的电子邮件地址已存在于主键约束中。 在这种情况下,它需要更改现有的客户ID,并删除与新电子邮件地址不匹配的行。
我还需要尝试保持隔离,以便在我更新时不会改变数据。 我尝试用MERGE语句来做这件事,但我似乎无法让它发挥作用,它抱怨我不能使用不在条款范围内的值,但我我想我在这里也可能遇到其他问题。
这是我到目前为止所做的。它甚至没有解决删除部分 - 只有upserting,但我甚至无法使这部分工作。我打算使用OUTPUT作为输入来删除类似于postgres版本的行。
WITH source AS (
SELECT cs.[site_key] as existing_site_key,
cs.list_id as existing_list_id,
cs.email_address as existing_email,
cs.customer_id as existing_customer_id,
cs.attribute1 as existing_attribute1,
cs.date1 as existing_date1,
cs.date2 as existing_date2,
cs2.email_address as conflicting_email,
cs2.customer_id AS conflicting_customer_id
FROM [dbo].[signup] cs
LEFT JOIN [dbo].[signup] cs2 ON cs2.email_address = :emailAddress
AND cs.site_key = cs2.site_key
AND cs.list_id = cs2.list_id
WHERE cs.customer_id = :customerId
)
MERGE signup WITH (HOLDLOCK) AS target
USING source
ON ( source.conflicting_customer_id is not null )
WHEN MATCHED AND source.existing_site_key = target.site_key AND source.existing_list_id = target.list_id AND source.conflicting_email = target.email_address THEN UPDATE
SET customer_id = :customerId
WHEN NOT MATCHED BY target AND source.existing_site_key = target.site_key AND source.existing_list_id = target.list_id AND source.conflicting_customer_id = :customerId THEN INSERT
(site_key, list_id, email_address, customer_id, attribute1, date1, date2) VALUES
(source.existing_site_key, source.existing_list_id, :emailAddress, source.customer_id, source.existing_attribute1, source.existing_date1, source.existing_date2)
谢谢, mikee