如何将 sp_executesql 结果放入变量中?

发布于 2024-07-18 10:00:28 字数 88 浏览 3 评论 0原文

我需要执行一段动态 SQL,然后需要将结果存储到变量中。

我知道我可以使用 sp_executesql,但找不到有关如何执行此操作的明确示例。

I have a piece of dynamic SQL I need to execute, I then need to store the result into a variable.

I know I can use sp_executesql but can't find clear examples around about how to do this.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

演多会厌 2024-07-25 10:00:28

如果您有 OUTPUT 参数,则可以执行此操作

DECLARE @retval int   
DECLARE @sSQL nvarchar(500);
DECLARE @ParmDefinition nvarchar(500);

DECLARE @tablename nvarchar(50)  
SELECT @tablename = N'products'  

SELECT @sSQL = N'SELECT @retvalOUT = MAX(ID) FROM ' + @tablename;  
SET @ParmDefinition = N'@retvalOUT int OUTPUT';

EXEC sp_executesql @sSQL, @ParmDefinition, @retvalOUT=@retval OUTPUT;

SELECT @retval;

,但如果没有,则无法修改 SP:

-- Assuming that your SP return 1 value
create table #temptable (ID int null)
insert into #temptable exec mysp 'Value1', 'Value2'
select * from #temptable

不太漂亮,但可以。

If you have OUTPUT parameters you can do

DECLARE @retval int   
DECLARE @sSQL nvarchar(500);
DECLARE @ParmDefinition nvarchar(500);

DECLARE @tablename nvarchar(50)  
SELECT @tablename = N'products'  

SELECT @sSQL = N'SELECT @retvalOUT = MAX(ID) FROM ' + @tablename;  
SET @ParmDefinition = N'@retvalOUT int OUTPUT';

EXEC sp_executesql @sSQL, @ParmDefinition, @retvalOUT=@retval OUTPUT;

SELECT @retval;

But if you don't, and can not modify the SP:

-- Assuming that your SP return 1 value
create table #temptable (ID int null)
insert into #temptable exec mysp 'Value1', 'Value2'
select * from #temptable

Not pretty, but works.

风吹雪碎 2024-07-25 10:00:28
DECLARE @vi INT
DECLARE @vQuery NVARCHAR(1000)

SET @vQuery = N'SELECT @vi= COUNT(*) FROM <TableName>'

EXEC SP_EXECUTESQL 
        @Query  = @vQuery
      , @Params = N'@vi INT OUTPUT'
      , @vi = @vi OUTPUT

SELECT @vi
DECLARE @vi INT
DECLARE @vQuery NVARCHAR(1000)

SET @vQuery = N'SELECT @vi= COUNT(*) FROM <TableName>'

EXEC SP_EXECUTESQL 
        @Query  = @vQuery
      , @Params = N'@vi INT OUTPUT'
      , @vi = @vi OUTPUT

SELECT @vi
你另情深 2024-07-25 10:00:28
DECLARE @tab AS TABLE (col1 VARCHAR(10), col2 varchar(10)) 
INSERT into @tab EXECUTE  sp_executesql N'
SELECT 1 AS col1, 2 AS col2
UNION ALL
SELECT 1 AS col1, 2 AS col2
UNION ALL
SELECT 1 AS col1, 2 AS col2'

SELECT * FROM @tab
DECLARE @tab AS TABLE (col1 VARCHAR(10), col2 varchar(10)) 
INSERT into @tab EXECUTE  sp_executesql N'
SELECT 1 AS col1, 2 AS col2
UNION ALL
SELECT 1 AS col1, 2 AS col2
UNION ALL
SELECT 1 AS col1, 2 AS col2'

SELECT * FROM @tab
最舍不得你 2024-07-25 10:00:28

如果您想返回超过 1 个值,请使用以下命令:

DECLARE @sqlstatement2      NVARCHAR(MAX);
DECLARE @retText            NVARCHAR(MAX);  
DECLARE @ParmDefinition     NVARCHAR(MAX);
DECLARE @retIndex           INT = 0;

SELECT @sqlstatement = 'SELECT @retIndexOUT=column1 @retTextOUT=column2 FROM XXX WHERE bla bla';

SET @ParmDefinition = N'@retIndexOUT INT OUTPUT, @retTextOUT NVARCHAR(MAX) OUTPUT';

exec sp_executesql @sqlstatement, @ParmDefinition, @retIndexOUT=@retIndex OUTPUT, @retTextOUT=@retText OUTPUT;

返回值位于 @retIndex 和 @retText 中

