Script -> Query for multiple databases - sql

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?

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

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.

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

Related

Displaying multiple database tables in one table

I have multiple databases with the same table (an Eventlog with different values). The names of these databases are subject to change. I am trying to display the Eventlog tables in one consolidated table with the corresponding database name.
I tried to using cursor and dynamic SQL statement to achieve this with no luck. As well, I'm not sure if that is the best approach. Would love some help!
-- Create a new table variable to record all the database name
DECLARE #Database_Table table ([TimeStamp] nvarchar(500)
,[EventIDNo] nvarchar(100)
,[EventDesc] nvarchar(1000))
--Create variable for database name and query variable
DECLARE #DB_Name VARCHAR(100) -- database name
DECLARE #query NVARCHAR(1000) -- query variable
--Declare the cursor
DECLARE db_cursor CURSOR FOR
-- Populate the cursor with the selected database name
SELECT name
FROM sys.databases
WHERE name NOT IN ('master','model','msdb','tempdb')
--Open the cursor
OPEN db_cursor
--Moves the cursor to the first point and put that in variable name
FETCH NEXT FROM db_cursor INTO #DB_Name
-- while loop to go through all the DB selected
WHILE ##FETCH_STATUS = 0
BEGIN
SET #query = N'INSERT INTO #Database_Table
SELECT #DB_Name, *
FROM ['+ #DB_Name +'].dbo.EventLog_vw as A'
EXEC (#query)
--Fetch the next record from the cursor
FETCH NEXT FROM db_cursor INTO #DB_Name
END
--Close and deallocate cursor
CLOSE db_cursor
DEALLOCATE db_cursor
SELECT *
FROM #Database_Table
If you need a single resultset and all tables have the same layout, this should work:
DECLARE #sql nvarchar(4000) =
(SELECT STRING_AGG(CONCAT(
'SELECT ''',
QUOTENAME(name),
''',
* FROM ',
QUOTENAME(name),
'..Table ',
CHAR(10)
), ' UNION ALL ' + CHAR(10))
FROM sys.databases);
SELECT #sql; -- for checking
EXEC(#sql);
If you're on compatibility level 130 or lower, you will have to use XML PATH(TYPE, '') to aggregate. I will leave that to you.
I notice a bug in this code:
SET #query = N'INSERT INTO #Database_Table
SELECT #DB_Name, *
FROM ['+ #DB_Name +'].dbo.EventLog_vw as A'
In the SELECT clause you reference #DB_Name inside the string itself without concatenating the value as a variable. You did do this correctly in the FROM clause.
Something like:
SET #query = N'INSERT INTO #Database_Table
SELECT ''' + #DB_Name + ''', *
FROM ['+ #DB_Name +'].dbo.EventLog_vw as A'

Create an ms sql query to get data from tables belonging to different schemas

I have a table named 'tbl_user' and this table exists under many schemas for example :
schema1.tbl_user
schema2.tbl_user
and so on. I would like to union all the data in these tables and get a single result and I cannot use any programming language because the environment I need to run it doesn't allow for it. Is there a way to accomplish this on sql server 2008 using stored procedures?
Edit : There are over a thousand schemas and schemas can be added and deleted as time goes.
using cursor, create the query dynamic and execute at when completed.
You can add additional filter if you want to filter out the schema.
DECLARE #NAME VARCHAR(100)
DECLARE #SQL NVARCHAR(300)
DECLARE #TABLE_NAME NVARCHAR(300)='userMaster'
DECLARE CUR CURSOR FOR
SELECT S.name
from sys.schemas s
inner join sys.sysusers u
on u.uid = s.principal_id
order by s.name
OPEN CUR
FETCH NEXT FROM CUR INTO #NAME
WHILE ##FETCH_STATUS = 0
BEGIN
IF (EXISTS (SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA =#NAME
AND TABLE_NAME =#TABLE_NAME))
BEGIN
IF(LEN(#SQL)>0)
begin
SET #SQL =#SQL+' UNION ALL SELECT * FROM ['+#NAME+'].['+#TABLE_NAME+']'
end
ELSE
begin
SET #SQL ='SELECT * FROM ['+#NAME+'].['+#TABLE_NAME+']'
end
--PRINT #SQL
END
FETCH NEXT FROM CUR INTO #NAME
END
CLOSE CUR
DEALLOCATE CUR
PRINT #SQL
exec(#SQL)
Try this and remember Tables should have the same structure.
SELECT *
FROM schema1.tbl_user
UNION
SELECT *
FROM schema2.tbl_user
And for large number of schemas you can use the following stored procedure. This will generate the UNION query dynamically and you can add the conditions to filter out the schema and table names.
DECLARE #table_name NVARCHAR(100)
DECLARE #schema_name NVARCHAR(100)
DECLARE #gettable CURSOR
DECLARE #query NVARCHAR(100)
SET #query=''
SET #gettable = CURSOR FOR
SELECT name,
schema_name(schema_id)
FROM sys.tables
OPEN #gettable
FETCH NEXT
FROM #gettable INTO #table_name, #schema_name
WHILE ##FETCH_STATUS = 0
BEGIN
SET #query = #query + 'SELECT * FROM ' + #schema_name + '.'+ #table_name + ' UNION ';
FETCH NEXT
FROM #gettable INTO #table_name, #schema_name
END
CLOSE #gettable
DEALLOCATE #gettable
print #query;

SQL Server 2014 - sp_describe_cursor_column for dynamic queries issue

Basically I want to know columns/aliases of result set from dynamic query. I tried to use sp_describe_cursor_column but without success.
It returns me that cursor does not exits. But I can fetch values from such cursor...
The code is :
ALTER PROC TestProc
AS
DECLARE #dynamicSQL nvarchar(200)
-- Have code that will construct the dynamic SQL
SET #dynamicSQL = ' select table_name, TABLE_TYPE from INFORMATION_SCHEMA.TABLES'
-- The cursor that will be filled by the dynamic SQL
DECLARE #outputCursor CURSOR
-- Create the dynamic SQL to fill a CURSOR instead
SET #dynamicSQL = 'SET #outputCursor = CURSOR FORWARD_ONLY STATIC FOR ' +
#dynamicSQL + ' ; OPEN #outputCursor'
-- Execute dynamic sql
exec sp_executesql -- sp_executesql will essentially create a sproc
#dynamicSQL, -- The SQL statement to execute (body of sproc)
N'#outputCursor CURSOR OUTPUT', -- The parameter list for the sproc: OUTPUT CURSOR
#outputCursor OUTPUT -- The parameter to pass to the sproc: the CURSOR
declare #Report cursor
exEC sp_describe_cursor_columns
#cursor_return = #Report OUTPUT
,#cursor_source = N'local'
,#cursor_identity = N'outputCursor';
-- Code that will just output the values from the cursor
DECLARE #tableName nvarchar(200), #table_type nvarchar(200);
FETCH NEXT FROM #outputCursor INTO #tableName, #table_type
-- Loop while there're more things in the cursor
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #tableName
FETCH NEXT FROM #outputCursor INTO #tableName, #table_type
END
-- Be nice, close & deallocate cursor
CLOSE #outputCursor
DEALLOCATE #outputCursor
And this is the result:
Msg 16916, Level 16, State 4, Procedure sp_describe_cursor_columns,
Line 23 A cursor with the name 'outputCursor' does not exist.
DATABASE_UPDATE
SYSTEM_CONFIGURATION ....
I want as result to see table_name , table_type.
Don't tell me that I can just extarct it from string, becasue user may send select * from xxxx.
I found some other way how to extract description of result set for dynamic queries.
declare #Table nvarchar(200) ;
DECLARE #dynamicSQL nvarchar(200)
SET #dynamicSQL = 'select table_name, TABLE_TYPE from INFORMATION_SCHEMA.TABLES'
SELECT #Table = COALESCE(#Table + ', ', '') + Name
FROM sys.dm_exec_describe_first_result_set
(#dynamicSQL, NULL,1)
print #Table

How to run single select statement across all the databases in the same server

I need to run a simple select statement across all the databases in the same server. I have around 30-40 databases. This table has same structure in all the databases.
select * from table1 where condition
Can you please let me know how to get the records from all databases?? BTW i'm using SQL Server
Exec sp_msforeachdB 'select top 5 cola from dbo.tablea'
I would use a cursor for this, sp_msforeachdB has a bad reputation for skipping databases.
A solution with a cursor would look something like this.....
DECLARE #DB_Name SYSNAME;
DECLARE #Sql NVARCHAR(MAX);
DECLARE Cur CURSOR LOCAL FAST_FORWARD FOR
SELECT Name
FROM sys.databases
WHERE name NOT IN ('master', 'tempdb','model','msdb')
OPEN Cur
FETCH NEXT FROM Cur INTO #DB_Name
WHILE (##FETCH_STATUS = 0)
BEGIN
SET #Sql = N' select * from '+QUOTENAME(#DB_Name)
+ N'.[sechmaName].table1 where condition'
Exec sp_executesql #Sql
FETCH NEXT FROM Cur INTO #DB_Name
END
CLOSE Cur;
DEALLOCATE Cur;
If you want the results in a single result set you can leverage the system views to generate dynamic sql. This also negates the need to use any looping.
declare #SQL nvarchar(max) = ''
select #SQL = #SQL + 'select * from ' + db.name + '.table1 where condition union all '
from sys.databases db
--optionally add a where clause here to filter out certain databases
order by db.name
set #SQL = left(#SQL, len(#SQL) - 10)
select #SQL
--uncomment the dynamic sql call when satisfied the sql is correct.
--exec sp_executesql #SQL
Try this: add all of your databases as mentioned in the SQL below:
SELECT a, b, c FROM Database1.Schema1.Table1
UNION
SELECT a, b, c FROM Database2.Schema2.Table2
...

Issue with Bracketed Identifier inside SQL Server OPENQUERY

Not sure, if this should go on Stack Overflow, or the DBA section.
Having an issue with a procedure I'm writing. I have a variable, lets say:
SET #name sysname --also tried to make as varchar
This is used inside a cursor, and will basically contain SQL Server names. Several of the server names are followed by instances names. For example
DECLARE #name = 'SERVER1\INSTANCE1'
Inside the cursor, I have this query.
SELECT #name, * FROM OPENQUERY(#name,
'SELECT
i.Name,
i.database_id,
b.mirroring_state
FROM msdb.sys.databases i
INNER JOIN msdb.sys.database_mirroring b
ON i.database_id = b.database_id
WHERE b.mirroring_state IS NOT NULL')
which doesn't work because of the \ inside the #name
However, if I try this, it works perfectly.
SELECT 'SERVER1\INSTANCE1', * FROM OPENQUERY([SERVER1\INSTANCE1],
The issue I'm having is trying to use the bracketed identifier with the #name inside the OPENQUERY.
I have tried several things, including various combinations of OPENQUERY('['+#name+']',
If you just try FROM OPENQUERY([#name], SQL Server parses it literally as #name.
Any ideas on how to use the servername\instance name without having these issues?
Edit, full section of the code:
DECLARE #name sysname,
#sql nvarchar(4000)
DECLARE c1 CURSOR FOR
SELECT SUBSTRING (Servername, 2, LEN(Servername)-2)
FROM AllServers
OPEN c1
FETCH NEXT FROM c1
INTO #name
WHILE ##FETCH_STATUS = 0
BEGIN
set #sql = 'INSERT INTO MirrorResults
SELECT ''[' + #name + ']'', * FROM OPENQUERY([' + #name + '],
''
SELECT
i.Name,
i.database_id,
b.mirroring_state
from msdb.sys.databases i
INNER JOIN msdb.sys.database_mirroring b
ON i.database_id = b.database_id
WHERE b.mirroring_state IS NOT NULL
'')'
EXECUTE sys.sp_executesql #sql;
FETCH NEXT FROM c1
END
CLOSE c1
DEALLOCATE c1
OPENQUERY can only be used with a valid linked server reference. As per MSDN:
OPENQUERY ( linked_server ,'query' )
linked_server
Is an identifier representing the name of the linked server.
So you can't use servername\instance as the first parameter. You'll have to create a linked server first, and then use the linked server name as the first parameter.
An example using dynamic SQL:
DECLARE #linked_server VARCHAR(20), #sql NVARCHAR(512);
SET #linked_server = 'SERVER1\INSTANCE1';
SET #sql = 'SELECT * FROM OPENQUERY([' + #linked_server + '], ''SELECT * FROM SomeTable;'');';
EXEC sys.sp_executesql #sql;