SQL Server-获取数据类型列表,最大长度,如果其为标识,并且其为不带object_ID函数的主键

时间:2019-06-09 12:49:20

标签: sql sql-server tsql

我需要一个查询,该查询将获取数据类型,最大长度,是否是其标识以及是否是来自特定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 :(得分:0)

  

我觉得动态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

这是很常见的情况,如果支持here-strings/text quoting,那就太好了。

答案 1 :(得分:0)

获取此信息的另一种更简单的方法是..

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。但是,即使必须对所有数据库都执行此操作,也将使光标变得简单得多。