我需要一个查询,该查询将获取数据类型,最大长度,是否是其标识以及是否是来自特定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
答案 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。但是,即使必须对所有数据库都执行此操作,也将使光标变得简单得多。