Selecting multiple schemas in a select statement - sql

The db being accessed is on Snowflake; not certain on the storage details behind the scenes.
I have a query right now that creates a new view from 41 data tables stored in separate schemas under the same database, looks something like this:
CREATE VIEW all_data AS
SELECT * FROM db.schema1.data UNION ALL
SELECT * FROM db.schema2.data UNION ALL
SELECT * FROM db.schema3.data
This query is run daily. My issue is I get new data tables added every few days and I have to go manually edit the query to include those new tables, as they're stored under separate schemas (and the naming scheme for the schemas isn't consistent either, for reasons outside my control). Is there a way I can select all the schemas inside a database with a subquery that would allow me to run the query daily without needing manual updates when new schemas + tables are added?
I'd like the resulting query to have a structure somewhat like
CREATE VIEW all_data as
SELECT * FROM [SELECT schemas from db].data
but not sure how that would work, and how to union the resulting data correctly.

Unfortunately, in Snowflake you can't dynamically construct SQL statements (yet). You can of course do what you want to achieve via a script in one of the supported languages (e.g. Python, JS), by first finding all the schemas and then constructing a full SQL statement.
Hope this helps.

You can definitely query the table and schema list available. SQL Authority has a good article on it:
http://blog.sqlauthority.com/2009/06/17/sql-server-list-schema-name-and-table-name-for-database/
In short the query winds up being along these lines to pull the list of tables and schema:
SELECT '['+SCHEMA_NAME(schema_id)+'].['+name+']'
AS SchemaTable
FROM sys.tables
Though you will have to add a database name to the where clause to point to the proper DB.

With the release of Snowflake Scripting dynamic recreation of the view inside Snowflake is now very possible.
create database dynamic_views;
create schema dynamic_views.schema_base;
create schema dynamic_views.schema1;
create table dynamic_views.schema1.data(id int) as select * from values (1);
We can use the INFORMATION_SCEMA.TABLES to find all DATA tables:
SELECT table_schema
FROM dynamic_views.information_schema.tables
WHERE table_name = 'DATA';
TABLE_SCHEMA
SCHEMA1
and now push that into a cursor and build up a view creation SQL
This SQL needs to be run in the new Snowsight Console see (Working with Classic Console):
declare
sql text;
add_union boolean := false;
c1 cursor for SELECT TABLE_SCHEMA
FROM dynamic_views.information_schema.TABLES
WHERE TABLE_NAME = 'DATA';
begin
sql := 'CREATE OR REPLACE VIEW dynamic_views.schema_base.all_data AS ';
for record in c1 do
if (add_union) then
sql := sql || 'UNION ALL ';
end if;
sql := sql || 'SELECT * FROM dynamic_views.'|| record.TABLE_SCHEMA ||'.data ';
add_union := true;
end for;
EXECUTE IMMEDIATE sql;
return sql;
end;
;
and we can use it:
select * from dynamic_views.schema_base.all_data;
ID
1
and add more:
create schema dynamic_views.schema2;
create table dynamic_views.schema2.data(id int) as select * from values (2);
rebuild:
anonymous block
CREATE OR REPLACE VIEW dynamic_views.schema_base.all_data AS SELECT * FROM dynamic_views.SCHEMA1.data UNION ALL SELECT * FROM dynamic_views.SCHEMA2.data
use it again:
select * from dynamic_views.schema_base.all_data;
ID
1
2
Note: You should not use SELECT * in production as the order of the table columns will be dependent of the create orders, and if newer tables have a different shape you view will become invalid.
So the explicit form really should be used:
'SELECT column1, column2, column4 FROM dynamic_views.'|| record.TABLE_SCHEMA ||'.data ';

