Many table to union by loop function - sql-server

I have 1000 tables and I need to union all tables
table_name like feature_20220503, feature_20220504 but it's not continuous numbers
It escape holiday
I want to use a loop function, but I don't know how to do it.
Does anyone could help.

You just need to make dynamic query like this
DECLARE #QUERY NVARCHAR(MAX) = ''
SELECT #QUERY = #QUERY+'UNION ALL SELECT * FROM '+TABLE_NAME+CHAR(13)
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE='BASE TABLE'
AND TABLE_NAME LIKE 'feature_%'
SET #QUERY = SUBSTRING(#QUERY,11,LEN(#QUERY))
INSERT INTO NewTable
EXEC(#QUERY)
Just Create new new table with the same schema as all the other tables and make sure you take only those tables from INFORMATION_SCHEMA

Related

Query a list of columns and tables from different databases in SQL Server

I have multiple databases and multiple tables in each one, how do I query a list that contains a column name, table name, and database name that matches with a key word in the column name?
Example: look for the database, table and column that has ID in the column name
WHERE column_name LIKE '%ID%'
master.sys.sp_MSforeachdb procedure executes the given command in all databases. Using that you can get columns of all databases. Question mark will be replace in every step with current database name. You can use ? everywhere in your command.
CREATE TABLE #Columns
(
database_name sysname,
table_name sysname,
column_name sysname
);
EXEC master.sys.sp_MSforeachdb '
USE [?]
INSERT #Columns (database_name, table_name, column_name)
SELECT DB_NAME(), OBJECT_NAME(c.object_id), c.name
FROM sys.columns AS c
WHERE c.name LIKE ''%ID%'''
SELECT * FROM #Columns AS c

Deleting specific rows from all tables

I have a database with a lot of tables.
In each table there is a column named "LicenseID" (bigint). How can I delete all rows, from all tables, that contain the value "2" in the column "LicenseID" ?
Best regards!
You didn't mentioned which database are you working with.
I assume it is SQL Server
First check your result with following query
SELECT * FROM table WHERE LicenseID LIKE '%2%'
or
SELECT * FROM table WHERE Contains(LicenseID, 2) > 0
Then Delete all those rows with above condition.
DELETE FROM table WHERE LicenseID LIKE '%2%'
or
DELETE FROM table WHERE Contains(LicenseID, 2) > 0
I have not tested it and don't know what type of database are you using. Answer can be little different in different databases.
You can use INFORMATION_SCHEMA.COLUMNS to generate the delete statements dynamically.
Something like this should do the trick:
DECLARE #Sql varchar(max) = ''
SELECT #Sql = #Sql + 'DELETE FROM '+ TABLE_NAME +' WHERE LicenseID = 2; '
FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME = 'LicenseID'
EXEC(#Sql)
The reason I've used INFORMATION_SCHEMA.COLUMNS and not INFORMATION_SCHEMA.TABLES is to prevent an error in case there is a table that doesn't contain the LicenseID column.

Need to drop a list of tables and should be made configurable in SQL SERVER

As a part of the new release, occasionally we take backups of few tables and then delete it over a stabilisation period.
I want to make it configurable, that is have a naming convention set up for the list of tables you want to delete and then drop it after the stabilisation period is over
Here is my plan.
Rename all the tables which I want to delete, with a suffix say "_TBR" (To be Removed).
Add a suffix with yyyymmdd format, eg. if the name of the table is EmployeeDetails, it will be EmployeeDetails_TBR_20150614.
Thus, I will create a stored procedure, that will select all the tables which has a suffix of TBR and place it into a temp table, along with date time
eg
select
name AS 'TableName',
convert (datetime, substring (name, len(name)-7,8)) as 'DeletedOn'
from
sys.tables
where
name like '%_tbr_%' and
type ='u'
now
SELECT TableName FROM #TempTable WHERE DeletedOn <=GETDATE()
This will select all the tables that are beyond the specified date.
Now, declare a cursor, and then loop in and delete it.
I will schedule a job to run this stored procedure every week, and it will take care of.
Is there any other smarter and optimized way of doing it?
One method:
CREATE PROC dbo.DropBackupTables
AS
DECLARE #DropScript nvarchar(MAX) = (
SELECT N'DROP TABLE ' + QUOTENAME(OBJECT_SCHEMA_NAME(object_id)) + N'.' + QUOTENAME(name) + N';'
FROM sys.tables
WHERE
name LIKE N'%[_]TBR[_][2][0-9][0-9][0-9][0-1][0-9][0-3][0-9]'
AND RIGHT(name, 8) <= CONVERT(char(8), GETDATE(), 112)
FOR XML PATH('')
);
EXEC sp_executesql #DropScript;
GO
Seems to me like you are over complicating things.
Instead of renaming the tables and using cursors, I would simply add another table to the database, containing the names of the tables that needs to be dropped, and the dates of the drop.
Then I would use a stored procedure to create a dynamic sql to actually drop these tables.
Something like this should do the trick:
CREATE TABLE TablesToDrop
(
Table_Name sysname PRIMARY KEY,
Delete_Date date
)
CREATE PROCEDURE DropTables
AS
BEGIN
DECLARE #Sql varchar(max) = ''
SELECT #Sql = #Sql + 'DROP TABLE '+ Table_Name +';'
FROM TablesToDrop
WHERE Delete_Date <= GETDATE()
EXEC(#Sql)
END
Note #1: be very careful about who is going to insert records to this table.
Note #2: the last table to be dropped is the TablesToDrop table...

SQL Query Delete rows from a table

I have a table #temp. The data in #temp are table names in a database. I wish to only show the table names of which the table has data. How can I do this without using dynamic SQL?
My sample data is as below:
create TABLE #temp (Table_Name VARCHAR(50))
insert into #temp values ('#temp1')
,('#temp2')
,('#temp3')
,('#temp4')
create TABLE #temp1 (Col1 int)
insert into #temp1 values (1)
,(3)
,(4)
create TABLE #temp2 (Col1 int)
insert into #temp2 values (7)
,(9)
,(6)
create TABLE #temp3 (Col1 int)
create TABLE #temp4 (Col1 int)
I manually delete the blank tables, How to do this using a query for numerous blank tables?
DELETE FROM #temp
WHERE Table_Name = '#temp3'
or Table_Name = '#temp4'
This is the result I want
select * from #temp
-- It only shows the two table names which are not blank
DROP TABLE #temp
DROP TABLE #temp1
DROP TABLE #temp2
DROP TABLE #temp3
DROP TABLE #temp4
This is my old query for this question:
DECLARE #TABLE_NAME VARCHAR(50), #COMMAND VARCHAR(500), #COUNT INT, #COUNTT INT
DECLARE #CountResults TABLE (CountReturned INT)
create TABLE #TABLE_NAME (TABLE_NAME VARCHAR(50))
SELECT #COUNTT= COUNT(*) FROM #temp
WHILE #COUNTT > 0
BEGIN
SELECT TOP 1 #TABLE_NAME = Table_Name FROM #temp
SET #COMMAND = 'SELECT COUNT(*) FROM ' + #TABLE_NAME
INSERT #CountResults EXEC (#COMMAND)
SET #Count = (SELECT * FROM #CountResults)
BEGIN TRANSACTION
DELETE #CountResults
ROLLBACK TRANSACTION
IF(#Count > 0)
BEGIN
INSERT INTO #TABLE_NAME VALUES (#TABLE_NAME)
END
DELETE FROM #temp WHERE Table_Name = #TABLE_NAME
SELECT #COUNTT= COUNT(*) FROM #temp
END
SELECT * FROM #TABLE_NAME
I don't know of any way to determine whether or not a table is empty without querying that table, which in your case means dynamic SQL. Your comments make it sound like you're okay with this but are looking for a way to do this more concisely than using a loop. Here's a (limited) possibility:
declare #sql nvarchar(max);
select #sql =
-- coalesce() ensures that UNION ALL is inserted before every SELECT but the first.
coalesce(#sql + N' union all ', N'') +
-- Select each table name. Note that SQL Server allows table names that contain
-- single quotes. In this case (or in the case of plain old bad/malicious data in
-- #temp), we need to make sure those characters are enclosed within the string
-- literal we're building.
N'select ''' + replace(table_name, N'''', N'''''') +
-- Use EXISTS to make sure there are one or more records in the table.
N''' where exists (select 1 from ' + quotename(table_name) + N')'
from #temp;
exec sp_executesql #sql;
This will build and execute a query that looks like this:
select '#temp1' where exists (select 1 from [#temp1])
union all
select '#temp2' where exists (select 1 from [#temp2])
union all
select '#temp3' where exists (select 1 from [#temp3])
union all
select '#temp4' where exists (select 1 from [#temp4])
This approach has a few limitations that you should be aware of:
The query will fail if #temp contains any string which is not the name of a table or view. Normally I'd suggest mitigating this by using object_id() or querying INFORMATION_SCHEMA.TABLES, but the fact that you've loaded #temp with the names of other temp tables complicates matters.
The query will also fail if #temp contains a table name that explicitly names the table schema, e.g. dbo.Stuff, because quotename() will render it as [dbo.Stuff] rather than [dbo].[Stuff]. But if you omit quotename(), you run the risk of incorrect and/or damaging behavior if a table_name contains spaces or other problematic characters.
In short, if you just want something for personal use and are okay with making certain assumptions about the data in #temp, then something like the above ought to work. But if you want something that will work correctly and safely under any circumstances, then it's going to take some doing, enough so that even if you could avoid using some kind of a loop, doing so is unlikely to make things any less complicated.
I have a method that does not use dynamic sql. It uses the sysindexes table, which according to Microsoft is subject to change at their whim. So this may not be a good candidate for a production system. But it could be a good place to start. This is also a bit easier if your source table is not a temp table, since temp tables have actual names that do not match the name used to create them.
This script worked for me on SQL Server 2008 r2.
-- drop table #MyTempTable;
Create table #MyTempTable(Table_Name varchar(50));
insert #MyTempTable values ('#MyTempTable2');
insert #MyTempTable values ('#MyTempTable3');
insert #MyTempTable values ('#MyTempTable4');
Create table #MyTempTable2 (Col1 int);
insert #MyTempTable2 values (1);
Create table #MyTempTable4 (Col1 int);
Create table #MyTempTable3 (Col1 int);
SELECT *
FROM #MyTempTable M1
JOIN tempdb.sys.tables T ON T.name LIKE (M1.Table_Name + '%')
JOIN [tempdb].[dbo].[sysindexes] S ON S.id = T.object_id
WHERE S.rowcnt > 0
It's not an ideal solution, but it satisfies your requirements. If you play around with it in your environment, it might give you some insight into a better way to achieve your larger goals. good luck.
EDIT: sysindexes will have one entry per index on the table. Or in the case of my example, for the heap (with no index.) So if your base tables have multiple indexes, you will need to modify the query a bit. Maybe change the JOIN and WHERE clause to a WHERE EXISTS SELECT * FROM [tempdb].[dbo].[sysindexes] S WHERE S.id = T.object_id AND S.rowcnt > 0 Play with it and you should be able to get where you were asking.
EDIT 2: Replacing sys.tables with sysobjects.
SELECT *
FROM #MyTempTable M1
JOIN [tempdb].[dbo].[sysobjects] O ON O.name LIKE (M1.Table_Name + '%')
JOIN [tempdb].[dbo].[sysindexes] S ON S.id = O.id
WHERE S.rowcnt > 0
Based on DeadZone's Query, the following works for non temp tables:
SELECT DISTINCT Table_Name
INTO #TABLE_NAME
FROM #Temp M1
JOIN [dbo].[sysobjects] O ON O.name LIKE (M1.Table_Name + '%')
JOIN [dbo].[sysindexes] S ON S.id = O.id
WHERE S.rowcnt > 0

In SQL Server, is there a way to avoid using a Cursor?

I have a table where each record has a Table_Name (name of a table). I then use a Cursor to select all table names related to some record in to a Cursor. Then I do a WHILE for each table name in the Cursor to do some job.
I want to know if it's possible to solve this problem without using a Cursor.
DECLARE tables_cursor CURSOR FAST_FORWARD FOR SELECT Table_Name FROM Some_Table WHERE ...
FETCH NEXT FROM tables_cursor INTO #Dynamic_Table_Name
WHILE ##FETCH_STATUS = 0
BEGIN
...
END
Foreach table name in the cursor I do a dynamic SQL query like this:
SELECT #sql = '
UPDATE dbo.' + #Dynamic_Table_Name + '
SET ...'
EXEC sp_executesql #sql, #params, ...
My question is this: Is it possible to avoid using Cursor to solve this problem?
Unfortunately the design of having table name to reference a table can't be changed, of which I would have done immediately if I could.
yes, you can solve this problem without using cursor. Instead you need to introduce the new table which stores the table name from actual table along with auto generated id column.
Check out the below sample query
declare #test table (id int identity,tableName varchar(20))
insert into #test
select 'abc' union all
select '123' union all
select '345' union all
select 'sdf' union all
select 'uhyi'
instead above query, you can use your query to populate the table variable
insert into #test
SELECT Table_Name FROM Some_Table WHERE ...
And
--select * from #test
declare #cnt int
declare #incr int
select #cnt = count(id) from #test
set #incr = 1
while (#incr <= #cnt)
begin
select tableName from #test where id = #incr
set #incr =#incr + 1
end
Yes, you could avoid the cursor, but you can't avoid the dynamic queries.
You could possibly make a query that returns all the dynamic queries concatenated together as a single string. That way you could execute them all without using a loop, but that's not really any better...
If you can't change the database design, you are stuck with dynamic queries.
Well, you can hide the use of a cursor by using the (undocumented, but widely-used) MS stored procedure sp_MSforeachdb (Google has lots of examples); but that uses a cursor internally, so if it's a philosophical objection then that doesn't really help.
I don't think there can be a set-based way to do this kind of thing, since each table probably has a different relational structure.

Resources