SQL Server - получить список типов данных, максимальная длина, если это идентификатор и если это первичный ключ без функции object_ID - PullRequest
0 голосов
/ 09 июня 2019

Мне нужен запрос, который получает тип данных, максимальную длину, если это тождество и если это первичный ключ из определенного table_name без использования функции object_id. Итак, что я имею сейчас (и также нашел это где-то в stackoverflow):

 SELECT col.COLUMN_NAME AS ColumnName
     , col.DATA_TYPE AS DataType
     , col.CHARACTER_MAXIMUM_LENGTH AS MaxLength
     , COLUMNPROPERTY(OBJECT_ID('[' + col.TABLE_SCHEMA + '].[' + col.TABLE_NAME + ']'), col.COLUMN_NAME, 'IsIdentity')AS IS_IDENTITY
     , CAST(ISNULL(pk.is_primary_key, 0)AS bit)AS IsPrimaryKey
  FROM INFORMATION_SCHEMA.COLUMNS AS col
       LEFT JOIN(SELECT SCHEMA_NAME(o.schema_id)AS TABLE_SCHEMA
                      , o.name AS TABLE_NAME
                      , c.name AS COLUMN_NAME
                      , i.is_primary_key
                   FROM sys.indexes AS i JOIN sys.index_columns AS ic ON i.object_id = ic.object_id
                                                                     AND i.index_id = ic.index_id
                                         JOIN sys.objects AS o ON i.object_id = o.object_id
                                         LEFT JOIN sys.columns AS c ON ic.object_id = c.object_id
                                                                   AND c.column_id = ic.column_id
                  WHERE i.is_primary_key = 1)AS pk ON col.TABLE_NAME = pk.TABLE_NAME
                                                  AND col.TABLE_SCHEMA = pk.TABLE_SCHEMA
                                                  AND col.COLUMN_NAME = pk.COLUMN_NAME
 WHERE col.TABLE_NAME = 'tbl_users'
 ORDER BY col.TABLE_NAME, col.ORDINAL_POSITION;

Я использую этот код для курсора, и когда я пытаюсь получить значение IS_IDENTITY, оно всегда пустое или что-то в этом роде. Я чувствую, что динамический SQL и курсор не нравится функция OBJECT_ID. Когда я запускаю этот запрос без курсора и прочего, он отлично работает.

ПОЛНЫЙ КОД:

ALTER Procedure [dbo].[sp_generateUpserts]
    @databaseName nvarchar(MAX)
AS
BEGIN

SET NOCOUNT ON

DECLARE @tranState BIT
IF @@TRANCOUNT = 0
BEGIN
    SET @tranState = 1
    BEGIN TRANSACTION tranState
END

