使用没有明显匹配条件的 table 变量进行 MERGE
MERGE using a table variable without an obvious matching criteria
我有一个供用户填写的 InfoPath 表单。它被分配了一个唯一的报告编号,在报告中他们可以 select 零个或多个受影响的部分,每个部分都有一堆列。随着报告的处理,受影响部件的列表可能会发生变化。向 SharePoint 提交或重新提交报告会调用一个 SQL 存储过程,其参数包括列数据,分号分隔,一个 NVARCHAR(MAX)
代表一列中的多行。存储过程解析以分号分隔的 NVARCHAR
以填充 table 变量,然后将该 table 变量与将用于数据分析的主 table 合并。当用户每个报告只有一个具有给定零件号的受影响零件时,一切都很好(因为我的 MERGE
语句使用它来确定它是否需要更新现有行或创建新行),最初是acceptable 限制。粗略SQL总结:
CREATE TABLE [Report_to_Parts](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Report_ID] [nvarchar](255) NOT NULL,
[PartNumberOrdered] [nvarchar](255) NULL,
[OtherColumns] [nvarchar](255) NULL)
CREATE PROCEDURE [MergeReport_sp]
@paramReport_ID nvarchar(255),
@paramPartNumberString nvarchar(MAX) = NULL,
@paramOtherColumnsString nvarchar(MAX) = NULL
AS
BEGIN
DECLARE @tempPartTable TABLE (
[Report_ID] [nvarchar](255) NOT NULL,
[PartNumberOrdered] [nvarchar](255) NOT NULL,
[OtherColumns] [nvarchar](255) NULL)
DECLARE @WorkingPartNumberString nvarchar(255),
@WorkingOtherColumnsString nvarchar(255)
-- Magic to parse the semicolon delimited parameters omitted
WHILE (PartsRemainingToParse)
BEGIN
INSERT INTO @tempPartTable (
[Report_ID],
[PartNumberOrdered],
[OtherColumns])
VALUES (
@paramReport_ID,
@WorkingPartNumberString,
@WorkingOtherColumnsString)
END
MERGE [Report_to_Parts]
USING @tempPartTable AS [Source]
ON (
[Report_to_Parts].[Report_ID] = @paramReport_ID AND
[Report_to_Parts].[PartNumberOrdered] = [Source].[PartNumberOrdered])
WHEN NOT MATCHED BY TARGET
THEN INSERT(
[Report_ID],
[PartNumberOrdered],
[OtherColumns])
VALUES (
@paramReport_ID,
[Source].[PartNumberOrdered],
[Source].[OtherColumns])
WHEN MATCHED
THEN UPDATE SET [OtherColumns]=[Source].[OtherColumns]
WHEN NOT MATCHED BY SOURCE AND [Report_to_Parts].[Report_ID] = @paramReport_ID
THEN DELETE;
END
当前工作的输入和结果,假设 [Report_ID] 为 123456 的部分在 [Report_to_Parts] 中不存在,但其他一些条目存在:
EXEC MergeReport_sp
@paramReport_ID = N'123456',
@paramPartNumberString = N'Part 1;abcd-efg;Part 3;'
@paramOtherColumnsString = N'There are many;other columns;but I simplified;'
SELECT * FROM [Report_to_Parts] WHERE [Report_ID] = N'123456'
---------------------------------------------------------
| ID | Report_ID | PartNumberOrdered | OtherColumns |
| 05 | 123456 | Part 1 | There are many |
| 06 | 123456 | abcd-efg | other columns |
| 07 | 123456 | Part 3 | but I simplified |
EXEC MergeReport_sp
@paramReport_ID = N'123456',
@paramPartNumberString = N'Part 1;Part 3;New Part;'
@paramOtherColumnsString = N'Updates;Work;Too;'
SELECT * FROM [Report_to_Parts] WHERE [Report_ID] = N'123456'
-----------------------------------------------------
| ID | Report_ID | PartNumberOrdered | OtherColumns |
| 05 | 123456 | Part 1 | Updates |
| 07 | 123456 | Part 3 | Work |
| 08 | 123456 | New Part | Too |
但是以下失败了,因为它试图以 PartNumberOrdered 为基础:
EXEC MergeReport_sp
@paramReport_ID = N'123456',
@paramPartNumberString = N'Part 1;Part 1;'
@paramOtherColumnsString = N'Thing 1;Thing 2;'
现在我需要它能够优雅地处理多个部件共享部件号的报告。离开 InfoPath and/or SharePoint 不是一个选项,也不是删除分号解析方面。我正在考虑的选项:
- 存储过程的每次调用,从 [Report_to_Parts] 中删除具有匹配 [Report_ID] 的所有行并插入所有 @tempPartTable 行。这是我想要的数据分析最终结果,但我担心它效率低得可怕,并且 [ID] 会随着系统的使用而变得不必要的大。
- 更改 [Report_to_Parts] 使其具有 [ID] 和 [Report_ID] 的复合键,并想出一种方法让 [ID] 从 1 开始自动递增每个 [Report_ID],然后如上删除插入。
- 为 [ID] 添加一列到 @tempPartTable,[Report_to_Parts]
SELECT
以获取此 [[=] table 中已有部件的 [ID] 42=]],根据它设置@tempPartTable [ID]s,MERGE
on [ID]。
- 更改 [Report_to_Parts] 使其具有复合键,为 [ID] 添加一列到 @tempPartTable,该列从 1 开始自动递增,
MERGE
按 [ID] 和 [Report_ID].
我在想出 3 的代码时遇到了困难,而且我当然也愿意接受其他想法。 Here's an earlier question I asked about this same system.
写这个问题给了我一些想法,我能够通过选项 4 解决这个问题。我修改了 [Report_to_Parts] 以便 [ID] 不是身份,而是一个复合主键以及[Report_ID]。然后我更新了存储过程代码(还包括解析 "magic" 以防有人偶然发现这个 post 试图解决解析问题):
ALTER PROCEDURE [MergeReport_sp]
@paramReport_ID nvarchar(255),
@paramPartNumberString nvarchar(MAX) = NULL,
@paramOtherColumnsString nvarchar(MAX) = NULL
AS
BEGIN
DECLARE @tempPartTable TABLE (
[ID] [int] NOT NULL,
[Report_ID] [nvarchar](255) NOT NULL,
[PartNumberOrdered] [nvarchar](255) NOT NULL,
[OtherColumns] [nvarchar](255) NULL)
DECLARE @WorkingPartNumberString nvarchar(255),
@WorkingOtherColumnsString nvarchar(255),
@PartNumberPos int = CHARINDEX(N';', @paramPartNumberString),
@OtherColumnsPos int = CHARINDEX(N';', @paramOtherColumnsString),
@PartTableID int = 1
WHILE (ISNULL(@PartNumberPos,0) > 0)
BEGIN
IF @OtherColumnsPos = 0 SET @OtherColumnsPos = 1
SET @WorkingPartNumberString = NULLIF(SUBSTRING(@paramPartNumberString,1,@PartNumberPos - 1), '')
SET @WorkingOtherColumnsString = NULLIF(SUBSTRING(@WorkingOtherColumnsString ,1,@OtherColumnsPos - 1), '')
INSERT INTO @tempPartTable (
[ID],
[Report_ID],
[PartNumberOrdered],
[OtherColumns])
VALUES (
@PartTableID,
@paramReport_ID,
@WorkingPartNumberString,
@WorkingOtherColumnsString)
SET @PartTableID = @PartTableID + 1
SET @paramPartNumberString = SUBSTRING(@paramPartNumberString, @PartNumberPos+1, LEN(@paramPartNumberString))
SET @paramOtherColumnsString = SUBSTRING(@paramOtherColumnsString, @OtherColumnsPos +1, LEN(@paramOtherColumnsString))
SET @PartNumberPos = CHARINDEX(N';',@paramPartNumberString)
SET @OtherColumnsPos = CHARINDEX(N';',@paramOtherColumnsString)
END
MERGE [Report_to_Parts]
USING @tempPartTable AS [Source]
ON (
[Report_to_Parts].[ID] = [Source].[ID] AND
[Report_to_Parts].[Report_ID] = @paramReport_ID)
WHEN NOT MATCHED BY TARGET
THEN INSERT(
[ID],
[Report_ID],
[PartNumberOrdered],
[OtherColumns])
VALUES (
[Source].[ID],
@paramReport_ID,
[Source].[PartNumberOrdered],
[Source].[OtherColumns])
WHEN MATCHED
THEN UPDATE SET
[PartNumberOrdered] = [Source].[PartNumberOrdered]
[OtherColumns]=[Source].[OtherColumns]
WHEN NOT MATCHED BY SOURCE AND [Report_to_Parts].[Report_ID] = @paramReport_ID
THEN DELETE;
END
我有一个供用户填写的 InfoPath 表单。它被分配了一个唯一的报告编号,在报告中他们可以 select 零个或多个受影响的部分,每个部分都有一堆列。随着报告的处理,受影响部件的列表可能会发生变化。向 SharePoint 提交或重新提交报告会调用一个 SQL 存储过程,其参数包括列数据,分号分隔,一个 NVARCHAR(MAX)
代表一列中的多行。存储过程解析以分号分隔的 NVARCHAR
以填充 table 变量,然后将该 table 变量与将用于数据分析的主 table 合并。当用户每个报告只有一个具有给定零件号的受影响零件时,一切都很好(因为我的 MERGE
语句使用它来确定它是否需要更新现有行或创建新行),最初是acceptable 限制。粗略SQL总结:
CREATE TABLE [Report_to_Parts](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Report_ID] [nvarchar](255) NOT NULL,
[PartNumberOrdered] [nvarchar](255) NULL,
[OtherColumns] [nvarchar](255) NULL)
CREATE PROCEDURE [MergeReport_sp]
@paramReport_ID nvarchar(255),
@paramPartNumberString nvarchar(MAX) = NULL,
@paramOtherColumnsString nvarchar(MAX) = NULL
AS
BEGIN
DECLARE @tempPartTable TABLE (
[Report_ID] [nvarchar](255) NOT NULL,
[PartNumberOrdered] [nvarchar](255) NOT NULL,
[OtherColumns] [nvarchar](255) NULL)
DECLARE @WorkingPartNumberString nvarchar(255),
@WorkingOtherColumnsString nvarchar(255)
-- Magic to parse the semicolon delimited parameters omitted
WHILE (PartsRemainingToParse)
BEGIN
INSERT INTO @tempPartTable (
[Report_ID],
[PartNumberOrdered],
[OtherColumns])
VALUES (
@paramReport_ID,
@WorkingPartNumberString,
@WorkingOtherColumnsString)
END
MERGE [Report_to_Parts]
USING @tempPartTable AS [Source]
ON (
[Report_to_Parts].[Report_ID] = @paramReport_ID AND
[Report_to_Parts].[PartNumberOrdered] = [Source].[PartNumberOrdered])
WHEN NOT MATCHED BY TARGET
THEN INSERT(
[Report_ID],
[PartNumberOrdered],
[OtherColumns])
VALUES (
@paramReport_ID,
[Source].[PartNumberOrdered],
[Source].[OtherColumns])
WHEN MATCHED
THEN UPDATE SET [OtherColumns]=[Source].[OtherColumns]
WHEN NOT MATCHED BY SOURCE AND [Report_to_Parts].[Report_ID] = @paramReport_ID
THEN DELETE;
END
当前工作的输入和结果,假设 [Report_ID] 为 123456 的部分在 [Report_to_Parts] 中不存在,但其他一些条目存在:
EXEC MergeReport_sp
@paramReport_ID = N'123456',
@paramPartNumberString = N'Part 1;abcd-efg;Part 3;'
@paramOtherColumnsString = N'There are many;other columns;but I simplified;'
SELECT * FROM [Report_to_Parts] WHERE [Report_ID] = N'123456'
---------------------------------------------------------
| ID | Report_ID | PartNumberOrdered | OtherColumns |
| 05 | 123456 | Part 1 | There are many |
| 06 | 123456 | abcd-efg | other columns |
| 07 | 123456 | Part 3 | but I simplified |
EXEC MergeReport_sp
@paramReport_ID = N'123456',
@paramPartNumberString = N'Part 1;Part 3;New Part;'
@paramOtherColumnsString = N'Updates;Work;Too;'
SELECT * FROM [Report_to_Parts] WHERE [Report_ID] = N'123456'
-----------------------------------------------------
| ID | Report_ID | PartNumberOrdered | OtherColumns |
| 05 | 123456 | Part 1 | Updates |
| 07 | 123456 | Part 3 | Work |
| 08 | 123456 | New Part | Too |
但是以下失败了,因为它试图以 PartNumberOrdered 为基础:
EXEC MergeReport_sp
@paramReport_ID = N'123456',
@paramPartNumberString = N'Part 1;Part 1;'
@paramOtherColumnsString = N'Thing 1;Thing 2;'
现在我需要它能够优雅地处理多个部件共享部件号的报告。离开 InfoPath and/or SharePoint 不是一个选项,也不是删除分号解析方面。我正在考虑的选项:
- 存储过程的每次调用,从 [Report_to_Parts] 中删除具有匹配 [Report_ID] 的所有行并插入所有 @tempPartTable 行。这是我想要的数据分析最终结果,但我担心它效率低得可怕,并且 [ID] 会随着系统的使用而变得不必要的大。
- 更改 [Report_to_Parts] 使其具有 [ID] 和 [Report_ID] 的复合键,并想出一种方法让 [ID] 从 1 开始自动递增每个 [Report_ID],然后如上删除插入。
- 为 [ID] 添加一列到 @tempPartTable,[Report_to_Parts]
SELECT
以获取此 [[=] table 中已有部件的 [ID] 42=]],根据它设置@tempPartTable [ID]s,MERGE
on [ID]。 - 更改 [Report_to_Parts] 使其具有复合键,为 [ID] 添加一列到 @tempPartTable,该列从 1 开始自动递增,
MERGE
按 [ID] 和 [Report_ID].
我在想出 3 的代码时遇到了困难,而且我当然也愿意接受其他想法。 Here's an earlier question I asked about this same system.
写这个问题给了我一些想法,我能够通过选项 4 解决这个问题。我修改了 [Report_to_Parts] 以便 [ID] 不是身份,而是一个复合主键以及[Report_ID]。然后我更新了存储过程代码(还包括解析 "magic" 以防有人偶然发现这个 post 试图解决解析问题):
ALTER PROCEDURE [MergeReport_sp]
@paramReport_ID nvarchar(255),
@paramPartNumberString nvarchar(MAX) = NULL,
@paramOtherColumnsString nvarchar(MAX) = NULL
AS
BEGIN
DECLARE @tempPartTable TABLE (
[ID] [int] NOT NULL,
[Report_ID] [nvarchar](255) NOT NULL,
[PartNumberOrdered] [nvarchar](255) NOT NULL,
[OtherColumns] [nvarchar](255) NULL)
DECLARE @WorkingPartNumberString nvarchar(255),
@WorkingOtherColumnsString nvarchar(255),
@PartNumberPos int = CHARINDEX(N';', @paramPartNumberString),
@OtherColumnsPos int = CHARINDEX(N';', @paramOtherColumnsString),
@PartTableID int = 1
WHILE (ISNULL(@PartNumberPos,0) > 0)
BEGIN
IF @OtherColumnsPos = 0 SET @OtherColumnsPos = 1
SET @WorkingPartNumberString = NULLIF(SUBSTRING(@paramPartNumberString,1,@PartNumberPos - 1), '')
SET @WorkingOtherColumnsString = NULLIF(SUBSTRING(@WorkingOtherColumnsString ,1,@OtherColumnsPos - 1), '')
INSERT INTO @tempPartTable (
[ID],
[Report_ID],
[PartNumberOrdered],
[OtherColumns])
VALUES (
@PartTableID,
@paramReport_ID,
@WorkingPartNumberString,
@WorkingOtherColumnsString)
SET @PartTableID = @PartTableID + 1
SET @paramPartNumberString = SUBSTRING(@paramPartNumberString, @PartNumberPos+1, LEN(@paramPartNumberString))
SET @paramOtherColumnsString = SUBSTRING(@paramOtherColumnsString, @OtherColumnsPos +1, LEN(@paramOtherColumnsString))
SET @PartNumberPos = CHARINDEX(N';',@paramPartNumberString)
SET @OtherColumnsPos = CHARINDEX(N';',@paramOtherColumnsString)
END
MERGE [Report_to_Parts]
USING @tempPartTable AS [Source]
ON (
[Report_to_Parts].[ID] = [Source].[ID] AND
[Report_to_Parts].[Report_ID] = @paramReport_ID)
WHEN NOT MATCHED BY TARGET
THEN INSERT(
[ID],
[Report_ID],
[PartNumberOrdered],
[OtherColumns])
VALUES (
[Source].[ID],
@paramReport_ID,
[Source].[PartNumberOrdered],
[Source].[OtherColumns])
WHEN MATCHED
THEN UPDATE SET
[PartNumberOrdered] = [Source].[PartNumberOrdered]
[OtherColumns]=[Source].[OtherColumns]
WHEN NOT MATCHED BY SOURCE AND [Report_to_Parts].[Report_ID] = @paramReport_ID
THEN DELETE;
END