I am trying to create a script to obtain data from a multiple databases in a server and then I want it to continue to the next server and do the same. I already created a script to work with one server and all its databases. The script is the same for all databases, tables in all the servers.
My question, is how do I have the script recognize the linked servers that I linked to my current server and continue to obtain data from those servers? Is there a hidden stored procedure or command that someone may know? I basically want to runs the below script through all my servers. When its done iterating through all the databases in one server, I want it to continue to the next server and do the same. I'm going to be saving all the data in a table.
Here is my script below, I want to have the script run through 5 servers which I currently have linked to one of my servers:
SET NOCOUNT ON DECLARE #DBNAME NVARCHAR(MAX) DECLARE #DBNAME1 NVARCHAR(MAX) DECLARE #varSQL NVARCHAR(MAX)
DROP TABLE TEMPIMGCOUNTERSERVER3 CREATE TABLE TEMPIMGCOUNTERSERVER3 (DBname NVARCHAR(MAX), Images INT, FileSize INT, DBCreation DATETIME)
DECLARE DBNAME CURSOR FAST_FORWARD FOR select name from sys.databases where [NAME] LIKE 'Z%' AND create_date between '2011-02-06' and '2011-02-12' ORDER BY [create_date]
OPEN DBNAME
FETCH NEXT FROM DBNAME INTO #DBname WHILE (##FETCH_STATUS=0) BEGIN
Set #varSQL='INSERT INTO TEMPIMGCOUNTERSERVER3 (DBNAME, IMAGES, FileSize, DBCreation)
SELECT ''['+#DBNAME+']'', SUM(PGCOUNT), sum(filesize/1024/1024), sys.databases.CREATE_DATE
FROM SYS.DATABASES, ['+#DBNAME+'].dbo.tbldoc WHERE created between ''2011-02-06'' and ''2011-02-12''
and sys.databases.name='''+#DBNAME+'''
GROUP BY sys.databases.NAME, sys.databases.CREATE_DATE'
EXEC SP_EXECUTESQL #varSQL
FETCH NEXT FROM DBNAME
INTO #DBNAME
END
CLOSE DBNAME
DEALLOCATE DBNAME
INSERT TEMPIMGCOUNTERSERVER3 (DBNAME, IMAGES, FILESIZE)
SELECT ##SERVERNAME + ' ' +'TOTAL IMAGES AND FILE SIZE', SUM(IMAGES), SUM(FILESIZE) FROM TEMPIMGCOUNTERSERVER3
SELECT DBNAME, IMAGES, FILESIZE, convert(VARCHAR(10),
DBCREATION, 101) AS DBCREATION FROM TEMPIMGCOUNTERSERVER3
GROUP BY DBNAME, IMAGES, FILESIZE, convert(VARCHAR(10), DBCREATION, 101)
HAVING SUM(IMAGES) is not null
ORDER BY DBCREATION
Should I add another cursor? Something with this script:
select srvname from master..sysservers where srvname like 'sql%'
i would just nest your cursor in another cursor that runs on the linked servers. filter the list of servers from master..sysservers on the srvproduct field where it equals "SQL Server". if there's anything is that list you don't want put it in a not in clause. then, modify your inner cursor to add the server name to the query, giving you a four part table name.
Related
I am using the stored procedure shown here for taking backups of databases from SQL Server.
It takes all backups for around 20 databases, now I want little change in the stored procedure.
It want below databases backups to pick through loop, how to modify existing stored procedure. Thanks
Emp_DB
Salary_DB
Company_DB
Attendance_DB
Code:
CREATE PROCEDURE Backupdbs
AS
BEGIN
DECLARE c1 CURSOR FOR
SELECT name FROM sys.databases WHERE database_id > 4
DECLARE #dbname varchar(100)
DECLARE #fname varchar(100)
OPEN C1
FETCH NEXT FROM c1 INTO #dbname
WHILE (##fetch_status = 0)
-- here, you're missing a
-- BEGIN
SET #fname = 'D:\Backup\'+#dbname+'.bak'
BACKUP DATABASE #dbname TO disk=#fname
FETCH NEXT FROM cl INTO #dbname
END
CLOSE c1
DEALLOCATE c1
END IF
Thanks
mg
A better scalable solution would be to have your own list of database names you want to back up, possibly with a groupId.
You can then pass a #groupId parameter and run a batch of database backups for the group or set of databases you choose - eg you can group large databases together, small together, frequently modified etc
So for example you could have the following table
create table BackupSets (
id int identity(1,1) primary key,
GroupId int,
[Name] sysname
)
and use it instead of sys.databases
select [Name] from BackupSets where groupId=#GroupId
You could also have a process as part of your backup to check for any names in sys.databases that don't exist in your list and insert them.
You could just add the databases in question to your where clause in your cursor query against sys.databases.
CREATE PROCEDURE Backupdbs
AS
BEGIN
DECLARE c1 CURSOR FOR
SELECT name FROM sys.databases WHERE database_id > 4
and name IN ('Emp_DB','Salary_DB','Company_DB','Attendance_DB')
DECLARE #dbname varchar(100)
DECLARE #fname varchar(100)
OPEN C1
FETCH NEXT FROM c1 INTO #dbname
WHILE (##fetch_status = 0)
-- here, you're missing a
-- BEGIN
SET #fname = 'D:\Backup\'+#dbname+'.bak'
BACKUP DATABASE #dbname TO disk=#fname
FETCH NEXT FROM cl INTO #dbname
END
CLOSE c1
DEALLOCATE c1
END IF
It is possible in SSMS to run the same query on some databases?
I was thinking something like an array with database names and cycling through it, maybe with SQLCMD mode.
some pseudocode:
:setvar arr ["db1", "db2", "db3"]
foreach $db in $arr
:setvar database $db
use $(database)
go
select * from table
Thanks
-- To Achive your Desire OutpUt You Have to Use Dynamic Query
-- you can Achieve this in TSQL
-- TO Know The Database ID Run Below Query
/*
SELECT * FROM Sys.databases WHERE database_id >4
*/
USE MASTER
GO
BEGIN TRAN
DECLARE #strt INT,#End INT,#Database NVARCHAR(255)
SELECT * INTO #T FROM Sys.databases WHERE database_id IN (4,5,6)-- Here you Have to Defined the Database ID
ORDER BY 1
SELECT ROW_NUMBER ()OVER (ORDER BY database_Id)Db_Id,* INTO #TT FROM #T
SET #strt=1
SELECT #End=Max(Db_ID)FROM #tt
WHILE #strt<=#END
BEGIN
DECLARE #string NVARCHAR(MAX)
SELECT #Database=NAME FROM #TT WHERE Db_ID=#strt
Set #string=' Select * from '+#Database+'..Table_Name'
SET #strt=#strt+1
PRINT #string
EXEC(#string)
END
ROLLBACK TRAN
Here's an example using two of my databases (Staging and Warehouse) to hit the sys.columns table in each. Just change out the IN filter with the names of whichever databases you want, and the ".sys.columns" with the schema/table name you need.
DECLARE #query NVARCHAR(MAX) = ''
;
SELECT #query += CONCAT('SELECT * FROM ',[name],'.sys.columns;')
FROM sys.databases
WHERE [name] IN ('Staging','Warehouse')
;
EXEC sp_executesql #query;
You can do this with a Local Server Group as well.
View --> Registered Servers
Right-click Local Server Group and create new
Add new server registrations to the group (any databases you want to query). Be sure to specify the database in the Connection Properties tab
Now you can right-click the Local Server Group folder and execute a new query, which will run on all the databases in that folder.
I am having SQL server 2008 and i am having 10 different databases in it and now i want to search one stored procedure that in which database the stored procedure is present.
Mentioned as duplicate by some ..... with out reading my question properly. My Requirement is i need to verify 'SP_Email' procedure. I which database is this procedure exists.
You can try this:
EXEC sp_msforeachdb
'if exists(select 1 from [?].sys.objects where name=''SP_Email'')
select ''?'' as FoundInDatabase from [?].sys.objects where name=''SP_Email'''
Please try this.
SELECT name DatabaseName
FROM sys.databases
WHERE OBJECT_ID(QUOTENAME(name) + '.dbo.ProcedureNameHere', 'P') IS NOT NULL;
This will return the database(s) name in which this particular object exist.
Replace ProcedureNameHere with your procedure name. In your case it would be SP_Email Keep rest of the things as it is.
you need to query sys.databases of master database to get list of databases and for each database name you get you need to query the db_name.sys.procedures to check if it exists.
try below query and give a feedback:
use master
go
declare #FullQuery varchar(max)
declare #DBName varchar(50)
set #FullQuery=''
declare cr cursor for select name from sys.databases where database_id > 4
open cr
fetch next from cr into #DBName
while(##fetch_status=0)
begin
set #FullQuery=#FullQuery+
' select name COLLATE SQL_Latin1_General_CP1_CI_AS from '+#DBName+'.sys.procedures where name like ''%proc_name%'' union'
fetch next from cr into #DBName
end
close cr
deallocate cr
set #FullQuery=substring(#FullQuery,1,len(#FullQuery)-5)
exec (#FullQuery)
SELECT OBJECT_ID('DataBase1.SchemaName.StoredProcedureName') /
OBJECT_ID('DataBase2.SchemaName.StoredProcedureName') /
OBJECT_ID('DataBase3.SchemaName.StoredProcedureName') /
...
It will return NULL if there is no such procedure. This will work if all databases are on same instance.
I have one instance of SSMS open and I am connected to one remote server as well as localhost. How can I get the names of all the servers that SSMS is currently connected to? The emblem of the remote server looks like
and the local looks like
Also, I would like to know if there's any problems with connecting to multiple servers from one instance of SSMS, and how to switch between servers through a script without clicking on a table name and doing something like select top 1000 rows
Okay there are multiple issues at work here as this is not always a simple answer. Depending on your environment and rights you may have one or more many permission groups that have access to one or many environments which have one or many servers that thus have access to one or many databases. However if you do have permission and you have linked servers set up with data access you can do something like this to get a listing of things you have access to. You could run this similarly on different environments making it into a procedure that you could call with ADO.NET or similar.
--declare variable for dynamic SQL
DECLARE
#SQL NVARCHAR(512)
, #x int
-- Create temp table to catch linked servers
Declare #Servers TABLE
(
Id int identity
, ServerName VARCHAR(128)
)
-- insert linked servers
insert into #Servers
select name
FROM sys.servers
-- remove temp table if it exists as it should not be prepopulated.
IF object_ID('tempdb..#Databases') IS NOT NULL
DROP TABLE tempdb..#Databases
;
-- Create temp table to catch built in sql stored procedure
CREATE TABLE #Databases --DECLARE #Procs table
(
ServerName varchar(64)
, DatabaseName VARCHAR(128)
)
SET #X = 1
-- Loops through the linked servers with matching criteria to examine how MANY there are. Do a while loop while they exist.
WHILE #X <= (SELECT count(*) FROM #Servers)
BEGIN
declare #DB varchar(128);
Select #DB = ServerName from #Servers where Id = #X -- get DB name from current cursor increment
-- Set up dynamic SQL but do not include master and other meta databases as no one cares about them.
SET #SQL = 'insert into #Databases select ''' + #Db + ''', name from ' + #DB + '.master.sys.databases
where name not in (''master'',''tempdb'',''model'',''msdb'')'
-- Execute the dynamic sql to insert into collection object
exec sp_executesql #SQL
-- increment for next iteration on next server
SET #X = #X + 1
END
;
SELECT *
FROM #Databases
I'm not entirely sure what you are asking. If you are asking if you can connect to multiple instances of SQL Server in a single query window the answer is yes. I went into detail on how and some of the implications here: Multiple instances, single query window
If on the other hand you are asking how to tell what instance you are connected to you can use ##SERVERNAME.
SELECT ##SERVERNAME
It will return the name of the instance you are connected to.
Typically you would connect to one instance per query window and flip between the windows to affect the specific instance you are interested in.
If you want to write a command to send you to a specific instance you can set your query window to SQLCMD mode (Query menu -> SQLCMD Mode) and use the :CONNECT command.
:CONNECT InstaneName
SELECT ##SERVERNAME
I have Scenario and i want to convert it to a query.
My Scenario:
I have lot of dbs with same structure "Clientxxxx" i want to make loop for all these dbs to get data from one table exists in all these dbs called "EventLog" these event log recorded in this table for clients exists in another db called "Portal"
I want to get every client in "portal" db with his eventlogs from "EventLog" Table in the other dbs "Clientxxxx"
db:Client1 db:Client2 db:Client3
table:"EventLog" table:"EventLog" table:"EventLog"
each client has his db and his data in Portal db
db:portal
table:Clients
query:
Client1 data
his event logs
client2 data
his event logs
and so on
........
........
........
........
I need some help please.
thanks
I would do the following:
Create a view in your Portal db that has this in it:
vw_AggregateClients:
SELECT 'Client1' as clientName, * from Client1.dbo.EventLog
UNION
SELECT 'Client2', * from Client2.dbo.EventLog
UNION
SELECT 'Client3', * from Client3.dbo.EventLog
And then query it like this:
SELECT * from vw_AggregateClients as ac
INNER JOIN Clients as c
ON ac.clientName = c.ClientName
If you the number of client dbs will be large or you don't know how many there will be then you will probably have to use dynamic sql. If you go that route give the article i linked to a good read.
Typically, I use the dynamic SQL approach, with a cursor to loop through all the databases, insert into a consolidated table variable, and then select out of the variable:
declare #return (dbname varchar(100),
<<dataset spec for EventLog>>)
declare #db varchar(100)
declare #sql varchar(max)
declare recscan cursor for
select name from sys.databases
where database_id not in (1,2,3,4) --excludes system databases
open recscan
fetch next from recscan into #db
while ##fetch_status = 0
begin
set #sql = 'select '''+#db+''',* from '+#db+'..eventlog'
insert into #return
exec(#sql)
fetch next from recscan into #db
end
close recscan
deallocate recscan
select * from #return
Note that I create an extra field and put the database name as an explicit string value in the dynamic query so that I can break out where the data came from. I also use 3 part naming for the table object, but you could insert a dynamically constructed USE statement into your SQL variable.