Reputation: 12181
I'm trying to get a list of all of the databases that contain two separate tables with specific names (in this case, 'MasterSchedules', 'Users').
The first thing I tried was to do this with a cursor:
DECLARE dbCursor CURSOR FOR
SELECT name
FROM sys.databases
WHERE name NOT IN ('master', 'tempdb', 'model', 'msdb')
DECLARE @dbname varchar(max)
OPEN dbCursor
FETCH NEXT FROM dbCursor INTO @dbname
WHILE @@FETCH_STATUS = 0
BEGIN
DECLARE @stmt nvarchar(max)
SET @stmt = 'USE ' + @dbname + ';';
EXECUTE sp_executesql @stmt
SELECT TABLE_CATALOG, TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'BASE TABLE'
AND TABLE_NAME in ('MasterSchedules', 'Users')
ORDER BY TABLE_SCHEMA + '.' + TABLE_NAME
FETCH NEXT FROM dbCursor INTO @dbname
END
CLOSE dbCursor;
DEALLOCATE dbCursor;
However, all this does is get the same data over and over again.
---------------------------------------
| | TABLE_CATALOG | TABLE_NAME |
|--------------------------------------
| 1 | QA_CTLG | MasterSchedules |
---------------------------------------
---------------------------------------
| | TABLE_CATALOG | TABLE_NAME |
|--------------------------------------
| 1 | QA_CTLG | MasterSchedules |
---------------------------------------
---------------------------------------
| | TABLE_CATALOG | TABLE_NAME |
|--------------------------------------
| 1 | QA_CTLG | MasterSchedules |
---------------------------------------
...
I've narrowed it down to the fact that EXECUTE sp_executesql...
apparently has no effect. Does anyone know why that is or how to fix it?
For the record, yes, I do realize that this will show databases that have at least one of those tables, not the ones that have both.
I thought of trying using some kind of dynamic SQL, but nothing I came up with was any better than just using the cursor.
I also tried to write a query to do this (like the one below):
select
name
from
sys.databases
where
name not in ('master', 'tempdb', 'model', 'msdb')
and exists (select top 1 TABLE_NAME
from [name].INFORMATION_SCHEMA.TABLES
where TABLE_NAME = 'Users')
and exists (select top 1 TABLE_NAME
from [name].INFORMATION_SCHEMA.TABLES
where TABLE_NAME = 'MasterSchedules')
Unsurprisingly, this returns an error
Invalid object name '@name.INFORMATION_SCHEMA.TABLES'
Is there something I can put in place of name
to get this to work (or a way to repair my cursor)?
Upvotes: 1
Views: 167
Reputation: 3970
Try this lightly modified version of your code... Use dynamic SQL instead of scripting out "USE" statements, and fixed up your ORDER BY to use a comma instead of a dot... this seemed to work for me, depending on exactly what you want your output to be:
DECLARE @dbname sysname;
DECLARE dbCursor CURSOR FOR
SELECT name FROM sys.databases WHERE name not in ('master', 'tempdb', 'model', 'msdb');
OPEN dbCursor;
FETCH NEXT FROM dbCursor INTO @dbname;
WHILE @@FETCH_STATUS = 0
BEGIN
DECLARE @stmt nvarchar(max)
SELECT @stmt = N'SELECT TABLE_CATALOG, TABLE_NAME FROM ' + @dbname + N'.INFORMATION_SCHEMA.TABLES ' +
N'WHERE TABLE_TYPE = ''BASE TABLE'' AND TABLE_NAME in (''MasterSchedules'', ''Users'') ' +
N'ORDER BY TABLE_SCHEMA, TABLE_NAME';
EXEC(@stmt);
FETCH NEXT FROM dbCursor INTO @dbname
END
CLOSE dbCursor;
DEALLOCATE dbCursor;
Upvotes: 1
Reputation: 768
you are going to have to use dynamic sql here and loop through each database without having the need to know the name of each database. assumes you have the proper access to query all the databases on your server:
use master
declare @cursor table (dbname varchar(150))
declare @dbname varchar(150)=''
declare @sql nvarchar(500)=''
if object_id('tempdb..#results') is not null drop table #results
create table #results (dbname varchar(150), tblname varchar(150))
set nocount on
insert into @cursor
select name
from sys.sysdatabases db
where name not in ('master', 'tempdb', 'model', 'msdb')
select @dbname = min(dbname) from @cursor -- first row
While @dbname Is Not Null
Begin
set @sql = ('
insert into #results (dbname, tblname)
select ' + char(39) + @dbname + char(39) + ' , name from ' + @dbname + '.sys.tables where name in (''MasterSchedules'', ''Users'')
')
exec sp_executesql @sql
select @dbname = min(dbname) from @cursor where dbname > @dbname -- next row
End
select * from #results
Upvotes: 0
Reputation: 33571
Here is a super simple way to do this using dynamic sql. This will return all the databases with those two table names. It doesn't need a loop, cursor or temp table.
declare @SQL nvarchar(max) = N''
select @SQL = @SQL + N'select DatabaseName = ''' + quotename(name) + '''
from ' + quotename(name) + '.sys.tables
where name in (''MasterSchedules'', ''Users'')
having COUNT(*) = 2 UNION ALL '
from sys.databases where name not in ('master', 'tempdb', 'model', 'msdb')
set @SQL = left(@SQL, len(@SQL) - 10)
exec sp_executesql @SQL
--EDIT-- You probably want to add the schema into this query. It will work as posted for tables in the default schema but if tables with either name exists in more than 1 schema this will not work as advertised.
Upvotes: 4