For a particular season and year when it started I have to get the following season and year when it starts, e.g. for Summer 2021 I should get Autumn 2021, and for Winter 2021 I should get Spring 2022.
"Seasons" table has the "Order" column which indicates the order of seasons in a year, so it looks like this:
SeasonID
Order
Name
1
1
Spring
2
2
Summer
3
3
Autumn
4
4
Winter
Remark: "Order" column may seem redundant, but this is actually simplified/adapted version of the problem I have, where "Order" column is neccesary.
I have a stored procedure that has #Year and #SeasonID as input parameters. I have to get following season/year in #FollowingSeasonID and #FollowingYear parameters, which I use later in the stored procedure. I'm not sure if I've used the best technique for that:
(...)
DECLARE #FollowingSeasonID int;
DECLARE #FollowingYear int;
if object_id('tempdb..#Years') is not null drop table #Years
CREATE TABLE #Years ([Year] int)
INSERT INTO #Years ([Year]) VALUES (#Year), (#Year + 1)
SELECT #FollowingSeasonID = FollowingSeasonID, #FollowingYear = FollowingYear
FROM
(SELECT SeasonID,
[Year],
LEAD(SeasonID) OVER (ORDER BY [Year], Order) AS FollowingSeasonID,
LEAD([Year]) OVER (ORDER BY [Year], Order) AS FollowingYear
FROM Seasons
CROSS JOIN #Years) t
WHERE SeasonID = #SeasonID AND [Year] = #Year
(...)
Is there a better way to do that? Could it be achieved in just one query?
I need those values in multiple stored procedures/views/... so I wanted to extract that part of code if a function. Scalar valued function can't return two values, so I have to create a table valued function (and I have to use table variable instead of temp table #Years). However, is there a better way to do that instead of having a table valued function that always returns just one row?
You can wrap this code in UDF and you should be able to get seasonId, year
DECLARE #season table(SeasonId int, SeasonOrder int, Name varchar(10))
INSERT INTo #season values
(1, 1 ,'Spring')
,(2, 2 ,'Summer')
,(3, 3 ,'Autumn')
,(4, 4 ,'Winter');
DECLARE #FollowingSeasonID int;
DECLARE #FollowingYear int;
DECLARE #year int = 2021, #SeasonId int = 2;
;WITH CTE_YearSeason AS
(
SELECT y,s.* FROM #season as s
CROSS APPLY
(VALUES (#year), (#year+1)
) as t(y)
), cte_ranking as
(
SELECT *, row_number() over (order by y,SeasonOrder) as rnk
FROM CTE_YearSeason)
SELECT SeasonId, Y as year
FROM cte_ranking as c
where c.rnk = (SELECT c1.rnk
from cte_ranking as c1
where c1.SeasonId = #SeasonId and c1.y = #year) +1
SeasonId
year
3
2021
Unless I've misunderstood, given a single seasonId you just need the following Id and reset it to 1 and increment the year if necessary?
declare #seasonid int=1, #Year int=2021
select FollowingSeasonID, #Year + yr FollowingYear
from (
select *,
IsNull(Lead(SeasonId) over(order by [order]),1) FollowingSeasonID,
case when Lead(SeasonId) over(order by [order]) is null then 1 else 0 end yr
from seasons
)s
where SeasonId=#SeasonId
If you don't want to use a TVF you could just use this as a view/cte, joining on currentId and return the followingId and yr value to add to your current year.
What I want to do is insert a range of dates into multiple rows for customerID=1. I have and insert for dbo.Customer(Dates), specifying my that I want to insert a record into the Dates column for my Customer table, right? I am getting error:
Cannot insert the value NULL into column 'CustomerId', table 'dbo.Customers'
Sorry if I am way off track here. I have looked at similar threads to find out what I am missing, but I'm not piecing this together. I am thinking it wants to overwrite the existing customer ID as NULL, but I am unsure why exactly since I'm specifying dbo.Customer(Dates) and not the existing customerID for that record.
declare #date_Start datetime = '03/01/2011'
declare #date_End datetime = '10/30/2011'
declare #date datetime = #date_Start
while #date <= #date_End
begin
insert into dbo.Customer(Dates) select #date
if DATEPART(dd,#date) = 0
set #date = DATEADD(dd, -1, DATEADD(mm,1,#date))
else
set #date = DATEADD(dd,1,#date)
end
select * from dbo.Customer
The primary key is customerId, but you are not inserting a value.
My guess is that you declared it as a primary key with something like this:
customerId int primary key,
You want it to be an identity column, so the database assigns a value:
customerId int identity(1, 1) primary key
Then, you don't need to assign a value into the column when you insert a new row -- the database does it for you.
Your Customer table has a column named CustomerId and which column is NOT Nullable so you have to provide that column value as well. If your column type is Int try the bellow code:
declare #date_Start datetime = '03/01/2011'
declare #date_End datetime = '10/30/2011'
declare #date datetime = #date_Start
DECLARE #cusId INT
SET #cusId = 1
while #date <= #date_End
begin
insert into dbo.Customer(CustomerId, Dates) select #cusId, #date
if DATEPART(dd,#date) = 0
set #date = DATEADD(dd, -1, DATEADD(mm,1,#date))
else
set #date = DATEADD(dd,1,#date)
SET #cusId = #cusId + 1;
end
select * from dbo.Customer
thank you for the feedback. I think I'm scrapping this and going to go with creating a separate table to JOIN. Not sure why I didn't start doing that before
I am trying to create a stored procedure that does manipulation of parameter passed in before inserting it into my table. One of the columns in my table is called DATE_CHANGED and basically what I gg to do here is to change a passed date parameter like December 1st 2017 to 20171201. This is an int value.
I wrote a stored procedure like this:
CREATE PROCEDURE date_generate
#startDate DATE
AS
BEGIN
DECLARE #DATE_KEY INT
#DATE_KEY = CONVERT(INT, FORMAT(#startDate, 'YYYYMMDD')
INSERT INTO table date_key = #DATE_KEY
END
However I get an error
Incorrect syntax near '#DATE_KEY
Are local variable declared only used for SQL query statement like
select *
from table
where date_key = #DATE_Key?
There is more than one error.
Use SET to assign values to a variable.
Have a look at INSERT statement too.
CREATE PROCEDURE date_generate
#startDate date
AS
BEGIN
DECLARE #DATE_KEY int;
SET #DATE_KEY = CONVERT(int, format(#startDate, 'YYYYMMDD'));
INSERT INTO DATE_CHANGED (date_key)
VALUES (#DATE_KEY);
END
This seems really strange. You don't even need a local variable. Based on your code, you could write:
create procedure date_generate (
#startDate date
) as
begin
insert into table (date_key)
values ( convert(int, format(#startDate, 'YYYYMMDD')) );
end; -- date_generate
Or, I might write:
create procedure date_generate (
#startDate date
) as
begin
insert into table (date_key)
values ( year(#startDate) * 10000 + month(#startDate) * 100 + day(#startDate) );
end;
Why you would have a table with a single date on each row doesn't really make sense to me. Why you would be storing that "date" as an integer also doesn't make sense.
As far as I've understood, your stored procedure accepts a DATE as a parameter, but you need to do an INSERT with an INT.
You can easily convert a DATE to a VARCHAR and then to a INT, this way:
DECLARE #DateASInt INT = CAST(CONVERT(VARCHAR(8), #startDate, 112) AS INT);
So, your stored procedure will be like this:
CREATE PROCEDURE date_generate
#startDate date
AS
BEGIN
INSERT INTO date_key
VALUES (CAST(CONVERT(VARCHAR(8), #startDate, 112) AS INT));
END
Problem Illustration
I am trying to find that magical query to generate summary information. I have mapped my problem into fictitious illustration. I have 'WaterLeakage%' table which records leakage occurred in hotel rooms over several year.
I have another table which records WaterConsumption in liters for each table.
Now i have to find actual water leakage in liters for given room number over given date range.
Basically i have to group several rows in 'WaterLeakage%' table to several rows in 'WaterConsumption' table. I am trying to figure out magical efficient query to find this. Unable to find it, please help.
DECLARE #START_DATE_PARAM DATE = '01/10/2017';
DECLARE #END_DATE_PARAM DATE = '01/31/2017';
DECLARE #ROOM_NUMBER INT = 101;
IF (EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '#WATER_CONSUMPTION'))
DROP TABLE #WATER_CONSUMPTION;
IF (EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '#WATER_LEAKAGE_PER'))
DROP TABLE #WATER_LEAKAGE_PER;
--Table for daily daily water consumption per room
CREATE TABLE #WATER_CONSUMPTION(
ROOM_NUMBER INT,
UDAY DATE,
WATER_CONSUMPTION_LITER INT
)
--Table for water leakage percent per room for date range
CREATE TABLE #WATER_LEAKAGE_PER
(
ROOM_NUMBER INT,
START_DATE DATE,
END_DATE DATE,
WATER_LEAKAGE_PERCENT INT
)
-- Raw Data
INSERT INTO #WATER_LEAKAGE_PER(ROOM_NUMBER,START_DATE,END_DATE,WATER_LEAKAGE_PERCENT)
VALUES(101,'2017/01/01','2017/01/02',5),
(102,'2017/01/01','2017/01/05',10),
(101,'2017/01/04','2017/02/06',10);
-- Raw Data
INSERT INTO #WATER_CONSUMPTION
VALUES(101,'2017/01/01',100),
(101,'2017/01/02',100),
(101,'2017/01/03',100),
(101,'2017/01/04',100),
(101,'2017/01/05',100),
(101,'2017/01/06',100),
(102,'2017/01/01',100),
(102,'2017/01/02',100),
(102,'2017/01/03',100),
(102,'2017/01/04',100),
(102,'2017/01/05',100);
DECLARE #TotalLeak REAL = 0;
SELECT * FROM #WATER_CONSUMPTION;
SELECT * FROM #WATER_LEAKAGE_PER;
SELECT * FROM #WATER_CONSUMPTION T1 JOIN (SELECT * FROM #WATER_LEAKAGE_PER WHERE ROOM_NUMBER=#ROOM_NUMBER) T2
ON (T1.ROOM_NUMBER=T2.ROOM_NUMBER AND T1.UDAY >= T2.START_DATE AND T1.UDAY <= T2.END_DATE);
DROP TABLE #WATER_CONSUMPTION;
DROP TABLE #WATER_LEAKAGE_PER;
I am very close to solution now. Basically i changed my thinking. I will join reverse now.
BEGIN
--Input Parameters for calculating water wastage between date range
DECLARE #START_DATE_PARAM DATE = '01/10/2017';
DECLARE #END_DATE_PARAM DATE = '01/31/2017';
--Table for daily daily water consumption per room
CREATE TABLE #WATER_CONSUMPTION(
ROOM_NUMBER INT,
UDAY DATE,
WATER_CONSUMPTION_LITER INT
)
--Table for water leakage percent per room for date range
CREATE TABLE #WATER_LEAKAGE_PER
(
ROOM_NUMBER INT,
START_DATE DATE,
END_DATE DATE,
WATER_LEAKAGE_PERCENT INT,
LEAKAGE_PER_DAY_IN_LITER INT
)
-- Leakage in liter per room for each day, This will have multiple entries for room and date if room number and date is available in multiple date ranges, ex. in #WATER_CONSUMPTION table for room number 101 we have multiple entries with overlapping dates
CREATE TABLE #DAY_WISE_LEAKAGE
(
ROOM_NUMBER INT,
LDATE DATE,
LEAKAGE_IN_LITER INT
)
-- Raw Data
INSERT INTO #WATER_LEAKAGE_PER(ROOM_NUMBER,START_DATE,END_DATE,WATER_LEAKAGE_PERCENT)
VALUES(101,'2017/01/15','2017/01/18',30),
(102,'2017/01/15','2017/01/18',10),
(101,'2017/01/15','2017/02/13',5);
-- Raw Data
INSERT INTO #WATER_CONSUMPTION
VALUES(101,'01/01/2017',1001),
(101,'01/02/2017',1001),
(101,'01/03/2017',1001),
(101,'01/04/2017',1001),
(101,'01/05/2017',1001),
(101,'01/06/2017',1001),
(101,'01/07/2017',1001),
(101,'01/08/2017',1001),
(101,'01/09/2017',1001),
(101,'01/10/2017',1001),
(101,'01/11/2017',1001),
(101,'01/12/2017',1001),
(101,'01/13/2017',1001),
(101,'01/14/2017',1001),
(101,'01/15/2017',1001),
(101,'01/16/2017',1001),
(101,'01/17/2017',1001),
(101,'01/18/2017',1001),
(101,'01/19/2017',1001),
(101,'01/20/2017',1001),
(101,'01/21/2017',1001),
(101,'01/22/2017',1001),
(101,'01/23/2017',1001),
(101,'01/24/2017',1001),
(101,'01/25/2017',1001),
(101,'01/26/2017',1001),
(101,'01/27/2017',1001),
(101,'01/28/2017',1001),
(101,'01/29/2017',1001),
(101,'01/30/2017',1001),
(101,'01/31/2017',1001);
DECLARE #ROOM_NUMBER INT
DECLARE #START_DATE DATE
DECLARE #END_DATE DATE
DECLARE #WATER_LEAKAGE_PERCENT INT
-- cursor for calculating water wastage pre date range per day available in #WATER_LEAKAGE_PER table
DECLARE WATER_LEAKAGE_PER_CURSOR CURSOR FOR
SELECT ROOM_NUMBER,START_DATE,END_DATE,WATER_LEAKAGE_PERCENT FROM #WATER_LEAKAGE_PER
OPEN WATER_LEAKAGE_PER_CURSOR
FETCH NEXT FROM WATER_LEAKAGE_PER_CURSOR
INTO #ROOM_NUMBER, #START_DATE ,#END_DATE, #WATER_LEAKAGE_PERCENT
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #TOTAL_WATER_USED_FOR_DATE_RANGE INT=0;
DECLARE #NUMBER_OF_DAYS INT=0;
DECLARE #LEAKAGE_PER_DAY_IN_LITER INT=0;
-- Total Liters of water used for 1 date range
SELECT #TOTAL_WATER_USED_FOR_DATE_RANGE =SUM(WATER_CONSUMPTION_LITER),#NUMBER_OF_DAYS=COUNT(1) FROM #WATER_CONSUMPTION WHERE ROOM_NUMBER=#ROOM_NUMBER AND UDAY BETWEEN #START_DATE AND #END_DATE;
-- Liters of water leakage per day for selevted date range in cursor
SELECT #LEAKAGE_PER_DAY_IN_LITER=((#TOTAL_WATER_USED_FOR_DATE_RANGE*#WATER_LEAKAGE_PERCENT)/100)/#NUMBER_OF_DAYS;
UPDATE #WATER_LEAKAGE_PER SET LEAKAGE_PER_DAY_IN_LITER = #LEAKAGE_PER_DAY_IN_LITER WHERE ROOM_NUMBER=#ROOM_NUMBER AND START_DATE = #START_DATE AND END_DATE=#END_DATE AND WATER_LEAKAGE_PERCENT=#WATER_LEAKAGE_PERCENT;
-- generate dates and water leakage, this will be used for actual calculation of water leakage in date range.
;WITH n AS
(
SELECT TOP (DATEDIFF(DAY, #START_DATE, #END_DATE) + 1)
n = ROW_NUMBER() OVER (ORDER BY [object_id])
FROM sys.all_objects
)
INSERT INTO #DAY_WISE_LEAKAGE SELECT #ROOM_NUMBER, DATEADD(DAY, n-1, #START_DATE),#LEAKAGE_PER_DAY_IN_LITER
FROM n;
FETCH NEXT FROM WATER_LEAKAGE_PER_CURSOR
INTO #ROOM_NUMBER, #START_DATE ,#END_DATE, #WATER_LEAKAGE_PERCENT
END
CLOSE WATER_LEAKAGE_PER_CURSOR;
DEALLOCATE WATER_LEAKAGE_PER_CURSOR;
-- Average of Liters of water leakage per Room number.
SELECT ROOM_NUMBER,SUM(LEAKAGE_IN_LITER) FROM #DAY_WISE_LEAKAGE WHERE LDATE BETWEEN #START_DATE_PARAM AND #END_DATE_PARAM GROUP BY ROOM_NUMBER;
DROP TABLE #WATER_CONSUMPTION;
DROP TABLE #WATER_LEAKAGE_PER;
DROP TABLE #DAY_WISE_LEAKAGE
END
How to create a stored procedure in SQL Server 2012 that returns a list of courses running between 2 months?
I've written code something like this:
create procedure final_RTrainerqualification
(#TrainerID char(10),
#Coursecode char(4) OUTPUT,
#qualcode nvarchar(30),
#coursedate datetime output)
DECLARE #MinDate DATE = '20160401',
#MaxDate DATE = '20160601';
SELECT coursedate
FROM dbo.RTrainerqualification
WHERE coursedate >= #MinDate
AND coursedate < #MaxDate;`
It should be returning the list of courses that run between these 2 dates mentioned but I am new to stored procedure so my question is how do I assign coursedates to the courses and make it return the list?
Edit- used T-SQL to recreate the code
#Coursecount smallint
declare #Coursedatebeg datetime, #Coursedateend datetime, #CourseCode char(4),#TrainerID char(10);
select #Coursedatebeg = '2015-04-20'
select #Coursedateend = '2015-06-20';
while #Coursedatebeg <= #Coursedateend
begin
select #CourseCode = #Coursedateend;
select #Coursecount = count(*) from RCourseInstance
where CourseCode between 'R222' and 'R224';
if #CourseCount <> 0
begin
Print 'Courses running between April and June 2015 ' ;
select Coursedate,CourseCode from RCourseInstance as t
inner join Coursedate as d on t.Coursedate = d.Coursedate
inner join CourseCode as c on c.CourseCode = t.CourseCode
where CourseCode between 'R222' and 'R224';
end
else
print 'No courses are running between these dates ' ;
set #Coursedatebeg = #Coursedatebeg + 2;
end
It is returning the print statement but also declaring that invalid object name Coursedate
what have I done incorrectly here?
Firstly, use ISO date strings to avoid regional settings issues. You are only returning the coursedate in the SELECT. If you want further information such as the coursecode then add it into the SELECT. i.e.
DECLARE #MinDate DATE = '2016-04-01',
#MaxDate DATE = '2016-06-01'
SELECT coursedate, coursecode, *
FROM dbo.RTrainerqualification
WHERE coursedate >= #MinDate
AND coursedate < #MaxDate;
The OUTPUT variables are not required - these are needed when you want to return a single value rather than a list.
This is fundamental SQL stuff so I would strongly recommend you do some reading on this. There is a simple stored procedure tutorial here to start off with.