如何加入sys.databases,sys.tables和sys.columns

时间:2018-10-31 09:01:22

标签: sql-server

我必须检查sql服务器实例的数据库子集中的表的子集中是否存在值。当心我需要这样做,因为我有30个具有相同架构名称和相似结构的数据库。单独查询所有数据库是浪费时间。

该查询会为现有表正确生成代码,但是对表中是否存在列的附加检查失败。 某些表中的列不存在,因此生成的代码不得包含对没有此列的表的查询。

要解决这个问题,我需要切实地找到一种方法,将sys.databases与sys.tables以及sys.columns结合起来。或以一种节省时间的方式查询所有必需数据库的替代方法。

SET NOCOUNT ON;

IF OBJECT_ID (N'tempdb.dbo.#temp') IS NOT NULL
   DROP TABLE #temp

CREATE TABLE #temp
(
      exist INT
    , DB VARCHAR(50)
    , tbname VARCHAR(500)
)

/*tables common root, 
all tables i need to query start with this prefix and a number between 1 and 50
and some resulting tables do not exist
ex: dbo.Z_WBL_ASCHEDA23 exist in wbcto, while dbo.Z_WBL_ASCHEDA23 does not exist in db wbgtg    
*/

DECLARE @TableName NVARCHAR(200) 
SELECT @TableName = 'dbo.Z_WBL_ASCHEDA'

DECLARE @SQL NVARCHAR(MAX)

;WITH n(n) AS
(
    SELECT 1
    UNION ALL
    SELECT n+1 FROM n WHERE n < 50
)
SELECT @SQL = STUFF((
    SELECT CHAR(13)+'SELECT COUNT(1), ''' + db.name + ''', '''+
        @TableName+CONVERT(VARCHAR, n.n)+'''  FROM ' +@TableName+CONVERT(VARCHAR, n.n) 
        + ' WHERE COALESCE(s_dettagli,'''') = ''CONTROLLATO'' '
        +CHAR(13)
    FROM sys.databases db
    INNER JOIN n ON 1=1
    INNER JOIN sys.tables t ON OBJECT_ID(db.name + '.' + @TableName+CONVERT(VARCHAR, n.n)) IS NOT NULL
    INNER JOIN sys.columns c ON t.OBJECT_ID = c.OBJECT_ID and c.name = 's_dettagli' 
    /*join on columns not working, generates sql for tables without 's_dettagli' column and query fails*/
    WHERE db.name like 'wb%' --check only databases starting with 'wb'
    FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')

select @SQL

INSERT INTO #temp (exist, DB, tbname)              
EXEC sys.sp_executesql @SQL

SELECT * 
FROM #temp t
where exist <> 0

编辑:添加从查询生成的一些SQL

SELECT COUNT(1), 'wb360', 'dbo.Z_WBL_ASCHEDA23'  FROM wb360.dbo.Z_WBL_ASCHEDA23 WHERE COALESCE(s_dettagli,'') = 'CONTROLLATO'  
SELECT COUNT(1), 'Wbbim', 'dbo.Z_WBL_ASCHEDA32'  FROM Wbbim.dbo.Z_WBL_ASCHEDA32 WHERE COALESCE(s_dettagli,'') = 'CONTROLLATO'

第一个查询的表不包含“ s_dettagli”列

EDIT2:解决方案

EXEC sp_MSforeachdb '
IF ''?'' not like ''wb%'' 
    RETURN
USE [?]
EXEC sp_MSforeachtable 
@replacechar = ''!'', 
@command1 = ''SELECT ''''?'''' AS db_name, ''''!'''' AS table_name, COUNT(*) FROM !  '',
@whereand = '' And Object_id In (
                                Select t.Object_id 
                                From sys.objects t
                                INNER JOIN sys.columns c on c.Object_id = t.Object_id
                                Where t.name like ''''Z_WBL_ASCHEDA%''''
                                AND c.name = ''''s_dettagli'''' )'' '

3 个答案:

答案 0 :(得分:0)

可以使用object_id字段将sys.columns连接到sys.tables(object_id是表本身的表示形式)。

sys.tables在查询的数据库上下文中运行,因此看不到另一个数据库中包含的表。 sys.databases可以在实例上的任何数据库上运行,并允许您查看同一实例上的其他数据库。因此,您不需要将表连接到数据库(这也是sys.tables中没有database_id字段的原因)。

我希望能有所帮助。如有任何澄清,请通知我。

答案 1 :(得分:0)

我会建议其他方式:

  1. 使用SSMS中已注册的服务器并在每个数据库here上运行脚本
  2. 使用exec sys.sp_MSforeachdb here
  3. 使用sqlcmd和powershell切换数据库

答案 2 :(得分:0)

我相信该脚本可以为您提供帮助:

SET NOCOUNT ON;

IF OBJECT_ID (N'tempdb.dbo.#Temp') IS NOT NULL
   DROP TABLE #Temp

CREATE TABLE #Temp
(
      exist INT
    , DB VARCHAR(50)
    , tbname VARCHAR(500)
)

DECLARE @SchemaName      NVARCHAR(200) 
DECLARE @TableName       NVARCHAR(200) 
DECLARE @ColumnName      NVARCHAR(200) 
DECLARE @SearchText      NVARCHAR(200) 
DECLARE @DBNameStartWith NVARCHAR(200) 
DECLARE @SQL             NVARCHAR(MAX)


SET @DBNameStartWith = 'wb'
SET @SchemaName      = 'dbo'
SET @TableName       = 'Z_WBL_ASCHEDA'
SET @ColumnName      = 's_dettagli'
SET @SearchText      = 'CONTROLLATO'


DECLARE @DatabaseName varchar(100) 

DECLARE Crsr CURSOR FOR 
SELECT name 
FROM MASTER.sys.sysdatabases 
WHERE name LIKE ''+@DBNameStartWith+'%'

OPEN Crsr 
FETCH NEXT FROM Crsr INTO @DatabaseName 

WHILE @@FETCH_STATUS = 0 
BEGIN 
     IF ISNULL((SELECT COUNT(1) FROM SYS.TABLES T,SYS.COLUMNS C WHERE T.object_id=C.object_id AND T.name=@TableName AND C.name=@ColumnName),0)>0
     BEGIN
          SET  @SQL = '
               IF EXISTS (SELECT 1 FROM '+@DatabaseName+'.SYS.TABLES T,'+@DatabaseName+'.SYS.COLUMNS C WHERE T.object_id=C.object_id AND T.name='''+@TableName+''' AND C.name='''+@ColumnName+''')
               BEGIN          
                    SELECT COUNT(1),'''+@DatabaseName+''','''+@TableName+''' 
                    FROM '+@DatabaseName+'.'+@SchemaName+'.'+@TableName+'
                    WHERE '+@ColumnName+'=''' +@SearchText+'''
               END'

          PRINT(@SQL)
          INSERT INTO #Temp
          EXEC sp_executesql @SQL 
     END
     FETCH NEXT FROM Crsr INTO @DatabaseName 
END 
CLOSE Crsr 
DEALLOCATE Crsr 

SELECT * FROM #Temp