【问题标题】:T-SQL: execute procedure obtaining parameter value from table (SQL Server 2012)T-SQL:执行从表中获取参数值的过程(SQL Server 2012)
【发布时间】:2016-11-30 18:58:02
【问题描述】:

我有一个接收 ID 参数并执行插入/更新的存储过程。

我有大约 25K 条记录需要更新,我一直在尝试找出一种方法来增强单参数存储过程,以便它可以处理多个值或来自表的 Id 集。

我最接近的方法是创建一个游标并在执行存储过程时循环遍历它,不幸的是,我已经意识到这是一种非常缓慢的方法。

我想知道是否可以帮助我修改我的存储过程,以使其具有更好的性能。

这是我使用游标的代码。

SET NOCOUNT ON;
DECLARE @Id    VARCHAR(32);

DECLARE csr CURSOR
FOR SELECT DISTINCT ID
    FROM table
    ORDER BY id;

OPEN csr;

FETCH NEXT FROM csr INTO
    @Id;

WHILE @@FETCH_STATUS = 0
    BEGIN
        PRINT 'Running for '+@Id;
        EXEC update_product_xml_single_id @Id;
        FETCH NEXT FROM csr INTO
            @Id;
    END;

CLOSE csr;
DEALLOCATE csr;

更新

遵循 objectNotFound 的建议:

--Create User-defined Table Type
CREATE TYPE IdTableType AS TABLE ([Id] VARCHAR(20) NOT NULL);

-- Modified old stored proc: update_product_xml_single_id, to receive the new type
CREATE PROCEDURE update_product_xml_tvp (@id IdTableType READONLY)

-- Called stored proc with a table variable
DECLARE @ID IdTableType

INSERT @ID 
SELECT DISTINCT ID
FROM Products
WHERE ID LIKE 'N0%'
ORDER BY ID;

EXEC update_product_xml_tvp  @ID

我面临的问题是原始存储过程或步骤#2。我唯一修改的是名称和参数类型,但是当我尝试保存更改时,出现如下错误:

消息 137,级别 16,状态 1,过程 update_product_xml_tvp,第 43 行 必须声明标量变量“@id”。

消息所抱怨的这个标量变量与我刚刚从 varchar 修改为 table 类型的参数相同。我不确定如何在此处填写表格,或者我的步骤是否错误。任何帮助都很好,谢谢。

