Associate Database Name with Table List - sql

It is no problem to list all tables with schemas on a server
SELECT SCHEMA_NAME(schema_id), name FROM sys.tables
How can I determine which database the tables reside in ?

sys.tables exists in all databases so I am not following the fact that you don't know the db you are in. you can run DB_NAME(DB_ID()) to get the db name
SELECT DB_NAME(DB_ID()),SCHEMA_NAME(schema_id), name FROM sys.tables
but in this case DB_NAME(DB_ID()) will return the same value for every row
to do it for all database, you can do this
EXEC sp_msforeachdb 'use [?] SELECT ''?'',SCHEMA_NAME(schema_id), name
FROM sys.tables'
You can of course dump it into a table as well
CREATE TABLE #output (DatabaseName VARCHAR(1000),
SchemaName VARCHAR(1000),
TableName VARCHAR(1000))
INSERT #output
EXEC sp_msforeachdb 'use [?] SELECT ''?'',SCHEMA_NAME(schema_id), name
FROM sys.tables'
SELECT * FROM #output
Just as a FYI, the sp_msforeachdb proc is undocumented and you should not use it for production code, to quickly find something is fine, for production code roll your own version of this proc
See also Aaron Bertrand's posts here:
Making a more reliable and flexible sp_MSforeachdb
Execute a Command in the Context of Each Database in SQL Server

I ran into this problem when I was creating query that I wanted to be able to run against a different database on my server, and include the other database's name name without hard-coding it into the query.
The query essentially looked like this:
SELECT DB_NAME() db_name
, SCHEMA_NAME(schema_id) schema_name
, name table_name
FROM OtherDB.sys.tables --The OtherDB is to specify that I am running
--this for a different database than the one
--I'm logged in to for my current session.
The problem was that even though I specify OtherDB.sys.tables in the from clause, DB_NAME() always returned the current database I was in. Yes, I could put a USE OtherDB at the beginning, but it seemed like there should be another way. I looked through every sys view I could find, but could never find anything that would link sys.databases and sys.tables.
What I eventually found was SQL Server's INFORMATION_SCHEMA.TABLES This view includes the Database name as the first column (referred to as TABLE_CATAOLG).
SELECT TABLE_CATALOG
, TABLE_SCHEMA
, TABLE_NAME
, TABLE_TYPE
FROM INFORMATION_SCHEMA.TABLES
With these views, you can easily compare the tables in two databases:
SELECT a.TABLE_CATALOG
, a.TABLE_SCHEMA
, a.TABLE_NAME
, a.TABLE_TYPE
, b.TABLE_CATALOG
, b.TABLE_SCHEMA
, b.TABLE_NAME
, b.TABLE_TYPE
FROM OneDatabase.INFORMATION_SCHEMA.TABLES a
FULL OUTER JOIN TwoDatabase.INFORMATION_SCHEMA.TABLES b
ON a.TABLE_SCHEMA = b.TABLE_SCHEMA
AND a.TABLE_NAME = b.TABLE_NAME
If the databases are on separate servers that are linked you should be able to use this query by using all four parts of the Fully Qualified Table Name.

If your intention is just to include the current database name, why not just:
SELECT DB_NAME(), SCHEMA_NAME(schema_id), name FROM sys.tables;
If your intention is to pull all names from all databases, I personally prefer dynamic SQL like this instead of sp_msforeachdb:
DECLARE #sql NVARCHAR(MAX) = N'';
SELECT #sql += CHAR(13) + CHAR(10) + 'UNION ALL
SELECT ''' + name + ''', s.name, t.name
FROM ' + QUOTENAME(name) + '.sys.tables AS t
INNER JOIN ' + QUOTENAME(name) + '.sys.schemas AS s
ON t.schema_id = s.schema_id'
FROM sys.databases
WHERE database_id > 4;
SET #sql = STUFF(#sql, 1, 13, '');
PRINT #sql;
-- EXEC sp_executesql #sql;

Related

How do you check a server for databases missing a specific table?