If you want to return more than 1 value use this:

DECLARE @sqlstatement2      NVARCHAR(MAX);
DECLARE @retText            NVARCHAR(MAX);  
DECLARE @ParmDefinition     NVARCHAR(MAX);
DECLARE @retIndex           INT = 0;

SELECT @sqlstatement = 'SELECT @retIndexOUT=column1 @retTextOUT=column2 FROM XXX WHERE bla bla';

SET @ParmDefinition = N'@retIndexOUT INT OUTPUT, @retTextOUT NVARCHAR(MAX) OUTPUT';

exec sp_executesql @sqlstatement, @ParmDefinition, @retIndexOUT=@retIndex OUTPUT, @retTextOUT=@retText OUTPUT;

returned values are in @retIndex and @retText

荆棘i 2024-07-25 10:00:28
DECLARE @ValueTable TABLE
    (
    Value VARCHAR (100)
    )

SELECT @sql = N'SELECT SRS_SizeSetDetails.'+@COLUMN_NAME+' FROM SRS_SizeSetDetails WHERE FSizeID = '''+@FSizeID+''' AND SRS_SizeSetID = '''+@SRS_SizeSetID+'''';

INSERT INTO @ValueTable
EXEC sp_executesql @sql;

SET @Value='';

SET @Value = (SELECT TOP 1  Value FROM @ValueTable)

DELETE FROM @ValueTable
DECLARE @ValueTable TABLE
    (
    Value VARCHAR (100)
    )

SELECT @sql = N'SELECT SRS_SizeSetDetails.'+@COLUMN_NAME+' FROM SRS_SizeSetDetails WHERE FSizeID = '''+@FSizeID+''' AND SRS_SizeSetID = '''+@SRS_SizeSetID+'''';

INSERT INTO @ValueTable
EXEC sp_executesql @sql;

SET @Value='';

SET @Value = (SELECT TOP 1  Value FROM @ValueTable)

DELETE FROM @ValueTable
冧九 2024-07-25 10:00:28

返回值通常不用于“返回”结果,而是返回成功(0)或错误号(1-65K)。 以上似乎表明 sp_executesql 没有返回值,这是不正确的。 sp_executesql 将返回 0 表示成功,返回任何其他数字表示失败。

在下面,@i 将返回 2727

DECLARE @s NVARCHAR(500)
DECLARE @i INT;
SET @s = 'USE [Blah]; UPDATE STATISTICS [dbo].[TableName] [NonExistantStatisticsName];';
EXEC @i = sys.sp_executesql @s
SELECT @i AS 'Blah'

SSMS 将显示这一点
消息 2727,11 级,状态 1,第 1 行
找不到索引“NonExistantStaticsName”。

Return values are generally not used to "return" a result but to return success (0) or an error number (1-65K). The above all seem to indicate that sp_executesql does not return a value, which is not correct. sp_executesql will return 0 for success and any other number for failure.

In the below, @i will return 2727

DECLARE @s NVARCHAR(500)
DECLARE @i INT;
SET @s = 'USE [Blah]; UPDATE STATISTICS [dbo].[TableName] [NonExistantStatisticsName];';
EXEC @i = sys.sp_executesql @s
SELECT @i AS 'Blah'

SSMS will show this
Msg 2727, Level 11, State 1, Line 1
Cannot find index 'NonExistantStaticsName'.

神回复 2024-07-25 10:00:28
Declare @variable int
Exec @variable = proc_name
Declare @variable int
Exec @variable = proc_name
海拔太高太耀眼 2024-07-25 10:00:28

这对我有用:

DECLARE @SQL NVARCHAR(4000)

DECLARE @tbl Table (
    Id int,
    Account varchar(50),
    Amount int
) 

-- Lots of code to Create my dynamic sql statement

insert into @tbl EXEC sp_executesql @SQL

select * from @tbl

This worked for me:

DECLARE @SQL NVARCHAR(4000)

DECLARE @tbl Table (
    Id int,
    Account varchar(50),
    Amount int
) 

-- Lots of code to Create my dynamic sql statement

insert into @tbl EXEC sp_executesql @SQL

select * from @tbl
撧情箌佬 2024-07-25 10:00:28

您可以尝试以下方法

DECLARE  @SqlStatement  NVARCHAR(MAX) = ''
       ,@result     XML
       ,@DatabaseName  VARCHAR(100)
       ,@SchemaName    VARCHAR(10)
       ,@ObjectName    VARCHAR(200);

SELECT   @DatabaseName = 'some database'
       ,@SchemaName   = 'some schema'
       ,@ObjectName   = 'some object (Table/View)'

SET @SqlStatement = '
                    SELECT @result = CONVERT(XML,
                                            STUFF( ( SELECT *
                                                     FROM 
                                                       (
                                                          SELECT TOP(100) 
                                                          * 
                                                          FROM ' + QUOTENAME(@DatabaseName) +'.'+ QUOTENAME(@SchemaName) +'.' + QUOTENAME(@ObjectName) + '
                                                       ) AS A1 
                                                    FOR XML PATH(''row''), ELEMENTS, ROOT(''recordset'')
                                                 ), 1, 0, '''')
                                       )
                ';

