使用 *tables* 作为表值参数 (TVP)

Pre*_*cco 20 sql-server-2008

MS SQL 2008 支持 TVP:一种将数据批量上传到存储过程进行处理的有用功能。

是否可以利用现有的表定义,而不是创建用户定义的类型?例如,是否可以创建具有以下签名的存储过程?

CREATE PROCEDURE usp_InsertProductionLocation
@TVP **LocationTable** READONLY
Run Code Online (Sandbox Code Playgroud)

文档似乎表明这是不可能的。

示例代码

/*
Sample code from:
http://msdn.microsoft.com/en-us/library/bb510489.aspx
*/

USE AdventureWorks2008R2;
GO

/* Create a table type. */
CREATE TYPE LocationTableType AS TABLE 
( LocationName VARCHAR(50)
, CostRate INT );
GO

/* Create a procedure to receive data for the table-valued parameter. */
CREATE PROCEDURE usp_InsertProductionLocation
    @TVP LocationTableType READONLY
    AS 
    SET NOCOUNT ON
    INSERT INTO [AdventureWorks2008R2].[Production].[Location]
           ([Name]
           ,[CostRate]
           ,[Availability]
           ,[ModifiedDate])
        SELECT *, 0, GETDATE()
        FROM  @TVP;
        GO

/* Declare a variable that references the type. */
DECLARE @LocationTVP 
AS LocationTableType;

/* Add data to the table variable. */
INSERT INTO @LocationTVP (LocationName, CostRate)
    SELECT [Name], 0.00
    FROM 
    [AdventureWorks2008R2].[Person].[StateProvince];

/* Pass the table variable data to a stored procedure. */
EXEC usp_InsertProductionLocation @LocationTVP;
GO

/*
The following is not part of the original source code:
*/

CREATE TABLE LocationTable(
 LocationName VARCHAR(50)
, CostRate INT );
GO
Run Code Online (Sandbox Code Playgroud)

Aar*_*and 19

不,您不能利用现有的表定义,您需要定义一个显式类型。这是在 2007 年提出的要求,并因“无法解决”而被关闭,但我仍然强烈鼓励您投票并发表评论,描述您的用例以及这将如何帮助您的业务提高生产力。您甚至可以指出这个问题来演示尝试和自动化这个过程是多么乏味。

不过,您今天可以动态地执行此操作……例如,对于您的简单定义:

-- you would pass these two in as parameters of course:
DECLARE
  @TableName SYSNAME = N'LocationTable',
  @TypeName  SYSNAME = N'LocationTypeTable';

DECLARE @sql NVARCHAR(MAX) = N'';

SELECT @sql = @sql + N',' + CHAR(13) + CHAR(10) + CHAR(9) 
    + QUOTENAME(c.name) + ' '
    + s.name + CASE WHEN LOWER(s.name) LIKE '%char' THEN 
        '(' + CONVERT(VARCHAR(12), (c.max_length/
        (CASE LOWER(LEFT(s.name, 1)) WHEN N'n' THEN 2 ELSE 1 END))) + ')' 
        ELSE '' END
        -- need much more conditionals here for other data types
    FROM sys.columns AS c
    INNER JOIN sys.types AS s
    ON c.system_type_id = s.system_type_id
    AND c.user_type_id = s.user_type_id
    WHERE c.[object_id] = OBJECT_ID(@TableName);

SELECT @sql = N'CREATE TYPE ' + @TypeName
    + ' AS TABLE ' + CHAR(13) + CHAR(10) + '(' + STUFF(@sql, 1, 1, '')
    + CHAR(13) + CHAR(10) + ');';

PRINT @sql;
-- EXEC sp_executesql @sql;
Run Code Online (Sandbox Code Playgroud)

结果:

CREATE TYPE LocationTypeTable AS TABLE 
(
    [LocationName] varchar(50),
    [CostRate] int
);
Run Code Online (Sandbox Code Playgroud)

免责声明:这不涉及 MAX 类型、数字的精度和小数位数等所有类型的其他内容。最终解决方案必须更加健壮以考虑所有潜在的列定义,但这应该给您一个开始。

在 SQL Server 2012 中,有新的 DMV 和存储过程,可以更轻松地从现有表(或存储过程或什至即席查询)派生列元数据,而不必处理针对 sys.types 和 sys 的所有条件逻辑。列。我在 12 月的博客中简要介绍了这些增强功能。它仍然很乏味,但它介于上面可怕的无法维护的意大利面和只能说“作为 [table x] 的副本”的能力之间......