53

I am looking for a way of dealing with the following situation:

  1. We have a database server with multiple databases on it (all have the same schema, different data).

  2. We are looking for a way to query across all the databases (and for it to be easy to configure, as more databases may be added at any time). This data access must be realtime.

Say, as an example, you have an application that inserts orders - each application has its own DB etc. What we are then looking for is an efficient way for a single application to then access the order information in all the other databases in order to query it and subsequently action it.

My searches to date have not revealed very much, however I think I may just be missing the appropriate keywords in order to find the correct info...

Paddy
  • 33,309
  • 15
  • 79
  • 114

5 Answers5

41

You must specify the database name before any database object.

Single database:

SELECT * FROM [dbo].[myTable]

Multiple dabases:

SELECT * FROM [DB01].[dbo].[myTable]
UNION ALL
SELECT * FROM [DB02].[dbo].[myTable]
UNION ALL
SELECT * FROM [DB03].[dbo].[myTable]
TcKs
  • 25,849
  • 11
  • 66
  • 104
  • Thanks for the answer, I am aware of the three part syntax and how to query across the individual DBs, I was just hoping for a slightly more elegant way than multiple unions like this. – Paddy Dec 16 '13 at 12:52
  • There is no universal built-in way, how to execute one script on multiple databases. You can have stored procedure which will dynamically concate strings in one large SQL select and then execute i with sp_sqlexec. – TcKs Dec 16 '13 at 13:09
25

It's not going to be the cleanest solution ever, but you could define a view on a "Master database" (if your individual databases are not going to stay constant) that includes the data from the individual databases, and allows you to execute queries on a single source.

For example...

CREATE VIEW vCombinedRecords AS
SELECT * FROM DB1.dbo.MyTable
UNION ALL
SELECT * FROM DB2.dbo.MyTable

Which allows you to do...

SELECT * FROM vCombinedRecords WHERE....

When your databases change, you just update the view definition to include the new tables.

Richard
  • 29,854
  • 11
  • 77
  • 120
  • 1
    Have this in mind alright, but it's a bit of a maintenance headache (adding new databases) and I would also start to worry about performance (potentially talking about hundreds of unions here). – Paddy Dec 16 '13 at 13:00
  • 4
    I don't believe you'll find a high-performance, easily-maintainable solution to search across multiple databases. Indexes are used for anything high-performance, and these only exist at the database level. – Richard Dec 16 '13 at 13:09
  • I can't get this to work on an Azure SQL server where I have 2 databases on the same server. Reference to database and/or server name in 'MyDatabaseNameHere' is not supported in this version of SQL Server. – Oliver Nilsen Dec 08 '20 at 11:39
5

Note - My answer below received a couple down votes, but only one comment giving any reason why it might be down-voted. The comment was that this answer is very similar to the accepted answer, but even less performant. I disagree with this opinion and I reproduce my response here - in the actual answer - so that anyone else reading my answer might have a better chance at seeing why this is not the same as the accepted answer at all, and in fact better addresses the original question.

My response to the suggestion this is similar to the accepted answer:

on the contrary - the original question notes that new databases are added regularly. The accepted solution will require maintenance each time a new database is added. The solution here will work regardless of whether any new databases are added (in line with the original question that states they all have the same schema). Further, the accepted answer requires you to duplicate the query once per database queried. If the query is complex, that gets ugly fast. The proposal here ensures a single source of truth for the logic being used in the query.

And the answer itself:

Shooting from the hip here.

use master;
go

create table #Temp (sourceDBName varchar(128), colA varchar(128), colB varchar(128));

exec sp_MSforeachDB ' USE [?];

insert into #Temp
SELECT DISTINCT
 ''?'',
tableA.colA,
tableB.colB
FROM tableA JOIN tableB on some_conditions
WHERE someCol LIKE ''%some_term%''
'

select sourceDBName, colA, colB from #Temp order by 1, 2, 3;

drop table #Temp;

This logic should allow you to apply a single query to all databases. To use it though, you will want to add logic to filter out system databases, or explicitly include only the databases you specify. To achieve that, you might like to put this logic into a stored procedure which then returns a result set, so in the end, your call to this logic is a select statement that returns a rowset you can join, filter, etc.

youcantryreachingme
  • 1,065
  • 11
  • 17
  • 1
    This seems to be a very similar if slightly less performant way to do this as is in the selected answer (a view defined in master). – Paddy Aug 02 '19 at 08:05
  • 1
    @Paddy, on the contrary - the original question notes that new databases are added regularly. The accepted solution will require maintenance each time a new database is added. The solution here will work regardless of whether any new databases are added (in line with the original question that states they all have the same schema). Further, the accepted answer requires you to duplicate the query once per database queried. If the query is complex, that gets ugly fast. The proposal here ensures a single source of truth for the logic being used in the query. – youcantryreachingme Aug 10 '20 at 02:44
  • While this works, it is not efficient. For one, it requires recreating the temporary table for every single query you are running. Not just because it is a temporary table, but also because the data is a copy of the original data, which may change at any point. Therefore, a view, as suggested in other answers, is way more suitable. – Bart May 11 '23 at 09:27
4

You can build the union dynamically:

select name from sys.databases

and then check if the database has the table:

select name from [dbname_from_above].sys.tables where name = 'YourTable'

That gives you all databases for the union. You can build the query client side or in dynamic SQL.

Andomar
  • 232,371
  • 49
  • 380
  • 404
1

Check out https://www.mssqltips.com/sqlservertip/2855/sql-server-multi-database-query-with-registered-servers/

SELECT * FROM (
SELECT 
    @@SERVERNAME as [ServerName],
    @@version [Version],
    Format(@@CONNECTIONS,'N0') [Conections],
    Format(@@CPU_BUSY ,'N0')[CPUBusy]
) SQLInfo
LEFT JOIN (
    SELECT 
        @@SERVERNAME AS [ServerName],
        SERVERPROPERTY('ProductVersion') [Version Build],
        SERVERPROPERTY ('Edition') AS [Edition],
        SERVERPROPERTY('ProductLevel') AS [Service Pack],
        CASE SERVERPROPERTY('IsIntegratedSecurityOnly') 
         WHEN 0 THEN 'SQL Server and Windows Authentication mode'
         WHEN 1 THEN 'Windows Authentication mode'
        END AS [Server Authentication],
        CASE SERVERPROPERTY('IsClustered') 
         WHEN 0 THEN 'False'
         WHEN 1 THEN 'True'
        END AS [Is Clustered?],
        SERVERPROPERTY('ComputerNamePhysicalNetBIOS') AS [Current Node Name],
        SERVERPROPERTY('Collation') AS [ SQL Collation],
        [cpu_count] AS [CPUs],
        [physical_memory_kb]*0.00000095367432 AS [RAM (GB)]
    FROM [sys].[dm_os_sys_info]
) SQLInfo2 on SQLInfo.[ServerName]=SQLInfo2.[ServerName]
LEFT JOIN (
    SELECT 
        @@SERVERNAME as [ServerName],
        NodeName,
        Status_Description,
        is_Current_Owner
    FROM [MASTER].[sys].[fn_virtualservernodes]()
)Clusterinfo on SQLInfo.[ServerName]=Clusterinfo.[ServerName]