EXEC sp_executesql @SqlStatement,N'@result XML OUTPUT', @result = @result OUTPUT;

SELECT DISTINCT
    QUOTENAME(r.value('fn:local-name(.)', 'VARCHAR(200)')) AS ColumnName
FROM @result.nodes('//recordset/*/*') AS records(r)
ORDER BY ColumnName

Here's something you can try

DECLARE  @SqlStatement  NVARCHAR(MAX) = ''
       ,@result     XML
       ,@DatabaseName  VARCHAR(100)
       ,@SchemaName    VARCHAR(10)
       ,@ObjectName    VARCHAR(200);

SELECT   @DatabaseName = 'some database'
       ,@SchemaName   = 'some schema'
       ,@ObjectName   = 'some object (Table/View)'

SET @SqlStatement = '
                    SELECT @result = CONVERT(XML,
                                            STUFF( ( SELECT *
                                                     FROM 
                                                       (
                                                          SELECT TOP(100) 
                                                          * 
                                                          FROM ' + QUOTENAME(@DatabaseName) +'.'+ QUOTENAME(@SchemaName) +'.' + QUOTENAME(@ObjectName) + '
                                                       ) AS A1 
                                                    FOR XML PATH(''row''), ELEMENTS, ROOT(''recordset'')
                                                 ), 1, 0, '''')
                                       )
                ';

EXEC sp_executesql @SqlStatement,N'@result XML OUTPUT', @result = @result OUTPUT;

SELECT DISTINCT
    QUOTENAME(r.value('fn:local-name(.)', 'VARCHAR(200)')) AS ColumnName
FROM @result.nodes('//recordset/*/*') AS records(r)
ORDER BY ColumnName
书信已泛黄 2024-07-25 10:00:28

本文中的大多数答案都容易受到 SQL 注入的攻击,因为它们将输入变量直接连接到 SQL 脚本中。 如果您使用没有动态代码的存储过程,这不是问题,但如果您使用动态 sql,即使存储过程也可能容易受到攻击。 动态sql是指sql脚本在存储过程内部编译或者作为参数传递给存储过程; 因此每次执行存储过程时都会重新编译。

为了保护自己免受 sql 注入的影响,您的输入值还必须通过参数引入到 sql 中。

下面是三个示例 - 最后两个使用存储过程。 第一个存储过程 (example-2) 不使用动态构建的 sql 字符串,而第二个存储过程 (example-3) 使用动态构建的 SQL 字符串。

-- Example-1: Use sp_executesql with parameters (not in a stored procedure)
-- Note: INPUT parameters prevent sql injection
DECLARE @myDate DATE 
SET     @myDate = '2000-05-27';
DECLARE @dayOfYear INT;
    
EXEC sp_executesql 
@Stmt = N'SELECT @paramOutput = DATEPART(dy, @paramInput)',
@params = N'@paramInput DATE, @paramOutput INT OUTPUT',
@paramInput = @myDate,
@paramOutput = @dayOfYear OUTPUT;
    
Select @dayOfYear
    
    
-- Example-2: Concatenate parameters in a Stored Procedure that does not use dynamic sql
DROP PROCEDURE IF EXISTS dbo.SP_Scalar_NotDynamic;
GO
Create procedure dbo.SP_Scalar_NotDynamic (@myDate DATE, @dayOfYear INT OUTPUT)
As
    SELECT @dayOfYear = DATEPART(dy, @myDate)
    RETURN;
GO
    
-- Test SP_Scalar_NotDynamic
DECLARE @myDate DATE 
SET @myDate = '2000-05-27';
DECLARE @dayNumber INT;
EXECUTE dbo.SP_Scalar_NotDynamic @myDate, @dayOfYear = @dayNumber OUTPUT; 
SELECT @dayNumber


-- Example-3: Use sp_executesql with parameters in a Stored Procedure that uses dynamic sql
DROP PROCEDURE IF EXISTS dbo.SP_Scalar_Dynamic;
GO
Create procedure dbo.SP_Scalar_Dynamic (@myDate DATE, @mySql NVARCHAR(100), @dayOfYear INT OUTPUT)
As
    EXEC sp_executesql
    @mySql,
    @params      = N'@paramInput DATE, @paramOutput INT OUTPUT',
    @paramInput  = @myDate,
    @paramOutput = @dayOfYear OUTPUT;
    RETURN;
GO
    
-- Test SP_Scalar_Dynamic
DECLARE @myDate DATE 
SET     @myDate = '2000-05-27';
DECLARE @mySql NVARCHAR(100)
SET     @mySql = N'SELECT @paramOutput = DATEPART(dy, @paramInput)'
DECLARE @dayNumber INT;
EXECUTE dbo.SP_Scalar_Dynamic @myDate, @mySql, @dayOfYear = @dayNumber OUTPUT; 
SELECT  @dayNumber

Most answers in this post are vulnerable to sql injection, because they concatenate the input variables directly into the sql script. This is not a problem if you use a stored procedure without dynamic code, but even a stored procedure can be vulnerable if you use dynamic sql. Dynamic sql means that the sql script is compiled inside the stored procedure or passed to the stored procedure as a parameter; and thus newly compiled every time the stored procedure executes.

To protect yourself against sql injections, your input values must ALSO be introduced to the sql via parameters.

Here are three examples - the last two using stored procedures. The first stored procedure (example-2) does not use a dynamically built sql string, while the second stored procedure (example-3) does.

-- Example-1: Use sp_executesql with parameters (not in a stored procedure)
-- Note: INPUT parameters prevent sql injection
DECLARE @myDate DATE 
SET     @myDate = '2000-05-27';
DECLARE @dayOfYear INT;
    
EXEC sp_executesql 
@Stmt = N'SELECT @paramOutput = DATEPART(dy, @paramInput)',
@params = N'@paramInput DATE, @paramOutput INT OUTPUT',
@paramInput = @myDate,
@paramOutput = @dayOfYear OUTPUT;
    
Select @dayOfYear
    
    
-- Example-2: Concatenate parameters in a Stored Procedure that does not use dynamic sql
DROP PROCEDURE IF EXISTS dbo.SP_Scalar_NotDynamic;
GO
Create procedure dbo.SP_Scalar_NotDynamic (@myDate DATE, @dayOfYear INT OUTPUT)
As
    SELECT @dayOfYear = DATEPART(dy, @myDate)
    RETURN;
GO
    
-- Test SP_Scalar_NotDynamic
DECLARE @myDate DATE 
SET @myDate = '2000-05-27';
DECLARE @dayNumber INT;
EXECUTE dbo.SP_Scalar_NotDynamic @myDate, @dayOfYear = @dayNumber OUTPUT; 
SELECT @dayNumber


-- Example-3: Use sp_executesql with parameters in a Stored Procedure that uses dynamic sql
DROP PROCEDURE IF EXISTS dbo.SP_Scalar_Dynamic;
GO
Create procedure dbo.SP_Scalar_Dynamic (@myDate DATE, @mySql NVARCHAR(100), @dayOfYear INT OUTPUT)
As
    EXEC sp_executesql
    @mySql,
    @params      = N'@paramInput DATE, @paramOutput INT OUTPUT',
    @paramInput  = @myDate,
    @paramOutput = @dayOfYear OUTPUT;
    RETURN;
GO
    
-- Test SP_Scalar_Dynamic
DECLARE @myDate DATE 
SET     @myDate = '2000-05-27';
DECLARE @mySql NVARCHAR(100)
SET     @mySql = N'SELECT @paramOutput = DATEPART(dy, @paramInput)'
DECLARE @dayNumber INT;
EXECUTE dbo.SP_Scalar_Dynamic @myDate, @mySql, @dayOfYear = @dayNumber OUTPUT; 
SELECT  @dayNumber
酒几许 2024-07-25 10:00:28

这是很久以前的事了,所以不确定是否仍然需要,但您可以使用 @@ROWCOUNT 变量来查看有多少行受到先前 sql 语句的影响。

例如,当您构造动态 Update 语句并使用 exec 运行它时,这很有用。 @@ROWCOUNT 将显示更新了多少行。

定义如下

This was a long time ago, so not sure if this is still needed, but you could use @@ROWCOUNT variable to see how many rows were affected with the previous sql statement.

This is helpful when for example you construct a dynamic Update statement and run it with exec. @@ROWCOUNT would show how many rows were updated.

Here is the definition

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文