need_an_answer
need_an_answer

Reputation: 23

Script -> Query for multiple databases

I have the following problem: I want to execute a query on multiple databases on my SQL Server. Every customer has a separate database. Those all have exactly the same table and their names are similar. So there is a database kde_01_Miller, then a kde_02_Mueller and so on ...

I want to execute a query in every one of those databases.

Here's what I have tried:

DECLARE @name VARCHAR(100) -- database name
DECLARE @dothis nvarchar(200)

DECLARE db_cursor CURSOR FOR
SELECT name
FROM master.dbo.sysdatabases
WHERE name like 'kde_0%'
order by name

OPEN db_cursor  
FETCH NEXT FROM db_cursor INTO @name  

WHILE @@FETCH_STATUS = 0
BEGIN  
       set @dothis = 'use [' + @name + ']'
       exec sp_executesql @dothis

       /* Start query */
       select description from dbo.basicdata
       /* End query */

       FETCH NEXT FROM db_cursor INTO @name  
END  

CLOSE db_cursor  
DEALLOCATE db_cursor

The problem is that the query does not work properly. The use statement seems not to be working. I get a result for every database I have, but the result is always the same one, dependent on the database I'm currently doing a query for.

I've also tried the following and it worked: Instead of my while-loop I did this:

WHILE @@FETCH_STATUS = 0
BEGIN  
       set @dothis= 'select description from ' + QUOTENAME(@name) + '.dbo.basicdata'
       exec sp_executesql @dothis

       FETCH NEXT FROM db_cursor INTO @name  
END 

But I don't like this way, because you need the quotename(@name) for every table.

How do I make the first example work?

Upvotes: 2

Views: 1656

Answers (3)

rob
rob

Reputation: 1

though already answered in finding a solution for the same issue I wrote this query. it provides the data and it shows the source of the database which provides the answer. note the order by is only to show double values but slows down the result

declare @results table (
    name varchar(50),
       clientnr numeric(20) ,
       dbname_source varchar(20)

);

insert @results
exec sp_msforeachdb N'
use [?]

if left(''?'',3) = ''ons''        -- only execute the query against databases  that match the naming pattern in this example starting with ons  (use your own database names make sure to exclude systemdatabases


    --and ''?'' <> ''MIS_MTA''
begin

--select script  that to be executed over multiple databases

    select distinct
       c.name,
          c.identificationNo as numeric,
          d.table_catalog



    from [?].dbo.clients as c ,  [?].INFORMATION_SCHEMA.COLUMNS as d               
       where 1=1
       and isnumeric(c.identificationNo) = 1

end;
';

select * from @results
where
isnumeric(clientnr) = 1

order by 2
;

example result:

name    clientnr    dbname_source
TestclientA 9000    OnsDB
TestclientA 9000    OnsDB_Fixed
Storcken    9999    OnsDB_Fixed
Storcken    9999    OnsDB

Upvotes: 0

TJP
TJP

Reputation: 24

While this question has already been answered, I thought I'd provide a second answer I believe is better. Instead of using a Cursor you can generate dynamic SQL to query multiple databases.

DECLARE @sql NVARCHAR(Max);

SELECT @sql = COALESCE(@sql, '') + 'SELECT * FROM '  + [name] + '.sys.tables' + CHAR(13)
FROM sys.databases

PRINT @sql
EXEC sp_executesql @sql

The above SQL will Generate the following SQL.

SELECT * FROM master.sys.tables
SELECT * FROM tempdb.sys.tables
SELECT * FROM model.sys.tables
SELECT * FROM msdb.sys.tables
SELECT * FROM StackOverflow.sys.tables
SELECT * FROM AdventureWorks2012.sys.tables
SELECT * FROM AdventureWorksDW2012.sys.tables

As you see I was able to run a query against multiple databases. I could even UNION the data together if I'd like.

Upvotes: 0

NeedAnswers
NeedAnswers

Reputation: 1435

That's not possible, since sp_executesql is executed as its own self-contained batch, that mean you did actually "use" other databases, but only in those batchs i mentioned earlier

I'll try to be more clear, this code of you is a batch, since there's no "GO" command inside (read my sql comments) :

DECLARE @name VARCHAR(100) -- database name
DECLARE @dothis nvarchar(200)

DECLARE db_cursor CURSOR FOR
SELECT name
FROM master.dbo.sysdatabases
WHERE name like 'kde_0%'
order by name

OPEN db_cursor  
FETCH NEXT FROM db_cursor INTO @name  

WHILE @@FETCH_STATUS = 0
BEGIN  
       set @dothis = 'use [' + @name + ']'
       -- this will create another batch and execute the @dothis
       -- it'll have nothing todo with your current executing batch, 
       -- which is calling the sp_executesql
       exec sp_executesql @dothis 

       /* Start query */
       select description from dbo.basicdata
       /* End query */

       FETCH NEXT FROM db_cursor INTO @name  
END  

CLOSE db_cursor  
DEALLOCATE db_cursor

So, there's only one way left, write whatever you want to do with the database inside the @dothis :

declare @dothis nvarchar(max)
set @dothis = '
use [' + @name + ']
-- query start
Select description from dbo.basicdata
-- query end 
'
exec sp_executesql @dothis 

Upvotes: 1

Related Questions