loop script is not working - sql

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

Related

Dynamically Creating a table and inserting Rows inside a SP

I am trying to run a Stored Procedure that Inserts into a Table.
I keep getting the Incorrect syntax near 'tbl_1_2'.
It doesn't Insert the data.I am using MSSQL2012
Alter PROCEDURE [dbo].[TT] AS
BEGIN
declare #tbl varchar(100);
set #tbl='tbl_1_2';
declare #sql varchar(100);
DECLARE #ptablename VARCHAR(max) = (#tbl)
set #ptablename=#tbl
SET #sql = 'INSERT INTO ' + #ptablename ;
EXEC (#sql)
select *
from (
values('1','2')
) t1 (c1, c2)
End
See this sample and compare with your code to understand your error.
Create Table Test (ID int, Name sysname)
Go
Declare #TableName sysname = 'Test'
DECLARE #ID int = 1
DECLARE #Name sysname = 'Meysam'
Declare #SQL nVarchar(4000) = 'INSERT #TableName (ID, Name) Values (#ID, N''#Name'')'
Set #SQL = REPLACE(#SQL, '#TableName', #TableName)
Set #SQL = REPLACE(#SQL, '#ID', #ID)
Set #SQL = REPLACE(#SQL, '#Name', #Name)
EXEC (#SQL)
SELECT * FROM Test

Get list of column names containing specific value SQL-Hue

I have 50 tables and I want to get list of all column names containing specific value in the column.
Something like: if 'Test' in column give me the column name. And I want to apply it for all 50 tables
Tables names are: abc1,abc2,abc3, … abcn.
There must be a better way to do this, but I did come across a query like this from a colleague a couple of years ago and I found the below (or a script similar to below that i may have tweaked before sending onto him) somewhere online (I imagine here on SO, but can't see to find where - I will update with link to original source if i can find it).
I would also just point out that this is a very heavy proc so I would take a backup of your db and restore it to a dev or test environment before executing it. Or even execute it against a test db that you might have in your localdb. The script searches all columns of all tables in a selected db for a string.
Personally I would probably export the data from each table to txt or csv and do a Find within them.
CREATE PROCEDURE FindMyData_String
#DataToFind NVARCHAR(4000),
#ExactMatch BIT = 0
AS
SET NOCOUNT ON
DECLARE #Temp TABLE(RowId INT IDENTITY(1,1), SchemaName sysname, TableName sysname, ColumnName SysName, DataType VARCHAR(100), DataFound BIT)
INSERT INTO #Temp(TableName,SchemaName, ColumnName, DataType)
SELECT C.Table_Name,C.TABLE_SCHEMA, C.Column_Name, C.Data_Type
FROM Information_Schema.Columns AS C
INNER Join Information_Schema.Tables AS T
ON C.Table_Name = T.Table_Name
AND C.TABLE_SCHEMA = T.TABLE_SCHEMA
WHERE Table_Type = 'Base Table'
And Data_Type In ('ntext','text','nvarchar','nchar','varchar','char')
DECLARE #i INT
DECLARE #MAX INT
DECLARE #TableName sysname
DECLARE #ColumnName sysname
DECLARE #SchemaName sysname
DECLARE #SQL NVARCHAR(4000)
DECLARE #PARAMETERS NVARCHAR(4000)
DECLARE #DataExists BIT
DECLARE #SQLTemplate NVARCHAR(4000)
SELECT #SQLTemplate = CASE WHEN #ExactMatch = 1
THEN 'If Exists(Select *
From ReplaceTableName
Where Convert(nVarChar(4000), [ReplaceColumnName])
= ''' + #DataToFind + '''
)
Set #DataExists = 1
Else
Set #DataExists = 0'
ELSE 'If Exists(Select *
From ReplaceTableName
Where Convert(nVarChar(4000), [ReplaceColumnName])
Like ''%' + #DataToFind + '%''
)
Set #DataExists = 1
Else
Set #DataExists = 0'
END,
#PARAMETERS = '#DataExists Bit OUTPUT',
#i = 1
SELECT #i = 1, #MAX = MAX(RowId)
FROM #Temp
WHILE #i <= #MAX
BEGIN
SELECT #SQL = REPLACE(REPLACE(#SQLTemplate, 'ReplaceTableName', QUOTENAME(SchemaName) + '.' + QUOTENAME(TableName)), 'ReplaceColumnName', ColumnName)
FROM #Temp
WHERE RowId = #i
PRINT #SQL
EXEC SP_EXECUTESQL #SQL, #PARAMETERS, #DataExists = #DataExists OUTPUT
IF #DataExists =1
UPDATE #Temp SET DataFound = 1 WHERE RowId = #i
SET #i = #i + 1
END
SELECT SchemaName,TableName, ColumnName
FROM #Temp
WHERE DataFound = 1
GO

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

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);

How to Execute script in multiple databases at once

In Sql Server
I have sample script
Declare #V table (name varchar(100))
INSERT INTO #V(name)
select name from sys.databases where database_id > 6
select NAME from #V
If I execute this Script I get the user databases list
In my instance I have below Databases :
DatabaseName
mohan
Ravi
How can I dynamically append the Database names to below script and execute the script in below Databases
Declare #V table (ID INT,name varchar(100))
INSERT INTO #V(Id,name)
select ROW_NUMBER()OVER(ORDER BY NAME),name from sys.databases where database_id > 6
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
PRINT #Name
SET #LoopCounter = #LoopCounter + 1
END
DECLARE #DatabaseName VARCHAR(50) = #Name
, #SQL NVARCHAR(MAX);
SET #SQL = N'USE ' + QUOTENAME(#DatabaseName)
+'CREATE TABLE T(ID INT)';
--PRINT(#SQL);
EXECUTE(#SQL);
So in all user databases Table will be created . I have tried with sp_msforeachdb
script :
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
But I'm looking for looping script .How to append those names to the Script
No need for a loop here. You can generate the entire statement in one pass. This query populates a var that contains multiple USE and CREATE TABLE statements:
DECLARE #Qry NVARCHAR(MAX) = '';
-- Build dynamic SQL statement here.
-- 1 Row returned per target database .
SELECT
#Qry +=
'
USE' + QUOTENAME(Name) + ';
CREATE TABLE T
(
ID INT
)
;
'
FROM
sys.Databases
WHERE
database_id > 6
;
-- Remove comments from last line when happy with query.
PRINT #Qry
--EXECUTE sp_ExecuteSQL #Qry
Returns
USE [X];
CREATE TABLE T
(
ID INT
)
;
USE [Y];
CREATE TABLE T
(
ID INT
)
;
...

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