【问题标题】:How to run a dynamic sql for each row of a table?如何为表的每一行运行动态sql?
【发布时间】:2012-12-05 17:22:04
【问题描述】:

我有一个表,其中包含一个名为 WhereClause 的列

DECLARE @UserGroups TABLE (WhereClause nvarchar(1000), totalCount int)

该表已填充了一些 WhereClause 的值,但没有填充 totalCount 列的值。

WhereClause 的一个例子是“FirstName is null”。

基本上对于表中的每一行,我需要从另一个表(用户)计算 COUNT(*) 并在上表中设置totalCount

以下不起作用:

SELECT UG.WhereClause, U.TotalCount
FROM @UserGroups as UG
OUTER APPLY (SELECT COUNT(*) as 'TotalCount' FROM [Users] WHERE UG.WhereClause)
) U

我尝试创建一个 sql 函数并在该函数中调用 exec sp_executesql 但它不受支持。

CREATE FUNCTION [dbo].[fn_UserGroupCount]
(   
    @whereClause as NVARCHAR(1000)
)
RETURNS @returnValue TABLE (TotalCount INT)
AS
BEGIN
    DECLARE @stmt nvarchar(1500)

    SET @stmt = 'SELECT COUNT(*) FROM [dbo].[Users]
    WHERE '  + @whereClause

    Exec sp_executesql @stmt

RETURN
END

SELECT * FROM [dbo].[fn_UserGroupCount]('Subject is null')

如前所述,如何填充我的@UserGroups 表?

谢谢

【问题讨论】:

  • 确实这个想法看起来不错,但是这种方法会增加处理过载,因为必须为每一行执行 D-SQL。您能找到满足您要求的其他替代方案吗?

标签: sql-server tsql function dynamic-sql sp-executesql


【解决方案1】:

试试这个,你必须使用动态 SQL 的临时表才能访问它(@UserGroups 不起作用,所以我用#UserGroups 替换它):

CREATE TABLE #UserGroups (WhereClause nvarchar(1000), totalCount int)
INSERT #UserGroups VALUES ('FirstName IS NULL', NULL)
INSERT #UserGroups VALUES ('FirstName IS NOT NULL', NULL)

DECLARE @sql VARCHAR(8000) = STUFF(
(
SELECT  ';UPDATE #UserGroups SET totalCount = (SELECT COUNT(*) FROM [Users] WHERE ' + WhereClause + ') WHERE WhereClause = ''' + WhereClause + ''''
FROM    #UserGroups
FOR XML PATH('')
), 1, 1, '')

PRINT @sql
EXEC(@sql)

SELECT  *
FROM    #UserGroups

DROP TABLE #UserGroups

但是,如果WhereClause 包含引号,可能会出现问题,例如FirstName LIKE 'Joe'

【讨论】:

  • 谢谢,但我不能使用它,因为它不是线程安全的。如果同时进行 2 个调用,则使用临时表失败。
  • 不客气。你有什么错误吗?错误文本说什么?您是否从应用程序调用此代码?
  • 我有一个不同的问题,但这个答案给了我需要修复它的线索!谢谢:)
【解决方案2】:

我会使用游标来构建一个包含更新语句的字符串,其中包含内置的 @whereclause 中的 select count(*)。然后您可以执行它来执行更新。请参阅下面的 SQLFiddle:

已更新现在可以在 where 子句中包含单引号以及 where 子句中的撇号的地方使用

SQL Fiddle

MS SQL Server 2008 架构设置

create table users
(
  userid int primary key identity,
  name varchar(50),
  subject varchar(50) null
);

INSERT INTO users (name, subject)
VALUES
    ('Fred', 'Maths'),
    ('Bill', null),
    ('Helen', 'English'),
    ('O''Keefe', 'Maths');

查询 1

DECLARE @WhereClause nvarchar(1000)
DECLARE @stmt nvarchar(1500) = ''

CREATE TABLE #UserGroups (WhereClause nvarchar(1000), totalCount int)
INSERT #UserGroups VALUES ('Subject IS NULL', NULL)
INSERT #UserGroups VALUES ('Subject IS NOT NULL', NULL)
INSERT #UserGroups VALUES ('Subject =''Maths''', NULL)
INSERT #UserGroups VALUES ('name =''O''''Keefe''', NULL)

DECLARE cur CURSOR FOR
  SELECT WhereClause FROM #UserGroups

OPEN cur

FETCH cur  INTO @WhereClause

WHILE @@FETCH_STATUS = 0
BEGIN

  SELECT @stmt = @stmt + 
      'UPDATE #UserGroups ' +
      'SET totalCount = ' +
      '(SELECT COUNT(*) FROM users WHERE ' + @WhereClause + ') ' +
      'WHERE WhereClause = ''' + REPLACE(@WhereClause, '''', '''''') + ''';' 
  FETCH cur  INTO @WhereClause
END

CLOSE cur
DEALLOCATE cur

--select @stmt
exec(@stmt)

SELECT *
FROM #UserGroups

DROP TABLE #UserGroups

Results

|         WHERECLAUSE | TOTALCOUNT |
------------------------------------
|     Subject IS NULL |          1 |
| Subject IS NOT NULL |          3 |
|    Subject ='Maths' |          2 |
|    name ='O''Keefe' |          1 |

【讨论】:

  • 如果 WhereClause 可能包含引号,则动态 SQL 是不安全的。 COUNT() 查询是安全的,但与 WhereClause 列的比较不安全。
  • @Damien_The_Unbeliever 已更新,因此它可以处理 where 子句中的引号。在比较值中包含引号的 where 子句可能仍然存在问题,例如name='O''基夫'
  • @TheLight 现在应该适用于所有带有撇号和文本字段比较的 whereclauses
  • 我喜欢假装CURSOR 命令不存在,除非我要更新正在迭代的行。我相信一个简单的while循环就可以了。
猜你喜欢
  • 2018-03-18
  • 2015-01-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-19
  • 2021-09-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多