Procedure which creates table along with dates - sql-server

I want to create a dyamic procedure which accepts tablename, startdate and enddate
which goes like this `
alter procedure task_date #tablename nvarchar(max), #start as datetime,#end as datetime
as begin
declare #t1 as nvarchar(max)
declare #t2 as nvarchar(max)
set #t1='create table '+#tablename+'_'+#start+'(int id)'
set #t1='create table '+#tablename+'_'+#end+'(int id)'
exec(#t1)
exec(#t2)
end
`
If I pass parameters to my stored procedure like this
`exec task_date name,'21/2/2017','24/2/2016`'
Then it should create two tables as abc_21Feb2017 , abc_22Feb2017, abc_23Feb2017, abc_24Feb2017, Means difference between the dates also should be created as tables along with startdate and enddate with underscore seperation
Note: My code is wrong here, i want the proper code along with correct logic, help me out

You could try following solution:
DECLARE #StartDate DATE = '2017-02-22';
DECLARE #EndDate DATE = '2017-02-24';
DECLARE #SqlStatement NVARCHAR(MAX) = N'';
WITH Num10(Number)
AS (
SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL
SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10
),
Num100(Number)
AS (
SELECT (a.Number - 1)*10 + b.Number FROM Num10 a CROSS JOIN Num10 b
)
SELECT #SqlStatement = #SqlStatement
+ N'CREATE TABLE ' + N'dbo.CocoJamboTable' + N'_' + REPLACE(CONVERT(VARCHAR(25), DATEADD(DAY, n.Number - 1, '2017-02-22'), 106), ' ', '') + N'(INT ID)'
+ CHAR(13) + CHAR(10)
FROM Num100 n
WHERE n.Number <= DATEDIFF(DAY, #StartDate, #EndDate) + 1;
PRINT #SqlStatement
--EXEC sp_executesql #SqlStatement
This will generate and execute(--UNCOMMENT LAST LINE) following script:
CREATE TABLE dbo.CocoJamboStupidNameTable_22Feb2017(ID INT)
CREATE TABLE dbo.CocoJamboStupidNameTable_23Feb2017(ID INT)
CREATE TABLE dbo.CocoJamboStupidNameTable_24Feb2017(ID INT)
Edit 1:
[1] N10 and N100 CTEs returns all numbers between 1 and 10 or 1 and 100.
These CTEs could be replaced with a table with numbers. An example of such table can be found here.
[2] Main SELECT statement returns numbers (starting with 1) from N100 CTE or from number table.
[3] DATEDIFF(DAY, #StartDate, #EndDate) + 1 compute number of days between #StartDate and #EndDate including (+1) last one (#EndDate). For example, number of days between 2017-02-22 and 2017-02-24 isn't 2 (24 - 22) but 2 + 1 = 3 (if we take into account 22 but also 24).
[4] WHERE n.Number <= DATEDIFF(DAY, #StartDate, #EndDate) + 1; filter all numbers between 1 and number of days (= difference between start date and end date).
[5] SELECT DATEADD(DAY, n.Number - 1, '2017-02-22') compute all dates between start date and also end date: 2017-02-22, 23, 24.
[6] SELECT N'CREATE TABLE .... DATEADD(DAY, n.Number, ... will generate the SQL CREATE TABLE scripts for every date between start date and end date.

A simple while loop with a test on date might suffice
use sandbox
go
drop procedure p
go
CREATE procedure [dbo].[p]
#start date ,
#end date
as
declare #t nvarchar(max)
while #start <= #end
begin
set #t= concat('create table abc_',replace(cast(#start as varchar(10)),'-','_'),char(40), 'id int' ,char(41))
print #t
EXEC sp_executesql #t
set #start = dateadd(d,1,#start)
end
GO
exec dbo.p '2017-06-21','2017-06-25'
select table_name from information_schema.tables where table_name like 'abc%'
Result
table_name
--------------
abc_2017_06_21
abc_2017_06_22
abc_2017_06_23
abc_2017_06_24
abc_2017_06_25
Note the use of ascii characters for left and right parenthesis and the replace to remove illegal - in table name.

Related

T-SQL return every other character from string

How do you return every other character from a string from a specific starting position?
Example: starting at position 1
1050231
Result:
1521
Starting at position 2
1050231
Result:
003
Using a numbers table is usually the best way to avoid loops in SQL.
If you don't already have a numbers table, you should go read Jeff Moden's The "Numbers" or "Tally" Table: What it is and how it replaces a loop.
To create a numbers table, you can use the following script:
SELECT TOP 10000 IDENTITY(int,1,1) AS Number
INTO Numbers
FROM sys.objects s1
CROSS JOIN sys.objects s2
ALTER TABLE Numbers ADD CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number)
Now that you have a numbers table, you can use it to select the specific chars from your string like this:
DECLARE #s varchar(20) = '1050231',
#Start int = 1
SELECT Substring(#s, Number, 1)
FROM Numbers
WHERE Number >= #Start
AND (Number - #Start) % 2 = 0
AND Number <= DATALENGTH(#s)
Late answer, but here's yet another option
Example
Declare #S varchar(max) = '1050231'
Declare #P int =1
Select NewValue = (Select substring(substring(#S,#P,len(#S)),N,1)
From (Select Top (len(#S)-#P+1) N=Row_Number() Over (Order By (Select NULL)) From master..spt_values n1) A
Where N%2=1
For XML Path('')
)
Returns
NewValue
1521
One method uses a recursive CTE:
with cte as (
select #pos as pos, #str as str
union all
select pos + 2, str
from cte
where pos + 2 <= len(#str)
)
select substring(str, pos, 1)
from cte;
Here is a rextester.
The ugly way--a while loop, since Gordon gave the recursive CTE approach.
declare #string varchar(64) = 1050231
declare #start int = 1
declare #result varchar(64) = ''
set #result = #result + substring(#string,#start,1)
while #start < len(#string)
begin
set #start = #start + 2
select #result = #result + substring(#string,#start,1)
end
select #result
You could use STUFF:
declare #i VARCHAR(20) = '1050231';
select #i = IIF(LEN(#i) >= sub.s, STUFF(#i,sub.s,1,''),#i)
FROM(SELECT 1 s UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) sub;
-- any tally table
SELECT #i;
-- 003
declare #i VARCHAR(20) = '1050231';
select #i = IIF(LEN(#i) > sub.s, STUFF(#i,sub.s+1,1,''),#i)
FROM(SELECT 1 s UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) sub;
SELECT #i;
-- 1521
DBFiddle Demo

SQL Server T-SQL: automate re-occuring SQL reports with stored procedure

GOAL: create a stored procedure that automates this report, so that when execute NameOfStoredProc is run, it does all 3 blocks and returns the query in block 3.
For into table, I want it to be dynamic based on getdate().
(I did not post actual table elements and records, but if needed I can make up something b/c actual data is sensitive)
Database: FY1516
From table: v_all_claim (actually a view)
Into table: March2017_Payments
Here's the code that I execute manually to generate the report.
Block 1:
--creates payment table
SELECT Recipient_ID, DOP, Provider_ID, program_code, poverty_code
INTO FY1516..March2017_Payments
FROM FY1516..v_all_Claim
WHERE amount <> 0
AND DOP BETWEEN '20170301' AND '20170331'
Block 2:
-- add one column to the table created in block 1, sets default value to '' and update to Y
-- if certain constraints are met
ALTER TABLE FY1516..March2017_Payments
ADD TITLE21_Flag varchar(1);
GO
UPDATE FY1516..March2017_Payments
SET TITLE21_Flag = ''
GO
UPDATE FY1516..March2017_Payments
SET TITLE21_Flag = 'Y'
WHERE program_code IN ('A', 'B', 'C')
Block 3 with select statement that gets copied into Excel:
SELECT *
FROM FY1516..March2017_Payments
My attempts thus far:
#start and #end are for between #start and #end
#previousMonth gives first 3 letters of previous month
#previousMonthYear gives the YYYY of the previous month
Hoping #previousMonth +#previousMonthYear +"_Payments" can be the tablename
USE FY1516
CREATE PROCEDURE NameOfStoredProc
AS
DECLARE #start VARCHAR(8), #end VARCHAR(8),
#previousMonth VARCHAR(3), #previousMonthYear VARCHAR(4);
SET #start = CONVERT(VARCHAR(8), DATEADD(MONTH, DATEDIFF(MONHT, 0, GETDATE()) - 1, 0), 112)
SET #end = CONVERT(VARCHAR(8), DATEADD(MONTH, DATEDIFF(MONTH, -1, GETDATE()) - 1, -1), 112)
SET #previousMonth = LEFT(DATENAME(MONTH, DATEADD(MONTH, -1, GETDATE())), 3)
SET #previousMonthYear = YEAR(DATEADD(MONTH, -1, GETDATE()))
You can combine Block 1 and Block 2 into a single statement:
--creates payment table
Select Recipient_ID
, DOP
, Provider_ID
,program_code
,poverty_code
,TITLE21_Flag = CASE WHEN program_code IN ('A','B','C') THEN 'Y' ELSE '' END
INTO FY1516..March2017_Payments
FROM FY1516..v_all_Claim
WHERE amount <> 0 and DOP between '20170301' and '20170331'
Then, in your proc, you can use dynamic SQL to create your tables. Here's an example:
Create procedure NameOfStoredProc
AS
declare #start varchar(8)
, #end varchar(8)
,#previousMonth varchar(3)
,#previousMonthYear varchar(4);
set #start = convert(varchar(8),dateadd(month, datediff(month,0,getdate())-1,0),112)
set #end = convert(varchar(8),DATEADD(MONTH, DATEDIFF(MONTH, -1, GETDATE())-1, -1),112)
set #previousMonth = left(datename(month, dateadd(month,-1,getdate())), 3)
set #previousMonthYear = year(dateadd(month,-1,getdate()))
DECLARE #SQLString NVARCHAR(MAX) = 'CREATE TABLE ' + #previousMonth +#previousMonthYear +'_Payments (ColA int)'
EXECUTE sp_executesql #SQLString
You'll want to replace the (ColA int) with your actual column names and data types.
EDIT:
Here's an example that includes Block 1/2 into the stored proc. It checks the existence of the table first, and then runs the appropriate SELECT query.
CREATE PROCEDURE NameOfStoredProc
AS
begin
declare #start varchar(8)
, #end varchar(8)
,#previousMonth varchar(3)
,#previousMonthYear varchar(4);
set #start = convert(varchar(8),dateadd(month, datediff(month,0,getdate())-1,0),112)
set #end = convert(varchar(8),DATEADD(MONTH, DATEDIFF(MONTH, -1, GETDATE())-1, -1),112)
set #previousMonth = left(datename(month, dateadd(month,-1,getdate())), 3)
set #previousMonthYear = year(dateadd(month,-1,getdate()))
DECLARE #SQLString NVARCHAR(MAX) =
'IF OBJECT_ID('''+#previousMonth +#previousMonthYear +'_Payments'', ''U'') IS NOT NULL
BEGIN
print 1
INSERT INTO FY1516..'+ #previousMonth +#previousMonthYear +'_Payments
Select Recipient_ID
, DOP
, Provider_ID
,program_code
,poverty_code
,TITLE21_Flag = CASE WHEN program_code IN (''A'',''B'',''C'') THEN ''Y'' ELSE '''' END
FROM FY1516..v_all_Claim
WHERE amount <> 0 and DOP between ''20170301'' and ''20170331''
END
ELSE
BEGIN
print 2
Select Recipient_ID
, DOP
, Provider_ID
,program_code
,poverty_code
,TITLE21_Flag = CASE WHEN program_code IN (''A'',''B'',''C'') THEN ''Y'' ELSE '''' END
INTO FY1516..'+ #previousMonth +#previousMonthYear +'_Payments
FROM FY1516..v_all_Claim
WHERE amount <> 0 and DOP between ''20170301'' and ''20170331''
END
'
EXECUTE sp_executesql #SQLString
SET #SQLString = 'SELECT * FROM '+#previousMonth +#previousMonthYear +'_Payments'
EXECUTE sp_executesql #SQLString
END

SQL stored procedure: increment months from a starting date to an end date

I'm trying to create a stored procedure in SQL Server that basically increments the months from a given start date up to a given end date and updates them into a predefined table.
Problem:
Somewhat like this:
Exec MonthRunner #Start ='2014-01-01', #End = '2014-06-01'
Should give me a table like this:
Date
2014-01-01
2014-02-01
2015-03-01
2015-04-01
2015-05-01
2015-06-01
Approach:
A loop programmed using a cursor. Something similar to this:
Create Procedure MonthRunner(#Start date, #End date)
AS
DECLARE #Date date
DECLARE #getid CURSOR
SET #getid = CURSOR FOR
Select (#Start)
Set #getid
OPEN #getid
FETCH NEXT
FROM #getid into #Date
WHILE ##FETCH_STATUS = 0
BEGIN
SET #Date = (Select Dateadd(Month, 1, #Date))
DECLARE #sqlrun varchar(max)
SET #sqlrun= 'Update myTable' + 'Set Date=' + #Date
EXEC #sqlrun
fetch next
from #getid into #Date
END
CLOSE #getid
DEALLOCATE #getid
So far my result is:
Update myTable Set Date='2014-02-01'
But there is no loop and also I don't know how to terminate the loop using the variable #End.
Your help would be greatly appreciated!
Thanks in advance,
Clemens
This can easily be done with a recursive CTE:
;WITH cte AS (
SELECT #Start AS [Month]
UNION ALL
SELECT DATEADD(MONTH, 1, [Month])
FROM cte
WHERE [Month] < #End
)
SELECT [Month]
FROM cte
OPTION (MAXRECURSION 0)
I looped similar to this:
DECLARE #Start date
DECLARE #End date
DECLARE #counter date
set #counter = #Start
while #counter <= #End
begin
print 'The counter is ' + cast(#counter as char)
set #counter = (Select Dateadd(Month, 1, #counter))
end
what do you think about this solution? (of course I have to change the text)
increment months from a starting date to an end date in sp
ALTER PROCEDURE "dbo"."monthIncrementSp"
AS
DECLARE #startDate VARCHAR(50);
DECLARE #endDate VARCHAR(50);
SET #startDate = '01-01-2017';
SET #endDate = '31-12-2017';
SELECT TOP (DATEDIFF(MONTH, CONVERT(DATE, #startDate, 105),CONVERT(DATE, #endDate, 105))+1)
DATEADD(MONTH, ROW_NUMBER() OVER (ORDER BY name)-1, CONVERT(DATE, #startDate, 105))
FROM sys.all_columns
Here's a table-valued function to get the beginning of a month:
CREATE FUNCTION dbo.MonthBegin (
#date DATE = NULL
, #offset INT = 0
)
RETURNS TABLE
AS
RETURN (
SELECT D = DATEADD(m, DATEDIFF(m, 0, #date) + #offset, 0)
);
Alone, this function doesn't quite do what you need. Combine it with a set of integers though and you can have a lot of fun:
DECLARE #seedDate DATE = GETDATE();
SELECT *
FROM dbo.RangeInt(-100, 100) Nums
CROSS APPLY dbo.MonthBegin(#seedDate, Nums.N) Dates;
The above example uses a TVF to generate a set of numbers between -100 and 100 and then passes those numbers to the MonthBegin TVF (along with a seed date). You could also write this to be based off of a numbers table or CTE... whatever is most familiar/comfortable to you.

SQL select between dates, but show all when start and end date are empty

In SQL, I can use wildcard '%' on string and Select statement will show all the records like
select * from tablea where title like '%'
My question is what if both the start and end date are empty in the BETWEEN & AND function in Select statement.
Example
`select * from table where inputdate between '2014-01-01'` and '2014-01-31'
It works fine in this statement because there are start and end date.
What if there isn't any start and end date, and I want to show all the records?
select * from table where inputdate between '%' and '%' <--- not working.
Many thanks.
You can write your query in a way that inputdate column is evaluated against the value passed or if the no value is passed to #StartDate variable just pick earliest possible date,
I have select 1900-01-01 as the earliest date but depending on your column type you can pick a more appropriate substitute start and end dates.
DECLARE #StartDate DATE = '2014-01-01'
DECLARE #EndDate DATE = '2014-01-31'
select * from table
where inputdate >= COALESCE(#StartDate, '19000101')
and inputdate <= COALESCE(#EndDate , '99991231')
Dynamic SQL
Another better way of handling this kind of query with optional parameters will be using dynamic sql something like this...
DECLARE #StartDate DATE = '2014-01-01'
DECLARE #EndDate DATE = '2014-01-31'
DECLARE #Sql NVARCHAR(MAX);
SET #Sql = N'SELECT * FROM Table 1 = 1 '
+ CASE WHEN #StartDate IS NOT NULL THEN
N' AND inputdate >= #StartDate ' ELSE N' ' END
+ CASE WHEN #EndDate IS NOT NULL THEN
N' AND inputdate <= #EndDate ' ELSE N' ' END
Execute sp_executesql #Sql
,N'#StartDate DATE, #EndDate DATE'
,#StartDate
,#EndDate
wildcard can only be used on string, But since you mentioned the column is Date, you can do something better. Date min/max has been defined in almost all DBMS, IDK about the max but the min is usually January 1, 1753. so you can try between ' January 1, 1753' and 'date max'
SELECT * FROM table t
WHERE t.Date >= IIF(#FromDate IS NULL, t.Date, #FromDate)
AND t.Date <= IIF(#ToDate IS NULL, t.Date, #ToDate)
works fine.

sql stored procedure execute command

I've created the following stored procedure on sql:
CREATE PROCEDURE Sp_generate_report (#YEAR INT,
#MONTH INT)
AS
DECLARE #CATEGORY VARCHAR(20)
DECLARE #BEGIN_DATE DATE
DECLARE #END_DATE DATE
BEGIN
SELECT #BEGIN_DATE = Cast(( Cast(#YEAR AS VARCHAR) + '-'
+ Cast(#MONTH AS VARCHAR) + '-' + '1' ) AS DATE)
SELECT #END_DATE = Cast(( Cast(#YEAR AS VARCHAR) + '-' +
Cast(#MONTH AS VARCHAR) + '-' + '32' ) AS DATE)
SELECT TOP 1 #CATEGORY = Name
FROM dbo.Profitible_categories(#BEGIN_DATE, #END_DATE)
INSERT INTO dbo.MONTHLY_SUMMARY_REPORTS
VALUES (#CATEGORY)
END
The procedure was created successfully, but when I try to execute it with the following command:
EXECUTE SP_GENERATE_REPORT 2012, 7
I get this error message:
Msg 241, Level 16, State 1, Procedure SP_GENERATE_REPORT, Line 8
Conversion failed when converting date and/or time from character
string.
thanks in advance for the help.
You can calculate the last day of the month without all that nasty string concatenation or trying to guess which month it is and pick the last day.
CREATE PROCEDURE dbo.Sp_generate_report
#YEAR INT,
#MONTH INT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #BEGIN_DATE DATE = DATEADD(MONTH, #MONTH-1, CONVERT(DATE,
CONVERT(CHAR(4), YEAR) + '0101'));
DECLARE #END_DATE DATE = DATEADD(DAY, -1, DATEADD(MONTH, 1, #BEGIN_DATE));
INSERT INTO dbo.MONTHLY_SUMMARY_REPORTS
SELECT TOP (1) Name
FROM dbo.Profitible_categories(#BEGIN_DATE, #END_DATE);
END
GO

Resources