【问题标题】:Create and Pass a Table-Valued Parameter in a Single Line在单行中创建和传递表值参数
【发布时间】:2015-02-08 21:14:52
【问题描述】:

使用 SQL Server 2012,是否可以消除声明表值参数 (TVP) 的需要,只需将其传递到存储过程?下面是一个非常简单的存储过程 (SP) 示例,它采用 TVP 和一个工作示例来执行该 SP,我必须在其中声明 TVP,填充它,然后将其传递给 SP。我希望能够简单地将人口标准直接传递给 EXEC 调用。这可能吗?

场景设置:

-- Create a sample Users table
CREATE TABLE Users (UserID int, UserName varchar(20))
INSERT INTO Users VALUES (1, 'Bob'), (2, 'Mary'), (3, 'John'), (4, 'Mark')

-- Create a TVP Type
CREATE TYPE UserIdTableType AS TABLE (UserID int)

-- Create SP That Uses TVP Type
CREATE PROCEDURE GetUsers
@UserIdFilter UserIdTableType READONLY
AS
    SELECT * FROM @UserIdFilter WHERE UserID > 2

要执行的工作方法:

DECLARE @MyIds AS UserIdTableType
INSERT INTO @MyIds SELECT UserID FROM Users
EXEC GetUsers @MyIds

请求的执行方法:

EXEC GetUsers (SELECT UserID FROM Users)

【问题讨论】:

  • 这是一个 TVP,程序不期望选择查询,您需要声明一个该程序接受的特定类型的 TVP,填充它然后将其传递给 proc,您不能简单只需传递一个选择查询而不是 TVP。
  • @M.All,有没有办法在一行中将其转换为 TVP?像EXEC GetUsers CAST((SELECT UserID FROM Users) as UserIdTableType) 这样的东西?我只是在寻找一种让调用代码看起来更干净的捷径。
  • 不,你不能,但你可以按原样传递你的查询,作为参数并在你的proc中执行它,你需要使用动态sql。
  • 是的,这就是我要避免的。
  • @bigmac 只是好奇你是否有机会尝试一下。它确实避免了动态 SQL :-)。

标签: sql-server stored-procedures table-valued-parameters


【解决方案1】:

不,您不能创建 TVP 内联或 CAST / CONVERT。它不是INTVARCHARDATETIME 等“数据类型”;这是一个完全不同的“表格类型”。用户定义的表类型 (UDTT) 只是用作表变量声明的定义/模式的元数据。当这样的表变量用作输入参数时,该用法被视为 TVP(表值参数)。但它仍然是一个表变量,其定义存储在tempdb 中。这是物理结构,不是内存结构,你不能CASTCONVERT一个Table,不管它是真实的、临时的还是变量。

虽然问题中给出的示例只是为了理解这个想法而过于简单,但您的总体目标似乎是代码重用/创建子例程(否则您可以轻松完成 SELECT * FROM Users WHERE UserID > 2)。不幸的是,T-SQL 不允许编写真正优雅/干净的代码,因此您必须接受一定程度的重复和/或笨拙。

但是,可以为结果集制作稍微通用的处理程序,只要它们至少具有必需的字段。你也可以

  • 传入一个 XML 参数,或者
  • 将结果转储到临时表中,只需在子过程调用中引用它(不需要是动态 SQL),因此不需要传递任何参数(至少不是数据集/结果的参数) /查询)

在这两种情况下,结构都比使用 TVP 更灵活,因为 TVP 必须是那些精确的字段。但是引用假定存在的临时表允许类似于以下内容:

Proc_1

SELECT *
INTO #MyTemp
FROM sys.tables;

EXEC dbo.Proc_4 @StartsWith = 'a', @HowMany = 10;

Proc_2

SELECT *
INTO #MyTemp
FROM sys.columns;

EXEC dbo.Proc_4 @StartsWith = 'bb', @HowMany = 20;

Proc_3

SELECT *
INTO #MyTemp
FROM sys.views;

EXEC dbo.Proc_4 @StartsWith = 'ccc', @HowMany = 33;

Proc_4

SELECT TOP (@HowMany) tmp.*
FROM   #MyTemp tmp
WHERE  tmp.[name] LIKE @StartsWith + '%'
ORDER BY tmp.[object_id] ASC;

【讨论】:

  • “将结果转储到临时表并仅在子进程中引用它”方法线程安全吗?
  • @JonSchneider 我不明白怎么可能。本地临时表对于创建它的 Session 来说是本地的。即使连接池保持相同的 Session,回到该 Session 时的第一个操作是运行一个内部存储过程,该过程会清除任何先前的本地临时表。此外,我曾在每秒处理数百到数千个事务的系统上工作过,做这样的事情(虽然不是很频繁),一切都很好。
  • 谢谢!我不确定 SQL Server 如何/是否在会话之间共享临时表。
  • @JonSchneider 欢迎您。如果会话,甚至重复使用来自同一会话 ID 的池中的连接,共享本地临时表,那么一大堆东西就会中断。并且它们无法在会话之间共享,因为如果您查看tempdb.sys.tables,您会看到每个本地临时表名称都附加了一个哈希码,提供了这种分离。
  • 请注意,此方法将为每次执行外部 proc (1/2/3) 重新编译内部 Proc_4 中的查询。请参阅this(滚动到“现在考虑在第二个存储过程中引用临时表的情况,如下所示:”)。当你回到 TVP 的冗长时,请记住他们don't carry statistics and are subject to "parameter sniffing"..
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-26
  • 2021-06-07
  • 1970-01-01
  • 1970-01-01
  • 2014-12-20
相关资源
最近更新 更多