How do I get list of all table names in all databases on all Sql Servers using TSQL? - sql

What is the best way to get the names of all of the tables in all Databases on all Sql Servers? There are many databases in many Servers. So I wanna list Server names and database names with table names.
For example
Id ServerName databaseName Tablename
1 server1 Product Vegetables
2 server2 Product Milks
3 server1 Customer People
Probably I will use following query with openquery() but I did'nt do it.
SET NOCOUNT ON
DECLARE #AllTables table (CompleteTableName nvarchar(4000))
INSERT INTO #AllTables (CompleteTableName)
EXEC sp_msforeachdb 'select ##SERVERNAME+''.''+''?''+''.''+s.name+''.''+t.name from [?].sys.tables t inner join sys.schemas s on t.schema_id=s.schema_id'
SET NOCOUNT OFF
SELECT * FROM #AllTables ORDER BY 1

delete from [DatabaseName].[dbo].[hak_tables]
declare #counter int;
declare #openquery nvarchar(2000), #tsql nvarchar(2000), #servername nvarchar(2000)
create table #temp(
id int,
servername nvarchar(50)
)
insert into #temp(id,servername)
values (1,'ServerName'),
(2,'[ServerName -2]'),
(3,'[ServerName -3]')
create table #tablehavuzu(
ID int identity (1,1),
sunucuAdi nvarchar(1000),
databaseName nvarchar(1000),
name nvarchar(1000),
type nvarchar(1000),
create_date datetime,
modify_date datetime
)
declare #cmd3 nvarchar(max)
set #counter = 1
while #counter <= (select count(1) from #temp)
begin
create table #databasename(
name nvarchar(1000),
counter int
)
set #servername = (select servername from #temp where id=#counter)
declare #sorg nvarchar(max)
set #sorg='insert into #databasename
select name,ROW_NUMBER() over(order by name asc) from '+#servername+'.master.sys.databases '
exec (#sorg)
declare #counter1 int;
set #counter1 = 1
declare #databaseCount int
set #databaseCount = (select count(1) from #databasename)
while #counter1 <= #databaseCount
begin
declare #databaseName nvarchar(1000),#sql nvarchar(1000)
begin try
set #databaseName = (select name from #databasename where counter=#counter1)
set #sql ='select ##SERVERNAME sunucuAdi, '''''+#databaseName+''''' as databaseName, name, type, create_date, modify_date from '+#servername+'.'+#databaseName+'.sys.tables'
set #cmd3= 'select * from openquery('+#servername+','''+#sql+''')'
insert into #tablehavuzu
exec (#cmd3)
end try
begin catch
end catch
set #counter1 = #counter1 +1;
end
set #counter = #counter + 1;
drop table #databasename
end
insert into [M3].[dbo].[hak_tables]
select * from #tablehavuzu
drop table #temp
drop table #tablehavuzu

The below query will list all the table of all the databases in an instance - So you can execute it in all the instances to get those -
declare #sql nvarchar(max);
select #sql =
(select ' UNION ALL
SELECT ' + + quotename(name,'''') + ' as database_name,
s.name COLLATE DATABASE_DEFAULT
AS schema_name,
t.name COLLATE DATABASE_DEFAULT as table_name
FROM '+ quotename(name) + '.sys.tables t
JOIN '+ quotename(name) + '.sys.schemas s
on s.schema_id = t.schema_id'
from sys.databases
where state=0
order by [name] for xml path(''), type).value('.', 'nvarchar(max)');
set #sql = stuff(#sql, 1, 12, '') + ' order by database_name,
schema_name,
table_name';
execute (#sql);

Related

Select a value from table for each database [duplicate]

I have (for testing purposes) many dbs with the same schema (=same tables and columns basically) on a sql server 2008 r2 instance.
i would like a query like
SELECT COUNT(*) FROM CUSTOMERS
on all DBs on the instance. I would like to have as result 2 columns:
1 - the DB Name
2 - the value of COUNT(*)
Example:
DBName // COUNT (*)
TestDB1 // 4
MyDB // 5
etc...
Note: i assume that CUSTOMERS table exists in all dbs (except master).
Try this one -
SET NOCOUNT ON;
IF OBJECT_ID (N'tempdb.dbo.#temp') IS NOT NULL
DROP TABLE #temp
CREATE TABLE #temp
(
[COUNT] INT
, DB VARCHAR(50)
)
DECLARE #TableName NVARCHAR(50)
SELECT #TableName = '[dbo].[CUSTOMERS]'
DECLARE #SQL NVARCHAR(MAX)
SELECT #SQL = STUFF((
SELECT CHAR(13) + 'SELECT ' + QUOTENAME(name, '''') + ', COUNT(1) FROM ' + QUOTENAME(name) + '.' + QUOTENAME(#TableName)
FROM sys.databases
WHERE OBJECT_ID(QUOTENAME(name) + '.' + QUOTENAME(#TableName)) IS NOT NULL
FOR XML PATH(''), TYPE).value('text()[1]', 'NVARCHAR(MAX)'), 1, 1, '')
INSERT INTO #temp (DB, [COUNT])
EXEC sys.sp_executesql #SQL
SELECT *
FROM #temp t
Output (for example, in AdventureWorks) -
COUNT DB
----------- --------------------------------------------------
19972 AdventureWorks2008R2
19975 AdventureWorks2012
19472 AdventureWorks2008R2_Live
Straight forward query
EXECUTE sp_MSForEachDB
'USE ?; SELECT DB_NAME()AS DBName,
COUNT(1)AS [Count] FROM CUSTOMERS'
This query will show you what you want to see, but will also throw errors for each DB without a table called "CUSTOMERS". You will need to work out a logic to handle that.
Raj
How about something like this:
DECLARE c_db_names CURSOR FOR
SELECT name
FROM sys.databases
WHERE name NOT IN('master', 'tempdb') --might need to exclude more dbs
OPEN c_db_names
FETCH c_db_names INTO #db_name
WHILE ##Fetch_Status = 0
BEGIN
EXEC('
INSERT INTO #report
SELECT
''' + #db_name + '''
,COUNT(*)
FROM ' + #db_name + '..linkfile
')
FETCH c_db_names INTO #db_name
END
CLOSE c_db_names
DEALLOCATE c_db_names
SELECT * FROM #report
declare #userdb_list table (name varchar(4000) not null);
-- fill the db list with custom subset
insert into #userdb_list
select name from sys.databases --can add where condition to filter db names
declare
#curr_userdb varchar(300),
#db_placeholder varchar(300),
#final_db_exec_query varchar(max),
#query varchar(max);
set #query = '' -- <add ur query here>
set #db_placeholder = 'use {db}';
set #curr_userdb = (select min(name) from #userdb_list);
while #curr_userdb is not null
begin
set #final_db_exec_query = replace(#db_placeholder, '{db}', #curr_userdb + ' ' + #query);
exec (#final_db_exec_query);
--print #final_db_exec_query
set #curr_userdb = (select min(name) from #userdb_list where name > #curr_userdb);
end
GO
Solution without cursor - clean and simple
Because I know that a question was just referred to here that asked a slightly different question... if you only want to execute on certain databases, those databases could be stored in some table. Here I stored in a temporary table.
CREATE TABLE #Databases (
DbName varchar(255))
INSERT INTO #Databases (DbName)
Values ('GIS_NewJersey'), ('GIS_Pennsylvania')
DECLARE #command varchar(1000)
SELECT #command = 'Use [' + DbName + '];
Update sde.SAP_Load
SET FullAddress = CONCAT_WS('','', HouseNumber, Street, City, Postal, RegionName)
Update sde.PREMISE
SET FullAddress = CONCAT_WS('', '', HouseNumber, Street, City, Postal, RegionName)
Update sde.PREMISE_GEOCODE
SET FullAddress = CONCAT_WS('', '', HouseNumber, Street, City, Postal, RegionName)'
FROM #Databases
EXEC #command

How to insert varchar variable in dynamic sql query

I have a query with a select statement in a loop and at the same time I want to insert a varchar variable and selected value into a temporary table, but I get an error like:
Msg 207, Level 16, State 1, Line 2
Invalid column name 'SP419001_SID'
This SP419001_SID is the value contained in the varchar variable #dbName.
This is my query:
CREATE TABLE #tempCounter
(
dbName1 varchar(max),
counterNumber1 int
)
DECLARE
#counter INT = 1,
#max INT = 0,
#dbName VARCHAR(100),
#count INT = 0,
#SQLTEXT VARCHAR(MAX),
#counterNumber VARCHAR(10)
SELECT #max = COUNT(id) FROM #tempPnamePadd
WHILE #counter <= #max
BEGIN
SET #dbName='';
-- Do whatever you want with each row in your table variable filtering by the Id column
SELECT #dbName = name
FROM #tempPnamePadd
WHERE Id = #counter
PRINT #dbName
SET #SQLTEXT =
--SELECT distinct PN.NAME_FORMAT_CODE, NAME_BUSINESS, INDIVIDUAL_FIRST, A.ADDRESS_ID, A.ADDR_LINE_1, A.ADDR_LINE_2, A.ADDR_LINE_3, A.CITY, A.STATE
'DECLARE #dbn VARCHAR(200)
SET #dbn ='+ #dbName +';
INSERT INTO #tempCounter
(dbname1, counternumber1)
SELECT #dbn ,
(SELECT count(*)
FROM '+ #dbName +'.dbo.PRELA PR
INNER JOIN '+ #dbName +'.dbo.PNAME PN
ON PR.NAME_ID = PN.NAME_ID
INNER JOIN '+ #dbName +'.dbo.PNALK NK
ON PN.NAME_ID = NK.NAME_ID
INNER JOIN '+ #dbName +'.dbo.PADDR A
ON NK.ADDRESS_ID = A.ADDRESS_ID
WHERE (NAME_FORMAT_CODE=''B'' and NAME_BUSINESS like ''%BN'') OR
(NAME_FORMAT_CODE <> ''B'' and INDIVIDUAL_FIRST = ''John'') OR
(ADDR_LINE_1=''WELLS STREET'' AND CITY=''HOLLYWOOD'' AND STATE=''IA'')
)
'
--PRINT #SQLTEXT
EXEC (#SQLTEXT)
SET #counter = #counter + 1
END
This is very likely not the most efficient way to do this; most likely you should be using STRING_AGG or FOR XML PATH and STUFF to do this.
Anyway, you need to parametrise your variable, and quote your dynamic objects. This results in the below:
CREATE TABLE #tempCounter (dbName1 sysname,
counterNumber1 int);
DECLARE #counter int = 1,
#max int = 0,
#dbName sysname,
#count int = 0,
#SQLTEXT nvarchar(MAX),
#counterNumber varchar(10);
SELECT #max = COUNT(id)
FROM #tempPnamePadd;
WHILE #counter <= #max
BEGIN
SET #dbName = '';
-- Do whatever you want with each row in your table variable filtering by the Id column
SELECT #dbName = name
FROM #tempPnamePadd
WHERE Id = #counter;
PRINT #dbName;
SET #SQLTEXT = N'INSERT INTO #tempCounter
(dbname1, counternumber1)
SELECT #dbn ,
(SELECT count(*)
FROM ' + QUOTENAME(#dbName) + N'.dbo.PRELA PR
INNER JOIN ' + QUOTENAME(#dbName) + N'.dbo.PNAME PN
ON PR.NAME_ID = PN.NAME_ID
INNER JOIN ' + QUOTENAME(#dbName) + N'.dbo.PNALK NK
ON PN.NAME_ID = NK.NAME_ID
INNER JOIN ' + QUOTENAME(#dbName) + N'.dbo.PADDR A
ON NK.ADDRESS_ID = A.ADDRESS_ID
WHERE (NAME_FORMAT_CODE=''B'' and NAME_BUSINESS like ''%BN'') OR
(NAME_FORMAT_CODE <> ''B'' and INDIVIDUAL_FIRST = ''John'') OR
(ADDR_LINE_1=''WELLS STREET'' AND CITY=''HOLLYWOOD'' AND STATE=''IA'')
)
';
--PRINT #SQLTEXT
EXEC sp_executesql #SQLTEXT, N'#dbn sysname', #dbn = #dbName;
SET #counter = #counter + 1;
END;
Note I have also changed some of your data types.
You did not quote around #dbName in your dynamic query.
So instead of
SET #dbn ='SP419001_SID';
you get
SET #dbn =SP419001_SID;
Do
'DECLARE #dbn VARCHAR(200)
SET #dbn ='''+ #dbName +''';
INSERT INTO #tempCounter
...'

loop script is not working

I'm just trying to create tables in user databases apart from system databases.
here is the script
Declare #V table (ID INT,name varchar(100))
DECLARE
#DatabaseName VARCHAR(50),
#SQL NVARCHAR(MAX)
INSERT INTO #V(Id,name)
select ROW_NUMBER()OVER(ORDER BY NAME),
name
from sys.databases
where database_id >= 5
DECLARE #LoopCounter INT , #MaxId INT,
#Name NVARCHAR(100)
SELECT #LoopCounter = min(id) ,
#MaxId = max(Id)
FROM #V
WHILE(#LoopCounter IS NOT NULL
AND #LoopCounter <= #MaxId)
BEGIN
SELECT #Name = Name
FROM #V WHERE Id = #LoopCounter
set #Name = #DatabaseName
SET #SQL = N'USE ' + QUOTENAME(#DatabaseName)
+'CREATE TABLE T(ID INT)';
SET #LoopCounter = #LoopCounter + 1
-- PRINT(#DatabaseName);
--PRINT(#SQL);
EXECUTE(#SQL)
END
why is it not creating table dynamically?
how to get only user databases in below script
instead of giving manually
DECLARE #Sql AS VARCHAR(4000)
SET #Sql = 'IF ''?'' NOT IN (''master'',''tempdb'',''model'',''msdb'',''ReportServer'',''ReportServerTempDB'')
EXECUTE (''
USE [?] CREATE TABLE T (ID INT)'')'
EXEC sp_MSforeachdb
#command1 = #Sql
The problems with your script are here:
WHILE(#LoopCounter IS NOT NULL
AND #LoopCounter <= #MaxId)
BEGIN
SELECT #Name = Name
FROM #V WHERE Id = #LoopCounter
--set #Name = #DatabaseName -- this will assign value from #DatabaseName into #Name, but you should do the vice versa:
set #DatabaseName = #Name
SET #SQL = N'USE ' + QUOTENAME(#DatabaseName)
+' CREATE TABLE T(ID INT)'; -- I would also add space here, it might work with [XXX]create table
SET #LoopCounter = #LoopCounter + 1
-- PRINT(#DatabaseName);
--PRINT(#SQL);
EXECUTE(#SQL)
END
Because #DatabaseName was null, the result of your #SQL was null, and executing null is most likely just ok.
Your draft of the version with the sp_MSforeachdb procedure was also fine; you asked for a version without writing the names of the system databases. Unfortunately this has still not been implemented - there is no reliable flag or property on a database that would clearly identify it as a user database. Microsoft themselves apply the (inverted) criteria I am using below, when they list the contents of the "System Databases" folder in SSMS... see also here: https://stackoverflow.com/a/28590305/1132334
SELECT
[name]
FROM sys.databases
WHERE CAST(CASE WHEN [name] IN ('master','model','msdb','tempdb') THEN 1 ELSE is_distributor END AS bit)=0
Here is a tested script:
DECLARE #sql varchar(max)
SET #sql='IF ''?'' IN (SELECT [name] FROM sys.databases WHERE CAST(CASE WHEN [name] IN (''master'',''model'',''msdb'',''tempdb'') THEN 1 ELSE is_distributor END AS bit)=0)
EXECUTE (''USE [?] CREATE TABLE T (ID int)'')'
EXEC sp_MSforeachdb #command1 = #Sql

How to get Row count by dynamically passing the table name

create procedure qa_cc
#tablename varchar(500)
AS
BEGIN
-- Create two integer values
DECLARE #tableOneCount varchar(50), #tableTwoCount varchar(50)
-- Get the number of rows from the first table
SELECT #tableOneCount = 'SELECT COUNT(*) FROM' + #tablename;
exec (#tableOneCount);
select #tableOneCount ;
END
exec qa_cc #tablename=table1
You could use sp_executesql, like:
declare #sql nvarchar(max);
set #sql = N'select #cnt = count(*) from ' + #tablename;
declare #cnt int;
exec sp_executesql #sql, N'#cnt int output', #cnt = #cnt output;
select #cnt;
SELECT SUM(pa.rows) RowCnt
FROM sys.tables ta
INNER JOIN sys.partitions pa
ON pa.OBJECT_ID = ta.OBJECT_ID
WHERE ta.is_ms_shipped = 0 AND pa.index_id IN (1,0)
and ta.name=#tablename
GROUP BY ta.name
See http://blog.sqlauthority.com/2010/09/08/sql-server-find-row-count-in-table-find-largest-table-in-database-part-2/
Looks like you were missing a space after the FROM keyword which was reading FROMTABLE rather than FROM Table
DECLARE #TABLE NVARCHAR(100)
SET #TABLE = 'CLIENT' -- YOUR TABLE NAME
EXEC('SELECT COUNT(*) FROM ' + #TABLE)
CREATE PROCEDURE [dbo].[TableRowCount]
#tableName text
AS
EXEC ('SELECT COUNT(*) FROM ' + #tableName)
RETURN ##ROWCOUNT
I hope that helps. Thank you.

While Loop to Iterate through Databases

I was wondering if someone could help me with creating a while loop to iterate through several databases to obtain data from one table from two columns. this is was I have done so far. nothing works because i do not know how to make the select statement work through each database with regards to the table that I am querying from each database (dbo.tbldoc)
DECLARE #Loop int
DECLARE #DBName varchar(300)
DECLARE #SQL varchar(max)
DECLARE #tableName VARCHAR(255)
SET #Loop = 1
SET #DBName = ''
WHILE #Loop = 1
BEGIN
SELECT [name] FROM sys.databases
WHERE [name] like 'z%' and create_date between '2010-10-17' and '2011-01-15'
ORDER BY [name]
SET #Loop = ##ROWCOUNT
IF #Loop = 0
BREAK
SET #SQL = ('USE ['+ #DBNAME +']')
IF EXISTS(SELECT [name] FROM sys.tables WHERE name != 'dbo.tbldoc' )
BEGIN
SELECT SUM(PGCOUNT), CREATED FROM **dbo.tbldoc**
END
ELSE
--BEGIN
PRINT 'ErrorLog'
END
I would consider sp_MSForEachDB which is a lot easier...
Edit:
EXEC sp_MSForEachDB 'USE [?]; IF DB_NAME() LIKE ''Z%%''
BEGIN
END
'
CREATE TABLE #T
(dbname sysname NOT NULL PRIMARY KEY,
SumPGCOUNT INT,
CREATED DATETIME)
DECLARE #Script NVARCHAR(MAX) = ''
SELECT #Script = #Script + '
USE ' + QUOTENAME(name) + '
IF EXISTS(SELECT * FROM sys.tables WHERE OBJECT_ID=OBJECT_ID(''dbo.tbldoc''))
INSERT INTO #T
SELECT db_name() AS dbname, SUM(PGCOUNT) AS SumPGCOUNT, CREATED
FROM dbo.tbldoc
GROUP BY CREATED;
'
FROM sys.databases
WHERE state=0 AND user_access=0 and has_dbaccess(name) = 1
AND [name] like 'z%' and create_date between '2010-10-17' and '2011-01-15'
ORDER BY [name]
IF (##ROWCOUNT > 0)
BEGIN
--PRINT #Script
EXEC (#Script)
SELECT * FROM #T
END
DROP TABLE #T
My code to search for data from more than one database would be:
use [master]
go
if object_id('tempdb.dbo.#database') is not null
drop TABLE #database
go
create TABLE #database(id INT identity primary key, name sysname)
go
set nocount on
insert into #database(name)
select name
from sys.databases
where name like '%tgsdb%' --CHANGE HERE THE FILTERING RULE FOR YOUR DATABASES!
and source_database_id is null
order by name
Select *
from #database
declare #id INT, #cnt INT, #sql NVARCHAR(max), #currentDb sysname;
select #id = 1, #cnt = max(id)
from #database
while #id <= #cnt
BEGIN
select #currentDb = name
from #database
where id = #id
set #sql = 'select Column1, Column2 from ' + #currentDb + '.dbo.Table1'
print #sql
exec (#sql);
print '--------------------------------------------------------------------------'
set #id = #id + 1;
END
go
DECLARE #Loop int
DECLARE #MaxLoop int
DECLARE #DBName varchar(300)
DECLARE #SQL varchar(max)
SET #Loop = 1
SET #DBName = ''
set nocount on
SET #MaxLoop = (select count([name]) FROM sys.databases where [name] like 'Z%')
WHILE #Loop <= #MaxLoop
BEGIN
SET #DBName = (select TableWithRowsNumbers.name from (select ROW_NUMBER() OVER (ORDER by [name]) as Row,[name] FROM sys.databases where [name] like 'Z%' ) TableWithRowsNumbers where Row = #Loop)
SET #SQL = 'USE [' + #DBName + ']'
exec (#SQL)
...
...
set #Loop = #Loop + 1
END
***Note: I didn't add the check if exists here.
I ended up writing one last week on the fly for some stuff I was doing.
Blog post here:
http://tsells.wordpress.com/2012/02/14/sql-server-database-iterator/
Here is the code.
SET NOCOUNT ON
GO
use master
go
Declare
#dbname nvarchar(500),
#variable1 int,
#variable2 int,
#variable3 int,
#totaldb int = 0,
#totaldbonserver int = 0,
#totaldbwithmatches int = 0
-- Get non system databases
Declare mycursor CURSOR for select name, database_id from SYS.databases where database_id > 4 order by name desc
open mycursor
fetch next from mycursor into #dbname, #variable1
while (##FETCH_STATUS <> -1)
BEGIN
DECLARE #ParmDefinition NVARCHAR(500)
Declare #mysql nvarchar(500) = 'select #variable2OUT = COUNT(*) from [' + #dbname + '].INFORMATION_SCHEMA.TABLES where Upper(TABLE_NAME) like ''MyTable''';
SET #ParmDefinition = N'#variable2OUT int OUTPUT'
set #totaldbonserver = #totaldbonserver + 1
Execute sp_executesql #mysql, #ParmDefinition, #variable2 OUTPUT
if #variable2 = 1
BEGIN
DECLARE #ParmDefinition2 NVARCHAR(500)
Declare #mysql2 nvarchar(500) = 'select #variable2OUT = COUNT(*) from [' + #dbname + '].dbo.MyTable';
SET #ParmDefinition2 = N'#variable2OUT int OUTPUT'
Execute sp_executesql #mysql2, #ParmDefinition2, #variable3 OUTPUT
set #totaldb = #totaldb + 1
if #variable3 > 1
BEGIN
Print #dbname + ' matched the criteria'
set #totaldbwithmatches = #totaldbwithmatches + 1
END
ELSE
Select 1
END
fetch next from mycursor into #dbname, #variable1
END
PRINT 'Total databases on server: '
Print #totaldbonserver
PRINT 'Total databases tested () : '
Print #totaldb
PRINT 'Total databases with matches: '
Print #totaldbwithmatches
CLOSE mycursor
DEALLOCATE mycursor
This doesn't use a loop. Hope this helps!
Note that "TABLE_OWNER" is that same as "SCHEMA Owner" and "TABLE_TYPE" will identify if the item is a table OR view.
--This will return all tables, table owners and table types for all database(s) that are NOT 'Offline'
--Offline database information will not appear
Declare #temp_table table(
DB_NAME varchar(max),
TABLE_OWNER varchar(max),
TABLE_NAME varchar(max),
TABLE_TYPE varchar(max),
REMARKS varchar(max)
)
INSERT INTO #temp_table (DB_NAME, TABLE_OWNER, TABLE_NAME, TABLE_TYPE,REMARKS)
EXECUTE master.sys.sp_MSforeachdb 'USE [?]; EXEC sp_tables'
SELECT DB_NAME, TABLE_OWNER, TABLE_NAME, TABLE_TYPE
FROM #temp_table
--Uncomment below if you are seaching for 1 database
--WHERE DB_NAME = '<Enter specific DB Name>'
--For all databases other than 'System Databases'
WHERE DB_NAME not in ('master','model','msdn','tempdb')
order by 1,2,3
You don't have to use a "USE DATABASE" statement. You can select from the particular database table by using a 3 part identifier as in:
select * from MyDatabase.dbo.MyTable