використовуючи * таблиці * як параметри табличного значення (TVP)


18

MS SQL 2008 підтримує TVP: корисна функція для масового завантаження даних у збережений процес для обробки.

Чи замість створення визначеного користувачем типу, чи можна використовувати існуюче визначення таблиці? Наприклад, чи можна створити збережену процедуру з наступним підписом?

CREATE PROCEDURE usp_InsertProductionLocation
@TVP **LocationTable** READONLY

Здається, що документація підказує, що це неможливо.

ЗРАЗДОВИЙ КОД

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

Відповіді:


16

Ні, ви не можете використовувати існуюче визначення таблиці, вам потрібно визначити явний тип. Про це запитали ще в 2007 році, і його закрили, оскільки "не виправлять", але я все-таки наполегливо рекомендую вам взяти участь у голосуванні та залишити коментар із описом вашої справи використання та як це допоможе вашому бізнесу бути більш продуктивним. Ви навіть можете вказати на це запитання, щоб продемонструвати, наскільки нудно це спробувати і автоматизувати.

Елемент на UserVoice (колишнє з'єднання №294130)

Ви можете зробити це сьогодні, динамічно, хоча ... наприклад для вашого простого визначення:

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

Результати:

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

Відмова від відповідальності: Це не стосується всіх інших речей, таких як типи MAX, точність та шкала чисельності тощо. Остаточне рішення повинно бути більш надійним для врахування всіх можливих визначень стовпців, але це повинно дати вам початок.

У SQL Server 2012 є нові DMV та збережені процедури, які значно полегшать отримання метаданих стовпців із існуючих таблиць (або збережених процедур або навіть спеціальних запитів) без необхідності возитися зі всією умовною логікою щодо sys.types та sys. стовпчики. У грудні я коротко розказав про ці вдосконалення . Це все ще нудно, але це десь між жахливими незбагненними спагетті вище і здатністю просто сказати "як копія [таблиці x]" ...


4

Я вирішив цю проблему, створивши наступну збережену процедуру для створення типу з тією ж схемою, яку може мати існуюча таблиця.

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

ви можете використовувати його так

Exec Sp_DefineTypeOutOfTableSchema 'Таблиця1, Ім'я Таблиця2'


Цей код не працював для мене. "Визначені користувачем функції, функції розділів та посилання стовпців не дозволяються у виразах у цьому контексті." Навіть якщо це все-таки спрацювало, це не схоже на те, що є щось для обробки схем, крім dbo.
MgSam

2

У Ерланда Соммарського є обширна стаття, що описує, як користуватися TVP.

Погляньте, воно того варте!

Коротше кажучи, ви не можете використовувати існуючий тип, як і раніше відповідь Аарона Бертранда . Але принаймні це об'ємна передача ..


1

Виходячи з відповіді Джітена, спрощення деякої логіки місцями, облік identityстовпців ( sys.скоріше використання таблиць 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.