Need to Query Tables that are dynamically created - sql

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

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;

Invalid object name when iterating over all tables

I am trying to iterate over all the tables with a given schema name and make a copy in the same db with another given schema.
This is the script I am using:
use DoctorWho
declare #sql_query as nvarchar(max)
select #sql_query = concat('insert into doctor_generated.' , table_name , ' select * from ' , table_name , ';')
FROM INFORMATION_SCHEMA.tables
WHERE table_schema LIKE 'dbo%';
exec (#sql_query);
However this throws an error:
Invalid object name 'doctor_generated.tblEpisodeEnemy
Upon searching this error, I've refreshed the local cache & made sure I am using the correct db.
Is there anything I am missing?
I suspect what you actually want is something like this. Firstly use string aggregation for your dynamic statement; I assume you are on a fully supported version of SQL Server as you don't state you aren't. Next use QUOTENAME to properly quote your objects and avoid injection.
Then you can execute your dynamic statement:
DECLARE #SQL nvarchar(MAX),
#CRLF nchar(2) = NCHAR(13) + NCHAR(10);
SELECT #SQL = STRING_AGG(N'SELECT * INTO doctor_generated.' + QUOTENAME(t.name) + N' FROM ' + QUOTENAME(s.name) + N'.' + QUOTENAME(t.name) + N';',#CRLF)
FROM sys.schemas s
JOIN sys.tables t ON s.schema_id = t.schema_id
WHERE s.[name] = N'dbo';
--PRINT #SQL;
EXEC sys.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;

Dynamic SQL query to list all master table's data

I have N numbers of Master tables in my database. To see all the data in the master tables, currently I'm writing SELECT query for each table. So I like to write a sql query to list all my master table's data in my database.
Since we are using a same standard for all master tables, so all the master tables are ending with the suffix Master only. Like RoleMaster, UserMaster, UserRoleMaster, OfficeMaster, PincodeMaster and many.
What I have tried:
The below query, returns the SELECT query for all master table.
SELECT 'SELECT * FROM ' + name FROM sysobjects WHERE xtype = 'U' AND name LIKE '%Master'
It returns
SELECT * FROM RoleMaster
SELECT * FROM UserMaster
SELECT * FROM UserRoleMaster
SELECT * FROM OfficeMaster
SELECT * FROM PincodeMaster
...
...
Executing the below query always returns one table's data only. The PRINT statement returns SELECT * FROM RoleMaster only.
DECLARE #DynamicSql AS VARCHAR (MAX) = '';
SELECT #DynamicSql = 'SELECT * FROM ' + name FROM sysobjects WHERE xtype = 'U' AND name LIKE '%Master'
PRINT #DynamicSql
EXEC (#DynamicSql)
What is the issue, what I missed here, why its not list all master table's data.
You need to add the #DynamicSql variable before start of the dynamic query, like SELECT #DynamicSql = #DynamicSql + '
After QUOTENAME(T.name) you need to add a delimiter + '; ' to split the each line.
So the below query will works in your case:
DECLARE #DynamicSql AS NVARCHAR (MAX) = '';
SELECT #DynamicSql = #DynamicSql +
'SELECT * FROM ' + QUOTENAME(S.name) + '.' + QUOTENAME(T.name) + '; '
FROM sys.objects T
JOIN sys.schemas S ON S.schema_id = T.schema_id
WHERE T.[type] = 'U' AND T.name LIKE '%Master'
--PRINT #DynamicSql
EXEC (#DynamicSql)
Edit: Based on this comment, I have updated the answer.

Associate Database Name with Table List

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;