I am attempting to locate all databases on a server that are missing a specific table. I've tried the query below but it is returning databases that actually do have the table. I know there is a problem with the query but I do not know how to fix it. I'm very beginner with sql knowledge.
So trying to find all databases on the server that do not have tables that begin with a name of etl.
EXEC
sys.sp_msforeachdb
'SELECT ''?'' DatabaseName, Name FROM [?].sys.Tables WHERE Name NOT LIKE ''%etl%'''
A cursor is not necessary here at all. You can build a big UNION ALL query to check each database for these tables.
DECLARE #sql nvarchar(max);
SELECT #sql = STRING_AGG(CAST('
SELECT ' + QUOTENAME(d.name, '''') + '
WHERE NOT EXISTS (SELECT 1
FROM ' + QUOTENAME(d.name) + '.sys.tables t
WHERE t.name LIKE N''etl%'')
' AS nvarchar(max)), ' UNION ALL ')
FROM sys.databases d;
EXEC sp_executesql #sql;

Remove all tables from schema except specific ones in SQL?

I have a SQL database and I would like to remove almost all tables related to a specific schema except a couple of them. Therefore I think I would need to edit the following sql query (which removes all tables of a specific schema):
EXEC sp_MSforeachtable
#command1 = 'DROP TABLE ?'
, #whereand = 'AND SCHEMA_NAME(schema_id) = ''your_schema_name'' '
Would you be able to suggest a smart and elegant way so that I can add a list of tables that I would like to keep and remove everything else?
If you want to keep using sp_msforeachtable, pass in the set of tables names to keep using a temp table. Here's an example using the boo schema:
create schema boo;
create table boo.t(i int);
create table #keep (name sysname);
insert #keep values ('myFirsttable'), ('mySecondTable'), ('myThirdTable');
exec sp_msforeachtable
#command1='drop table ?; print ''dropped ?''',
#whereand = 'and schema_name(schema_id) = ''your_schema_name'' and object_name(object_id) not in (select name from #keep)';
But personally, I'd probably just write my own stored procedure with a cursor. It's harder to mess up.
Note that this solution expects you to put the tables you want to keep into the temp table. Charlieface's solution expects you to put the names of tables you want to drop into the table variable.
You could place a list of tables you want to delete stored in a table variable or Table-Valued Parameter #tables then you can simply execute dynamic SQL with it.
DECLARE #tables TABLE (tablename sysname);
INSERT #tables (tablename)
SELECT t.name
FROM sys.tables t
WHERE t.schema_id = SCHEMA_ID('your_schema_name');
DECLARE #sql nvarchar(max) =
(
SELECT STRING_AGG(CAST(
'DROP TABLE ' + QUOTENAME('your_schema_name') + '.' + QUOTENAME(tablename) + ';'
AS nvarchar(max)), '
' )
FROM #tables
);
EXEC sp_executesql #sql;
Alternatively, select it directly from sys.tables
DECLARE #sql nvarchar(max) =
(
SELECT STRING_AGG(CAST(
'DROP TABLE ' + QUOTENAME(SCHEMA_NAME(t.schema_id)) + '.' + QUOTENAME(t.name) + ';'
AS nvarchar(max)), '
' )
FROM sys.tables t
WHERE t.schema_id = SCHEMA_ID('your_schema_name')
);
EXEC sp_executesql #sql;

Running query against all information schema in sql instance

I know there are many variation of this question but I haven't found one that gives me exactly the results.
Edit: I am trying to get all columns from all tables and views in all databases from the sql server instance
I have narrowed down exactly the information I want returned from the information schema of each database but I can't quite get the syntax of the query right
declare #SQL nvarchar(max) = ''
select #SQL = #SQL + 'SELECT ''' + d.name + ''' as DatabaseName, TABLE_CATALOG, TABLE_SCHEMA ,
TABLE_NAME , COLUMN_NAME , ORDINAL_POSITION , COLUMN_DEFAULT , DATA_TYPE , CHARACTER_MAXIMUM_LENGTH
FROM ''' + d.name + '.INFORMATION_SCHEMA.COLUMNS'
from sys.databases d
where d.name not in('master', 'tempdb', 'msdb', 'model')
exec sp_executesql #SQL
I am missing something to complete my query but can't quite figure it out

Microsoft SQL Server: Query to see list of all databases, and their corresponding tables, and columns

What I have found seems to only show this for one database at a time (e.g., select * from information_schema.columns). Is there something like select * from sys.databases which includes not just all of the database names, but also their own table and column names?
Would like to see all of these for the server: database names, table names, column names.
I'm trying to get familiar with a server at a new company, and want to just explore by browsing the table names and columns in every database. Also, I do not have write access, and am doing this in Tableau custom SQL.
Are you looking something like this? You can try msforeachdb
EXEC sp_MSforeachdb N'
BEGIN
use ?
select * from information_schema.tables
select * from information_schema.columns
END';
One I've used before:
EXEC master.sys.sp_MSForEachDB N'
IF ''?'' NOT IN (''master'', ''model'', ''msdb'', ''tempdb'')
BEGIN
USE ?
SELECT ''?'' AS theDatabase
, OBJECT_SCHEMA_NAME([tables].[object_id],DB_ID()) AS theSchema
, [tables].name AS theTable
, [columns].[name] AS theColumn
, [types].[name] AS theDatatype
, [columns].max_length AS columnSize
, [columns].[precision] AS columnPrecision
, [columns].scale AS columnScale
, [columns].is_nullable AS isNullable
FROM sys.tables AS [tables]
INNER JOIN sys.all_columns [columns] ON [tables].[object_id] = [columns].[object_id]
INNER JOIN sys.types [types] ON [columns].system_type_id = [types].system_type_id
AND [columns].user_type_id = [types].user_type_id
WHERE [tables].is_ms_shipped = 0
ORDER BY [tables].name, [columns].column_id
END
'
I usually dump it into a temp table so I can better manipulate the resulting data.
I haven't run into the issue with sp_MSforEachDB missing tables, but I'll check out Aaron's article about the issue. He's definitely a name I trust. :-)
I am not a big fan of undocumented procedures. And sp_msforeachdb will sometimes skip databases and nobody really seem to know why. Aaron Bertrand wrote alternatives here:
Making a more reliable and flexible sp_MSforeachdb
Execute a Command in the Context of Each Database in SQL Server
I also really don't care for cursors which is all that undocumented procedure is. If you look at the code it is one big ugly cursor. For something as simple as you desire I prefer to use some dynamic sql and sys tables to create it.
Here is how I would go about this task. It really isn't much more code than undocumented procedure. The big advantage is that is a LOT simpler to debug and there is no cursor. :)
declare #SQL nvarchar(max) = ''
select #SQL = #SQL + 'select ''' + d.name + ''' as DatabaseName
, t.name collate SQL_Latin1_General_CP1_CI_AS as TableName
, c.name collate SQL_Latin1_General_CP1_CI_AS as ColumnName
from ' + quotename(d.name) + '.sys.tables t
join ' + quotename(d.name) + '.sys.columns c on c.object_id = t.object_id UNION ALL '
from sys.databases d
set #SQL = LEFT(#SQL, len(#SQL) - 10) + ' order by DatabaseName, TableName, ColumnName'
select #SQL --once you are satisfied the sql is correct just uncomment the line below
--exec sp_executesql #SQL
Yo, maybe try this?
--Database Names
SELECT name
FROM sys.databases
--Table Names
SELECT name
FROM sys.tables
--Column Names
SELECT name
FROM sys.columns
That help?

Need to Query Tables that are dynamically created

We have an application that stores logged data in a Database called "ACManager" and Table called "Events_1".
When this table gets to a certain number of records the software creates another table called "Events_2". This continues as the data grows. I need to be able to query this data automatically as if it's all in one table without interference. Using a UNION will eventually create invalid querys when a new table is created dynamically by the application. Please also take into account performance.
So we need to Query as one table without UNION:
Select *
FROM ACManager.Events_1 , ACManager.Events_2 , ACManager.Events_xxxx(as needed)
Use dynamic sql. try this
DECLARE #query VARCHAR(MAX)
SET #query='Select *
FROM SELECT STUFF((SELECT '','' + name
from sys.tables where name like ''Events%''
FOR XML PATH('''')), 1, 1, '''') '
EXEC #query
In this script you create procedure. In body of the procedure uses dynamic sql which build sql statement and then run this statement
CREATE PROCEDURE dbo.getEvents
AS
DECLARE #dml nvarchar(max)
SELECT #dml = COALESCE(#dml + ' UNION ALL SELECT * FROM ', 'SELECT * FROM ')
+ QUOTENAME(s.name) + '.' + QUOTENAME(t.name)
FROM sys.schemas s INNER JOIN sys.tables t ON s.schema_id = t.schema_id
WHERE s.name = 'dbo' AND t.name LIKE 'event%'
--PRINT #dml
EXEC sp_executesql #dml
See demo on SQLFiddle