SQL Server Check Identity values over multiple databases

w46czmvw  于 2023-10-15  发布在  其他
关注(0)|答案(2)|浏览(130)

I've the following statement that returns me the current identity values for one database

  1. ;WITH cte
  2. AS
  3. (SELECT
  4. A.TABLE_CATALOG AS CATALOG
  5. ,A.TABLE_SCHEMA AS "SCHEMA"
  6. ,A.TABLE_NAME AS "TABLE"
  7. ,B.COLUMN_NAME AS "COLUMN"
  8. ,IDENT_SEED(A.TABLE_SCHEMA + '.' + A.TABLE_NAME) AS SEED
  9. ,IDENT_INCR(A.TABLE_SCHEMA + '.' + A.TABLE_NAME) AS INCREMENT
  10. ,IDENT_CURRENT(A.TABLE_SCHEMA + '.' + A.TABLE_NAME) AS CURR_VALUE
  11. ,b.DATA_TYPE AS TYPE
  12. ,CASE LOWER(B.DATA_TYPE)
  13. WHEN 'bigint' THEN IDENT_CURRENT(A.TABLE_NAME) / 9223372036854775807
  14. WHEN 'int' THEN IDENT_CURRENT(A.TABLE_NAME) / 2147483647
  15. WHEN 'smallint' THEN IDENT_CURRENT(A.TABLE_NAME) / 32767
  16. WHEN 'tinyint' THEN IDENT_CURRENT(A.TABLE_NAME) / 255
  17. END AS [RATIO]
  18. FROM INFORMATION_SCHEMA.TABLES A
  19. ,INFORMATION_SCHEMA.columns B
  20. WHERE A.TABLE_CATALOG = B.TABLE_CATALOG
  21. AND A.TABLE_SCHEMA = B.TABLE_SCHEMA
  22. AND A.TABLE_NAME = B.TABLE_NAME
  23. AND COLUMNPROPERTY(OBJECT_ID(A.TABLE_SCHEMA + '.' + A.TABLE_NAME), B.COLUMN_NAME, 'IsIdentity') = 1
  24. AND OBJECTPROPERTY(OBJECT_ID(A.TABLE_SCHEMA + '.' + A.TABLE_NAME), 'TableHasIdentity') = 1
  25. AND A.TABLE_TYPE = 'BASE TABLE')
  26. SELECT * FROM cte WHERE RATIO > @threshold
CATALOGSCHEMATABLECOLUMNSEEDINCREMENTCURR_VALUETYPERATIO
Database1dboTableName1ColumnName1111118658505int0.520916
Database1dboTableName2ColumnName211926923907int0.431633

How can I run this (or an equal) statment against all databases on the server so I get a result in one table?
| CATALOG | SCHEMA | TABLE | COLUMN | SEED | INCREMENT | CURR_VALUE | TYPE | RATIO |
| ------------ | ------------ | ------------ | ------------ | ------------ | ------------ | ------------ | ------------ | ------------ |
| Database1 | dbo | TableName1 | ColumnName1 | 1 | 1 | 1118658505 | int | 0.520916 |
| Database1 | dbo | TableName2 | ColumnName2 | 1 | 1 | 926923907 | int | 0.431633 |
| Database2 | dbo | TableName3 | ColumnName3 | 1 | 1 | 401894827 | int | 0.187146 |
| Database2 | dbo | TableName4 | ColumnName4 | 1 | 1 | 756925871 | int | 0.352471 |

I've tried to use USE [<DATABASE_NAME>] but would need dynamic SQL and would also receive multiple return sets instead of one.

Any ideas?

uhry853o

uhry853o1#

