Можете ли вы создать CLR UDT, чтобы разрешить общий тип таблиц в разных базах данных? - PullRequest
3 голосов
/ 07 ноября 2011

Если бы у меня был SQL-оператор, такой как этот:

CREATE TYPE [dbo].[typeRateLimitVariables] AS TABLE(
            [vchColumnName] [varchar](250) NULL,
            [decColumnValue] [decimal](25, 10) NULL
)

И я использовал ее как табличную переменную для UDF в базе данных, у меня был бы достаточный объем. Но скажем, я хотел вызвать скалярную UDF из другой базы данных на том же сервере, и в результате я столкнулся с ошибкой неизвестного типа.

Я пытался создать тип в вызывающей БД, но не об. тогда я получаю несоответствие типов, потому что, хотя каждый из UDT имеет одно и то же имя, они имеют разные области и, следовательно, разные типы.

Я знаю, что вы можете создавать типы CLR, регистрировать сборку в SQL Server, а затем получать универсальный доступ к пользовательскому типу.

Моя идея состоит в том, чтобы создать CLR UDT типа "TABLE", однако я не вижу, как это можно реализовать, поскольку знаю, что это должен быть тип CLR "SqlDbType.Structured";

Мои вопросы:

  1. Есть ли способ без использования CLR для создания глобальной области в SQL 2008 R2 для табличной переменной, а если нет ...
  2. Как определить UDT в C # CLR, в котором UDT по сути является UDT «КАК ТАБЛИЦА»

1 Ответ

4 голосов
/ 23 июня 2014

Я знаю, что вы можете создавать типы CLR, регистрировать сборку в SQL Server и затем обращаться к пользовательскому типу универсально.

Вы уверены в этом?Определяемые пользователем типы являются объектами уровня базы данных, а не уровня сервера.Единственный способ получить к ним универсальный доступ - это загрузить сборку в каждую из баз данных и создать пользовательский тип в каждой базе данных.Это много указано в документации MSDN для Регистрация пользовательских типов в SQL Server :

Использование UDT в разных базах данных
UDT по определениюограничен одной базой данных.Поэтому UDT, определенный в одной базе данных, нельзя использовать в определении столбца в другой базе данных.Чтобы использовать UDT в нескольких базах данных, необходимо выполнить операторы CREATE ASSEMBLY и CREATE TYPE в каждой базе данных на идентичных сборках.Сборки считаются идентичными, если они имеют одинаковое имя, строгое имя, культуру, версию, набор разрешений и двоичное содержимое.

Как только UDT зарегистрирован и доступен в обеих базах данных, вы можете преобразовать значение UDT из одной базы данных для использования в другой.Идентичные UDT могут использоваться в разных базах данных в следующих сценариях:

  • Вызов хранимой процедуры, определенной в разных базах данных.
  • Запрос таблиц, определенных в разных базах данных.
  • Выбор данных UDT из одного столбца UDT таблицы базы данных и вставка их во вторую базу данных с идентичным столбцом UDT.

В этих ситуациях любое преобразование, требуемое сервером, происходит автоматически.Вы не можете выполнить преобразования явно, используя функции Transact-SQL CAST или CONVERT.

Чтобы ответить на ваши конкретные вопросы:

1) Есть ли способ безиспользуя CLR для создания глобальной области в SQL 2008 R2 для табличной переменной, а если нет ...

Ни типы таблиц, ни определяемые пользователем типы не доступны для разных баз данных, в одном случае принимается какUDT CLR, как указано выше в документации MSDN.

2) Как определить UDT в C # CLR, в котором UDT по сути является UDT «КАК ТАБЛИЦА»

Вы не можете, поскольку это две разные вещи (то есть «Тип» против «Тип таблицы»), в отличие от двух разных способов реализации (т.е. T-SQL UDF / Хранимый Proc против SQLCLR UDF / Хранимый Proc).

РЕДАКТИРОВАНИЕ:

На чисто техническом уровне возможно использование типов (таблиц и пользовательских типов) в разных базах данных,но только путем переключения текущего контекста черезUSE команда, которая может использоваться только в специальном / динамическом SQL.Следовательно, это использование имеет ограниченную применимость на практическом уровне, но тем не менее это все же возможно, как показано в следующем примере:

SET ANSI_NULLS ON;
SET QUOTED_IDENTIFIER ON;
SET NOCOUNT ON;
GO

USE [msdb];
GO

PRINT 'Creating [GlobalTableDef] Table Type in [msdb]...';
CREATE TYPE dbo.GlobalTableDef
AS TABLE
(
    [ID] INT NOT NULL IDENTITY(17, 22),
    [CreateDate] DATETIME NOT NULL DEFAULT (GETDATE()),
    [Something] NVARCHAR(2000) NULL
);
GO

PRINT 'Creating [TotalBytes] Function in [msdb]...';
GO
CREATE FUNCTION dbo.TotalBytes
(
    @TableToSummarize dbo.GlobalTableDef READONLY
)
RETURNS INT
AS
BEGIN
    DECLARE @TotalBytes INT = 0;

SELECT  @TotalBytes += (4 + 8 + DATALENGTH(COALESCE(tmp.Something, '')))
    FROM    @TableToSummarize tmp;

    RETURN @TotalBytes;
END;
GO

PRINT 'Testing the Table Type and Function...';
DECLARE @TmpTable dbo.GlobalTableDef;
INSERT INTO @TmpTable (Something) VALUES (N'this is a test');
INSERT INTO @TmpTable (Something) VALUES (NULL);
INSERT INTO @TmpTable (Something) VALUES (N'still seems to be a test');

SELECT * FROM @TmpTable;

SELECT dbo.TotalBytes(@TmpTable) AS [TotalBytesUsed];
GO

USE [tempdb];
GO
PRINT 'Creating [TypeTest] Proc in [tempdb]...';
GO

CREATE PROCEDURE dbo.TypeTest
AS
SET NOCOUNT ON;

    SELECT 1 AS [Step], DB_NAME() AS [CurrentDB];

    EXEC('
        SELECT 2 AS [Step], DB_NAME() AS [CurrentDB];
        USE [msdb];
        SELECT 3 AS [Step], DB_NAME() AS [CurrentDB];
        DECLARE @TmpTable dbo.GlobalTableDef;
        USE [tempdb];
        SELECT 4 AS [Step], DB_NAME() AS [CurrentDB];

        -- local query to prove context is tempdb
        SELECT TOP 5 * FROM sys.objects;

        INSERT INTO @TmpTable (Something) VALUES (N''this is a new test'');
        INSERT INTO @TmpTable (Something) VALUES (NULL);
        INSERT INTO @TmpTable (Something) VALUES (N''non-empty value'');
        INSERT INTO @TmpTable (Something) VALUES (NULL);
        INSERT INTO @TmpTable (Something) VALUES (N''woo-hoo!!!!!!!!!!!!!!!'');
        SELECT * FROM @TmpTable;

        SELECT [msdb].dbo.TotalBytes(@TmpTable) AS [TotalBytesUsed];
    ');

GO

USE [master];
GO

SELECT 5 AS [Step], DB_NAME() AS [CurrentDB];
EXEC tempdb.dbo.TypeTest;

--------------------------------

USE [tempdb];
GO
IF (OBJECT_ID(N'tempdb.dbo.TypeTest') IS NOT NULL)
BEGIN
    PRINT 'Dropping [TypeTest] Proc from [tempdb]...';
    DROP PROCEDURE dbo.TypeTest;
END;
GO

USE [msdb];
GO
IF (OBJECT_ID(N'dbo.TotalBytes') IS NOT NULL)
BEGIN
    PRINT 'Dropping [TotalBytes] Function from [msdb]...';
    DROP FUNCTION dbo.TotalBytes;
END;
GO

IF (EXISTS(
        SELECT  *
        FROM    sys.table_types stt
        WHERE   stt.name = N'GlobalTableDef'
    ))
BEGIN
    PRINT 'Dropping [GlobalTableDef] Table Type from [msdb]...';
    DROP TYPE dbo.GlobalTableDef;
END;
GO
...