query every single database - sql

I would like to search every database in my DB search (they all have the same table name) with the following:
SELECT DISTINCT NAME FROM TABLE WHERE NAME = 'blah'
I have tried this:
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].[TABLE]'
DECLARE #SQL NVARCHAR(MAX)
SELECT #SQL = STUFF((
SELECT CHAR(13) + 'SELECT ''' + name + ''', COUNT(1) FROM [' + name + '].' + #TableName
FROM sys.databases
WHERE OBJECT_ID(name + '.' + #TableName) IS NOT NULL
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
INSERT INTO #temp (DB, [COUNT])
EXEC sys.sp_executesql #SQL
SELECT *
FROM #temp t

The easiest way is to use (the undocumented) sp_msforeachdb. It's usage is:
exec sp_msforeachdb #command1 = N'insert into #tmp select "?", * from [?].dbo.table';
Of note is the ?, which is a placeholder for the database name. Within the #command1 string, double-quotes are used in place of single-quotes.
This procedure will also enumerate the system databases, and you can fairly easily sidestep that by appending if db_id("?") > 4 to skip them.

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

Iterate through temporary table columns to select them

I have a final temporary table (#tempTable) with unknown columns number.
My final select is like this, it works :
SELECT temp.* FROM #tempTable temp
But instead of a '*' I would like to call each columns individually :
SELECT temp.col1, temp.col2 FROM #tempTable temp
To do so I need to iterate through my columns names and create a procedure, I tried something like this :
DECLARE #ColName VARCHAR(255)
SELECT #ColName = min(name) FROM tempdb.sys.columns
WHERE object_id = Object_id('tempdb..#TEMPTABLE');
WHILE #ColName is not null
BEGIN
-- i need to do it all in once and not each time....
declare #sql varchar(max) = 'SELECT tp.'+'#COlName'+'FROM #TEMPTABLE tp'
exec(#sql)
-- Increment the value, how to go to next column ?
select #ColName = min(name) FROM tempdb.sys.columns WHERE object_id =
Object_id('tempdb..#TEMPTABLE') > #ColName -- does not work because it is a string (column name)
END
Try this:
DECLARE #ColName VARCHAR(2000) = 'select '
SELECT #ColName = #ColName + ' temp.' + name + ',' FROM tempdb.sys.columns
WHERE object_id = Object_id('tempdb..#TEMPTABLE')
--delete last character, which is comma and append table name
#ColName = substring(#ColName, 1, LEN(#ColName) - 1) + ' from #TEMPTABLE temp'
exec(#ColName)
This query construct whole table list combined in select ... from ... statement. I increased size of the varchar variable, so it can accomodate long queries.
Also, IMO variable name such as #sql or #query would be more meaningful.
A set based approach
IF OBJECT_ID('tempdb..#TEMPTABLE','U') IS NOT NULL
DROP TABLE #TEMPTABLE;
CREATE TABLE #TEMPTABLE (
Id INT IDENTITY(1,1)
,Col1 INT
,Col2 BIGINT
,Col3 BIGINT
,Col4 DATETIME
,Col5 DATETIME
) ;
DECLARE #SQL NVARCHAR(MAX)
SELECT #SQL = N'SELECT ' + SUBSTRING((
SELECT N', temp.' + S.name
FROM
tempdb.sys.columns S
WHERE
S.object_id = OBJECT_ID('tempdb..#TEMPTABLE')
ORDER BY
S.column_id
FOR XML PATH('')
)
,2
,200000
) + N' FROM #TEMPTABLE temp'
EXEC sys.sp_executesql #SQL

Count duplicate records from any table using stored procedure

I have a stored procedure which can check any table on duplicates. It returns a result set with all the duplicate rows from the given table.
What I want now is to count the amount of duplicates, get the table name & rundate query and insert it into a table.
The problem I'm facing currently is that the SP returns a set with a variable amount of columns. Therefore I can't insert the result of the SP in a predefined temptable.
Does anyone know a (smarter) way to do this?
Expected result
|tableName|Date|sumDupRows|
SP I'm using:
create proc [dbo].[sp_duplicates] #table nvarchar(50) as
declare #query nvarchar(max)
declare #groupby nvarchar(max)
set #groupby = stuff((select ',' + name
FROM sys.columns
WHERE object_id = OBJECT_ID(#table)
FOR xml path('')), 1, 1, '')
set #query = 'select *, count(*) as duplicates
from '+#table+'
group by '+#groupby+'
having count(*) > 1'
exec (#query)
GO
You've already found duplicate records. Use another select to aggregate them.
create proc [dbo].[sp_duplicates] #table nvarchar(50) as
declare #query nvarchar(max)
declare #groupby nvarchar(max)
set #groupby = stuff((select ',' + name
FROM sys.columns
WHERE object_id = OBJECT_ID(#table)
FOR xml path('')), 1, 1, '')
set #query = 'SELECT ' +
#table + ' AS tableName,
GETDATE() AS MaxDate,
SUM(duplicates) AS TotalDuplicates
FROM
(
select count(*) as duplicates
from '+#table+'
group by '+#groupby+'
having count(*) > 1
) A'
exec (#query)
GO

Dynamic SQL - Picking the values from the temp table

I have a temp table that has the following information.
TableName ColumnName PrimaryKeyColumnName Primarykey(A GUID)
(4 columns)
I need to include one more new column Value that should grab data based on the condition below
"for each row in the temp table"
select ColumnName from TableName where PrimaryKeyColumnName = Primarykey
To be even more precise , the query must retrieve the values from the table directly.
I am sure this can be achieved using cursor. But it might impact my query performance.
Could this be achieved using sp_sqlexecute? I tried converting the query as a varchar.
Like this
set #sql = 'select '+ #final.[primary field] +'from ' + #final.tablename +
'where '+ #final.PrimaryKeyColumnName + '='+ #final.Primarykey
exec sp_sqlexecute #sql
Possible this be helpful for you -
IF OBJECT_ID('tempdb.dbo.#temp') IS NOT NULL
DROP TABLE #temp
GO
CREATE TABLE #temp
(
[primary field] SYSNAME,
tablename SYSNAME,
PrimaryKeyColumnName SYSNAME,
Primarykey SYSNAME
)
INSERT INTO #temp ([primary field], tablename, PrimaryKeyColumnName, Primarykey)
VALUES
('[column1]','[table1]','[column3]', '[column12]'),
('[column2]','[table2]','[column4]', '[column24]')
DECLARE #SQL NVARCHAR(MAX)
SET #SQL = (
SELECT CHAR(13) + 'SELECT '+ [primary field] + ' FROM ' + tablename +
' WHERE '+ PrimaryKeyColumnName + ' = '+ Primarykey
FROM #temp
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)')
PRINT #SQL
--EXEC sys.sp_executesql #SQL
Output -
SELECT [column1] FROM [table1] WHERE [column3] = [column12]
SELECT [column2] FROM [table2] WHERE [column4] = [column24]
I am not sure that I understood what you are trying to do but try this:
declare #sql varchar(8000);
set #sql = null;
select #sql =
coalesce(#sql+' union all select '+ColumnName+' from '+ tablename,
'select '+ColumnName+' from '+ tablename)
from final where ColumnName = PrimaryKeyColumnName;
exec sp_sqlexecute #sql;
Notice that the #sql is limited -in my sample 8000- if the temp table you have is too big you gonna have to make a separate executes,needs some changes in the code.
Always you have to set an initial value for #SQL as null.
I think no need for cursor, this will be solved using some SQL tricks.
Hope this will help...
Create Table #tempValueFetch
(
TableName sysname,
PrimaryKeyColumnName sysname,
AttributeColumnName sysname,
Primaryfield sysname,
PrimaryKey sysname,
Label nvarchar(max),
Value nvarchar(max)
)
this temp table has been populated with 5 columns namely TableName,PrimaryKeyColumnName,AttributeColumnName ,Primaryfield ,PrimaryKey
The columns label and value has to updated dynamically.Note that primary key is a uniqueidentifier

confusion with quotes and double quotes in a query

i have a query that works beautifully:
CREATE Procedure BCP_Text_File
(
#table varchar(100),
#FileName varchar(100)
)
AS
If exists(Select * from information_Schema.tables where table_name=#table)
Begin
Declare #str varchar(1000)
set #str='Exec Master..xp_Cmdshell ''bcp "Select * from '+db_name()+'..'+#table+'" queryout "'+#FileName+'" -c'''
Exec(#str)
end
else
Select 'The table '+#table+' does not exist in the database'
but i need to add this in there:
select column_name
from information_schema.columns
where table_name = #table
order by ordinal_position
so far i have:
alter Procedure BCP_Text_File
(
#table varchar(100),
#FileName varchar(100)
)
AS
If exists(Select * from information_Schema.tables where table_name=#table)
Begin
Declare #str varchar(1000)
set #str='Exec Master..xp_Cmdshell ''bcp "
select column_name
from information_schema.columns
where table_name = '+db_name()+'..'+#table+'
order by ordinal_position
Select * from '+db_name()+'..'+#table+'" queryout "'+#FileName+'" -c'''
Exec(#str)
end
else
Select 'The table '+#table+' does not exist in the database'
but i think i am missplacing the single quotes and/or double quotes. i am adding this select statement so that my result has the field names as the first row.
thanks so much for any help or guidance.
Perhaps this is what you want? This assumes that (a) none of your column names have commas in them, and (b) the output of each column, when implicitly converted to a string, is okay.
ALTER PROCEDURE dbo.BCP_Text_File
#table NVARCHAR(255),
#filename VARCHAR(100)
AS
BEGIN
SET NOCOUNT ON;
IF OBJECT_ID(#table) IS NOT NULL
BEGIN
DECLARE
#sql NVARCHAR(MAX),
#cols NVARCHAR(MAX) = N'';
SELECT #cols += ',' + name
FROM sys.columns
WHERE [object_id] = OBJECT_ID(#table)
ORDER BY column_id;
SELECT #cols = STUFF(#cols, 1, 1, '');
SET #sql = N'EXEC master..xp_cmdshell ''bcp "SELECT '''''
+ REPLACE(#cols, ',', ''''',''''') + ''''' UNION ALL SELECT '
+ 'RTRIM(' + REPLACE(#cols, ',', '),RTRIM(') + ') FROM '
+ DB_NAME() + '..' + #table + '" queryout "' + #filename + '" -c''';
EXEC sp_executesql #sql;
END
ELSE
BEGIN
SELECT 'The table '+#table+' does not exist in the database';
END
END
GO
But I have to agree with the advice you've gotten from others on this and other questions - this approach is very brittle. You're trying to crack open a pistachio with a steamroller.
PS I removed references to INFORMATION_SCHEMA, because I think the catalog views are more reliable and more consistent.