【问题标题】:SQL Server: how to execute query with cursor inside stored procedure and how to pass a list of params to the stored proceduresSQL Server:如何在存储过程中使用游标执行查询以及如何将参数列表传递给存储过程
【发布时间】:2020-09-04 19:50:42
【问题描述】:

我正在尝试在 SQL Server 中编写一个存储过程,它将:

  • 将整数列表作为输入(假设这些整数是“profile_id”)
  • 将所有列名为“profile_id”的表名提取到游标中
  • 遍历光标并在其与输入参数列表中的其中一个匹配时打印 profile_id 值。

现在的问题是:我正在执行这样的程序:

EXEC dbo.de_dup '1234,2345';

在尝试执行下面注释掉的行时出现语法错误(请参阅过程):

set @id = (select profile_id from @tname where profile_id in @a_profile_id );

问题:

  1. 在游标内执行和设置值的正确方法是什么?
  2. (在我们的例子中)将整数列表传递给此过程的方法是什么?

这是我的程序:

ALTER PROCEDURE dbo.de_dup
    (@a_profile_id nvarchar(MAX)) 
AS
    DECLARE @tname VARCHAR(max),
            @id int;

    DECLARE tables_cursor CURSOR FOR 
        SELECT 
            a.TABLE_CATALOG +'.'+a.TABLE_SCHEMA + '.'+ a.TABLE_NAME AS table_name
        FROM 
            JobApp.INFORMATION_SCHEMA.COLUMNS a
        LEFT OUTER JOIN 
            JobApp.INFORMATION_SCHEMA.VIEWS b ON a.TABLE_CATALOG = b.TABLE_CATALOG
                                              AND a.TABLE_SCHEMA = b.TABLE_SCHEMA
                                              AND a.TABLE_NAME = b.TABLE_NAME
        WHERE 
            a.COLUMN_NAME = 'profile_id'
        GROUP BY 
            a.TABLE_CATALOG, a.TABLE_SCHEMA, a.TABLE_NAME, a.COLUMN_NAME;

    OPEN tables_cursor;

    FETCH NEXT FROM tables_cursor INTO @tname;

    WHILE @@FETCH_STATUS = 0
    BEGIN
        PRINT @a_profile_id ;
        PRINT @tname ;
        --set @id= (select profile_id from @tname where profile_id in @a_profile_id );
        --PRINT 'id : ' + @id;

        FETCH NEXT FROM tables_cursor INTO @tname;
    END;

    CLOSE tables_cursor;
    DEALLOCATE tables_cursor;
GO;

如果我提供更多说明,请告诉我。提前致谢。

【问题讨论】:

  • 第一个问题,为什么要使用光标。
  • 这是我提供的最简单的示例,我们将使用光标进行操作。我们将对每个表名做更复杂的事情
  • 能否请您发布错误?当你想执行一个表名在一个变量中的查询时,你需要使用动态 SQL 的概念
  • 您希望仅打印配置文件 ID? sql-server 版本是什么?
  • 我们想在查询中使用传入的配置文件 ID:(从 tname 中选择 profile_id,其中 profile_id 在 a_profile_id 中)。 --- 例如:“select profile_id from dao.client where profile_id in (1234,2345)”

标签: sql sql-server stored-procedures database-cursor


【解决方案1】:

此解决方案使用动态 SQL,据我所知,如果我们在变量中包含表名,则需要使用动态 SQL。

DBFIDDLE工作代码

查询:

CREATE PROCEDURE dbo.de_dup (@a_profile_id NVARCHAR(MAX))
AS
BEGIN
    DECLARE @tname VARCHAR(max)
        ,@id INT
        ,@dynamicSQL NVARCHAR(MAX);
    DECLARE @matched_tables TABLE (Name NVARCHAR(255));
    DECLARE @matched_profileIds TABLE (profile_id INT);
    DECLARE @profile_ids NVARCHAR(MAX) = @a_profile_id

    INSERT INTO @matched_tables
    SELECT DISTINCT a.TABLE_SCHEMA + '.' + a.TABLE_NAME AS table_name
    FROM INFORMATION_SCHEMA.COLUMNS a
    WHERE a.COLUMN_NAME = 'profile_id'

    WHILE EXISTS (
            SELECT 1
            FROM @matched_tables
            )
    BEGIN
        SELECT TOP 1 @tname = [Name]
        FROM @matched_tables

        SET @dynamicSQL = CONCAT (
                'select profile_id from '
                ,@tname
                ,' WHERE '
                ,''','
                ,@profile_ids
                ,','''
                ,' LIKE '
                ,'''%,'
                ,''''
                ,' + CAST(profile_id AS NVARCHAR(MAX)) + '
                ,''',%'
                ,''''
                )

        PRINT @dynamicSQL;

        INSERT INTO @matched_profileIds
        EXEC (@dynamicSQL)

        DELETE
        FROM @matched_tables
        WHERE [Name] = @tname
    END

    SELECT *
    FROM @matched_profileIds
END

形成的动态 SQL 是

选择 profile_id FROM dbo.TestTable WHERE ',123,456,789,1011,1213,' LIKE '%,' + CAST(profile_id AS NVARCHAR(MAX)) + ',%'

【讨论】:

    【解决方案2】:

    所以我用一个名为Split 的表值函数解决了一个类似的问题。它将分隔列表拆分为表中的行,然后您可以JOIN 或将其用作代码中的子查询。

    CREATE FUNCTION [dbo].[Split]
    (
        @char_array varchar(500), @delimiter char(1)
    )
    RETURNS 
    @parsed_array table
    (
        Parsed varchar(50)
    )
    AS
    BEGIN
        DECLARE @parsed varchar(50), @pos int
    
        SET @char_array = LTRIM(RTRIM(@char_array))+ @delimiter
        SET @pos = CHARINDEX(@delimiter, @char_array, 1)
    
        IF REPLACE(@char_array, @delimiter, '') <> ''
        BEGIN
            WHILE @pos > 0
            BEGIN
                SET @parsed = LTRIM(RTRIM(LEFT(@char_array, @pos - 1)))
                IF @parsed <> ''
                BEGIN
                    INSERT INTO @parsed_array (Parsed) 
                    VALUES (@parsed)
                END
                SET @char_array = RIGHT(@char_array, LEN(@char_array) - @pos)
                SET @pos = CHARINDEX(@delimiter, @char_array, 1)
            END
        END 
        RETURN
    END
    GO
    

    你会这样使用它

    SELECT f.Parsed INTO #s FROM dbo.Split(@a_profile_id, ',') f;
    

    然后在您的查询中(为简洁起见仅相关部分)

    select profile_id from @tname where profile_id in(select Parsed from #s);
    

    我省略了set @id=,因为如果select 语句返回多个结果,这将对@id 的值产生不可预测的结果。但是你表示这不是实际的代码,所以......

    免责声明:我从网上的其他人那里得到了拆分功能的内容。如果我能记得我会正确地归因于谁。

    【讨论】:

    • 如果你的sql-server版本是2016+,你可以使用STRING_SPLIT
    • @SowmyadharGourishetty 谢谢,那个内置功能比我一直用的那个旧的好!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-04-28
    • 2017-12-29
    • 1970-01-01
    • 1970-01-01
    • 2012-04-09
    • 1970-01-01
    • 2014-09-26
    相关资源
    最近更新 更多