SQL request over multiple databases and tables - sql

I'm having quite the problem.
The process data from our machines is stored in multiple MS SQL Databases.
It is possible to mount and unmount data which is no longer used or which should be archived.
Thats the reason, why there are multiple databases.
In each Database there exists multiple tables with the data values for one or more measuring points.
Which a JOIN Query i can get the values from one table with the corresponding table and tagname:
> SELECT HA_DATA_yyyy.R_xxxxx.time HA_DATA_yyyy.R_xxxxx.value, HA_DATA_yyyy.tags.tagname FROM HA_DATA_yyyy.R_xxxxx
> INNER JOIN HA_DATA_yyyy.RI_xxxxx ON HA_DATA_yyyy.R_xxxxx.id = HA_DATA_yyyy.RI_xxxxx.id
> INNER JOIN HA_DATA_yyyy.tags on HA_DATA_yyyy.RI_xxxxx.tag_uuid = HA_DATA_yyyy.tags.tag_uuid
> WHERE (HA_DATA_yyyy.tags.tagname like 'tagname')
yyyy - represents a number for the database
xxxxx - represents a number which is unique on the database-server, but differents in each database.
But now I'm looking for a solution to get this for all R_xxxxx tables of a database and for all mounted databases.
Is there any way to do this without external software? Just with the right query request or user defined function / stored procedure?

