SQL - 一次插入和更新多个记录

use*_*048 6 sql-server

我有一个存储过程,负责一次插入或更新多个记录.为了提高性能,我想在我的存储过程中执行此操作.

此存储过程采用逗号分隔的许可ID列表和状态.许可证ID存储在名为@PermitIDs的变量中.状态存储在名为@Status的变量中.我有一个用户定义的函数,将这个以逗号分隔的许可ID列表转换为表.我需要遍历每个ID,并在名为PermitStatus的表中插入或更新.

如果不存在具有许可ID的记录,我想添加记录.如果确实存在,我想用给定的@Status值更新记录.我知道如何为单个ID执行此操作,但我不知道如何为多个ID执行此操作.对于单个ID,我执行以下操作:

-- Determine whether to add or edit the PermitStatus
DECLARE @count int
SET @count = (SELECT Count(ID) FROM PermitStatus WHERE [PermitID]=@PermitID)

-- If no records were found, insert the record, otherwise add
IF @count = 0
BEGIN
  INSERT INTO
    PermitStatus
  (
    [PermitID],
    [UpdatedOn],
    [Status]
  )
  VALUES
  (
    @PermitID,
    GETUTCDATE(),
    1
  )
  END
  ELSE
    UPDATE
      PermitStatus
    SET
      [UpdatedOn]=GETUTCDATE(),
      [Status]=@Status
    WHERE
      [PermitID]=@PermitID
Run Code Online (Sandbox Code Playgroud)

如何循环我的用户定义函数返回的表中的记录,以根据需要动态插入或更新记录?

KM.*_*KM. 4

创建一个 split 函数,并按如下方式使用它:

SELECT
    *
    FROM YourTable  y
    INNER JOIN dbo.splitFunction(@Parameter) s ON y.ID=s.Value
Run Code Online (Sandbox Code Playgroud)

我更喜欢数字表方法

要使此方法发挥作用,您需要执行以下一个时间表设置:

SELECT TOP 10000 IDENTITY(int,1,1) AS Number
    INTO Numbers
    FROM sys.objects s1
    CROSS JOIN sys.objects s2
ALTER TABLE Numbers ADD CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number)
Run Code Online (Sandbox Code Playgroud)

设置 Numbers 表后,创建此函数:

CREATE FUNCTION [dbo].[FN_ListToTableAll]
(
     @SplitOn  char(1)      --REQUIRED, the character to split the @List string on
    ,@List     varchar(8000)--REQUIRED, the list to split apart
)
RETURNS TABLE
AS
RETURN 
(

    ----------------
    --SINGLE QUERY-- --this WILL return empty rows
    ----------------
    SELECT
        ROW_NUMBER() OVER(ORDER BY number) AS RowNumber
            ,LTRIM(RTRIM(SUBSTRING(ListValue, number+1, CHARINDEX(@SplitOn, ListValue, number+1)-number - 1))) AS ListValue
        FROM (
                 SELECT @SplitOn + @List + @SplitOn AS ListValue
             ) AS InnerQuery
            INNER JOIN Numbers n ON n.Number < LEN(InnerQuery.ListValue)
        WHERE SUBSTRING(ListValue, number, 1) = @SplitOn

);
GO 
Run Code Online (Sandbox Code Playgroud)

现在,您可以轻松地将 CSV 字符串拆分为表并加入其中:

select * from dbo.FN_ListToTableAll(',','1,2,3,,,4,5,6777,,,')
Run Code Online (Sandbox Code Playgroud)

输出:

RowNumber   ListValue
----------- ----------
1           1
2           2
3           3
4           
5           
6           4
7           5
8           6777
9           
10          
11          

(11 row(s) affected)  
Run Code Online (Sandbox Code Playgroud)

要使您需要的功能发挥作用,请执行以下操作:

--this would be the existing table
DECLARE @OldData  table (RowID  int, RowStatus char(1))

INSERT INTO @OldData VALUES (10,'z')
INSERT INTO @OldData VALUES (20,'z')
INSERT INTO @OldData VALUES (30,'z')
INSERT INTO @OldData VALUES (70,'z')
INSERT INTO @OldData VALUES (80,'z')
INSERT INTO @OldData VALUES (90,'z')


--these would be the stored procedure input parameters
DECLARE @IDList      varchar(500)
       ,@StatusList  varchar(500)
SELECT @IDList='10,20,30,40,50,60'
      ,@StatusList='A,B,C,D,E,F'

--stored procedure local variable
DECLARE @InputList  table (RowID  int, RowStatus char(1))

--convert input prameters into a table
INSERT INTO @InputList
        (RowID,RowStatus)
    SELECT
        i.ListValue,s.ListValue
        FROM dbo.FN_ListToTableAll(',',@IDList)            i
            INNER JOIN dbo.FN_ListToTableAll(',',@StatusList)  s ON i.RowNumber=s.RowNumber

--update all old existing rows
UPDATE o
    SET RowStatus=i.RowStatus
    FROM @OldData               o WITH (UPDLOCK, HOLDLOCK) --to avoid race condition when there is high concurrency as per @emtucifor
        INNER JOIN @InputList   i ON o.RowID=i.RowID

--insert only the new rows
INSERT INTO @OldData
        (RowID, RowStatus)
    SELECT
        i.RowID, i.RowStatus
        FROM @InputList               i
            LEFT OUTER JOIN @OldData  o ON i.RowID=o.RowID
        WHERE o.RowID IS NULL

--display the old table
SELECT * FROM @OldData order BY RowID
Run Code Online (Sandbox Code Playgroud)

输出:

RowID       RowStatus
----------- ---------
10          A
20          B
30          C
40          D
50          E
60          F
70          z
80          z
90          z

(9 row(s) affected)
Run Code Online (Sandbox Code Playgroud)

编辑感谢@Emtucifor,单击此处获取有关竞争条件的提示,我在答案中包含了锁定提示,以防止高并发时出现竞争条件问题。