【问题标题】:WITH statement in dynamic sql动态 sql 中的 WITH 语句
【发布时间】:2016-04-25 06:54:29
【问题描述】:

我需要将查询作为存储过程存储在 SQL Server 中。

我还需要传递定义表名和列名的参数。

这是我想要的查询,我尝试将它存储在一个字符串中,然后EXECUTE 它但没有成功,我能解决这个问题吗?

CREATE PROCEDURE sp_selectAllParents @id int, @tableid varchar(30), @tablename varchar(30)
AS BEGIN
 SET NOCOUNT ON;
 WITH ct AS (
  SELECT * FROM @tablename t WHERE @tableid = @id
  UNION ALL
  SELECT t.* FROM @tablename t JOIN ct ON t.parentId = ct.@tableid 
 )
 SELECT * FROM @tablename t WHERE @tableid NOT IN (SELECT @tableid FROM ct)
END

编辑: 我的尝试是:

DECLARE @sql varchar(255)
SET @sql = 'WITH ct AS (SELECT * FROM @tablename t WHERE @tableid = @id UNION ALL SELECT t.* FROM @tablename t JOIN ct ON t.parentId = ct.@tableid) SELECT * FROM @tablename t WHERE @tableid NOT IN (SELECT @tableid FROM ct)'
EXEC(@sql)

【问题讨论】:

  • 你能展示一下你的尝试吗?
  • 您真的有这么多具有​​层次结构的不同表,因此需要一个通用过程吗?这充满了问题,我原以为只为每个表编写一个过程会更安全、更简单,而且更不容易出错(提供的表或列名无效)。在不相关的注释上,您应该avoid the prefex sp_ for your own procedures
  • 构建以表名和/或列名作为参数以从中获取结果的存储过程通常是一个坏主意。你应该重新考虑你的解决方案。
  • 我知道这不是一个好习惯,但我正在修复一个旧项目,我只能在不修改现有架构的情况下这样做

标签: sql-server stored-procedures with-statement dynamicquery


【解决方案1】:

如果您在这种情况下将表名称作为参数发送,则需要创建动态查询字符串。可能有助于以下脚本

 CREATE PROCEDURE sp_selectAllParents @id int, @tableid varchar(30), @tablename varchar(30)
 AS BEGIN
 SET NOCOUNT ON;
 SET @query=N'WITH ct AS (
  SELECT * FROM @tablename t WHERE @tableid = @id
  UNION ALL
  SELECT t.* FROM @tablename t JOIN ct ON t.parentId = ct.@tableid 
  )
  SELECT * FROM @tablename t WHERE @tableid NOT IN (SELECT @tableid FROM ct)'

 EXECUTE sp_executesql @query, @id,@tablename,@tableid
 END

【讨论】:

  • @GarethD 是的,谢谢
【解决方案2】:

因为我已经在评论部分,这样做是个坏主意。你真的应该重新考虑你的解决方案。

存储过程必须如下所示:

CREATE PROCEDURE selectAllParents @id int, @tableid sysname, @tablename sysname
AS 
BEGIN
 SET NOCOUNT ON;

 -- Guards against SQL Injection attacks (replace ' with '')
 SET @tableid=REPLACE(@tableid,'''','''''');
 SET @tablename=REPLACE(@tablename,'''','''''');

 DECLARE @stmt NVARCHAR(4000);
 SET @stmt=
   ';WITH ct AS ('+
     'SELECT * FROM ' + QUOTENAME(@tablename) + ' t WHERE ' + QUOTENAME(@tableid) + '= @id ' +
     'UNION ALL ' +
     'SELECT t.* FROM ' + QUOTENAME(@tablename) + ' t JOIN ct ON t.parentId = ct.' + QUOTENAME(@tableid) +
   ')'+
   'SELECT * FROM ' + QUOTENAME(@tablename) +' t WHERE ' + QUOTENAME(@tableid) + ' NOT IN (SELECT ' + QUOTENAME(@tableid) +' FROM ct);';

 EXEC sp_executesql
    @stmt,
    N'@id int',
    @id;
END
GO

【讨论】:

  • 我肯定会在此处的对象名称周围使用QUOTENAME,并且我可能会使用SYSNAME 作为对象名称参数的数据类型。可能也值得添加验证并检查列名和表名是否有效。我知道这很多超出了问题的范围,但是,在提供您已经建议反对的解决方案时,我认为有一定的责任至少减轻使用它可能造成的损害。跨度>
  • @GarethD Guards,quotenames & sysnames added.
  • 保护的替换位不是必需的,因为 quotename 会转义所有这些,并且实际上可能会使名称无效(但前提是有人创建了像 [table'1] 这样的表并且他们可能应该得到一个错误如果他们有)。我的意思是像 - IF OBJECT_ID(@tablename, 'U') IS NULL RETURN; -- TABLE DOES NOT EXIST SO EXITIF COL_LENGTH(@tablename, @tableid) IS NULL RETURN; -- COLUMN DOES NOT EXIST IN TABLE SO EXIT,应该更清楚一点,抱歉。
  • @GarethD 哦,好吧,我将把它留给 OP 来申请。如果他希望使用不存在的表或列运行,那就这样吧。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-08
相关资源
最近更新 更多