【问题标题】:Return dynamic SQL table from function or stored procedure to use in a query从函数或存储过程返回动态 SQL 表以在查询中使用
【发布时间】:2020-03-02 21:24:11
【问题描述】:

我有许多表都有一个 DATETIME 类型的“已删除”字段,我用它来将记录标记为已删除(而不是完全删除它们)。

我经常发现自己从这些表中请求数据并希望排除已删除的记录。我现在正在做的事情是这样的:

SELECT A.* 
FROM (SELECT * FROM [TargetTable] WHERE (Deleted IS NULL)) AS A
WHERE (.........)

是否可以有一个存储过程/函数,我可以将表名传递给它并返回过滤后的表?比如:

SELECT A.*
FROM fnExcludeDeleted('MyTable') AS A
WHERE (.........)

我尝试使用表值函数来执行此操作,但据我所知,我需要定义结果表的列。我不能这样做,因为它并不总是我使用该函数的同一张表。

【问题讨论】:

  • 您可以使用 SP 和动态 SQL 执行此操作,但是将子句 DELETE IS NULL 添加到您的查询有什么问题...
  • 或者使用视图来做同样的事情。您也可以考虑将其编入索引。
  • @Larnu 我对 SP 不够熟悉,不知道该怎么做。并且找不到怎么做,因为我不知道要什么。
  • 我个人建议你不要。需要时写下你的很短的WHERE 子句。
  • @SMor 我无法索引视图,因为如果我尝试使用“WITH SCHEMABINDING”,我会收到错误消息“在模式绑定对象中不允许使用语法'*'。”如果我不使用“WITH SCHEMABINDING”,我将无法为视图编制索引。

标签: sql-server function stored-procedures dynamic


【解决方案1】:

在我看来,你不能用表值函数来做到这一点,因为它们需要静态表声明。不幸的是,您的表声明不一样。因此,您无法借助表值函数来实现这一点。在以下存储过程中可能有助于解决您的问题。

这个存储过程基于以下步骤;

  1. 生成声明的表脚本
  2. 创建一个全局临时文件,它的结构与声明的相同 表
  3. 将原始表数据填充到临时表中
  4. 返回临时表的结果集

注意:此查询使用GetTableCreateScript 存储过程来获取声明的表创建脚本。

已编辑:SQL Server 2017 的较低版本删除了 STRING_AGG,并添加了 XML PATH 以合并行。

  DROP TABLE IF EXISTS TargetTable1
GO
CREATE TABLE TargetTable1 (Col1 INT , Deleted DATE)
GO
---Populating test data---
INSERT INTO TargetTable1 VALUES (1,GETDATE())
INSERT INTO TargetTable1 VALUES (2,NULL)
INSERT INTO TargetTable1 VALUES (3,GETDATE())
INSERT INTO TargetTable1 VALUES (4,NULL)
GO
CREATE OR ALTER PROC DynamicSQL 
@TableName AS VARCHAR(200)
AS
DROP TABLE IF EXISTS  ##TempDynamic
DECLARE @SQLDynamicTable AS VARCHAR(MAX)
DECLARE @SQLDynamicRows AS VARCHAR(MAX)
CREATE TABLE #DynamicSQL (S VARCHAR(MAX))


INSERT INTO #DynamicSQL
EXEC    [dbo].GetTableCreateScript @TableName --->Generate Table Definition
---You can find GetTableCreateScript  create script https://www.c-sharpcorner.com/blogs/generate-table-defination-in-sql-server-without-gui

--SELECT @SQLDynamicTable=REPLACE(STRING_AGG(S,' '),@TableName, '##TempDynamic') FROM #DynamicSQL ----->Create TempTable for SQL Server 2017 and upper version

SELECT  @SQLDynamicTable=STUFF((
         SELECT ' ' + s
            FROM #DynamicSQL 
            FOR XML PATH('')
         ), 1, 1, '')


SET @SQLDynamicTable = REPLACE(@SQLDynamicTable,@TableName, '##TempDynamic')
PRINT @SQLDynamicTable
EXEC (@SQLDynamicTable)
SET @SQLDynamicRows = 'INSERT INTO ##TempDynamic SELECT * FROM ' + @TableName
EXEC(@SQLDynamicRows)
SELECT * FROM ##TempDynamic
WHERE Deleted IS NULL

GO 
EXEC DynamicSQL 'TargetTable1'



    +------+---------+
    | Col1 | Deleted |
    +------+---------+
    |    2 | NULL    |
    |    4 | NULL    |
    +------+---------+

【讨论】:

  • 谢谢。当我尝试创建程序时,出现以下错误:Msg 156, Level 15, State 1, Procedure DynamicSQL, Line 4 [Batch Start Line 0] Incorrect syntax near the keyword 'IF'. Msg 195, Level 15, State 10, Procedure DynamicSQL, Line 12 [Batch Start Line 0] 'STRING_AGG' is not a recognized built-in function name.
  • 我认为您使用的是低于 SQL Server 2017 的版本,因此我编辑了查询。 SQL Server 2017 的低版本不支持 STRING_AGG 函数
  • 好的,我设法创建了存储过程。谢谢!现在如何在查询中使用他们的结果作为表格?
  • SELECT * FROM ##TempDynamic 查询中的语句。
  • 我不确定我是否理解。我是否需要先执行存储过程,然后执行使用##TempDynamic 的查询?因为如果是这样的话,那么这并不是我真正想要的。我需要我的代码中的现有查询通过简单地更改表名来保持它们的工作方式,比如“MyFunction('TableName')”。
猜你喜欢
  • 1970-01-01
  • 2022-01-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-07
  • 2016-10-17
相关资源
最近更新 更多