SQL服务器:如何在存储过程中使用游标执行查询以及如何将参数列表传递给存储过程
SQL Server: how to execute query with cursor inside stored procedure and how to pass a list of params to the stored procedures
我正在尝试在 SQL 服务器中编写一个存储过程,它将:
- 将整数列表作为输入(假设这些整数是“profile_id”)
- 将具有名为“profile_id”的列的所有 table 名称选取到游标中
- 遍历光标并打印 profile_id 值,当它与参数输入列表中的其中之一匹配时。
现在的问题是:我正在执行这样的程序:
EXEC dbo.de_dup '1234,2345';
尝试执行下面注释掉的行时出现语法错误(请参阅过程):
set @id = (select profile_id from @tname where profile_id in @a_profile_id );
问题:
- 在游标内执行和设置值的正确方法是什么?
- (在我们的例子中)将整数列表传递给此过程的方式是什么?
这是我的程序:
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,据我所知,如果变量中有 table 名称,我们需要使用动态 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
Dynamic SQL that gets formed is
SELECT profile_id
FROM dbo.TestTable
WHERE ',123,456,789,1011,1213,' LIKE '%,' + CAST(profile_id AS NVARCHAR(MAX)) + ',%'
所以我用一个名为 Split
的 table-valued 函数解决了一个类似的问题。它将分隔列表拆分为 table 中的行,然后您可以 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 语句 returns 多个结果,那将对 @id
的值产生不可预测的 table 结果。但是你指出这不是实际代码所以...
免责声明:我从网上其他人那里得到了 Split 函数的内容。如果我能记住我会正确归因于谁。
我正在尝试在 SQL 服务器中编写一个存储过程,它将:
- 将整数列表作为输入(假设这些整数是“profile_id”)
- 将具有名为“profile_id”的列的所有 table 名称选取到游标中
- 遍历光标并打印 profile_id 值,当它与参数输入列表中的其中之一匹配时。
现在的问题是:我正在执行这样的程序:
EXEC dbo.de_dup '1234,2345';
尝试执行下面注释掉的行时出现语法错误(请参阅过程):
set @id = (select profile_id from @tname where profile_id in @a_profile_id );
问题:
- 在游标内执行和设置值的正确方法是什么?
- (在我们的例子中)将整数列表传递给此过程的方式是什么?
这是我的程序:
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,据我所知,如果变量中有 table 名称,我们需要使用动态 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
Dynamic SQL that gets formed is
SELECT profile_id FROM dbo.TestTable WHERE ',123,456,789,1011,1213,' LIKE '%,' + CAST(profile_id AS NVARCHAR(MAX)) + ',%'
所以我用一个名为 Split
的 table-valued 函数解决了一个类似的问题。它将分隔列表拆分为 table 中的行,然后您可以 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 语句 returns 多个结果,那将对 @id
的值产生不可预测的 table 结果。但是你指出这不是实际代码所以...
免责声明:我从网上其他人那里得到了 Split 函数的内容。如果我能记住我会正确归因于谁。