You can do something like this:

  1. DECLARE @SQL nvarchar(max)
  2. SELECT @SQL = STRING_AGG(CAST(N'
  3. select N' + QUOTENAME(name, N'''') + N' collate database_default AS [catalog]
  4. , ss.name collate database_default AS [schema]
  5. , so.name collate database_default as [tablename]
  6. , s.name collate database_default AS [column]
  7. , st.name collate database_default AS [type]
  8. , seed_value, increment_value, s.last_value
  9. , CASE LOWER(st.name)
  10. WHEN ''bigint'' THEN CONVERT(BIGINT, last_value) / 9223372036854775807
  11. WHEN ''int'' THEN CONVERT(INT, last_value) / 2147483647
  12. WHEN ''smallint'' THEN CONVERT(SMALLINT, last_value) / 32767
  13. WHEN ''tinyint'' THEN CONVERT(TINYINT, last_value) / 255
  14. END AS [RATIO]
  15. from ' + QUOTENAME(name) + N'.sys.identity_columns s
  16. inner join ' + QUOTENAME(name) + N'.sys.objects so
  17. ON so.object_id = s.object_id
  18. inner join ' + QUOTENAME(name) + N'.sys.types st
  19. ON st.user_type_id = s.user_type_id
  20. INNER JOIN ' + QUOTENAME(name) + N'.sys.schemas ss
  21. ON ss.schema_id = so.schema_id
  22. where so.is_ms_shipped = 0
  23. ' AS NVARCHAR(MAX)), N' UNION ALL ')
  24. FROM sys.databases
  25. where state_desc = 'ONLINE'
  26. EXEC (@SQL)

It builds dynamic sql string which unions all databases and executes it once.

I simplified the usage of views so you use something a bit more modern, but you can probably adapt your original script from the general idea.

展开查看全部
l2osamch

l2osamch2#

The query itself can be simplified a lot by querying the sys.identity_columns system view, which already contains table, column, last value, seed and even size information in bytes, in the form of the max_length column

For example :

  1. select DB_NAME() As DB,
  2. OBJECT_SCHEMA_NAME(object_ID) as SchemaName,
  3. object_name(object_id) as TableName,
  4. name as ColumnName,
  5. TYPE_NAME(system_type_id) as ColumnType,
  6. cast(Seed_value as bigint) Seed_Value,
  7. cast(Increment_value as bigint) as Increment_Value,
  8. POWER(2.0,max_length*7) as MaxSize,
  9. cast(last_value as bigint) as Last_value,
  10. cast(Last_value as bigint)/POWER(2.0,max_length*7) as ratio
  11. from sys.identity_columns;

The maximum size is generated by raising 2 to the number of bits. max_length*7 is used instead of max_length*8 because the question seems to only count positive ID values. Nothing prevents negative ID values though.

seed_value , increment_value and last_value are a sql_variant columns that must be cast to a numeric type before they're used in calculations.

This query can be executed against all databases using the undocumented but globally (as in used by everyone,everywhere) sp_MSForEachDB stored procedure

  1. declare @sql nvarchar(2000)=N'USE ?;
  2. select DB_NAME() As DB,
  3. OBJECT_SCHEMA_NAME(object_ID) as SchemaName,
  4. object_name(object_id) as TableName,
  5. name as ColumnName,
  6. TYPE_NAME(system_type_id) as ColumnType,
  7. cast(Seed_value as bigint) Seed_Value,
  8. cast(Increment_value as bigint) as Increment_Value,
  9. POWER(2.0,max_length*7) as MaxSize,
  10. cast(last_value as bigint) as Last_value,
  11. cast(Last_value as bigint)/POWER(2.0,max_length*7) as ratio
  12. from sys.identity_columns;'
  13. exec sp_MSForEachDB @sql

This produces multiple result sets. These can be inserted into a temporary table or table variable:

  1. declare @ratios table (
  2. DB nvarchar(100),
  3. TableName nvarchar(100),
  4. ColumnName nvarchar(100),
  5. ColumnType nvarchar(20),
  6. Seed_Value bigint,
  7. Increment_Value bigint,
  8. MaxSize bigint,
  9. Last_value bigint,
  10. ratio float);
  11. insert into @ratios
  12. exec sp_MSForEachDB @sql
  13. select *
  14. from @ratios
  15. Where ratio>0.001

All this can be wrapped into a stored procedure that returns rows that exceed the ratio:

  1. create procedure large_identities(@ratio float)
  2. as
  3. declare @sql nvarchar(2000)=N'USE ?;
  4. select DB_NAME() As DB,
  5. OBJECT_SCHEMA_NAME(object_ID) as SchemaName,
  6. object_name(object_id) as TableName,
  7. name as ColumnName,
  8. TYPE_NAME(system_type_id) as ColumnType,
  9. cast(Seed_value as bigint) Seed_Value,
  10. cast(Increment_value as bigint) as Increment_Value,
  11. POWER(2.0,max_length*7) as MaxSize,
  12. cast(last_value as bigint) as Last_value,
  13. cast(Last_value as bigint)/POWER(2.0,max_length*7) as ratio
  14. from sys.identity_columns;'
  15. declare @ratios table (
  16. DB nvarchar(100),
  17. TableName nvarchar(100),
  18. ColumnName nvarchar(100),
  19. ColumnType nvarchar(20),
  20. Seed_Value bigint,
  21. Increment_Value bigint,
  22. MaxSize bigint,
  23. Last_value bigint,
  24. ratio float);
  25. insert into @ratios
  26. exec sp_MSForEachDB @sql
  27. select *
  28. from @ratios
  29. Where ratio>@ratio

And called with the ratio as a parameter:

  1. exec large_identities 0.001
展开查看全部

相关问题