SQL Server:如何在存储过程内部使用游标执行查询以及如何将参数列表传递给存储过程

问题描述

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

  • 以整数列表作为输入(假设这些整数是“ profile_id”)
  • 在游标中拾取所有具有名为“ 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,据我所知,如果表名包含在变量中,则需要使用动态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 从dbo.TestTable WHERE',123,456,789,1011,1213,'LIKE'%,'+ CAST(profile_id AS NVARCHAR(MAX))+',%'

,

因此,我使用名为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,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的值产生不可预测的结果。但是您指出这仍然不是实际的代码,所以...

免责声明:我从网上其他人那里获得了Split功能的精华。如果我还记得我应该正确地归因于谁的话。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...