Running a query across multiple databases - sql

I recently asked a question about making a query that would document the results of a test query in a separate table.
There was, however, one thing that I didn't ask in the previous question that seemed like it was a large enough matter to simply make a new topic about. This query (courtesy of Steve Mangiameli):
DECLARE #testStatus NVARCHAR(MAX);
IF (
SELECT COUNT(*)
FROM Table1
WHERE Table1.Column1 = ''
) = 0
SET #testStatus = 'Test Passed'
ELSE
SET #testStatus = 'Test Failed'
INSERT INTO Table2 (FileName, Date, Result)
VALUES ('File1', GetDate(), #testStatus)
needs to be run across multiple databases. Here is the current version that I have worked up using another query of mine that does a similar thing but this one doesn't work. EDIT: To be more clear, I get an error message `Must declare the scalar variable "#testStatus".
DECLARE #dbname NVARCHAR(200);
DECLARE #testStatus NVARCHAR(MAX);
DECLARE #query NVARCHAR(MAX);
DECLARE db_cursor CURSOR FOR
SELECT name FROM sys.databases
WHERE name LIKE '%DBTag%'
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #dbname
WHILE ##FETCH_STATUS = 0
BEGIN
SET #query = CAST('
IF (
SELECT COUNT(*)
FROM [' +#dbname+ '].dbo.Table1
WHERE [' +#dbname+ '].dbo.Table1.Column1 = ''''
) = 0
SET #testStatus = ''Test Passed''
ELSE
SET #testStatus = ''Test Failed''
INSERT INTO [Database].dbo.Table2(FileName, Result, Date)
VALUES (''File1'', #testStatus, GETDATE())'
AS NVARCHAR(MAX))
EXECUTE (#query)
FETCH NEXT FROM db_cursor INTO #dbname
END
CLOSE db_cursor
DEALLOCATE db_cursor;
I'm wondering if there is something fundamentally "wrong" with how I've even gone about trying to make this work.

The variable is declared outside of your dynamic sql. When the dynamic sql executes your variables are out of scope. You can fix this easily by declaring the variable again inside your dynamic sql.
SET #query = CAST('
declare #testStatus varchar(20);
IF (
SELECT COUNT(*)
FROM [' +#dbname+ '].dbo.Table1
WHERE [' +#dbname+ '].dbo.Table1.Column1 = ''''
) = 0
SET #testStatus = ''Test Passed''
ELSE
SET #testStatus = ''Test Failed''
INSERT INTO [Database].dbo.Table2(FileName, Result, Date)
VALUES (''File1'', #testStatus, GETDATE())'
AS NVARCHAR(MAX))

Related

Fetch data without using union all every month

Select * from [dbo].[TRNS0119]
union all
Select * from [dbo].[TRNS0219]
union all
Select * from [dbo].[TRNS0319]
union all
Select * from [dbo].[TRNS0419]
In my attendance SQL Server database, every month a new table gets created with in/out records. I have to use union all to fetch data from all 12 month's data. I want to my query to fetch data automatically when new table gets created with a particular name. I am not SA user so can not make any changes / triggers / procedures.
So far I am doing this for every month but I want to know how I can resolve this without any intervention every month
for this solution, you must have same fields in your dynamic tables.
Tablename can be anything in your database, this will works for all tablenames.
Try this if you get your results.
declare #table table([Name] varchar(200))
declare #query VARCHAR(max)
declare #strQuery varchar(max) = '';
set #query='SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES';
insert into #table exec (#query)
--select * from #table (You will get all your table names)
declare #tablename varchar(100)
declare #i int=0
DECLARE curFetchData CURSOR FOR SELECT [Name] from #table
OPEN curFetchData
FETCH NEXT FROM curFetchData INTO #tablename
WHILE ##Fetch_status = 0
BEGIN
IF(#i>0)
set #strQuery = #strQuery + (' UNION ALL ')
set #strQuery = #strQuery + (' SELECT * from '+#tablename+' ')
set #i=#i+1;
FETCH NEXT FROM curFetchData INTO #tablename
END
CLOSE curFetchData
DEALLOCATE curFetchData
PRINT (#strQuery)
EXEC (#strQuery)
Try this query. Please ensure the input parameter.
DECLARE #inputYear INT,#tablename VARCHAR(20)=''
SET #inputYear =2019---- year(getdate())
DECLARE #v nvarchar(30)='TRNS%'+right(convert(varchar(4),#inputyear),2)
DECLARE #query nvarchar(max)=''
SELECT #query=#query+' UNION ALL SELECT * FROM ' + NAME from sys.tables where name like #v
SELECT #query= STUFF(#query,1,10,'')
EXECUTE sp_executesql #query

Using a variable string in a select * from statement

Is something like this possible?:
DECLARE #test nvarchar(30) = 'Appt'
SELECT * FROM #test.dbo.tablename with (NOLOCK)
or possibly
DECLARE #test nvarchar(30) = 'Appt'
SELECT * FROM #test + '.dbo.tablename' with (NOLOCK)
No, right?
I'm not looking to put the whole string into a variable and then use "EXEC." I have long scripts that I want to just try to replace that with when it occurs.
Thank you.
This requires Dynamic SQL. Basically you create a string that builds the SQL statement dynamically based on your query. That string is then executed with an EXEC Statement. A basic example is like this:
DECLARE #test nvarchar(30) = 'Appt'
DECLARE #sql as varchar(max)
SET #SQL = 'SELECT * FROM' + #test + '.dbo.tablename' + 'with (NOLOCK)'
EXEC #SQL
Assuming your tablename was Appt.dbo.tablename
I think you meant:
SET #SQL = 'SELECT * FROM' + 'dbo.' + #test + ' with (NOLOCK)'
Nice explanation logixologist.
Because Tom wants to run it for multiple select statements, i was thinking he could store all the table names in a column and use a cursor to fetch the table values. Considering you would create a table first such as:
CREATE TABLE tableName ( name varchar(20));
then run the following code once you inserted the right values in the table above.
Declare #table VARCHAR(255)
DECLARE #sql as varchar(max)
SET #SQL = 'SELECT * FROM' + 'dbo.' + #table + ' with (NOLOCK)'
DECLARE table_cursor CURSOR FOR
select distinct([name]) FROM DBname.dbo.tableName
Where [name] is not null
OPEN table_cursor
FETCH Next from table_cursor
INTO #table
WHILE ##FETCH_STATUS =0
BEGIN
EXEC #SQL
FETCH NEXT FROM table_cursor
INTO #table
END
CLOSE table_cursor
DEALLOCATE table_cursor
The cursor would return the table name for the select statement. I think you could use the same logic for insert statements.

Replacing a cursor with apply to loop through all tables in a database

I have a collection (100+) of tables all of which contain two fields
RowChanged bit
ChangedFields bit
Now, an error has occurred which leave some entries with RowChanged = 1 while ChangedFields is empty. I therefore need to go through these and set RowChanged = 0 where ChangedFields empty.
I have achieved this by the following cursor.
BEGIN TRANSACTION
USE DatabaseName --Database name to clean
DECLARE #Table_Name VARCHAR(50)
DECLARE #Query VARCHAR(250)
DECLARE Table_Cursor CURSOR FOR SELECT Name FROM sys.tables;
DECLARE #Affected_Rows INTEGER = 0
OPEN Table_Cursor
FETCH NEXT FROM Table_Cursor INTO #Table_Name
WHILE ##FETCH_STATUS = 0
BEGIN
SET #Query = 'Update '+#Table_Name+' Set RowChanged = 0 Where RowChanged = 1 And (LEN(RTRIM(CONVERT(NVARCHAR(100), ChangedFields))) = 0 OR ChangedFields IS NULL)'
EXEC (#Query)
SET #Affected_Rows = #Affected_Rows + COALESCE(##ROWCOUNT, 0)
FETCH NEXT FROM Table_Cursor INTO #Table_Name
END
SELECT #Affected_Rows AS Affected_Rows
CLOSE Table_Cursor
DEALLOCATE Table_Cursor
ROLLBACK --Change to COMMIT in order to save changes
While this does work, I have a genetic aversion against using cursors. Also I have just learned that Apply can in many cases achieve what Cursors did pre-2005.
What I need to do is to go though all tables in the database and check for the condition where RowChanged = 1 and ChangedFields like '' or NULL.
I have tried working this out with TVFs and what not, but I keep coming up short. While I could do operations in a single table, getting the list from sys.tables and doing something on several tables have eluded me.
You can replace the cursor with while loop statement. Try using statement like the following to achive the desired o/p.
select name into #table from sys.tables
while (select count(*) from #table)>0
begin
set rowcount 1
select #Table_Name = name from sys.tables
set rowcount 0
.......
.......
delete from #table where name = #Table_Name
end
The above snippet is from sybase, you might need to make minor modifications for the syntax. I hope that helps.
declare #stmt nvarchar(max)
select
#stmt =
isnull(#stmt + nchar(13) + nchar(10), '') +
'update '+ name +' set RowChanged = 0 Where RowChanged = 1 And (LEN(RTRIM(CONVERT(NVARCHAR(100), ChangedFields))) = 0 OR ChangedFields IS NULL)'
from sys.tables
print #stmt
sp_executesql #stmt = #stmt
there's also the undocumented stored procedure sp_MSforeachtable - SQL Server sp_msforeachtable usage to select only those tables which meet some condition. Actually, I've never used it in my work, so it's just for information.

How to execute a stored procedure inside a select query

SELECT col1,
col2,
col3,
EXEC GetAIntFromStoredProc(T.col1) AS col4
FROM Tbl AS T
WHERE (col2 = #parm)
How to write this SQL query in SQL Server 2008?
Thanks #twoleggedhorse.
Here is the solution.
First we created a function
CREATE FUNCTION GetAIntFromStoredProc(#parm Nvarchar(50)) RETURNS INTEGER
AS
BEGIN
DECLARE #id INTEGER
set #id= (select TOP(1) id From tbl where col=#parm)
RETURN #id
END
then we do the select query
Select col1, col2, col3,
GetAIntFromStoredProc(T.col1) As col4
From Tbl as T
Where col2=#parm
Functions are easy to call inside a select loop, but they don't let you run inserts, updates, deletes, etc. They are only useful for query operations. You need a stored procedure to manipulate the data.
So, the real answer to this question is that you must iterate through the results of a select statement via a "cursor" and call the procedure from within that loop. Here's an example:
DECLARE #myId int;
DECLARE #myName nvarchar(60);
DECLARE myCursor CURSOR FORWARD_ONLY FOR
SELECT Id, Name FROM SomeTable;
OPEN myCursor;
FETCH NEXT FROM myCursor INTO #myId, #myName;
WHILE ##FETCH_STATUS = 0 BEGIN
EXECUTE dbo.myCustomProcedure #myId, #myName;
FETCH NEXT FROM myCursor INTO #myId, #myName;
END;
CLOSE myCursor;
DEALLOCATE myCursor;
Note that ##FETCH_STATUS is a standard variable which gets updated for you. The rest of the object names here are custom.
You can create a temp table matching your proc output and insert into it.
CREATE TABLE #Temp (
Col1 INT
)
INSERT INTO #Temp
EXEC MyProc
As long as you're not doing any INSERT or UPDATE statements in your stored procedure, you will probably want to make it a function.
Stored procedures are for executing by an outside program, or on a timed interval.
The answers here will explain it better than I can:
Function vs. Stored Procedure in SQL Server
"Not Possible".
You can use a function instead of the stored procedure.
"Not Possible". You can do this using this query.
Initialize here
declare #sql nvarchar(4000)=''
Set Value & exec command of your sp with parameters
SET #sql += ' Exec spName #param'
EXECUTE sp_executesql #sql, N'#param type', #param = #param
Don't forget, if you just want to use the SP as a one-off query real quick to check something, all you have to do is pull the innards of the SP out and paste it in a new query window and do whatever you like at that point because it is no longer a SP.
Create a dynamic view and get result from it.......
CREATE PROCEDURE dbo.usp_userwise_columns_value
(
#userid BIGINT
)
AS
BEGIN
DECLARE #maincmd NVARCHAR(max);
DECLARE #columnlist NVARCHAR(max);
DECLARE #columnname VARCHAR(150);
DECLARE #nickname VARCHAR(50);
SET #maincmd = '';
SET #columnname = '';
SET #columnlist = '';
SET #nickname = '';
DECLARE CUR_COLUMNLIST CURSOR FAST_FORWARD
FOR
SELECT columnname , nickname
FROM dbo.v_userwise_columns
WHERE userid = #userid
OPEN CUR_COLUMNLIST
IF ##ERROR <> 0
BEGIN
ROLLBACK
RETURN
END
FETCH NEXT FROM CUR_COLUMNLIST
INTO #columnname, #nickname
WHILE ##FETCH_STATUS = 0
BEGIN
SET #columnlist = #columnlist + #columnname + ','
FETCH NEXT FROM CUR_COLUMNLIST
INTO #columnname, #nickname
END
CLOSE CUR_COLUMNLIST
DEALLOCATE CUR_COLUMNLIST
IF NOT EXISTS (SELECT * FROM sys.views WHERE name = 'v_userwise_columns_value')
BEGIN
SET #maincmd = 'CREATE VIEW dbo.v_userwise_columns_value AS SELECT sjoid, CONVERT(BIGINT, ' + CONVERT(VARCHAR(10), #userid) + ') as userid , '
+ CHAR(39) + #nickname + CHAR(39) + ' as nickname, '
+ #columnlist + ' compcode FROM dbo.SJOTran '
END
ELSE
BEGIN
SET #maincmd = 'ALTER VIEW dbo.v_userwise_columns_value AS SELECT sjoid, CONVERT(BIGINT, ' + CONVERT(VARCHAR(10), #userid) + ') as userid , '
+ CHAR(39) + #nickname + CHAR(39) + ' as nickname, '
+ #columnlist + ' compcode FROM dbo.SJOTran '
END
EXECUTE sp_executesql #maincmd
END
-----------------------------------------------
SELECT * FROM dbo.v_userwise_columns_value

How to do a huge search for Primary Key ID's that is used across the database where these Primary Key ID's have similar values in columns

BackDrop: We are researching why a number of accounts were missed in a process. We have went back to as far as we have data. We now have a rather large list of accounts that for whatever reason were missed. Now this process without going into too much detail is VERY VERY complex and we need to know why these accounts and only these accounts were missed. As any DataBase we have many many automated procedures that run all the time, so there is really at this point no telling what on earth happened to cause these accounts to get missed. My only bet I think at solving this is to find similarities between these accounts. Obviously we have tried looking at the more common places and have since found nothing.
Issue: I want to use SQL to return all the tablenames and columnnames in our database Where these list of accounts have the same value in a column or columns of a table. I have created a query to find tablenames, columns, and so forth but dont know how to bring it all together to create one query that will give me all the results I want. I am certain that a cursor will need to be used and lots of inner joining but I am just not sure how this should be done.
Again:
Lets say I have account numbers 123456 and 654321 and I know our DataBase has 3,000 tables with a column reference to account number with a name of either AccountNumber, AccountNum, or Account. I want to search and find all tables that have a column with the name AccountNumber, AccountNum, or Account that has a value of 123456 or 654321. Then with these tables, for each table I want to take the rows Where the column whether the name be AccountNumber, AccountNum, or Account has a value of either 123456 and 654321 and then for each of those rows I want to check each column of each row to see if the columns on a row for account number 123456 is eqaul to a column on a row for account number 654321 , if so then I want it to return the column name and the tablename. This way I can see what these accounts have in common.
ADVANCED PORTION:
IF some poor soul is able to do the above then I'd also like to create a query that will return
The tablename and when it was updated. I would get the updated value by checking each column in each table and if the column has a type of "timestamp" or a default value of "GetDate()" then that column would be used as updated. In final result set that shows were all changes have happened for those account nubmers it will order by updated.
A first approach, rustic (I'm not that used to T-SQL, I did more PL/SQL), but which may help you going further, AND TESTED IN SQL SERVER 2008. Hope it works in 2005...)
So, we create two procedures, one calling the other
The provided code can only check, in one time
- for 2 differents IDs
- for all concerned fields (Account, AccountNum, AccountNumber)
The idea (checking for AccountNumber column)
Find the tables (in table INFORMATION_SCHEMA.columns, which lists your database tables) which have a column with one of the 3 names provided
For every table found :
create a dynamic query :
select count(*) from <THE_TABLE> where <Account_column_name> IN (123456 654321);
If we have 2 results (mean that our Ids are both present in table), we launch the second procedure, with parameters : #TableName = <THE_TABLE>, #FieldName = <Account_column_name>, #FirstId = 123456, #SecondId = 654321
We find the column names for <THE_TABLE> (again in INFORMATION_SCHEMA.columns).
For every column found :
create a dynamic query
select count(*) from <THE_TABLE> as T1
inner join <THE_TABLE> as T2 on T1.<COLUMN_NAME> = T2.<COLUMN_NAME>
where T1.<Account_column_name>= 123456
and T2.<Account_column_name>= 654321
if count(*) = 1, it means that the same value exists in the same column of the same table for the given ids.
In that case, we print <THE_TABLE> and <THE_COLUMN>
To launch search, in sql management studio, just make
EXEC GetSimilarValuesForFieldAndValue 123456, 654321
and in the "Messages" part, you should have a list of "results".
CREATE procedure [dbo].[GetSimilarValuesForFieldAndValue](#FirstId int, #SecondId int)
AS
DECLARE #sql nvarchar(MAX);
DECLARE #params NVARCHAR(MAX);
DECLARE #Count int;
DECLARE #Name NVARCHAR(MAX);
DECLARE #FieldName NVARCHAR(MAX);
DECLARE db_cursor CURSOR for
select TABLE_NAME, COLUMN_NAME FROM INFORMATION_SCHEMA.columns
where COLUMN_NAME IN('Account', 'AccountNumber', 'AccountNum');
OPEN db_cursor
FETCH next from db_cursor into #Name, #FieldName
while ##FETCH_STATUS = 0
begin
select #sql =
N' SELECT #Count=Count(*) FROM ' + #Name +
N' WHERE ' +#FieldName+' IN (#FirstId,#SecondId)'
SELECT #params = N'#FieldName NVARCHAR(MAX), #FirstId int, #SecondId int, #Count int out'
EXEC sp_executesql #sql, #params, #FieldName, #FirstId, #SecondId, #Count OUT
if (#Count = 2)
begin
exec dbo.CompareFields #Name, #FieldName, #FirstId, #SecondId
end
FETCH NEXT FROM db_cursor INTO #Name, #FieldName
end
close db_cursor
DEALLOCATE db_cursor
GO
The second one :
CREATE procedure [dbo].[CompareFields](#TableName NVARCHAR(MAX), #FieldName NVARCHAR(MAX), #FirstId int, #SecondId int)
as
DECLARE #ColumnName NVARCHAR(MAX)
DECLARE #Sql NVARCHAR(MAX)
DECLARE #Params NVARCHAR(MAX)
DECLARE #Count int
DECLARE cfCursor CURSOR FOR
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = ''+#TableName+' '
AND COLUMN_NAME <> ' '+#FieldName+''
OPEN cfCursor
FETCH next from cfCursor into #ColumnName
while ##FETCH_STATUS = 0
begin
select #Sql =
N' SELECT #Count = count(*) from ' +#TableName + ' T1 '+
N' INNER JOIN ' + #TableName + ' T2 ON T1.' +#ColumnName + ' = T2.' + #ColumnName +
N' WHERE T1.' +#FieldName + ' = '+ CAST(#FirstId as varchar) +
N' AND T2.' + #FieldName + ' = '+CAST(#SecondId as varchar)
SELECT #Params =
N'#TableName VARCHAR(MAX), #ColumnName VARCHAR(MAX), '+
N'#FieldName VARCHAR(MAX), #FirstId int, #SecondId int, #Count int out'
exec sp_executesql #sql, #Params, #TableName, #ColumnName, #FieldName, #FirstId, #SecondId, #Count OUT
if #Count = 1
begin
--print tableName and column Name if value is identic
print 'Table : ' + #TableName + ' : same value for ' + #ColumnName
end
FETCH NEXT FROM cfCursor INTO #ColumnName
end
close cfCursor
DEALLOCATE cfCursor
GO
I actually had to do this for Guids at one point. Here is the script for doing with Guids. One sec and I'll work on modifying it to suit your needs:
DECLARE #table VARCHAR(100)
DECLARE #column VARCHAR(100)
DECLARE #value INT
SET #value = '06B8BD6C-A8EC-4EB3-9562-6666EE86952D'
DECLARE table_cursor CURSOR
FOR select tbl.Name, cols.name as TableName FROM sys.columns cols JOIN
sys.tables tbl on cols.object_id = tbl.object_id
where system_type_id = 36
OPEN table_cursor
FETCH NEXT FROM table_cursor
INTO #table, #column;
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #SQL NVARCHAR(1000)
SET #SQL = 'SELECT ''' + #Table + ''' AS TBL,''' +
#column + ''' AS COL FROM [' + #table + ']
WITH(NOLOCK) WHERE ' + #column + ' = ''' + CAST(#value AS VARCHAR(50)) + ''''
print #sql
EXEC sp_executesql #Sql
FETCH NEXT FROM table_cursor
INTO #table, #column;
END
CLOSE table_cursor
DEALLOCATE table_cursor
Updated to handle for searching on a field name:
DECLARE #table VARCHAR(100)
DECLARE #column VARCHAR(100)
DECLARE #value UNIQUEIDENTIFIER
SET #value = --ENTER YOUR ACCOUNT NUMBER HERE
DECLARE table_cursor CURSOR
select tbl.Name, cols.name as TableName FROM sys.columns cols JOIN
sys.tables tbl on cols.object_id = tbl.object_id
where cols.Name = 'AccountNumber'
OR cols.Name = 'AccountNum' OR cols.Name = 'Account'
OPEN table_cursor
FETCH NEXT FROM table_cursor
INTO #table, #column;
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #SQL NVARCHAR(1000)
SET #SQL = 'SELECT ''' + #Table + ''' AS TBL,''' + #column +
''' AS COL FROM [' + #table + '] WITH(NOLOCK)
WHERE ' + #column + ' = ''' + CAST(#value AS VARCHAR(50)) + ''''
print #sql
EXEC sp_executesql #Sql
FETCH NEXT FROM table_cursor
INTO #table, #column;
END
CLOSE table_cursor
DEALLOCATE table_cursor