maybe dynamic sql is an option.
as a starting point you could use the list of databases:
insert into #dblist (dbname)
select d.name from sys.databases d where d.name like 'HA_DATA_%'
then for each database gather the list of tables to read data from (you can go with cursors or other loop as you prefer):
declare #dbname varchar(128) = ''
declare #dynsql nvarchar(max)
create table #listoftables (name varchar(128), db varchar(128))
while exists(select top 1 dbname from #dblist where dbname > #dbname order by dbname)
begin
set #dynsql = 'insert into #listoftables(name,db) select name,''' + #db + ''' from '+ #db +'.sys.tables'
exec sp_executesql #statement = #dynsql
-- move on to next db
select top 1 #dbname = dbname from #dblist where dbname > #dbname order by dbname
end
now you have a table list to loop onto to build a dynamic query to read all the data at once.
beware of the many issues you may incur using dynamic sql; here and there you can find just the first 2 results gathered with some explanations on why you have to be careful using dynamic sql.

Please have a look at the answer of this Stackoverflow question:
Archiving large amounts of old data in SQL Server
I think it might be what you need.
Update:
You can query the mounted databases by using the SQL query:
select Name into #myDatabases -- get all mounted databases into #myDatabases
from sys.databases
where (has_dbaccess(name) > 0)
and name not in ('master', 'tempdb', 'model', 'msdb')
order by 1
select * from #myDatabases -- list all mounted databases
drop table #myDatabases -- don't forget to drop it at the end
This can be used to create a dynamic SQL statement, which you execute via the sp_executesql command:
EXECUTE sp_executesql #SQL_String, #Parameter_Definition, #Param1, ..., #ParamN
The parameter definition is a string containing the list of parameter names and their datatypes.
So you can build up your own query based on the database list above and then execute it.

Related

How to find, across multiple databases, a specific table (common to most/all) that is not empty

I am working in an environment where many users have the same (or almost identical) test database set up on a common MSSQL server. We are talking about well over 100 databases for testing purposes. And at the very least, 95+% of them will contain the table I am trying to target.
These test databases are only filled with junk data - I will not be impacting anyone by doing any kind of a search. I am looking at one table, specifically, and I need to determine if any test database has that table actually containing any data at all. It doesn’t matter what the data is, I just need to find a table actually containing any data, so I can determine why that data exists in the first place. (This DB is quite old - almost two decades, so sometimes no-one has a clear answer why something in it exists).
I have been trying to build an SQL statement that iterates through all the databases, and checks that particular table specifically to see if it has any content, to bring back a list of databases that have that table containing data.
So to be specific: I need to find all databases where a specific table has any content at all (COUNT(*) > 0). Right now totally stuck with not much of any clues as to how to proceed.
In both methods replace <tablename> with the table name
Using sp_foreachdb
You can use sp_foreachDb
CREATE TABLE ##TBLTEMP(dbname varchar(100), rowscount int)
DECLARE #command varchar(4000)
SELECT #command =
'if exists(select 1 from [?].INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME =''<TABLE NAME>'') insert into ##TBLTEMP(dbname,rowscount) select ''[?]'',count(*) from [?].dbo.<tablename>'
EXEC sp_MSforeachdb #command
SELECT * FROM ##TBLTEMP WHERE rowscount > 0
DROP TABLE ##TBLTEMP
Using CURSOR
CREATE TABLE ##TBLTEMP(dbname varchar(100), rowscount int)
DECLARE #dbname Varchar(100), #strQuery varchar(4000)
DECLARE csr CURSOR FOR SELECT [name] FROM sys.databases
FETCH NEXT FROM csr INTO #dbname
WHILE ##FETCH_STATUS = 0
BEGIN
SET #strQuery = 'if exists(select 1 from [' + #dbname +'].INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME =''<TABLE NAME>'') INSERT INTO ##TBLTEMP(dbname,rowscount) SELECT ''' + #dbname + '' ', COUNT(*) FROM [' + #dbname + '].[dbo].<table name>'
EXEC(#strQuery)
FETCH NEXT FROM csr INTO #dbname
END
CLOSE csr
DEALLOCATE csr
SELECT * FROM ##TBLTEMP where rowscount > 0
References
Sp MSforeachDB
Run same command on all SQL Server databases without cursors
DECLARE CURSOR (Transact-SQL)

How to set 1 database name containing a specific table to variable?

So, a little backstory on this one:
I have a lengthy query that I use frequently for my jobs that utilizes the sp_MSForEachDB stored proc. The query creates a temporary table, and then goes through and searches sys.tables, and if a database has a table called T_Study, it will run the query and pull these results into the temporary table.
What I am now trying to do is join in another table from another database, and this database is of a different type. These database are all distinguished by the existence T_BatchStores. All databases that have a T_BatchStores table will also have a table dbo.T_TSyntax, and this is the table on which I will need to join.
Essentially, I am trying to build a universal query that I can run on any server containing this software, and I have these few universal tables, but the names of the databases themselves will vary.
So what I want to do is, in my query that populates my temporary table, add the following:
JOIN '+#MEDDB+'.dbo.T_TSyntax and then my ON clause, etc. Keep in mind that this join occurs within my begin and end clauses and that sp_MSforEachDb will be run on this.
I want #MEDDB to be just a randomly selected name of ANY database on their SQL instance that contains a T_BatchStores table - it does not matter what the name is - I just don't want to modify the query every time I run it.
How can I use my DECLARE #MEDDB and SET/SELECT #MEDDB to work with these requirements?
Also, in some reading, I read that I should use SYSNAME as the data type, but I also know NVARCHAR would work - but I'd just like a tip on which would be ideal.
I have tried the following:
DECLARE #MEDDB SYSNAME
SET #MEDDB = (SELECT TOP 1 name FROM sys.databases WHERE EXISTS (SELECT name FROM sys.tables WHERE name = '''T_BATCHSTORES'''))
SELECT #MEDDB
But this returns 1 row with a NULL value. I'm very much a beginner to SQL, so any assistance would be greatly appreciated!
Note: I am only using Microsoft SQL Server 2008 and 2012 at the present time.
Thanks!
Okay, after heavily modifying the query so that the names aren't actually exactly the same as our actual table structure, here is a slimmed-down version of the query I'm using so that you'll see what I'm going for:
/* Add or change variables as needed and comment back in WHERE statements in Insert section as needed.
You do not need to delete any variables in this section, even if you do not need them.
Simply comment in or out relevant data in the WHERE clause in Section 4. */
/* Section 1: Declaring variables. */
DECLARE #STUID NVARCHAR (65)
DECLARE #IMUID NVARCHAR (200)
DECLARE #ACCN NVARCHAR (100)
DECLARE #MEDDB NVARCHAR (255)
/* Section 2: Assigning values to variables such as an Image file's UID. */
SET #STUID = 'enterSTUID'
SET #IMUID = 'enterIMUIDhere'
SET #ACCN = 'enterACCNhere'
SET #MEDDB = (SELECT TOP 1 name FROM sys.databases WHERE [name] LIKE '%med%'
AND [name] NOT LIKE '%audit%')
/* Section 3: Creating our temporary table to dump our results. */
IF OBJECT_ID('tempdb.dbo.#tempBatchResultsD6') IS NULL
BEGIN
CREATE TABLE #tempBatchResultsD6
(
Database_Name VARCHAR (200),
THING1 VARCHAR(100) NOT NULL,
THING2 VARCHAR(200) NOT NULL,
THING3 DATETIME NOT NULL,
TSyntaxUID VARCHAR (66) NOT NULL,
TSyntaxDesc VARCHAR (128) NOT NULL
)
END
TRUNCATE TABLE #tempBatchResultsD6
/* Section 4: Query that will be used to populate the contents of the temporary table created above.
Utilizing the stored procedure "sp_MSForEachDb," this will search every database on the SQL instance.
Here, we are limiting our results to only searching specific databases by only returning results from databases that have a "T_Studies" table. */
DECLARE #Command NVARCHAR(MAX)
SET #Command = N'USE [?];
IF EXISTS (SELECT * FROM sys.tables WHERE [name] = ''T_Studies'')
BEGIN
INSERT #tempBatchResultsD6
SELECT
DB_Name () as Database_Name,
THING1,
THING2,
THING3,
TS.TSyntaxUID,
TS.TSyntaxDesc
FROM T_Studies ST WITH (nolock)
JOIN T_Patients PT WITH (nolock) ON ST.ST_PT_FOLDERID = PT.PT_FOLDERID
JOIN T_Series SE WITH (nolock) ON ST.ST_FOLDERID = SE.SE_ST_FOLDERID
JOIN T_Images IM WITH (nolock) ON SE.SE_FOLDERID = IM.IM_SE_FOLDERID
JOIN '+#MEDDB+'.dbo.T_TSyntaxes TS WITH (nolock) on IM.IM_TSyntaxID = TS.TSyntaxUID
WHERE ST.STUID = '''+#STUID+'''
--WHERE IM.IM_UID = '''+#IMUID+'''
--WHERE ST.ST_ACCNNO = '''+#ACCN+'''
END'
EXEC sp_MSForEachDb #Command
/* Section 5: Querying our temporary table to get our results. */
SELECT
Database_Name,
THING1,
THING2,
THING3,
TSyntaxUID,
TSyntaxDesc
FROM #tempBatchResultsD6
ORDER BY Database_Name
So as you can see, this is a massive temp table that will pull from all databases that have a T_Studies table in them. It's huge in its actual form, but this is trimmed down significantly.
The problem comes in section 2, where I am using #MEDDB to choose a random database name if the name contains the word "Med" but not the word "audit." This is problematic because it assumes consistent naming across all sites - and while these names are suggested, they are never a guarantee.
To guarantee consistency, I am trying to populate the #MEDDB variable with the name of ANY database on their system that contains a T_BatchStores table instead of the WHERE [name] like portion.
Any advice with this new info would be greatly appreciated!
I am not sure whether I understand your question completely.
But if you wanna loop through all databases, you can use something like the following code. "SELECT XXX" would then be your query, by using "use ' + #DB_Name + ';" you switch to the desired database to execute the your query.
DECLARE #DB_Name varchar(100)
DECLARE #Command nvarchar(max)
DECLARE database_cursor CURSOR FOR
SELECT name
FROM MASTER.sys.sysdatabases
OPEN database_cursor
FETCH NEXT FROM database_cursor INTO #DB_Name
WHILE ##FETCH_STATUS = 0
BEGIN
set #Command =
'
use ' + #DB_Name + ';
SELECT XXX
'
EXEC sp_executesql #Command
FETCH NEXT FROM database_cursor INTO #DB_Name
END
CLOSE database_cursor
DEALLOCATE database_cursor
DECLARE #MEDDB NVARCHAR(500)
SET #MEDDB = 'SELECT TOP 1 name FROM sys.databases WHERE EXISTS (SELECT name FROM sys.tables WHERE name = ''T_BATCHSTORES'')'
EXEC (#MEDDB)
This is Dynamic SQL. Of course, I'm not getting any results, either, but I'm not in your database. Still, this allows you to be 'dynamic', and use a variable to modify things:
DECLARE #MEDDB NVARCHAR(500)
DECLARE #MEDDB2 NVARCHAR(100) = 'T_BATCHSTORES'
SET #MEDDB = 'SELECT TOP 1 name FROM sys.databases WHERE EXISTS (SELECT name FROM sys.tables WHERE name = ''' + #MEDDB2 + ''')'
EXEC (#MEDDB)
My recommendation is that you run simpler queries that show what you want, and then you build them into the dynamic method.
For instance, I'm in AdventureWorks right now, so I can do:
SELECT * FROM sys.databases
This gives me a list of all the databases that are available in here.
So maybe I expand that to
SELECT * FROM sys.databases WHERE name = 'AdventureWorks'
This gives me just one line of results. So if that's what I want, I build it into the dynamic portion:
DECLARE #MEDDB NVARCHAR(500)
DECLARE #MEDDB2 NVARCHAR(100) = 'AdventureWorks'
SET #MEDDB = 'SELECT * FROM sys.databases WHERE name =''' + #MEDDB2 + ''''
EXEC (#MEDDB)
It just all depends what you're looking for. I always find the data I want first, and then figure out how to make my query look for it.

Get list of all databases that have a view named 'foo' in them

I have a few servers that have a bunch of databases in them.
Some of the databases have a view called vw_mydata.
What I want to do is create a list of all databases containing a view named vw_mydata and then execute that view and store it's contents in a table that then contains al the data from all the vw_mydata.
I know I can find all the databases containing that view using
sp_msforeachdb 'select "?" AS dbName from [?].sys.views where name like ''vw_mydata'''
But then I have as many recordsets as I have databases. How do I use that to loop through the databases?
What I would preferis a single neat list of the databasenames that I then can store in a resultset. Then it would be pretty straightforward.
I have thought about running above TSQL and storing the results in a table but I would like to keep it all in one SSIS package and not having all kind of tables/procedures lying around. Can I use a #table in a Execute SQL Task in SSIS?
DECLARE #Tsql VARCHAR(MAX)
SET #Tsql = ''
SELECT #Tsql = #Tsql + 'SELECT ''' + d.name + ''' AS dbName FROM [' + d.name + '].sys.views WHERE name LIKE ''vw_mydata'' UNION '
FROM master.sys.databases d
--"trim" the last UNION from the end of the tsql.
SET #Tsql = LEFT(#Tsql, LEN(#Tsql) - 6)
PRINT #Tsql
--Uncomment when ready to proceed
--EXEC (#Tsql)
To use a temp table in SSIS, you'll need to use a global temp table (##TABLE).
On the properties for the connection, I'm pretty sure you'll need to set RetainSameConnection to TRUE.
On the SQL task after you create the temp table, you'll need to set DelayValidation to TRUE.

Use SELECT results as a variable in a loop

I've searched here and elsewhere, and haven't found an answer yet. Hope I didn't miss it.
Using SQL Server Management Studio 2008 R2.
I have n specific databases on my server (there are other DBs as well, but I'm only interested in some of them)
Each of these databases has a table within it, which all have the same name. The only difference is the DB name. I want to aggregate these tables together to make one big table on a different database (different to the other DBs).
I can get the db names from the results of a query.
N is unknown.
Is a loop the way to go about this?
I was thinking something along the lines of the following pseudocode:
Set #dbnames = SELECT DISTINCT dbname FROM MyServer.dbo.MyTable
For each #name in #dbnames
INSERT INTO ADifferentDB.dbo.MyOtherTable
SELECT * FROM #name.dbo.table
Next name
(Clearly I'm new to using SQL variable as well, as you can see)
Your first problem is about iterating the databases: you cand do that with a cursor
Then you have another problem, executing a query where part of it is variable (database's name). You can do that with execute function.
All that is something similar to this:
DECLARE #query VARCHAR(max)
DECLARE #dbname VARCHAR(100)
DECLARE my_db_cursor CURSOR
FOR SELECT DISTINCT dbname FROM MyServer.dbo.MyTable
OPEN my_db_cursor
FETCH NEXT FROM my_db_cursor
INTO #dbname
WHILE ##FETCH_STATUS = 0
BEGIN
SET #query = 'INSERT INTO ADifferentDB.dbo.MyOtherTable
SELECT * FROM ' + #dbname + '.dbo.table'
EXECUTE(#query)
FETCH NEXT FROM my_db_cursor
INTO #dbname
END
CLOSE my_db_cursor
DEALLOCATE my_db_cursor
what you want to do is define a CURSOR for row-level operation. here is some doc
I would suggest using sp_MSForEachDB:
EXEC sp_MSForEachDB '
-- Include only the databases you care about.
IF NOT EXISTS (
SELECT *
FROM MySever.dbo.MyTable
WHERE dbname = ''?''
)
-- Exit if the database is not in your table.
RETURN
-- Otherwise, perform your insert.
INSERT INTO ADifferentDB.dbo.MyOtherTable
SELECT * FROM ?.dbo.table
'
In this case, ? is a token that is replaced with each database on the server.

Create view from data accross multiple databases

After some searching around i couldn't find a good answer that covers my issue.
I am working on the consolidation of around 100 databases. The structure is the same and they are all on the same server. All the databases have a table with login information.
We have created a core database with all the connection information from the other databases.
Now we need to create a view in the core database that contains all the login credentials from all the databases.
This means we need to use a loop to go through all the databases and select the user name and password.
Any ideas or suggestions are welcome
One possible solution is to create a stored procedure
DECLARE #sql varchar(max), #Database1 varchar(300)
set #Database1 = 'tempdb'
SET #sql='
USE '+#Database1+';
IF EXISTS (SELECT 1 FROM SYS.VIEWS WHERE NAME =''test_view'')
BEGIN
DROP VIEW test_view
PRINT ''VIEW EXISTS''
END'
PRINT #sql
EXEC(#sql)
declare #sql1 varchar(max)
// Modify below query as per your requirement its just for an idea
select #sql1 = IsNull(#sql1 + 'union all ','') +
'select * from ' + name + '.dbo.tblUser'
from sys.databases
where name like 'DbNamePrefix%'
set #sql1 = 'create view dbo.YourView as ' + #sql1
exec (#sql1)
Make a database job and schedule it as per your requirement.
To reference to your tables in the second database use this:
[DBName].[dbo].[TableName]
e.g.
CREATE VIEW [dbo].[ViewName]
as
select
a.ID,
a.Name,
b.Address
from TableA a
join SecondDBName.dbo.Table b
on ... ---Remaining code here...
NOTE: This will work only on the same server - if your databases are on different servers then you will need to create a linked server.
Take a look at this. Can this be one of the answers to your question?
http://blog.springsource.org/2007/01/23/dynamic-datasource-routing/