sử dụng * bảng * làm tham số có giá trị bảng (TVP)


18

MS SQL 2008 hỗ trợ TVP: một tính năng hữu ích để tải dữ liệu hàng loạt lên một quy trình được lưu trữ để xử lý.

Thay vì tạo một kiểu do người dùng định nghĩa, có thể tận dụng một định nghĩa bảng hiện có không? Ví dụ, có thể tạo một thủ tục được lưu trữ với chữ ký sau đây không?

CREATE PROCEDURE usp_InsertProductionLocation
@TVP **LocationTable** READONLY

Các tài liệu dường như cho thấy rằng điều này là không thể.

MẪU MÃ

/*
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

Câu trả lời:


16

Không, bạn không thể tận dụng một định nghĩa bảng hiện có, bạn cần xác định một loại rõ ràng. Điều này đã được yêu cầu quay lại vào năm 2007 và đã bị đóng vì "sẽ không sửa" nhưng tôi vẫn khuyến khích bạn bỏ phiếu và để lại nhận xét mô tả trường hợp sử dụng của bạn và cách điều này sẽ giúp doanh nghiệp của bạn hiệu quả hơn. Bạn thậm chí có thể chỉ ra câu hỏi này để chứng minh rằng nó có thể tẻ nhạt đến mức nào để thử và tự động hóa điều này.

Mục trên UserVoice (Kết nối trước đây # 294130)

Bạn có thể làm điều này ngay hôm nay, một cách linh hoạt, mặc dù ... ví dụ cho định nghĩa đơn giản của bạn:

-- 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;

Các kết quả:

CREATE TYPE LocationTypeTable AS TABLE 
(
    [LocationName] varchar(50),
    [CostRate] int
);

Tuyên bố miễn trừ trách nhiệm: Điều này không giải quyết tất cả các loại khác như loại MAX, độ chính xác và thang đo cho số, v.v ... Giải pháp cuối cùng sẽ phải mạnh mẽ hơn để tính đến tất cả các định nghĩa cột tiềm năng nhưng điều này sẽ giúp bạn bắt đầu.

Trong SQL Server 2012, có các DMV mới và các thủ tục được lưu trữ sẽ giúp việc lấy siêu dữ liệu cột từ các bảng hiện có (hoặc các thủ tục được lưu trữ hoặc thậm chí các truy vấn ad hoc) dễ dàng hơn mà không phải lộn xộn với tất cả logic có điều kiện chống lại sys.types và sys. cột. Tôi viết blog ngắn gọn về những cải tiến này vào tháng 12 . Nó vẫn tẻ nhạt, nhưng nó ở đâu đó giữa món mì spaghetti khủng khiếp không thể nhầm lẫn ở trên và khả năng chỉ nói "như bản sao của [bảng x]" ...


4

Tôi đã giải quyết vấn đề này bằng cách tạo thủ tục được lưu trữ sau đây để tạo một loại có cùng bảng lược đồ hiện có.

Create PROCEDURE [dbo].[Sp_DefineTypeOutOfTableSchema]
@TableNames NVARCHAR(500)
AS
BEGIN

DECLARE @TableName NVARCHAR(100)
DECLARE @strSQL NVARCHAR(max)
DECLARE @strSQLCol NVARCHAR(1000)
DECLARE @ColName NVARCHAR(100)
DECLARE @ColDataTaype NVARCHAR(50)
DECLARE @ColDefault NVARCHAR(50)
DECLARE @ColIsNulable NVARCHAR(50)
DECLARE @ColCharMaxlen NVARCHAR(50)
DECLARE @ColNumPrec NVARCHAR(50)
DECLARE @ColNumScal NVARCHAR(50)


IF LEN(@TableNames) > 0 SET @TableNames = @TableNames + ',' 

WHILE LEN(@TableNames) > 0 
    BEGIN

        SELECT @TableName = LTRIM(SUBSTRING(@TableNames, 1, CHARINDEX(',', @TableNames) - 1))

        DECLARE schemaCur CURSOR FOR 
        SELECT COLUMN_NAME,DATA_TYPE,IS_NULLABLE,COLUMN_DEFAULT,CHARACTER_MAXIMUM_LENGTH,NUMERIC_PRECISION,NUMERIC_SCALE  FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME =@TableName

        OPEN schemaCur

        SELECT @strSQL=''

        FETCH NEXT FROM schemaCur
        INTO @ColName,@ColDataTaype,@ColIsNulable,@ColDefault,@ColCharMaxlen,@ColNumPrec,@ColNumScal

        WHILE @@FETCH_STATUS = 0
        BEGIN
            SELECT @strSQLCol=''
            SELECT @strSQLCol= '['+@ColName+'] '+'[' + @ColDataTaype +'] '

            IF @ColDataTaype='nvarchar' or @ColDataTaype='char'  or @ColDataTaype='varchar' or @ColDataTaype='vchar'
                BEGIN
                    SELECT @strSQLCol=@strSQLCol+ '(' + @ColCharMaxlen +') '
                END 
            ELSE IF @ColDataTaype='numeric' or @ColDataTaype='decimal' 
                BEGIN
                    SELECT @strSQLCol=@strSQLCol +'(' + @ColNumPrec +',' +@ColNumScal + ') '
                END 

            IF @ColIsNulable='YES'
                BEGIN
                    SELECT @strSQLCol=@strSQLCol+ 'NULL '
                END 
            ELSE
                BEGIN
                    SELECT @strSQLCol=@strSQLCol+ ' NOT NULL '
                END 
            IF @ColDefault IS NOT NULL
                BEGIN
                    SELECT @strSQLCol=@strSQLCol+ ' DEFAULT(' +@ColDefault + '),'
                END 
            ELSE
                BEGIN
                    SELECT @strSQLCol=@strSQLCol+ ' ,'
                END

            SELECT @strSQL=@strSQL+@strSQLCol
                --print @strSQL
            FETCH NEXT FROM schemaCur
            INTO @ColName,@ColDataTaype,@ColIsNulable,@ColDefault,@ColCharMaxlen,@ColNumPrec,@ColNumScal

        END

        CLOSE schemaCur
        DEALLOCATE schemaCur

        --print @strSQL
        SELECT @strSQL=left( @strSQL, len(@strSQL)-1)
        --print @strSQL

        IF EXISTS (SELECT * FROM sys.types WHERE IS_TABLE_TYPE = 1 AND name = 't_' +@TableName)
        BEGIN           
            EXEC('DROP TYPE t_' +@TableName )
        END

        SELECT @strSQL =  'CREATE TYPE t_' + @TableName + ' AS TABLE (' +  @strSQL + ')'
        --print @strSQL
        EXEC (@strSQL)
        SELECT @TableNames = SUBSTRING(@TableNames, CHARINDEX(',', @TableNames) + 1, LEN(@TableNames))
END
END

bạn có thể sử dụng nó như thế này

Exec Sp_DefineTypeOutOfTableSchema 'Table1name, Table2name'


Mã này đã không làm việc cho tôi. "Các hàm do người dùng định nghĩa, các hàm phân vùng và tham chiếu cột không được phép trong các biểu thức trong ngữ cảnh này." Ngay cả khi nó đã hoạt động, có vẻ như không có gì để xử lý các lược đồ ngoài dbo.
MgSam

2

Erland Sommarskog có một bài viết rộng mô tả cách sử dụng TVP.

Có một cái nhìn, nó có giá trị nó!

Nói tóm lại, bạn không thể sử dụng một loại hiện có, giống như câu trả lời trước đó của Aaron Bertrand . Nhưng ít nhất đó là một sự chuyển giao số lượng lớn ..


1

Dựa trên câu trả lời của Jiten, đơn giản hóa một số logic ở các vị trí, tính toán identitycác cột (sử dụng sys.bảng chứ không phải SCHEMEA)

CREATE PROCEDURE [dbo].[usp_DefineTypeFromTable]
@TableNames NVARCHAR(500)
AS
BEGIN

DECLARE @TableName NVARCHAR(100)
DECLARE @strSQL NVARCHAR(max)
DECLARE @strSQLCol NVARCHAR(1000)
DECLARE @ColName NVARCHAR(100)
DECLARE @ColDataTaype NVARCHAR(50)
DECLARE @ColDefault NVARCHAR(50)
DECLARE @ColIsNulable NVARCHAR(50)
DECLARE @ColFirst NVARCHAR(50)
DECLARE @ColSecond NVARCHAR(50)
DECLARE @ColID NVARCHAR(50)
DECLARE @ColCompute NVARCHAR(50)

IF LEN(@TableNames) > 0 SET @TableNames = @TableNames + ',' 
WHILE LEN(@TableNames) > 0 
    BEGIN
        SELECT @TableName = TRIM(LEFT(@TableNames, CHARINDEX(',', @TableNames) - 1))
        DECLARE schemaCur CURSOR FOR 
            SELECT  
                c.name as column_name,
                t.name as [type_name],
                c.is_nullable,
                convert(nvarchar(4000), object_definition(ColumnProperty(c.object_id, c.name, 'default'))) as column_default,
                CASE
                    WHEN c.collation_name IS NOT NULL THEN c.max_length 
                    WHEN t.name like 'datetime%' THEN c.scale
                    WHEN c.scale = 0 THEN NULL
                    ELSE c.precision
                END as firstValue,
                CASE
                    WHEN (c.scale = 0 or t.name like 'datetime%') THEN NULL
                    ELSE c.scale
                END as secondValue,
                c.is_identity, -- would be best to know seed,increment
                c.is_computed -- should really look up col definition. `convert(nvarchar(4000), object_definition(ColumnProperty(c.object_id, c.name, 'computed')))` as computed ?

            FROM sys.columns as c join 
                 sys.all_objects as o 
                    on c.object_id=o.object_id join
                 sys.types as t
                    on c.user_type_id=t.user_type_id
            WHERE
                o.type in ('U','V','TF','IF','TT') and --'S' to include built-in tables/types
                o.name = @TableName
            ORDER BY o.name, c.column_id
        OPEN schemaCur
        SELECT @strSQL=''
        FETCH NEXT FROM schemaCur
            INTO @ColName,@ColDataTaype,@ColIsNulable,@ColDefault,@ColFirst,@ColSecond,@ColID,@ColCompute
        WHILE @@FETCH_STATUS = 0 BEGIN
--            SELECT @strSQLCol=''
            SELECT @strSQLCol= '['+@ColName+'] '+'[' + @ColDataTaype +'] '
            IF @ColSecond is NULL
                BEGIN
                    IF @ColFirst is not NULL SELECT @strSQLCol += '(' + @ColFirst + ') '
                END 
            ELSE SELECT @strSQLCol += '(' + @ColFirst +',' +@ColSecond + ') '
            IF @ColID>0 SELECT @strSQLCol += ' IDENTITY(1,1)'
            IF @ColIsNulable>0 SELECT @strSQLCol += 'NULL'
                ELSE SELECT @strSQLCol += ' NOT NULL'
            IF @ColDefault IS NOT NULL SELECT @strSQLCol += ' DEFAULT(' +@ColDefault + '),'
                ELSE SELECT @strSQLCol += ','
            SELECT @strSQL += @strSQLCol
                --print @strSQL
            FETCH NEXT FROM schemaCur
            INTO @ColName,@ColDataTaype,@ColIsNulable,@ColDefault,@ColFirst,@ColSecond,@ColID,@ColCompute
        END

        CLOSE schemaCur
        DEALLOCATE schemaCur

        SELECT @strSQL=left(@strSQL, len(@strSQL)-1)

        IF EXISTS (SELECT * FROM sys.types WHERE IS_TABLE_TYPE = 1 AND name = 'tt_' +@TableName)
        BEGIN           
            EXEC('DROP TYPE tt_' +@TableName )
        END

        SELECT @strSQL = 'CREATE TYPE tt_' + @TableName + ' AS TABLE (' +  @strSQL + ')'
        -- print @strSQL
        EXEC (@strSQL)
        SELECT @TableNames = SUBSTRING(@TableNames, CHARINDEX(',', @TableNames) + 1, LEN(@TableNames))
    END
END
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.