BEGIN TRY
    Declare @TABLE_NAME varchar(100)
    Declare @COLUMN_NAME varchar(100)
    Declare @COLUMN_NAME_WITH_SPACE varchar(100)
    Declare @DATA_TYPE varchar(100)
    Declare @CHARACTER_MAXIMUM_LENGTH INT
    Declare @IS_PK INT
    Declare @IS_IDENTITY INT

    DECLARE @statement nvarchar(MAX)
    SET @statement = N'USE [' + @databaseName + ']; 
    DECLARE cursorUpsert CURSOR FOR 
    SELECT TABLE_NAME
    FROM INFORMATION_SCHEMA.TABLES 
    ORDER BY TABLE_NAME'
    EXECUTE sp_executesql @statement

    DECLARE @use_db nvarchar(max)
    DECLARE @generateSpStatement varchar(MAX)

    DECLARE @spParameters varchar(MAX) = ''
    DECLARE @whereColumns varchar(MAX) = ''
    DECLARE @updateStatement varchar(MAX) = ''
    DECLARE @insertStatement varchar(MAX) = ''
    DECLARE @valueStatement varchar(MAX) = ''

    OPEN cursorUpsert 
    FETCH NEXT FROM cursorUpsert 
    INTO @TABLE_NAME

    WHILE @@FETCH_STATUS = 0
    BEGIN
        DECLARE @statementColumns nvarchar(MAX)
        SET @statementColumns = N'USE [' + @databaseName + ']; 
        DECLARE cursorUpsertColumns CURSOR FOR 
        SELECT col.COLUMN_NAME
     , col.DATA_TYPE
     , col.CHARACTER_MAXIMUM_LENGTH
    , COLUMNPROPERTY(OBJECT_ID(QUOTENAME(col.TABLE_SCHEMA) + ''.'' +  QUOTENAME(col.TABLE_NAME)), col.COLUMN_NAME, ''IsIdentity'')AS IS_IDENTITY
     , CAST(ISNULL(pk.is_primary_key, 0) AS bit) AS IS_PK
  FROM INFORMATION_SCHEMA.COLUMNS AS col
       LEFT JOIN(SELECT SCHEMA_NAME(o.schema_id)AS TABLE_SCHEMA
                      , o.name AS TABLE_NAME
                      , c.name AS COLUMN_NAME
                      , i.is_primary_key
                   FROM sys.indexes AS i JOIN sys.index_columns AS ic ON i.object_id = ic.object_id
                                                                     AND i.index_id = ic.index_id
                                         JOIN sys.objects AS o ON i.object_id = o.object_id
                                         LEFT JOIN sys.columns AS c ON ic.object_id = c.object_id
                                                                   AND c.column_id = ic.column_id
                  WHERE i.is_primary_key = 1)AS pk ON col.TABLE_NAME = pk.TABLE_NAME
                                                  AND col.TABLE_SCHEMA = pk.TABLE_SCHEMA
                                                  AND col.COLUMN_NAME = pk.COLUMN_NAME
 WHERE col.TABLE_NAME = ''' + @TABLE_NAME + '''
 ORDER BY col.TABLE_NAME, col.ORDINAL_POSITION;'
        EXECUTE sp_executesql @statementColumns

        OPEN cursorUpsertColumns 
        FETCH NEXT FROM cursorUpsertColumns 
        INTO @COLUMN_NAME, @DATA_TYPE, @CHARACTER_MAXIMUM_LENGTH, @IS_IDENTITY, @IS_PK

        WHILE @@FETCH_STATUS = 0
        BEGIN
            -- Parameters for the SP
            IF @COLUMN_NAME LIKE '% %'
            BEGIN
                SET @COLUMN_NAME_WITH_SPACE = @COLUMN_NAME
                SET @COLUMN_NAME_WITH_SPACE = REPLACE(@COLUMN_NAME_WITH_SPACE,' ','_')
                SET @spParameters = @spParameters + CHAR(13) + '@' + @COLUMN_NAME_WITH_SPACE + ' ' + @DATA_TYPE
            END
            ELSE
            BEGIN
                SET @spParameters = @spParameters + CHAR(13) + '@' + @COLUMN_NAME + ' ' + @DATA_TYPE
            END

            IF @DATA_TYPE IN ('varchar', 'nvarchar', 'char', 'nchar') 
            BEGIN
                IF @CHARACTER_MAXIMUM_LENGTH = '-1'
                    BEGIN
                        SET @spParameters = @spParameters + '(MAX)'
                    END
                ELSE
                    BEGIN
                        SET @spParameters = @spParameters + '(' + CAST(@CHARACTER_MAXIMUM_LENGTH As Varchar(10)) + ')'
                    END
            END
            -- Add a comma after each parameter
            SET @spParameters = @spParameters + ', '

            IF @COLUMN_NAME IN ('top')
            BEGIN
                IF @IS_IDENTITY != 1
                BEGIN
                    print('YES IDENTITY')
                END
                -- Add where parameters: ColumnName=@ColumnName AND
                SET @whereColumns = @whereColumns + CHAR(32) + '[' + @COLUMN_NAME + ']=@' + @COLUMN_NAME + ' AND'
                -- Add update parameters: column1 = value1, etc.
                IF @IS_IDENTITY != 1 OR @IS_PK != 1
                BEGIN
                    SET @updateStatement = @updateStatement + CHAR(32) + '[' + @COLUMN_NAME + ']=@' + @COLUMN_NAME + ',' 
                END
                -- Add insert columns
                SET @insertStatement = @insertStatement + CHAR(32) + '[' + @COLUMN_NAME + '],'
                -- Add values
                SET @valueStatement = @valueStatement + CHAR(32) + '@' + @COLUMN_NAME + ','
            END
            ELSE IF @COLUMN_NAME LIKE '% %'
            BEGIN
            IF @IS_IDENTITY != 1
                BEGIN
                    print('YES IDENTITY')
                END
                -- Add where parameters: ColumnName=@ColumnName AND
                SET @whereColumns = @whereColumns + CHAR(32) + '[' + @COLUMN_NAME + ']=@' + @COLUMN_NAME_WITH_SPACE + ' AND'
                -- Add update parameters: column1 = value1, etc.
                IF @IS_IDENTITY != 1 OR @IS_PK != 1
                BEGIN
                    SET @updateStatement = @updateStatement + CHAR(32) + '[' + @COLUMN_NAME + ']=@' + @COLUMN_NAME_WITH_SPACE + ',' 
                END
                -- Add insert columns
                SET @insertStatement = @insertStatement + CHAR(32) + '['+ @COLUMN_NAME + '],'
                -- Add values
                SET @valueStatement = @valueStatement + CHAR(32) + '@' + @COLUMN_NAME_WITH_SPACE + ','
            END
            ELSE
            BEGIN
            IF @IS_IDENTITY != 1
                BEGIN
                    print('YES IDENTITY')
                END
                -- Add where parameters: ColumnName=@ColumnName AND
                SET @whereColumns = @whereColumns + CHAR(32) + @COLUMN_NAME + '=@' + @COLUMN_NAME + ' AND'
                -- Add update parameters: column1 = value1, etc.
                IF @IS_IDENTITY != 1 OR @IS_PK != 1
                BEGIN
                    SET @updateStatement = @updateStatement + CHAR(32) + @COLUMN_NAME + '=@' + @COLUMN_NAME + ',' 
                END
                -- Add insert columns
                SET @insertStatement = @insertStatement + CHAR(32) + @COLUMN_NAME + ','
                -- Add values
                SET @valueStatement = @valueStatement + CHAR(32) + '@' + @COLUMN_NAME + ','
            END

            FETCH NEXT FROM cursorUpsertColumns 
            INTO @COLUMN_NAME, @DATA_TYPE, @CHARACTER_MAXIMUM_LENGTH, @IS_IDENTITY, @IS_PK  
            if @@FETCH_STATUS!=0
                begin
                    -- Last row, remove things
                    -- Remove the last AND word
                    SET @whereColumns = left (@whereColumns, len(@whereColumns) -3)
                    -- Remove the last comma from the parameter
                    SET @spParameters = left (@spParameters, len(@spParameters) -1)
                    -- Remove the last comma from the updateStatement
                    SET @updateStatement = left (@updateStatement, len(@updateStatement) -1)
                    -- Remove the last comma from the insertStatement
                    SET @insertStatement = left (@insertStatement, len(@insertStatement) -1)
                    -- Remove the last comma from the valueStatement
                    SET @valueStatement = left (@valueStatement, len(@valueStatement) -1)
                end

        END; 
        CLOSE cursorUpsertColumns;
        DEALLOCATE cursorUpsertColumns;
        --- End Cursor Columns

        -- Generate the SP
        SET @generateSpStatement = 'CREATE Procedure [dbo].[sp_' + @TABLE_NAME + '_upsert]' + @spParameters
        SET @generateSpStatement = @generateSpStatement + CHAR(13) + 'AS BEGIN' + CHAR(13)
        SET @generateSpStatement = @generateSpStatement + CHAR(13)
        SET @generateSpStatement = @generateSpStatement + CHAR(9) + 'SET NOCOUNT ON' + CHAR(13)
        SET @generateSpStatement = @generateSpStatement + CHAR(9) + 'DECLARE @tranState BIT' + CHAR(13)
        SET @generateSpStatement = @generateSpStatement + CHAR(9) + 'IF @@TRANCOUNT = 0' + CHAR(13)
        SET @generateSpStatement = @generateSpStatement + CHAR(9) + 'BEGIN' + CHAR(13)
        SET @generateSpStatement = @generateSpStatement + CHAR(9) + CHAR(9) +'SET @tranState = 1' + CHAR(13)
        SET @generateSpStatement = @generateSpStatement + CHAR(9) + CHAR(9) +'set transaction isolation level serializable' + CHAR(13)
        SET @generateSpStatement = @generateSpStatement + CHAR(9) + CHAR(9) +'BEGIN TRANSACTION tranState' + CHAR(13)
        SET @generateSpStatement = @generateSpStatement + CHAR(9) + 'END' + CHAR(13)
        SET @generateSpStatement = @generateSpStatement + CHAR(13) + 'BEGIN TRY' + CHAR(13)
        SET @generateSpStatement = @generateSpStatement + CHAR(9) + 'IF EXISTS(SELECT 1 FROM ' + @TABLE_NAME + ' WITH (updlock) WHERE' + @whereColumns +  ')' + CHAR(13)
        SET @generateSpStatement = @generateSpStatement + CHAR(9) + CHAR(9) + 'UPDATE ' + @TABLE_NAME + ' SET' + @updateStatement + ' WHERE ' + @whereColumns + ';' + CHAR(13)
        SET @generateSpStatement = @generateSpStatement + CHAR(9) + 'ELSE' + CHAR(13)
        SET @generateSpStatement = @generateSpStatement + CHAR(9) + CHAR(9) + 'INSERT INTO ' + @TABLE_NAME + ' ('+ @insertStatement + ') VALUES (' + @valueStatement + ');' + CHAR(13)
        SET @generateSpStatement = @generateSpStatement + CHAR(13)
        SET @generateSpStatement = @generateSpStatement + CHAR(9) + 'IF @tranState = 1 AND XACT_STATE() = 1' + CHAR(13)
        SET @generateSpStatement = @generateSpStatement + CHAR(9) + CHAR(9) + 'COMMIT TRANSACTION tranState' + CHAR(13)
        SET @generateSpStatement = @generateSpStatement + 'END TRY' + CHAR(13)
        SET @generateSpStatement = @generateSpStatement + CHAR(13) + 'BEGIN CATCH' + CHAR(13)
        SET @generateSpStatement = @generateSpStatement + CHAR(9) + 'DECLARE @Error_Message VARCHAR(5000)' + CHAR(13)
        SET @generateSpStatement = @generateSpStatement + CHAR(9) + 'DECLARE @Error_Severity INT' + CHAR(13)
        SET @generateSpStatement = @generateSpStatement + CHAR(9) + 'DECLARE @Error_State INT' + CHAR(13)
        SET @generateSpStatement = @generateSpStatement + CHAR(9) + 'SELECT @Error_Message = ERROR_MESSAGE()' + CHAR(13)
        SET @generateSpStatement = @generateSpStatement + CHAR(9) + 'SELECT @Error_Severity = ERROR_SEVERITY()' + CHAR(13)
        SET @generateSpStatement = @generateSpStatement + CHAR(9) + 'SELECT @Error_State = ERROR_STATE()' + CHAR(13)
        SET @generateSpStatement = @generateSpStatement + CHAR(13)
        SET @generateSpStatement = @generateSpStatement + CHAR(9) + 'IF @tranState = 1 AND XACT_STATE() <> 0' + CHAR(13)
        SET @generateSpStatement = @generateSpStatement + CHAR(9) + CHAR(9) +'ROLLBACK TRANSACTION' + CHAR(13)
        SET @generateSpStatement = @generateSpStatement + CHAR(13)
        SET @generateSpStatement = @generateSpStatement + CHAR(9) + 'RAISERROR (@Error_Message, @Error_Severity, @Error_State)' + CHAR(13)
        SET @generateSpStatement = @generateSpStatement + 'END CATCH' + CHAR(13)
        SET @generateSpStatement = @generateSpStatement + CHAR(13)
        SET @generateSpStatement = @generateSpStatement + 'END' + CHAR(13)

        --print(@generateSpStatement)

        -- Reset Variables
        SET @generateSpStatement = ''
        SET @spParameters = ''
        SET @whereColumns = ''
        SET @updateStatement = ''
        SET @insertStatement = ''
        SET @valueStatement = ''

      FETCH NEXT FROM cursorUpsert 
      INTO @TABLE_NAME
    END; 

    CLOSE cursorUpsert;
    DEALLOCATE cursorUpsert;

    IF @tranState = 1 
        AND XACT_STATE() = 1
        COMMIT TRANSACTION tranState