【问题讨论】:

    标签: sql-server tsql stored-procedures


    【解决方案1】:

    您需要将表值参数作为存储过程 (update_product_xml_single_id) 的输入。因此,您将传递一个列表(例如 @TVP ),而不是传递一个 int 类型的标量值,该列表包含您当前从 OP 中的表“table”中处理的所有 ID。在您的存储过程中,您可以像使用任何其他普通表一样使用该表变量@TVP。

    此链接向您展示了如何通过示例进行操作(我已在下面复制了该示例):

    https://msdn.microsoft.com/en-us/library/bb510489.aspx?f=255&MSPPError=-2147217396

    来自上述 msdn 链接的示例:

    USE AdventureWorks2012;  
    GO  
    
    -- Step 1
    /* Create a table type. */  
    CREATE TYPE LocationTableType AS TABLE   
    ( LocationName VARCHAR(50)  
    , CostRate INT );  
    GO  
    
    -- Step 2
    /* Create a procedure to receive data for the table-valued parameter. */  
    CREATE PROCEDURE dbo. usp_InsertProductionLocation  
        @TVP LocationTableType READONLY  
        AS   
    BEGIN
        SET NOCOUNT ON  
        INSERT INTO AdventureWorks2012.Production.Location  
               (Name  
               ,CostRate  
               ,Availability  
               ,ModifiedDate)  
            SELECT *, 0, GETDATE()  
            FROM  @TVP;  
      END
      GO  
    

    将 TVP 传递给存储过程的用法:

    /* Declare a table variable that references the type. */  
    DECLARE @LocationTVP AS LocationTableType;  -- This was created in Step 1 above.
    
    /* Add data to the table variable. So this would be the same as your Declare Cursor SQL except its inserting into a Table Valued Parameter @LocationTVP */  
    INSERT INTO @LocationTVP (LocationName, CostRate)  
        SELECT Name, 0.00  
        FROM AdventureWorks2012.Person.StateProvince;  
    
    /* Pass the table variable data in @LocationTVP to a stored procedure that now expects a Table valued parameter as input */  
    EXEC usp_InsertProductionLocation @LocationTVP;  
    GO  
    

    编辑:

    我刚刚测试了您的 CREATE SP 代码,它运行良好,这是我尝试过的

    CREATE TYPE IdTableType AS TABLE ([Id] VARCHAR(20) NOT NULL);
    GO
    
    -- Modified old stored proc: update_product_xml_single_id, to receive the new type
    CREATE PROCEDURE update_product_xml_tvp 
    ( @id IdTableType READONLY)
    AS
    BEGIN
         SELECT * FROM  @id --- Obviously you will be using it differently but this is just to prove that it works
    END 
    

    您的插入语句中也缺少“INTO”

    这个:

    INSERT @ID 
    SELECT DISTINCT ID
    FROM Products
    WHERE ID LIKE 'N0%'
    ORDER BY ID;
    

    应该是

    INSERT INTO @ID 
    SELECT DISTINCT ID
    FROM Products
    WHERE ID LIKE 'N0%'
    ORDER BY ID;
    

    我将 INTO 部分添加到我的代码中。但它仍然给我同样的错误。 我想确保您知道存储的过程 update_product_xml_tvp 是与 CURSOR 一起使用的相同存储过程,我唯一更改的是输入参数类型。它曾经是: @pf_id VARCHAR(100)

    现在是: @id id 只读

    由于我没有修改该过程中代码中的任何内容,因此错误来自以下部分: WHERE ai.ID = @id

    有些东西看起来不对,比如 @id 不再是与 ai.id 相同的数据类型,因此比较不再有效,但我不知道如何更正,或者在哪里更正。

    所以为了确保这是正确的,我最终得到了

     1. A user defined table type named id
    
     CREATE TYPE idTableType AS TABLE ([some_id] VARCHAR(20) NOT NULL);
    
     2. A stored procedure that populates that table type:
    
     CREATE PROC [dbo].[mn_pass_id_table] ( @id_spVariable idTableType READONLY )
     AS
     BEGIN
         SET NOCOUNT ON
         SELECT DISTINCT ID
         FROM Products
         WHERE ID LIKE 'N0%'
         ORDER BY ID;
     END
    
     3. And a modification to the stored procedure that existed already.
    
        From:
        ALTER PROCEDURE update_product_xml_single ( @id VARCHAR(100))
    
        To:       
        ALTER PROCEDURE update_product_xml_tvp (@id_spVariable idTableType READONLY)
    

    在这个过程中,有 4 个 WHERE 条件我没有更改,也没有修改,这给了我错误,让我无法更新存储过程:

    WHERE ai.ID  IN ( Select some_id from  @id_spVariable  )
    

    我的理解是,最终,我将能够从一个新的查询窗口执行以下操作:

    DECLARE @ID_Var_That_We_Will_Pass_To_Your_SP as idTableType 
    
    INSERT INTO  @ID_Var_That_We_Will_Pass_To_Your_SP 
    SELECT SOME_COLUMN FROM REAL_TABLE  ( This will be the one that you currently use to Declare the Cursor )
    
    EXEC update_product_xml_tvp  @ID_Var_That_We_Will_Pass_To_Your_SP 
    

    我确定我在这里搞砸了。我真的很想把它做好。感谢您的耐心和帮助。

    【讨论】:

    • 谢谢 objectNotFound,在我的代码中如何实现更改有点令人困惑,但我会尝试一下。
    • @erasmocarlos 非常直接......我复制/粘贴的示例中的最后 3 个语句在 SP 之外。我将编辑帖子以澄清这一点。
    • 第一节第2步,为什么insert从一个名为Location的表中获取数据,然后在insert语句的最后三个语句中,tvp会被另一个表中的数据填充。
    • @erasmocarlos 不,插入从类型为 LocationTableType 的@TVP(非位置)获取数据。这个@TVP 是SP 的一个参数,并且将PAss 值转换为@TVP 变量的方式显示在我的答案中使用部分下列出的3 个stmts 中。为避免混淆,请将 LocationTableType 视为默认情况下不可用的新自定义数据类型。(如 int、char、date、float 等)。
    • @erasmocarlos 不,你不能......这就是使它成为“基于集合”的解决方案的全部意义。这是基于集合的编程的关键,也是 SQL 编程的基本概念。我无法在帖子中解释这一点。我建议你阅读一些关于 SQL 的好文章。
    【解决方案2】:

    我是初学者,但我认为这会帮助你尝试一下。

    创建用户自定义函数

    create type [dbo].[IdTableType ] as table 
    (
    [Id] BIGINT NOT NULL
    )
    GO
    

    现在

    CREATE PROCEDURE update_product_xml_tvp 
        (
        @table1 IdTableType  READONLY
        )
    AS
    BEGIN TRY
    Declare  @ID BIGINT
    
     INSERT into YOUR_TABLE 
      (
       ID
       )
     SELECT DISTINCT ID
     FROM @table1
     WHERE ID LIKE 'N0%'
     ORDER BY ID;
    
    END TRY
    BEGIN CATCH
    print error_message()
    END CATCH
    

    并通过数组调用它

    【讨论】:

    • 您好 abcool,感谢您的帮助。让我感到困惑的是 update_product_xml_tvp 已经是一个存储过程。如果你注意到,我在我的代码上使用了光标,但是光标性能太慢了。
    • 几个月前我有同样的事情,我需要在一次插入时多次向 2 个表插入多个值。所以,到目前为止,我没有任何缓慢或任何错误,事实上我喜欢它。所以我建议你复制你的代码并创建备份并删除上面的过程和/(或创建新的)表类型和过程,然后告诉我你从哪个平台调用这个过程?如果可以的话,我可以提供提示
    猜你喜欢
    • 2019-04-03
    • 1970-01-01
    • 1970-01-01
    • 2013-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-05
    相关资源
    最近更新 更多