【问题标题】:INSERT using T-SQL with 2 table-value functions使用带有 2 个表值函数的 T-SQL 插入
【发布时间】:2020-02-10 05:18:04
【问题描述】:

我有一个存储过程,它接受两个 NVARCHAR 参数,这些参数接受以逗号分隔的名称和索引值列表,并将它们一次性插入到 SQL Server 数据库表中。

CREATE PROCEDURE [dbo].[SplitAndSave]   
    @name nvarchar(MAX),
    @vals nvarchar(MAX)

数据将采用与@vals 相同的顺序匹配每个@name id 的格式:

@name = 1,2,3,4
@vals = 9,8,7,6

我找到了一个函数,它可以将一列拆分为一个独立工作的表,并将数据拆分为所需的行:

CREATE FUNCTION [dbo].[fnSplitString] ( 
    @string NVARCHAR(MAX), 
    @delimiter CHAR(1) 
) 
RETURNS @output TABLE(splitdata NVARCHAR(MAX) 
) 
BEGIN 

DECLARE @start INT, @end INT 
SELECT @start = 1, @end = CHARINDEX(@delimiter, @string) 

WHILE @start < LEN(@string) + 1 BEGIN 
    IF @end = 0  
        SET @end = LEN(@string) + 1

    INSERT INTO @output (splitdata)  
    VALUES(SUBSTRING(@string, @start, @end - @start)) 
    SET @start = @end + 1 
    SET @end = CHARINDEX(@delimiter, @string, @start)

END 

RETURN 

END

在我的存储过程中,我试图“组合”这两个表函数:

INSERT INTO KEY_DATA (name, value)
(SELECT splitdata from fnSplitString(@name, ',')), (SELECT splitdata from fnSplitString(@value, ','))

但我得到一个错误:

消息 512,级别 16,状态 1,过程 dbo.SplitAndSave,第 17 行 [批处理开始行 2]
子查询返回超过 1 个值。当子查询跟随 =、!=、、>= 或子查询用作表达式时,这是不允许的。

我想我可能需要将第一个表包装到第二个表中,但我认为这不能“一次性”完成。

有什么想法可以做到这一点吗?目前使用 SQL Server Management Studio v17.8

【问题讨论】:

  • 检查 SQL Server 中的official MS Docs on functions,您会看到:限制和限制:用户定义的函数不能用于执行的操作修改数据库状态。您不能在函数中执行此操作 - 您需要改用存储过程
  • 是的,知道我必须使用存储过程来完成这项任务。
  • 如果可能的话,您应该避免在 SQL 中完全使用逗号分隔的字符串 - 您最好将单个表值参数传递到您的存储过程中。

标签: sql-server tsql stored-procedures table-valued-parameters


【解决方案1】:

问题不在于功能,而在于您如何插入数据。以下代码:

INSERT INTO KEY_DATA (name, value)
(SELECT splitdata from fnSplitString(@name, ',')), (SELECT splitdata from fnSplitString(@value, ','))

除了:

INSERT INTO KEY_DATA (name, value)
(one value of name), (one value of value)

但在您的陈述中返回的值不止一个。这就是问题所在。

您需要在函数中添加订单索引并通过它加入函数。像这样的:

INSERT INTO KEY_DATA (name, value)
SELECT DS1.splitdata 
      ,DS2.splitdata
from fnSplitString(@name, ',') DS1
INNER JOIN fnSplitString(@value, ',') DS2
    ON DS1.[OrderIndex] = DS2.[OrderINdex];

例如:

CREATE OR ALTER FUNCTION [dbo].[fnSplitString] 
( 
    @string NVARCHAR(MAX), 
    @delimiter CHAR(1) 
) 
RETURNS @output TABLE
(
    [orderID] BIGINT
   ,[splitdata] NVARCHAR(MAX)
) 
BEGIN 

    DECLARE @XML XML = CAST(N'<r><![CDATA[' + REPLACE(@string, @delimiter, ']]></r><r><![CDATA[') + ']]></r>' AS XML)

    INSERT INTO @output ([orderID], splitdata)
    SELECT  ROW_NUMBER() OVER(ORDER BY T.c)
           ,RTRIM(LTRIM(T.c.value('.', 'nvarchar(MAX)')))
    FROM @XML.nodes('//r') T(c)

    RETURN 

END 
GO


DECLARE @name NVARCHAR(MAX) = '1,2,3,4'
       ,@vals NVARCHAR(MAX) = '9,8,7,6'


SELECT DS1.[splitdata] 
      ,DS2.[splitdata]
from dbo.fnSplitString(@name, ',') DS1
INNER JOIN dbo.fnSplitString(@vals, ',') DS2
    ON DS1.[orderID] = DS2.[orderID];

【讨论】:

  • 谢谢,索引键和连接两个表都有效。
猜你喜欢
  • 1970-01-01
  • 2011-11-27
  • 1970-01-01
  • 2011-06-06
  • 1970-01-01
  • 1970-01-01
  • 2016-09-28
  • 1970-01-01
  • 2016-05-05
相关资源
最近更新 更多