END TRY
BEGIN CATCH

    DECLARE @Error_Message VARCHAR(5000)
    DECLARE @Error_Severity INT
    DECLARE @Error_State INT

    SELECT @Error_Message = ERROR_MESSAGE()
    SELECT @Error_Severity = ERROR_SEVERITY()
    SELECT @Error_State = ERROR_STATE()

    IF @tranState = 1 AND XACT_STATE() <> 0
        ROLLBACK TRANSACTION

    RAISERROR (@Error_Message, @Error_Severity, @Error_State)

END CATCH
END

Ответы [ 2 ]

0 голосов
/ 09 июня 2019

Другим немного более простым способом получения этой информации было бы что-то вроде ..

Declare   @Schema SYSNAME = 'dbo'
        , @Table  SYSNAME=  'Orders'


SELECT 
      name                  Column_Name
    , system_type_name      Data_Type
    , max_length            Max_Length
    , is_identity_column    Is_Identity_Column
    , ISNULL(c.PK_Column,0) Is_Primary_Key_Column
FROM sys.dm_exec_describe_first_result_set (N'SELECT * FROM '+ @Schema +'.' + @Table, null, 0) r
OUTER APPLY (
                SELECT 1 PK_Column
                FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE s
                WHERE OBJECTPROPERTY(OBJECT_ID(s.CONSTRAINT_SCHEMA + '.' + QUOTENAME(s.CONSTRAINT_NAME)), 'IsPrimaryKey') = 1
                AND s.TABLE_NAME = @Table
                AND s.TABLE_SCHEMA = @Schema
                AND r.name COLLATE DATABASE_DEFAULT = s.COLUMN_NAME 
            ) c(PK_Column)

