How to delete tables with specific names in SQL Server? - sql

Guru's please help me with this.
I have a master table called ABC, each month we create new table and suffix with month and year ABC_012010, ABC_022010.
I have to keep these tables for 7 years, and delete any table which are more than 7 years.
So the job would delete ABC_012010 in Feb 2017, ABC_022010 would be deleted in March 2017 and so on
The job runs every month to check and delete any table, which fits the criteria.

TRY THIS: You need dynamic query, loop and temporary tables to perform this operation and your table format must be in the same format as you have mentioned in your example. You can perform INSERT, UPDATE, DELETE, DROP, TRUNCATE in this way in multiple tables:
--Selecting table name in date format with `LEFT` `RIGHT` function
SELECT name,
CAST(CONCAT(LEFT(RIGHT(name, 6), 2), '-01-', RIGHT(RIGHT(name, 6), 4)) AS DATE) AS tableDate,
CAST(GETDATE() AS DATE) AS currentDate
INTO #tmp_date
FROM sys.objects
WHERE TYPE = 'u' AND NAME LIKE 'ABC[_]%'
--Storing table names that are older than 7 years from current date
SELECT id = IDENTITY(INT, 1, 1),
NAME
INTO #object_name FROM #tmp_date
WHERE (DATEDIFF(MM,tableDate, currentDate)-1) > = 84
DECLARE #i INT = 1, #j INT, #sql VARCHAR(500), #object_name VARCHAR(100)
SELECT #j = MAX(id) FROM #object_name
WHILE #i < = #j
BEGIN
SELECT #object_name = NAME FROM #object_name WHERE id = #i
SET #sql = 'DELETE FROM ' + #object_name --can use `DROP` to drop the table
EXEC (#sql)
SET #i = #i+1
END

try the following:
IF OBJECT_ID('TEMPDB.DBO.#TEMP_DEL', 'U') IS NOT NULL
DROP TABLE #TEMP_DEL
DECLARE #CYMO VARCHAR(8) = (SELECT '01' + RIGHT(REPLACE(CONVERT(CHAR(10), DATEADD(YY, -7, GETDATE()), 103), '/', ''), 6))
SELECT [NAME] TABLE_NAME INTO #TEMP_DEL FROM DBNAME.SYS.TABLES
WHERE [NAME] LIKE 'ABC%' AND '01'+RIGHT(NAME,6) < #CYMO
WHILE ((SELECT COUNT(1) FROM #TEMP_DEL) > 0)
BEGIN
DECLARE #TAB VARCHAR(100) = (SELECT TOP 1 TABLE_NAME FROM #TEMP_DEL)
DECLARE #CMD NVARCHAR(200) = 'DROP TABLE '+#TAB
EXEC SP_EXECUTESQL #CMD
DELETE #TEMP_DEL WHERE TABLE_NAME = #TAB
END
Replace DBNAME with your database name.
HTH!
Thanks.

You could use dynamic query, while and if conditionals to construct a stored procedure or scheduled job (as you like) to solve this issue. Try this.
CREATE PROCEDURE dbo.delete_abc #Date date, #years_diff int
AS
--- Declare variables to use
DECLARE #sqlCommand varchar(30)
DECLARE #table varchar(20)
DECLARE #month int
DECLARE #year int
--- Temporary table with 'ABC_'s table names
SELECT TABLE_NAME
INTO #Temp
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME LIKE 'ABC_%'
AND TABLE_TYPE='BASE TABLE' -- If you want only tables
---- While there are tables to compare
WHILE EXISTS(SELECT * FROM #Temp)
BEGIN
--- Extract Month and Year of the first row in 'ABC_'s table
SET #month = (SELECT TOP 1 SUBSTRING(TABLE_NAME,5,2) FROM #Temp)
SET #year = (SELECT TOP 1 SUBSTRING(TABLE_NAME,7,4) FROM #Temp)
--- Compare if date difference of the table is more than #years_diff years from #Date
IF DATEDIFF(YEAR, CAST(CAST(#year AS VARCHAR) + '-' + CAST(#month AS VARCHAR) + '-01' AS DATE), DATEADD(MONTH, DATEDIFF(MONTH, 0, #Date), 0)) > #years_diff
BEGIN
--- Construct and execute delete query
SET #table = (SELECT TOP 1 TABLE_NAME FROM #Temp)
SET #sqlCommand = 'DROP TABLE ' + #table --- or 'DELETE FROM ' if you only want to delete all rows from table
EXEC(#sqlCommand)
END
--- Delete first row of 'ABC_'s tables in temporary
DELETE TOP (1) FROM #Temp;
END
--- Drop temporary table
DROP TABLE #Temp
GO;
Once it's created, you can use this stored procedure like this or with the parameters you like:
DECLARE #Now date
SET #Now = GETDATE()
EXEC dbo.delete_abc #years_diff = 7, #Date = #Now

Wouldn't it be easy enough to make a delete query?
Delete From [Your_Table] Where [Date_Column] = 'ABC_012010'

Related

Declare a variable based on name in sys.tables then delete the table based on that variable in dynamic SQL

So what I expect is for the first piece of code to find the table name then if that table name exists and is more than 3 days old drop that table.
My issue with this code is that the code is not replacing #temp_name with the actual table DrinkSales. So the variable is not being correctly set in the select statement.
Current Code:
declare #table varchar(100) = 'DrinkSales'
DECLARE #temp_name VARCHAR(100)
declare #drop varchar(max) = '
DECLARE #temp_name VARCHAR(100)
select #temp_name= name
FROM sys.objects
WHERE DATEDIFF(day, create_date, getdate()) > 3
and name = '''+#table+'''
select #temp_name
--if object_id(''dbo.'+#table+''', ''U'') is not null -- needs to be changed to detect if variable is null rather than table.
--drop table dbo.'+#table+'
'
print(#drop)
exec(#drop)
So the result should be:
DECLARE #temp_name VARCHAR(100)
select #temp_name= name
FROM sys.objects
WHERE DATEDIFF(day, create_date, getdate()) > 3
and name = 'DrinkSales'
select #temp_name
--if object_id('dbo.DrinkSales', 'U') is not null -- this should be changed to
--drop table dbo.DrinkSales
*if #temp_name is not null *
*drop dbo.drinksales*
(1 row affected)
I think you were over-quoting - a common problem in dynamic SQL.
You can (and should) minimise the dynamic SQL required as follows:
declare #schema varchar(100) = 'dbo', #table varchar(100) = 'Proposal', #temp_name varchar(100);
if exists (
select 1
from sys.objects
where datediff(day, create_date, getdate()) > 3
and [name] = #table
and [schema_id] = schema_id(#schema)
)
begin
declare #drop varchar(max) = 'drop table ' + quotename(#schema) + '.' + quotename(#table) + ';';
print(#drop)
--exec(#drop)
end;
Its important to use quotename to protect against SQL injection.
Note also the addition of the schema as suggested by #David Browne.

Dynamic query based on datetime parameter

I have a SQL Server database with date representation name like below & each database contains table A (table A having column like id, datetime, value, value1 etc).
Jan2018
Feb2018
Mar2018 and so on..
My search condition is user selected date (eg. from 01-Jan-2018 to 01-Jun-2018) which I am passing to a stored procedure (max range 6 month). I want to generate dynamic query to get data from these database based on datetime passed.
How to achieve this functionality as I found difficult to implement.
Can you try this query
CREATE PROCEDURE Myproc #FromDate DATE,
#ToDate DATE
AS
BEGIN
DECLARE #SQL NVARCHAR(max)='',
#unionall VARCHAR(10)=''
WITH cte
AS (SELECT #FromDate dt,
1 mont
UNION ALL
SELECT Dateadd(month, 1, dt) dt,
mont + 1 mont
FROM cte
WHERE mont < Datediff(month, #FromDate, #ToDate)
)
SELECT #SQL += #unionall + '
select * from ['
+ LEFT (CONVERT(VARCHAR, Datename (month, dt )), 3)
+ CONVERT (VARCHAR, Year (dt))
+ '].[dbo].[tablename]',
#unionall = ' union all '
FROM cte
PRINT #SQL
EXECUTE( #SQL)
END
You should query sys.databases to find a database you need.
Then, as you can only use static declarations of databases, you should create a textual select statement and execute it.
I tried it on my dbs and it worked.
This is my code:
declare #date varchar(20) = '2018'
declare #dbName varchar(20)
declare #sSql varchar(200)
declare #sConditions varchar(20) = ''
Set #dbName = (SELECT name FROM master.sys.databases
where name like '%' + #date + '%')
print #dbName
Select #sSql = 'Select * From ' + #dbName + '.dbo.MyDB '
--+ ' Where ' + #sConditions
Execute (#sSql)
if you need to query all that fit year. do like this:
declare #date varchar(20) = 'a'
SELECT name Into #dbnames
FROM master.sys.databases
where name like '%' + #date + '%'
this brings a table of all sutable dbs. then query each one of them using loop. like cursor
Are you looking for
CREATE PROCEDURE MyProc
#FromDate DATE,
#ToDate DATE,
#Target SysName
AS
BEGIN
DECLARE #SQL NVARCHAR(MAX)= N'SELECT * FROM [' +
#Target +
'] WHERE [Dates] >= #FromDate AND [Dates] <= #ToDate';
EXECUTE sp_executesql #SQL,
N'#FromDate DATE, #ToDate DATE',
#FromDate,
#ToDate;
END
Demo
As I now understand what you are trying to do, you can
CREATE PROCEDURE ProcName
#FromDate DATE,
#ToDate DATE
AS
BEGIN
--Declare a variable to hold the Dynamic SQL
DECLARE #SQL NVARCHAR(MAX) = N'';
--Generate the databases names
WITH CTE AS
(
SELECT #FromDate D,
1 N
UNION ALL
SELECT DATEADD(Month, N, #FromDate),
N + 1
FROM CTE
WHERE N <= DATEDIFF(Month, #FromDate, #ToDate)
)
--Build the SELECT statement
SELECT #SQL = #SQL+
N'SELECT * FROM ['+
CONVERT(VARCHAR(3), D, 100)+
CAST(YEAR(D) AS VARCHAR(4))+
'].dbo.TableName UNION ALL ' --Or UNION as you want
FROM CTE;
--Remove the last UNION ALL
SET #SQL = LEFT(#SQL, LEN(#SQL) - 10); --If UNION then just -6
--Execute the statement
EXECUTE sp_executesql #SQL;
END

How to write a SQL DB Backup script with format: Demo_YYYYMMDD_XX.bak

how to write a daily backup script with unique name format: Demo_YYYYMMDD_XX
where XX is an incremental number based on the backup history.
Example as below.
1st Backup: Demo_20170704_01.bak
2nd Backup: Demo_20170704_02.bak
3rd Backup: Demo_20170704_03.bak
XX will increase +1 every time the backup process is run on the same day and same folder(path).
Managed to come out with the script below. Any improvement needed? thanks
DECLARE #path VARCHAR(500)
DECLARE #name VARCHAR(500)
DECLARE #pathwithname VARCHAR(500)
DECLARE #time DATETIME
DECLARE #year VARCHAR(4)
DECLARE #month VARCHAR(2)
DECLARE #day VARCHAR(2)
DECLARE #counter varchar(10)
SET #path = 'C:\Backup Path\'
SELECT #time = GETDATE()
SELECT #year = (SELECT CONVERT(VARCHAR(4), DATEPART(yy, #time)))
SELECT #month = (SELECT CONVERT(VARCHAR(2), FORMAT(DATEPART(mm,#time),'00')))
SELECT #day = (SELECT CONVERT(VARCHAR(2), FORMAT(DATEPART(dd,#time),'00')))
SELECT #counter = (
select format(count(A.database_name),'000')
from msdb.dbo.backupset A join msdb.dbo.backupmediafamily B
on A.media_set_id = B.media_set_id
where A.database_name = 'Demo'
and (select convert(date,A.backup_start_date, 120)) = (select convert(date,getdate(), 120))
)
SELECT #name ='Demo' + '_' + #year + #month + #day + '_' + #counter
SET #pathwithname = #path + #name + '.bak'
BACKUP DATABASE Demo
TO DISK = #pathwithname WITH NOFORMAT, NOINIT, SKIP, REWIND, NOUNLOAD, STATS = 10
You have two choices.
list files in directory that match your pattern (date), count and increment
keep a count of how many backups you make each day (increment)
Since you have a database and are backing up that database, why not just do the latter? And record your daily backups to a table that has,
create table backups (
dbback DATE NOT NULL DEFAULT GETDATE()
,dbname varchar(99) NOT NULL
,name varchar(99) NOT NULL
,index int NOT NULL
,primary key(db,backdb)
) ;
You can record the pair (db,backdb) to record entries for multiple databases (dbname, dbback).
WITH t as ( select (backups.index+1) as nextnum
from backups where dbname = 'database_name' and dbback = CAST(GETDATE() AS DATE)
union select 1 as nextnum from dual )
insert into backups (dbback,dbname,name,index)
values (CAST(GETDATE() AS DATE),'database_name','yourname',t.nextnum) ;
Note that by using union you ensure that you get the base (first) row in the temporary table

Selecting multiple database tables from between datetime

I want to select a database table between the range of the date selected from the datepicker on my web, my sample database table names are:
output_11FEB2016
output_13FEB2016
output_15FEB2016
output_21FEB2016
I want to select tables to show and show their contents on my web, here is my current codes from what I understand on my research.
ALTER PROCEDURE [dbo].[gen048_mLIST]
-- Add the parameters for the stored procedure here
#gfromDate varchar(10),
#gtoDate varchar(10)
AS
SET NOCOUNT ON
declare #sql varchar(5000)
set #sql='select * output_'
if(#gfromDate<>'' and #gtoDate<>'')
begin
set #sql=#sql+'between '+convert(datetime,'''+#gfromDate+''')+' and '+convert(datetime,'''+#gtoDate+''')+' '
--print #sql
exec(#sql)
-- [dbo].[gen048_mLIST] '2-16-2016','2-18-2016'
END
Sorry for my messed up codes and explanation, I appreciate those who can help me figure out my problem.
You will have to select the rows from multiple tables,UNION it and display it in your application.I've used hardcoded dates to generate the SQL but you can modify/extend this to suit your requirements.
declare #gfromDate varchar(10) = '11/02/2016'
declare #gtoDate varchar(10) = '24/02/2016'
declare #fromDate datetime
declare #toDate datetime
declare #totaldays int
set #fromDate = (select convert (date, #gfromDate, 104))
set #toDate = (select convert (date, #gtoDate, 104))
-- get total number of days between from and to dates
set #totaldays = (select datediff(day,#fromdate,#toDate))
declare #sql varchar(max) = ''
declare #tablename varchar(20)
declare #counter int = 1
-- generate the sql to get data from the tables within a date range
while #counter < #totaldays
begin
set #tablename = (select convert(varchar(11), #fromDate, 106))
set #tablename = replace(#tablename,' ','')
-- check if table exists
--if object_id(#tablename, 'U') is not null
--begin
set #sql = #sql + 'select * from output_' + #tablename
if(#counter < #totaldays-1)
begin
set #sql = #sql + ' union '
end
set #fromDate = dateadd(day,1,#fromDate)
set #counter = #counter + 1
--end
end
print #sql
SQL Generated
select * from output_11Feb2016 union
select * from output_12Feb2016 union
select * from output_13Feb2016 union
select * from output_14Feb2016 union
select * from output_15Feb2016 union
select * from output_16Feb2016 union
select * from output_17Feb2016 union
select * from output_18Feb2016 union
select * from output_19Feb2016 union
select * from output_20Feb2016 union
select * from output_21Feb2016 union
select * from output_22Feb2016

Using variable name to run query on multiple tables

what I am trying to do is run a query multiple times over multiple tables, so what I have here is a table of the table names that cycles through setting #tablename to the name of the table on each iteration that I want to run the query on.
As you can see below #tablename is the name of the table I want to run the queries on but how do i run these queries using #tablename as the table name?
CREATE TABLE [BusinessListings].[dbo].[temptablenames]
(id int,
name nvarchar(50),
)
INSERT INTO [BusinessListings].[dbo].[temptablenames] (id, name)
VALUES
(1,'MongoOrganisationsACT1'),
(2,'MongoOrganisationsNSW1'),
(3,'MongoOrganisationsNT1'),
(4,'MongoOrganisationsQLD1'),
(5,'MongoOrganisationsSA1'),
(6,'MongoOrganisationsTAS1'),
(7,'MongoOrganisationsVIC1'),
(8,'MongoOrganisationsWA1');
DECLARE #tablename sysname,
#id int
SET #id = 1
WHILE (#id < 9)
BEGIN
select #tablename = name from temptablenames where id = #id
select #tablename
select _key_out, sum(quality_score) as sumscore, count(*) as reccount, (sum(quality_score) / count(*)) as ave
into tempga0
from #tablename
group by _key_out
select _key_out, count(*) as reccount
into tempga3
from #tablename
where dedupe_result is null
group by _key_out
having count(*)>1
select a._key_out, max(quality_score) as maxdedupetotalscore
into tempga4
from
#tablename a
join
tempga3 b
on a._key_out = B._key_out
--where isdeleted is null
group by a._key_out
--- keep records
update #tablename
set dedupe_result = 'Keep'
from
#tablename a
join
tempga4 b
on a._key_out = B._key_out
where a.quality_score = b.maxdedupetotalscore
--and isdeleted is null
and dedupe_result is null
SET #id = #id + 1
END
GO
DROP TABLE [BusinessListings].[dbo].[temptablenames]
note: this is only part of the queries that I want run, I just want to figure out how to subsitute the variable in the query as the table name. Also I know this isnt good form but there is a reason I need to do it this way.
updated working code here:
DECLARE #tablename nvarchar(30),
#id int,
#SQLStr nvarchar(1000)
SET #id = 1
WHILE (#id < 9)
BEGIN
select #tablename = name from temptablenames where id = #id
IF OBJECT_ID('tempga0') IS NOT NULL
DROP TABLE tempga0
set #SQLStr = 'select _key_out, sum(quality_score) as sumscore, count(*) as reccount, (sum(quality_score) / count(*)) as ave
into tempga0
from ' + #tablename + ' group by _key_out'
exec(#SQLStr)
SET #id = #id + 1
END
GO
Use the Exec command. Write your query in a variable like and execute it
Declare #SQLStr = 'Select * into X from ' + #tablename
exec(#SQLStr)
You just have to be carefull. I see that you are using into statements. You will have to check that the table does not already exist because you will get an exception. You will need to drop the tables, or a better way would be to do this before you start your loop:
CREATE TABLE tempga0 (
_key_out int,
sumscore numeric(18,9),
reccount int,
ave numeric(18,9))
--rest of the tables to be created here...
Create all the tables, and when you start your While loop add a
WHILE (#id < 9)
BEGIN
TRUNCATE TABLE tempga0
--truncate the rest of the tables
--Do the rest of your stuff here
END
Hope it helps