Populate value for new column based on other columns - sql-server

I have a table in SQL Server 2016, It has 3 columns, (Id,Month,Year) now I want to add a new column to the existing table and its value should be populated based on the Month and Year column's value for existing records and even for new records which are inserted later.
Expected output:
Id Month Year Del_Range
------------------------------------
1 January 2020 2020-01-01
2 February 2020 2020-02-01
3 March 2020 2020-03-01
Query:
CREATE TABLE tempdb..#test
(
Id int,
[Month] varchar(50),
[Year] int,
)
GO
INSERT INTO tempdb..#test (Id, [Month], [Year])
VALUES (1, 'January', 2020),
(2, 'February', 2020),
(3, 'March', '2020')
GO
SELECT * FROM tempdb..#test
GO
ALTER TABLE tempdb..#test
ADD Del_Range DATE
-- Need to add logic to populate value for this column based on value from Month and Year to get expected output

try this below query
alter table #test add Del_Range as convert(date, concat([year], [month], id))
insert into #test(id, month, year)
select 4, 'april', 2020
union
select 4, 'May', 2020

You can try this command.
alter table #test add Del_Range as cast(cast(Year as varchar(4)) + Month + '01' as date)--for first day for a month

Related

Dates Overlapping cases for Leave System

I'm making date overlapping cases in SQL Server where I want to check a person applying for leave should get to know if 2 or more persons have already applied for leave for the those dates. Now the catch is, suppose 2 people have already taken leave as:
Person 1: 1st Feb 2020 to 2nd Feb 2020
Person 2: 1st Feb 2020 to 5th Feb 2020
Now the person 3 applying leave for 1st Feb 2020 to 5th Feb 2020 should get the leave for 3rd Feb 2020 to 5th Feb 2020.
There can be much more cases to it. How to achieve it?
So far I have made a stored procedure which returns the total no is leave applied during a period.
Eg: if my table have a record for 1st Feb to 1st Feb it will return the total no of people who have applied to those specific dates. It returns null leave applied for 1st Feb to 2nd Feb.
Stored Procedure:
ALTER procedure [dbo].[check_leave_application](
#from_date date,
#to_date date
)
as
begin
declare #total_leaves_applied int
declare #id_count int
if #from_date = #to_date
begin
select #total_leaves_applied=count(id) from leaveRequest where current_status in (0,1) and
((cast(FromDate as date) >= #from_date and (cast(FromDate as date)<=#to_date))
or (cast(ToDate as date) between #from_date and #to_date))
end
select #total_leaves_applied as total_leaves_applied
end
Table Structure:
id FromDate ToDate request_date current_status
3 2020-01-22 2020-01-23 2020-01-22 15:41:07.943 0
3 2020-01-22 2020-01-23 2020-01-22 16:01:54.787 0
I'm not sure if this is what you're looking for, but it might be a good starting point. Using Common Table Expressions (CTE), SQL Server allows you to generate a sequence of data.
You can use this to generate a table of dates and then join your Employee Leave table to it, allowing you to COUNT how many employees took leave for a given day. Knowing that number each day, you then simply take the MAX to find out if any day has been booked more than twice:
-- Just creating a temp table to simulate your data
SELECT *
INTO #EmployeeLeave
FROM (
SELECT 1 AS id, '2020-01-22' AS FromDate, '2020-01-23' AS ToDate, '2020-01-22 15:41:07.943' AS request_date, 0 AS current_status UNION ALL
SELECT 2 AS id, '2020-01-22' AS FromDate, '2020-01-23' AS ToDate, '2020-01-22 16:01:54.787' AS request_date, 0 AS current_status UNION ALL
SELECT 3 AS id, '2020-01-03' AS FromDate, '2020-01-07' AS ToDate, '2020-01-03 12:00:00.000' AS request_date, 0 AS current_status UNION ALL
SELECT 4 AS id, '2020-01-05' AS FromDate, '2020-01-09' AS ToDate, '2020-01-03 12:00:00.000' AS request_date, 0 AS current_status UNION ALL
SELECT 5 AS id, '2020-01-06' AS FromDate, '2020-01-06' AS ToDate, '2020-01-03 12:00:00.000' AS request_date, 0 AS current_status UNION ALL
SELECT 6 AS id, '2020-01-01' AS FromDate, '2020-01-02' AS ToDate, '2020-01-03 12:00:00.000' AS request_date, 0 AS current_status UNION ALL
SELECT 7 AS id, '2020-01-01' AS FromDate, '2020-01-01' AS ToDate, '2020-01-03 12:00:00.000' AS request_date, 0 AS current_status
) A
-- Try book leave from 1st to 3rd of January
DECLARE #FromDate DATE = '2020-01-01'
DECLARE #ToDate DATE = '2020-01-03';
-- Generate a Dates table
WITH Dates AS (
SELECT [Date] = #FromDate
UNION ALL
SELECT [Date] = DATEADD(DAY, 1, [Date])
FROM Dates
WHERE [Date] < #ToDate
)
-- Use dates table to get the maximum number of people that have booked leave on a particular day within #FromDate and #ToDate
SELECT
MAX(EmployeesBookedLeave) AS HighestBookedDayInInterval
FROM (
-- For each day, get number of Leave records that have been joined
SELECT
Date, COUNT(*) AS EmployeesBookedLeave
FROM Dates D
JOIN #EmployeeLeave E
-- join Leave records that contain each Date
ON D.Date BETWEEN E.FromDate AND E.ToDate
GROUP BY Date
) A
-- Clean up
DROP TABLE #EmployeeLeave
Now if HighestBookedDayInInterval is > 2, the employee cannot book that day.

How to dynamically allocate all months based to a column on condition in SQL Server?

I have a table in which I am storing the Product Name and it's Renewal Date along with the payment plan (Monthly/Quarterly/Yearly). Now if the payment plan of the product is Yearly or Monthly it will display the get the month of Renewal and show the Rate/Amount against that month but if the payment plan is Monthly it should display the Rate/Amount in front of each month as shown below.
For example if a product named ABC has payment plan of Yearly, subscription rate of 276 and Renewal Date 2019-12-01 and there is another product XYZ with payment plan of Monthly, subscription rate of 17 and Renewal Date 2019-08-15 then the result set I want should something like this
ProductName RenewalMonth Rate
------------------------------------
ABC December 276
XYZ January 17
XYZ February 17
XYZ March 17
XYZ April 17
XYZ May 17
XYZ June 17
XYZ July 17
XYZ August 17
XYZ September 17
XYZ October 17
XYZ November 17
XYZ December 17
Here is the query which I have wrote which is returning data that's present in the database fine but not the months other than December for Product XYZ. Keeping in mind this should only display same rate for all other months provided in the desired dates where Payment Plan is 'Monthly', for other payment plans it should show rate in front of given month as present in the database.
Sample data is as follows:
CREATE TABLE ItemDefinition
(
ID INT NOT NULL,
ProductName VARCHAR(50),
PaymentPlan VARCHAR(50),
RenewalDate DATETIME,
UnitRate NUMERIC(18, 0)
);
INSERT INTO ItemDefinition
VALUES (1, 'ABC', 'Yearly', '2019-12-01', 276),
(1, 'XYZ', 'Monthly', '2019-08-15', 17);
And the query I am writing is
SELECT
ProductName, SUM(UnitRate) Rate,
DATENAME(MONTH , DATEADD(MONTH , MONTH(RenewalDate)-1 , '1900-01-01')) RenewalMonth
FROM
ItemDefinition
WHERE
MONTH(RenewalDate) IS NOT NULL
AND RenewalDate BETWEEN #dtStart AND #dtEnd
GROUP BY
ProductName, MONTH(RenewalDate)
ORDER BY
MONTH(RenewalDate)
It might be something like this:
DECLARE #DateBeg DATE = '2019-01-01'
,#DateEnd DAte = '2020-12-01';
WITH Ranges AS
(
SELECT *
,#DateBeg AS [DateBeg]
,#DateEnd AS [DateEnd]
FROM ItemDefinition DS
)
SELECT *
,DATENAME(MONTH ,ISNULL([GeneratedDate], [RenewalDate])) AS RenewalMonth
FROM Ranges
OUTER APPLY
(
SELECT DATEADD(MONTH, [number], [DateBeg])
FROM
(
select number
from master.dbo.spt_values
where [type] = 'P'
) numbers
WHERE DATEADD(MONTH, [number], [DateBeg]) < [DateEnd]
AND [PaymentPlan] = 'Monthly'
) AutoDates ([GeneratedDate]);
You can change the DateEnd parameter to something less and you will see how less months are generated.
The idea is to have start and end date for each row and depending on it to generate your months.
To get the records for the years use the following:
WITH Ranges AS
(
SELECT *
,#DateBeg AS [DateBeg]
,#DateEnd AS [DateEnd]
FROM ItemDefinition DS
)
SELECT *
,DATENAME(MONTH ,ISNULL([GeneratedDate], [RenewalDate])) AS RenewalMonth
,IIF([PaymentPlan] = 'Monthly', [UnitRate], IIF(CONVERT(VARCHAR(7), [RenewalDate], 121) = CONVERT(VARCHAR(7), [GeneratedDate], 121), [UnitRate], NULL))
FROM Ranges
OUTER APPLY
(
SELECT DATEADD(MONTH, [number], [DateBeg])
FROM
(
select number
from master.dbo.spt_values
where [type] = 'P'
) numbers
WHERE DATEADD(MONTH, [number], [DateBeg]) < [DateEnd]
) AutoDates ([GeneratedDate]);
or the following to get the year rate for the first record:
DECLARE #DateBeg DATE = '2019-01-01'
,#DateEnd DAte = '2020-12-01';
WITH Ranges AS
(
SELECT *
,#DateBeg AS [DateBeg]
,#DateEnd AS [DateEnd]
FROM ItemDefinition DS
)
SELECT *
,DATENAME(MONTH ,ISNULL([GeneratedDate], [RenewalDate])) AS RenewalMonth
,IIF([PaymentPlan] = 'Monthly', [UnitRate], IIF([number] = 0, [UnitRate], NULL))
FROM Ranges
OUTER APPLY
(
SELECT DATEADD(MONTH, [number], [DateBeg])
,[number]
FROM
(
select number
from master.dbo.spt_values
where [type] = 'P'
) numbers
WHERE DATEADD(MONTH, [number], [DateBeg]) < [DateEnd]
) AutoDates ([GeneratedDate], [number]);
My advice is to introduce an additional table that will have a single record for a Yearly plan and 12 records for Monthly plan. For example:
create table PaymentPlanInterval(
PaymentPlan VARCHAR(50),
Interval varchar(50)
)
And perhaps this table may contain 2 records for Semi-annual payment plan and 4 records for quartely plan.
In order to get your desired result you should be joining your ItemDefinition table with PaymentPlanInterval. Voila.

How can I add empty rows in SQL SERVER for this example?

Sql Fiddle Example
I have this table structure:
CREATE TABLE Schedule
([Day] varchar(13), [Starts] varchar(57), [Ends] varchar(57))
;
INSERT INTO Schedule
([Day], [Starts], [Ends])
VALUES
('2', '09:00', '15:00'),
('5', '10:00', '12:00'),
('3', '09:00', '18:00')
;
And this simple query to show the current rows:
SELECT
DATENAME(weekday, Day - 1) as days,
Starts,
Ends
FROM
Schedule
order by Day
The last query get this result:
Day Starts Ends
--- ------- ----
Tuesday 09:00 15:00
Wednesday 09:00 18:00
Friday 10:00 12:00
I want to fill the table with the days that doesn't appear in the registers
This is the final result I want to get:
Day Starts Ends
--- ------- ----
Monday NULL NULL
Tuesday 09:00 15:00
Wednesday 09:00 18:00
Thursday NULL NULL
Friday 10:00 12:00
How can I get this?, thanks
I would create an empy base table and do a LEFT JOIN:
CREATE TABLE Schedule
(Day VARCHAR(13), Starts VARCHAR(57), Ends VARCHAR(57));
INSERT INTO Schedule (Day, Starts,Ends)
VALUES
('2', '09:00','15:00'),
('5', '10:00','12:00'),
('3', '09:00','18:00');
CREATE TABLE ScheduleBase
(Day VARCHAR(13), Starts VARCHAR(57), Ends VARCHAR(57));
INSERT INTO ScheduleBase
(Day, Starts, Ends)
VALUES
('1', NULL, NULL),
('2', NULL, NULL),
('3', NULL, NULL),
('4', NULL, NULL),
('5', NULL, NULL)
SELECT SB.Day, S.Starts, S.Ends FROM ScheduleBase AS SB LEFT JOIN Schedule
AS S ON SB.Day = S.Day
You can use this if you do not want to create new table
select
DATENAME(weekday, Day - 1) as days, max(Starts), max(Ends)
from (
SELECT
Day, Starts, Ends
FROM
Schedule
union all
select
*, null, null
from (values (1),(2),(3),(4),(5),(6),(7)) t(d)
) t
group by Day
Alternatively, create a reference table for days then Right Join to your select query.
SELECT DATENAME(WEEKDAY, TBL.days - 1) AS days ,
starts ,
ends
FROM ( SELECT Day days ,
Starts ,
Ends
FROM Schedule
) T
RIGHT JOIN ( SELECT [days]
FROM ( SELECT 1 [days]
UNION
SELECT 2
UNION
SELECT 3
UNION
SELECT 4
UNION
SELECT 5
UNION
SELECT 6
UNION
SELECT 7
) S
) TBL ON TBL.days = t.days
Results:
days starts ends
------------ --------- ------------
Monday NULL NULL
Tuesday 09:00 15:00
Wednesday 09:00 18:00
Thursday NULL NULL
Friday 10:00 12:00
Saturday NULL NULL
Sunday NULL NULL
(7 row(s) affected)
You can use a common table expressin to generate a week calendar "table" on the fly:
with range (num) 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
)
SELECT
DATENAME(weekday, range.num - 1),
Starts,
Ends
FROM
Schedule
right outer join range on range.num = Schedule.day
order by range.num
Fiddle

T-SQL Get Records for this year grouped by month

I have a table of data which looks like this
ID CreatedDate
A123 2015-01-01
B124 2016-01-02
A125 2016-01-03
A126 2016-01-04
What I would like to do is group by month (as text) for this year only. I have some up with the following query but it returns data from all years not just this one:
Select Count(ID), DateName(month,createddate) from table
Where (DatePart(year,createddate)=datepart(year,getdate())
Group by DateName(month,createddate)
This returns
Count CreatedDate
4 January
Instead of
Count CreatedDate
3 January
Where have I gone wrong? I'm sure it's something to do with converting the date to month where it goes wrong
Just tested your code:
;WITH [table] AS (
SELECT *
FROM (VALUES
('A123', '2015-01-01'),
('B124', '2016-01-02'),
('A125', '2016-01-03'),
('A126', '2016-01-04')
) as t(ID, CreatedDate)
)
SELECT COUNT(ID),
DATENAME(month,CreatedDate)
FROM [table]
WHERE DATEPART(year,CreatedDate)=DATEPART(year,getdate())
GROUP BY DATENAME(month,CreatedDate)
Output was
3 January
I removed ( near WHERE
select count(id) as Count,
case when month(createddate)=1 THEN 'Januray' END as CreatedDate
from [table]
--where year(createddate)=2016 optional if you only want the 2016 count
group by month(createddate),year(createdDate)

How to calculate totals by week start and end dates based on date column in the table?

my sql server table named timesheet has 4 columns
employee_id int
task_start_date datetime
task_end_date datetime
hours_worked int
I need to derive week start date and week end date and total hours the employee has worked in that week. My resulting dataset should have 4 columns -
Week_Start_Date Week_End_Date Employee_Id Total_Hours_Charged
In other words, i need to get total hours charged by each employee for all the weeks from that table. Could anyone please help me write a query for that?
SQL Server 2005 and above
Assumptions:
Task start and end date values are always same.
Week start and end dates are calculated based on the task_start_date.
You can use the DATEPART function to find the day of the week for a given date and then use DATEADD function to provide the output from DATEPART as input to calculate the first and last days of the week.
Click here to view the demo in SQL Fiddle.
Script:
The script calculates the week totals hours assuming that week begins on Sunday and ends on Saturday.
CREATE TABLE timesheet
(
employee_id int
, task_start_date datetime
, task_end_date datetime
, hours_worked int
);
INSERT INTO timesheet
(employee_id, task_start_date, task_end_date, hours_worked)
VALUES
(1, '20120331', '20120331', 6),
(1, '20120401', '20120401', 3),
(1, '20120403', '20120403', 8),
(1, '20120409', '20120409', 5),
(1, '20120412', '20120412', 4),
(2, '20120402', '20120402', 7),
(2, '20120403', '20120403', 6),
(3, '20120409', '20120409', 4),
(1, '20120412', '20120412', 8);
;WITH empworkhours AS
(
SELECT DATEADD(DAY
, -(DATEPART(dw, task_start_date) -1)
, task_start_date) AS week_start
, DATEADD(DAY
, 7 - (DATEPART(dw, task_start_date))
, task_start_date) AS week_end
, employee_id
, hours_worked
FROM timesheet
)
SELECT week_start
, week_end
, employee_id
, SUM(hours_worked) total_hrs_per_week
FROM empworkhours
GROUP BY week_start
, week_end
, employee_id;
Output:
WEEK_START WEEK_END EMPLOYEE_ID TOTAL_HRS_PER_WEEK
-------------- -------------- ----------- -----------------
March, 25 2012 March, 31 2012 1 6
April, 01 2012 April, 07 2012 1 11
April, 01 2012 April, 07 2012 2 13
April, 08 2012 April, 14 2012 1 17
April, 08 2012 April, 14 2012 3 4

Resources