Вы можете поместить этот код в функцию и просто вызвать эту функцию, применяя ее в представлении каталога sys.tables.

Представление динамического управления sys.dm_exec_describe_first_result_set также содержит много другой полезной информации.

Предположим, что вы создаете такую ​​функцию ..

CREATE FUNCTION dbo.fn_get_Column_Info (
  @Schema SYSNAME
, @Table  SYSNAME)
RETURNS TABLE 
AS
RETURN
(

SELECT 
      name                  Column_Name
    , system_type_name      Data_Type
    , max_length            Max_Length
    , is_identity_column    Is_Identity_Column
    , ISNULL(c.PK_Column,0) Is_Primary_Key_Column
FROM sys.dm_exec_describe_first_result_set (N'SELECT * FROM '+ @Schema +'.' + @Table, null, 0) r
OUTER APPLY (
                SELECT 1 PK_Column
                FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE s
                WHERE OBJECTPROPERTY(OBJECT_ID(s.CONSTRAINT_SCHEMA + '.' + QUOTENAME(s.CONSTRAINT_NAME)), 'IsPrimaryKey') = 1
                AND s.TABLE_NAME = @Table
                AND s.TABLE_SCHEMA = @Schema
                AND r.name COLLATE DATABASE_DEFAULT = s.COLUMN_NAME 
            ) c(PK_Column)
);
GO

Тогда ваш запрос для получения всей необходимой информации будет так же прост, как ..

SELECT s.name , t.name,  f.*
FROM sys.schemas s
INNER JOIN sys.Tables t ON s.schema_id = t.schema_id
CROSS APPLY dbo.fn_get_Column_Info(s.name , t.name) f;

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

0 голосов
/ 09 июня 2019

Мне кажется, что динамический sql и курсор не любят функцию OBJECT_ID.

Это не имеет ничего общего с OBJECT_ID, но, скорее всего, неверное двойное наложение при использовании с динамической строкой:

COLUMNPROPERTY(OBJECT_ID(''['' + col.TABLE_SCHEMA + ''].['' + col.TABLE_NAME + '']''), col.COLUMN_NAME, ''IsIdentity'')AS IS_IDENTITY

В любом случае вам следует избегать ручного добавления [ и использовать вместо него QUOTENAME:

COLUMNPROPERTY(OBJECT_ID(QUOTENAME(col.TABLE_SCHEMA) + ''.'' +  QUOTENAME(col.TABLE_NAME)), col.COLUMN_NAME, ''IsIdentity'')AS IS_IDENTITY

Это распространенный случай, и было бы очень хорошо, если бы здесь-строки / цитирование текста .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...