for anyone who want the solution for this question this is my Idea with useing FETCH
Declare #str nvarchar(maX)
Declare #i int
Set #i =(Select max(id ) from Clinics );
set #str='';
declare #Id int
declare cur CURSOR LOCAL for
select [Id] from [dbo].[Clinics]
GROUP BY [Id]
open cur
fetch next from cur into #Id
while ##FETCH_STATUS = 0 BEGIN
if #i>#id
begin
set #str=#str+ 'sELECT '+ LTRIM(RTRIM(Convert(varchar(6),#Id))) + ',* fROM ' + quotename(LTRIM(RTRIM(CONVERT(VARCHAR(8),#Id))))+'.[Clinic_Benefits] UNION ALL ';
end
else
begin
set #str=#str+ 'sELECT '+ LTRIM(RTRIM(Convert(varchar(6),#Id))) + ',* fROM ' + quotename(LTRIM(RTRIM(CONVERT(VARCHAR(8),#Id))))+'.[Clinic_Benefits] ';
end
fetch next from cur into #Id
END
close cur
deallocate cur
print #str;
exec (#str);

Related

How delete rows using a variable in SQL Server?

Hope you can help me with the issue below.
DETAILS: I am trying to create a stored procedure for my Azure Data Factory project where I can pass a variable table name through to tell SQL Server to delete rows.
This all is part of an ERP tool with transactional data. I would delete rows that are in the target, but changed in the source.
Example: rowid 1 - €100 in the target but changes in the source later to €80. Then I need to delete that row with €100 in the target and copy the new value from the source with the same rowid to the target
I'm migrating from a Oracle environment to a Microsoft environment. In Oracle I've this SQL for the stored procedure:
create or replace procedure AAAAA_delete
as
cursor c_del
is
select 'delete from ' ||table_name ||' a where not exists (select 1 from ' ||replace(table_name,'AAAAA','BBBBB') ||' b where a.rowid = b.rowid)' deletions
from all_tables
where table_name like 'AAAAA%'
and table_name not like 'AAAAA_LOAD%'
and table_name not in ('AAAAA_TIME','AAAAA_CONFIGURATION')
order by 1;
begin
for r_del in c_del
loop
execute immediate r_del.deletions;
commit;
end loop;
end;
** the names have been changed to AAAA and BBBB on purpose to publish the code **
I'm wondering how i can transform this into MS SQL.
WHY?: My whole ETL process in Azure Data Factory is based on variables. All the table names etc are stored in a metadata table. And it depends on +- 70 tables.
QUESTION: Is it possible to create a generic stored procedure to drop rows depending on the name I pass through.
Yes your code can be adapted for MS SQL;
create procedure AAAAA_delete AS
DECLARE #VARA NVARCHAR(100);
DECLARE #EXEC NVARCHAR(1000);
DECLARE CURSER CURSOR
LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR
SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
where table_name like 'AAAAA%'
and table_name not like 'AAAAA_LOAD%'
and table_name not in ('AAAAA_TIME','AAAAA_CONFIGURATION')
order by 1
OPEN CURSER
FETCH NEXT FROM CURSER INTO #VARA
WHILE ##FETCH_STATUS = 0
BEGIN
SET #EXEC = N'delete from ' +#VARA +' a where not exists (select 1 from ' + replace(#VARA,'AAAAA','BBBBB') +' b where a.rowid = b.rowid)'
EXEC(#EXEC)
FETCH NEXT FROM CURSER INTO #VARA
END
CLOSE CURSER
DEALLOCATE CURSER

Select on multiple tables which are a result of an other select

I need to create a function which searches all the tables that contain a certain field
select distinct(table_name) from information_schema.columns where column_name='fieldNeeded';
and then, to do a query on each table that is found:
select * from table_name where ... <parameters> ;
Is there a way to do so?
If you use SQL-server...
You can use the result of the first query is input for your cursor (a for-loop in T-SQL) in where you dynamically create and execute the query. Note 'dynamic sql' is creating an sql-command in string (varchar) format and then run that string as sql-commant (something like: EXEC #SqlCommand), although there are other (better) ways to execute dynamic sql.
To finish things up nicely, you can start with creating a temp-table and insert the result in the temp-table. And in each iteration of the cursor you can store the result in the temp-table
In pseudo-code it would look something like:
Create #TempTable with some columns
Create Cursor for:
select distinct(table_name)
from information_schema.columns
where column_name='fieldNeeded'
For each element in the cursor (fetch next #table_name from the cursor)
DECLARE #SqlCommand VARCHAR(250) =
'Insert Into #TempTable select * from ' + #table_name + 'where ... <parameters>'
PRINT #SqlCommand -- can be useful to check the code you created
EXEC #SqlCommand
End of cursor
SELECT * FROM #TempTable

SQL request over multiple databases and tables

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.

Search databases for table with values

I am using SQL Server 2008. My question is, is it possible, given a table name, to construct a query that returns a list of databases that contain that table WITH values (meaning it is not empty)?
For example, I have a table called tbl_Name. I have 100 databases, and in 90 of them tbl_Name is empty. Can I get a list of the 10 databases where tbl_Name has values?
You can do this with a cursor, and the stored procedure sp_msforeachdb.
sp_msforeachdb is, as the name suggests, a proc that runs something for each database. You can use this to list all db's with a given table name:
sp_msforeachdb 'SELECT "?" AS db from [?].sys.tables WHERE name = ''tbl_Name'''
Inserting those records into a temp table makes it easy to iterate over in a cursor:
DROP TABLE #db_List
DROP TABLE #Not_Empty
GO
CREATE TABLE #db_List (db VARCHAR(MAX))
CREATE TABLE #Not_Empty (db VARCHAR(MAX))
GO
sp_msforeachdb 'INSERT INTO #db_List SELECT "?" AS db from [?].sys.tables WHERE name = ''tbl_Name'''
GO
SET NOCOUNT ON
DECLARE #Iterator varchar(MAX)
,#strSQL varchar(MAX)
DECLARE xyz CURSOR
FOR
--Select stuff to iterate over
SELECT *
FROM #db_List
OPEN xyz
FETCH NEXT FROM xyz
INTO #Iterator
WHILE ##FETCH_STATUS = 0
BEGIN
--Do stuff
SET #strSQL = 'INSERT INTO #Not_Empty
SELECT '''+#Iterator+'''
WHERE EXISTS (SELECT TOP 1 * FROM '+#Iterator+'..tbl_Name)
'
Exec (#strSQL)
FETCH NEXT FROM xyz
INTO #Iterator
END
CLOSE xyz
DEALLOCATE xyz
GO
SET NOCOUNT OFF
Quick summary:
Start in master DB select from sys.databases, get all the DB's, cursor thru each one (USE the DB). Use EXEC( sqlStatement ) to perform your command etc...
Now that you're in the DB (Each DB has a table named sys.objects, so your goal from the above paragraph is to simply issue a command like EXEC(USE dbName) ) list through the list of tables, select from sys.objects, look for object types of 'U' (user table). Get the name, construct your SELECT * FROM INTO #yourTemp WHERE blah blah blah insert your results in a #temp table you created at the very beginning.. Issue the SQL via EXEC()

How can I truncate all tables from a MySQL Database?

Is there any way to truncate all tables from a specific MySQL database name without using any other language than SQL? I mean no linux shell scripts. (why? because it will run on windows, MacOSX and linux servers).
the problem is that the client its selecting the database name from a list in a control panel webpage (wich will be displaying MySQL databases from different servers *nix and windows), and then he will want to truncate all the tables inside that database (yes that is the main task of the web form).
Alex
Ok, I solved it by myself here is the stored procedure :)
BEGIN
DECLARE done BOOLEAN DEFAULT FALSE;
DECLARE truncatestmnt TEXT; -- this is where the truncate statement will be retrieved from cursor
-- This is the magic query that will bring all the table names from the database
DECLARE c1 CURSOR FOR SELECT Concat('TRUNCATE TABLE ', TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES WHERE INFORMATION_SCHEMA.TABLES.TABLE_SCHEMA = "#DatabaseName";
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = TRUE;
OPEN c1;
c1_loop: LOOP
FETCH c1 INTO truncatestmnt;
IF `done` THEN LEAVE c1_loop; END IF;
SET #x = truncatestmnt;
PREPARE stm1 FROM #x;
EXECUTE stm1;
END LOOP c1_loop;
CLOSE c1;
END
What I am making its calling all tables from the given database, this will help if the tables inside the given database have no pattern to follow.
So by calling DECLARE c1 CURSOR FOR SELECT Concat('TRUNCATE TABLE ', TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES WHERE INFORMATION_SCHEMA.TABLES.TABLE_SCHEMA = "#DatabaseName"; and saving results into a cursor I can fetch all the TRUNCATE TABLE x statements generated by the "n" quantity of tables inside the given database, then by just preparing and executing each statement in the cursor it will truncate all the tables inside the given database.
BTW #DatabaseName must be given as parameter to the stored procedure
Hope this helps someone else too :)
Alex
create procedure drop_tables_like(pattern varchar(255), db varchar(255))
begin
select #str_sql:=concat('drop table ', group_concat(table_name))
from information_schema.tables
where table_schema=db and table_name like pattern;
prepare stmt from #str_sql;
execute stmt;
drop prepare stmt;
end
then call
call drop_tables_like('%', 'dababase_name')