MERGE使用没有明显匹配条件的表变量

时间:2015-05-13 18:14:47

标签: sql tsql merge sql-server-2008-r2

我有一个用户填写的InfoPath表单。它会被分配一个唯一的报告编号,并且在报告中他们可以选择零个或多个受影响的部分,每个部分都有一堆列。受影响的部件列表可以在处理报告时更改。向SharePoint提交或重新提交报表会调用SQL存储过程,其参数包括以分号分隔的列数据,其中一个NVARCHAR(MAX)表示一列中的多行。存储过程解析以分号分隔的NVARCHAR以填充表变量,然后将该表变量与将用于数据分析的主表合并。当用户每个报告只有一个具有给定部件号的受影响部件时,一切都很有效(因为我的MERGE语句使用它来确定是否需要更新现有行或创建新行),最初是可接受的限制。粗略的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和/或SharePoint不是一个选项,也不是删除分号解析方面。选项我考虑:

  1. 每次调用存储过程时,都会删除[Report_to_Parts]中具有匹配[Report_ID]的所有行并插入所有@tempPartTable行。这是我想要进行数据分析的最终结果,但我担心它非常低效,并且[ID]在使用系统时会变得不必要地大。
  2. 更改[Report_to_Parts],使其具有[ID]和[Report_ID]的复合键,并为每个[Report_ID]找出从[1]开始的自动递增方式,然后按上述方式删除和插入。 / LI>
  3. 从[Report_to_Parts]为[ID],SELECT添加一列到@tempPartTable,以获取[Report_ID]表格中已有部分的[ID],设置@tempPartTable [ID] s那个,MERGE和[ID]。
  4. 更改[Report_to_Parts],使其具有复合键,为[ID]的@tempPartTable添加一列,从1开始自动递增,[{1]}加[ID]和[Report_ID]。
  5. 我在提出3代码时遇到问题,而且我对其他想法也很开放。 Here's an earlier question I asked about this same system.

1 个答案:

答案 0 :(得分:0)

写这个问题给了我一些想法,我能够用选项4来解决这个问题。我修改了[Report_to_Parts],以便[ID]不是一个身份,并且是一个复合主键和[Report_ID]。然后我更新了存储过程代码(还包括解析"魔法"万一有人在这篇文章中偶然发现试图解决解析问题):

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