SQL Stored Procedures, Combining rows with derived variables - sql

I've been away for sometime and need help with, what is probably a simple solution but at this time I'm stuck. I have this select statement and need to combine rows with like elements for the same department but the group by would have a very large select list, is there another way like MERGE? Here is the sql stmt I'm dealing with along with the output:
SELECT distinct sg.Description as 'Groups', si.Store_Name, a.Store_ID, si.Store_ID1 as 'Store #2', d.Department_ID as 'Dept', a.Vendor_ID,
IIF(d.Delivery_Day = 1, dbo.DayOfWeekToString(d.Order_Day),null) as 'Sun',
IIF(d.Delivery_Day = 1, CONCAT(CONVERT(varchar, d.Deadline_Hour) , ':', RIGHT('00' + CONVERT(varchar, d.Deadline_Minute), 2)) ,null ) as 'time',
IIF(d.Delivery_Day = 2, dbo.DayOfWeekToString(d.Order_Day),null) as 'Mon',
IIF(d.Delivery_Day = 2, CONCAT(CONVERT(varchar, d.Deadline_Hour) , ':', RIGHT('00' + CONVERT(varchar, d.Deadline_Minute), 2)) ,null ) as 'time',
IIF(d.Delivery_Day = 3, dbo.DayOfWeekToString(d.Order_Day),null) as 'Tue',
IIF(d.Delivery_Day = 3, CONCAT(CONVERT(varchar, d.Deadline_Hour) , ':', RIGHT('00' + CONVERT(varchar, d.Deadline_Minute), 2)) ,null ) as 'time',
IIF(d.Delivery_Day = 4, dbo.DayOfWeekToString(d.Order_Day),null) as 'Wed',
IIF(d.Delivery_Day = 4, CONCAT(CONVERT(varchar, d.Deadline_Hour) , ':', RIGHT('00' + CONVERT(varchar, d.Deadline_Minute), 2)) ,null ) as 'time',
IIF(d.Delivery_Day = 5, dbo.DayOfWeekToString(d.Order_Day),null) as 'Thu',
IIF(d.Delivery_Day = 5, CONCAT(CONVERT(varchar, d.Deadline_Hour) , ':', RIGHT('00' + CONVERT(varchar, d.Deadline_Minute), 2)) ,null ) as 'time',
IIF(d.Delivery_Day = 6, dbo.DayOfWeekToString(d.Order_Day),null) as 'Fri',
IIF(d.Delivery_Day = 6, CONCAT(CONVERT(varchar, d.Deadline_Hour) , ':', RIGHT('00' + CONVERT(varchar, d.Deadline_Minute), 2)) ,null ) as 'time',
IIF(d.Delivery_Day = 7, dbo.DayOfWeekToString(d.Order_Day),null) as 'Sat',
IIF(d.Delivery_Day = 7, CONCAT(CONVERT(varchar, d.Deadline_Hour) , ':', RIGHT('00' + CONVERT(varchar, d.Deadline_Minute), 2)),null) as 'time'
from Delivery_Schedule_Header a Join Vendor_Info b ON (a.Vendor_ID = b.Vendor_ID) Join Delivery_Schedule_Detail d on d.UID_Delivery = a.UID
Join Store_Info si on si.Store_ID = a.Store_ID Join Store_Group sg on sg.GroupStore_UID = si.GroupStore_UID
Where a.Vendor_ID = #Vendor_Id and si.GroupStore_UID = Case when #Group_Id = -1 then si.GroupStore_UID else #Group_Id end
and the results:
Groups Store_Name Store_ID Store #2 Dept Vendor_ID Sun time Mon time Tue time Wed time Thu time Fri time Sat time
Balls Price Chopper 18 18 18 3200 70038000 NULL NULL NULL NULL NULL NULL NULL NULL Wednesday 11:00 NULL NULL NULL NULL
Balls Price Chopper 18 18 18 3200 70038000 NULL NULL NULL NULL Monday 11:00 NULL NULL NULL NULL NULL NULL NULL NULL
Balls Price Chopper 18 18 18 3200 70038000 Saturday 11:00 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL
These three rows need to be combined under the same department
sorry if this is rather elementary but I've been out of this type of work for sometime now and need a refresher. Thanks

You want conitional aggregation. This works by creating a group by clause that contains all columns except those you want to merge, and then surround the conditional expression with an aggregate function (such as MIN() or MAX():
SELECT
sg.Description as Groups,
si.Store_Name,
a.Store_ID,
si.Store_ID1 as Store_2,
d.Department_ID as Dept,
a.Vendor_ID,
MAX(CASE WHEN d.Delivery_Day = 1 THEN dbo.DayOfWeekToString(d.Order_Day) END) as Sun,
MAX(CASE
WHEN d.Delivery_Day = 1
THEN CONCAT(CONVERT(varchar, d.Deadline_Hour) , ':', RIGHT('00' + CONVERT(varchar, d.Deadline_Minute), 2))
) as Sun_time,
...
FROM Delivery_Schedule_Header a
JOIN Vendor_Info b ON a.Vendor_ID = b.Vendor_ID
JOIN Delivery_Schedule_Detail d ON d.UID_Delivery = a.UID
JOIN Store_Info si ON si.Store_ID = a.Store_ID
JOIN Store_Group sg ON sg.GroupStore_UID = si.GroupStore_UID
WHERE
a.Vendor_ID = #Vendor_Id
AND si.GroupStore_UID = CASE WHEN #Group_Id = -1 THEN si.GroupStore_UID ELSE #Group_Id END
GROUP BY
sg.Description as Groups,
si.Store_Name,
a.Store_ID,
si.Store_ID1 as Store_2,
d.Department_ID as Dept,
a.Vendor_ID
Side notes:
I rewrote the IIF() expressions to use CASE (the latter is standard SQL, while the former is TSQL-specific)
single quotes should be used for litteral strings, not for column identifiers (although SQL Server tolerates this)
you have multiple columns in the resultset that have the same name (time), this is not a good practice

Related

How can I manipulate a string a T-SQL?

business_id
open_day
open_time
close_day
close_time
1
FRIDAY
08:00
FRIDAY
12:00
1
FRIDAY
13:00
FRIDAY
17:00
1
MONDAY
08:00
MONDAY
17:00
2
SATURDAY
08:00
SATURDAY
16:00
2
SUNDAY
08:00
SUNDAY
16:00
3
MONDAY
08:00
MONDAY
16:00
I have a task to create and format opening hours for a business, I'm required to group these by days in a single string. However, it is repeating the day. Would it be possible to search for a particular value if it occurs twice within a string? I have gotten this far:
create table open_times (business_id int, open_day varchar(10), open_time varchar(10), close_day varchar(10), close_time varchar(10) )
insert into open_times (business_id, open_day, open_time, close_day, close_time) values (1, 'FRIDAY', '08:00', 'FRIDAY', '12:00')
insert into open_times (business_id, open_day, open_time, close_day, close_time) values (1, 'FRIDAY', '13:00', 'FRIDAY', '17:00')
insert into open_times (business_id, open_day, open_time, close_day, close_time) values (1, 'MONDAY', '08:00', 'MONDAY', '17:00')
insert into open_times (business_id, open_day, open_time, close_day, close_time) values (2, 'SATURDAY', '08:00', 'SATURDAY', '16:00')
insert into open_times (business_id, open_day, open_time, close_day, close_time) values (2, 'SUNDAY', '08:00', 'SUNDAY', '16:00')
insert into open_times (business_id, open_day, open_time, close_day, close_time) values (3, 'MONDAY', '08:00', 'MONDAY', '16:00')
drop table open_times
I appreciate your help.
select
business_id
,left(name_values, LEN(name_values)-1) as opening_hours
from
(
select
results.business_id
,STUFF((
select
( case when open_day = 'FRIDAY' then 'Fr' when open_day = 'MONDAY' then 'Mo' when open_day = 'TUESDAY' then 'Tu' when open_day = 'WEDNESDAY' then 'We' When open_day = 'THURSDAY' then 'Th' when open_day = 'SATURDAY' then 'Sa' else 'Su' end )
+ ' ' + open_time + '-' + close_time + '; '
from open_times
where business_id = results.business_id
for xml path(''),type).value('(./text())[1]','VARCHAR(MAX)'),1,0, '') as name_values
from open_times results
group by business_id
) innerquery
Current Output for Business 1: 'Fr 08:00-12:00; Fr 13:00-17:00; Mo 08:00-17:00'
Desired Output For Business 1: 'Fr 08:00-12:00, 13:00-17:00; Mo 08:00-17:00'
You can use STRING_AGG here. You just need to do two levels of grouping, once per day, then again for the whole business_id
SELECT
ot.business_id,
Times = STRING_AGG(CONCAT(
UPPER(LEFT(ot.open_day, 1)),
LOWER(SUBSTRING(ot.open_day, 2, 1)),
' ',
ot.Times)
, '; ')
FROM (
SELECT
ot.business_id,
ot.open_day,
Times = STRING_AGG(CONCAT(
ot.open_time,
'-',
ot.close_time),
', ')
FROM open_times ot
GROUP BY ot.business_id, ot.open_day
) ot
GROUP BY ot.business_id;
db<>fiddle
You can do this with one level of aggregation by incorporating window functions into your logic:
select ot.business_id,
stuff((select (case when seqnum = 1 then '; ' else ', ' end) +
(case when seqnum = 1 and ot2.open_day = 'FRIDAY' then 'Fr '
when seqnum = 1 and ot2.open_day = 'MONDAY' then 'Mo '
when seqnum = 1 and ot2.open_day = 'TUESDAY' then 'Tu '
when seqnum = 1 and ot2.open_day = 'WEDNESDAY' then 'We '
when seqnum = 1 and ot2.open_day = 'THURSDAY' then 'Th '
when seqnum = 1 and ot2.open_day = 'SATURDAY' then 'Sa '
when seqnum = 1 then 'Su '
else ''
end ) +
ot2.open_time + '-' + ot2.close_time
from (select ot2.*,
row_number() over (partition by ot2.open_day order by ot2.open_time) as seqnum
from open_times ot2
where ot2.business_id = ot.business_id
) ot2
order by ot2.open_day, ot2.open_time
for xml path(''),type
).value('(./text())[1]', 'VARCHAR(MAX)'), 1, 2, ''
) as name_values
from open_times ot
group by ot.business_id;
Here is a db<>fiddle.
Your version of the query is actually incorrect, because you do not have an order by in the XML subquery. The results can be in any order.
In addition, you don't just want to remove the duplicate names, but you also want to change the delimiter from ; to ,.
The idea in the above query is that the times are enumerated on each day. The delimiter is ; for the first time and , for the subsequent ones. The abbreviation is used only for the first one. I also added qualifications to all the column references, a highly recommended practice.
Note that you can simplify the case logic to:
left(ot2.open_day, 1) + lower(substring(ot2.open_day, 2, 1))
For older versions use FOR XML PATH aggregation trick. Assuming no more then 2 open intervals a day
with t as (
select business_id, left(open_day,2) + ' ' + min(open_time + '-' + close_time) +
case when min(open_time) = max(open_time) then ''
else ', ' + max(open_time + '-' + close_time) end ots
from open_times
group by business_id, open_day
)
select business_id, stuff(
(select '; ' + ots
from t t2
where t2.business_id = t1.business_id
order by left(ots,2)
for xml path(''))
, 1, 2, '') opening_hours
from t t1
group by business_id

SELECT top 1 descending if DB contains current date data, else select top 1 ascending

I have a list of months that get populated into my DB. These months then get populated into a drop down list for a web Application. The client wants the current month to be the default option in the drop down list. However, the current month's data is not always available (Or ActiveFlag=Y in the database). So in that case, the client wants the default to be the first month of the year instead.
So I am testing that the drop down list and the DB table match using Selenium automation with C#. I have an SQL query that returns the Top 1 sorted in Descending order (This works to return the last active month in the year, because BroadcastMonthofYearNbr contains integers 1 through 12 month numbers):
SELECT top 1 CONCAT(RTRIM(left(M.Name, CHARINDEX(',',M.Name)-1)), ' (', STUFF(REPLACE('/'+CONVERT( VARCHAR(10), M.StartDate, 101), '/0', '/'), 1, 1, ''), '-', STUFF(REPLACE('/'+CONVERT(VARCHAR(10), M.EndDate, 101), '/0', '/'), 1, 1, ''), ')')
FROM [AudienceProjections].[dbo].[BroadcastCalendarQuarterMonthMap] QMP,
AudienceProjections.dbo.BroadcastCalendarYear Y,
AudienceProjections.dbo.BroadcastCalendarMonth M,
AudienceProjections.dbo.BroadcastCalendarQuarter Q
WHERE y.BroadcastCalendarYearUuid = q.BroadcastCalendarYearUuid
AND q.BroadcastCalendarQuarterUuid = qmp.BroadcastCalendarQuarterUuid
AND qmp.BroadcastCalendarMonthUuid = m.BroadcastCalendarMonthUuid
AND M.ActiveStatus = 'ACTIVE'
AND y.ActiveStatus = 'ACTIVE'
AND q.BroadcastQuarterType = 'STANDARD'
AND y.BroadcastCalendarYear = YEAR(GETDATE())
ORDER BY qmp.BroadcastMonthofYearNbr DESC
The query above returns this:
No Column Name
April (3/28/2016-4/24/2016)
The current month for when I am posting this question is May 5th. Notice that the above query result returns April, not May. This is because May data has not yet been imported into the DB (See below table for distinction of the data being there or not. I.e Active versus Inactive). So my query wont work in this case. I would now need a CASE THEN ELSE, or an IF EXISTS type of function, to return January if this situation occurs
Here is the Month table that you can see have May as INACTIVE:
Name StartDate EndDate ActiveStatus
January, 2016 2015-12-28 2016-01-31 ACTIVE
February, 2016 2016-02-01 2016-02-28 ACTIVE
March, 2016 2016-02-29 2016-03-27 ACTIVE
April, 2016 2016-03-28 2016-04-24 ACTIVE
May, 2016 2016-04-25 2016-05-29 INACTIVE
NOTE: I forgot about the situation of the current date being January, and if January isnt there, then the Year wouldnt be there yet either. So the condition in the query would need to take Year into account. The year table (BroadcastCalendarYear) also has an ActiveStatus column which can be used.
So if the year is also inactive, then return me a string that says "No data in DB for Year".
I assume you will be able to adapt the following to your specific schema but here is an example as order by that would work. Note you may not need and should remove some of the where statements hard to know without knowing the dataset.
Also what month should be returned if it is January of the current year and nothing is active? You may have to adapt the order by depending on that answer.
SELECT TOP 1 *
FROM
#Months
WHERE
ActiveStates = 'ACTIVE'
AND ????
ORDER BY
CASE WHEN YEAR(GETDATE()) = YearInt THEN 9999 ELSE YearInt END DESC -- Assumes you want to order by most recent to oldest and that youcould potentially have a future year loaded in the table. Othwerwuse simply use YearInt DESC
,CASE WHEN MONTH(GETDATE()) = MonthInt THEN 0 ELSE 1 END --Puts precedence on current month
,MonthInt DESC
I hope this helps.
IF OBJECT_ID(N'tempdb..#Month') IS NOT NULL
DROP TABLE #Month
CREATE TABLE #Month
(Year INT, Month VARCHAR(3), ActiveStatus CHAR(1))
INSERT INTO #Month(Year, Month, ActiveStatus)
VALUES(2016,'Jan','A')
,(2016,'Feb','A')
,(2016,'Mar','A')
,(2016,'Apr','A')
,(2016,'May','I')
;WITH x AS
(
SELECT
*,EOMONTH(CONCAT(Year,'-',Month,'-','01')) As Date
FROM #Month
)
SELECT
*
FROM X
WHERE
-- current month is active or current month is inactive then first month of the current year
Year = YEAR(GETDATE()) AND
(DATEPART(MONTH,Date)=DATEPART(MONTH,GETDATE()) AND ActiveStatus='A') OR (1=DATEPART(MONTH,Date) AND EXISTS(SELECT 1 FROM x WHERE DATEPART(MONTH,Date)=DATEPART(MONTH,GETDATE()) AND ActiveStatus='I'))
I figured it out with a long and convoluted solution, but it works.
SELECT CASE WHEN t.month = (SELECT left(M.Name, CHARINDEX(',',M.Name)-1) FROM [AudienceProjections].[dbo].[BroadcastCalendarMonth] M
WHERE StartDate <= GETDATE() AND EndDate >= GETDATE() ) THEN CONCAT(RTRIM(left(t.Name, CHARINDEX(',',t.Name)-1)), ' (', STUFF(REPLACE('/'+CONVERT( VARCHAR(10), t.StartDate, 101), '/0', '/'), 1, 1, ''), '-', STUFF(REPLACE('/'+CONVERT(VARCHAR(10), t.EndDate, 101), '/0', '/'), 1, 1, ''), ')') ELSE ( SELECT CONCAT(RTRIM(left(M.Name, CHARINDEX(',',M.Name)-1)), ' (', STUFF(REPLACE('/'+CONVERT( VARCHAR(10), M.StartDate, 101), '/0', '/'), 1, 1, ''), '-', STUFF(REPLACE('/'+CONVERT(VARCHAR(10), M.EndDate, 101), '/0', '/'), 1, 1, ''), ')') FROM AudienceProjections.dbo.BroadcastCalendarMonth M WHERE NAME = CONCAT( DATENAME(month,DATEADD(yy, DATEDIFF(yy,0,getdate()), 0)), ', ', DATENAME(year,DATEADD(yy, DATEDIFF(yy,0,getdate()), 0)) ) ) END month FROM (
SELECT top 1 left(M.Name, CHARINDEX(',',M.Name)-1) AS month,
M.Name, M.StartDate, M.EndDate
FROM [AudienceProjections].[dbo].[BroadcastCalendarQuarterMonthMap] QMP,
AudienceProjections.dbo.BroadcastCalendarYear Y,
AudienceProjections.dbo.BroadcastCalendarMonth M,
AudienceProjections.dbo.BroadcastCalendarQuarter Q
WHERE y.BroadcastCalendarYearUuid = q.BroadcastCalendarYearUuid
AND q.BroadcastCalendarQuarterUuid = qmp.BroadcastCalendarQuarterUuid
AND qmp.BroadcastCalendarMonthUuid = m.BroadcastCalendarMonthUuid
AND M.ActiveStatus = 'ACTIVE'
AND q.BroadcastQuarterType = 'STANDARD'
AND y.ActiveStatus = 'ACTIVE'
AND y.BroadcastCalendarYear = YEAR(GETDATE())
ORDER BY qmp.BroadcastMonthofYearNbr DESC) t
Using IF EXISTS, ELSE IF EXISTS, ELSE accomplishes all 3 conditions:
IF EXISTS (
SELECT CONCAT(q.DisplayName, ' (', STUFF(REPLACE('/'+CONVERT( VARCHAR(10), q.StartDate, 101), '/0', '/'), 1, 1, ''), '-', STUFF(REPLACE('/'+CONVERT(VARCHAR(10), q.EndDate, 101), '/0', '/'), 1, 1, ''), ')')
FROM AudienceProjections.dbo.BroadcastCalendarYear Y,
AudienceProjections.dbo.BroadcastCalendarQuarter Q
WHERE y.BroadcastCalendarYearUuid = q.BroadcastCalendarYearUuid
AND q.BroadcastQuarterType = 'STANDARD'
AND y.ActiveStatus = 'ACTIVE'
AND q.ActiveStatus = 'ACTIVE'
AND y.BroadcastCalendarYear = YEAR(GETDATE())
AND Q.StartDate <= GETDATE()
and Q.EndDate >= GETDATE())
SELECT CONCAT(q.DisplayName, ' (', STUFF(REPLACE('/'+CONVERT( VARCHAR(10), q.StartDate, 101), '/0', '/'), 1, 1, ''), '-', STUFF(REPLACE('/'+CONVERT(VARCHAR(10), q.EndDate, 101), '/0', '/'), 1, 1, ''), ')')
FROM AudienceProjections.dbo.BroadcastCalendarYear Y,
AudienceProjections.dbo.BroadcastCalendarQuarter Q
WHERE y.BroadcastCalendarYearUuid = q.BroadcastCalendarYearUuid
AND q.BroadcastQuarterType = 'STANDARD'
AND y.ActiveStatus = 'ACTIVE'
AND q.ActiveStatus = 'ACTIVE'
AND y.BroadcastCalendarYear = YEAR(GETDATE())
AND Q.StartDate <= GETDATE()
and Q.EndDate >= GETDATE() ELSE IF EXISTS (
SELECT TOP 1 CONCAT(q.DisplayName, ' (', STUFF(REPLACE('/'+CONVERT( VARCHAR(10), q.StartDate, 101), '/0', '/'), 1, 1, ''), '-', STUFF(REPLACE('/'+CONVERT(VARCHAR(10), q.EndDate, 101), '/0', '/'), 1, 1, ''), ')')
FROM [AudienceProjections].[dbo].[BroadcastCalendarQuarterMonthMap] QMP,
AudienceProjections.dbo.BroadcastCalendarYear Y,
AudienceProjections.dbo.BroadcastCalendarQuarter Q
WHERE y.BroadcastCalendarYearUuid = q.BroadcastCalendarYearUuid
AND q.BroadcastCalendarQuarterUuid = qmp.BroadcastCalendarQuarterUuid
AND q.BroadcastQuarterType = 'STANDARD'
AND y.ActiveStatus = 'ACTIVE'
AND q.ActiveStatus = 'ACTIVE'
AND y.BroadcastCalendarYear = YEAR(GETDATE()) ORDER BY qmp.BroadcastMonthofYearNbr ASC ) SELECT TOP 1 CONCAT(q.DisplayName, ' (', STUFF(REPLACE('/'+CONVERT( VARCHAR(10), q.StartDate, 101), '/0', '/'), 1, 1, ''), '-', STUFF(REPLACE('/'+CONVERT(VARCHAR(10), q.EndDate, 101), '/0', '/'), 1, 1, ''), ')')
FROM [AudienceProjections].[dbo].[BroadcastCalendarQuarterMonthMap] QMP,
AudienceProjections.dbo.BroadcastCalendarYear Y,
AudienceProjections.dbo.BroadcastCalendarQuarter Q
WHERE y.BroadcastCalendarYearUuid = q.BroadcastCalendarYearUuid
AND q.BroadcastCalendarQuarterUuid = qmp.BroadcastCalendarQuarterUuid
AND q.BroadcastQuarterType = 'STANDARD'
AND y.ActiveStatus = 'ACTIVE'
AND q.ActiveStatus = 'ACTIVE'
AND y.BroadcastCalendarYear = YEAR(GETDATE())
ORDER BY qmp.BroadcastMonthofYearNbr ASC; ELSE SELECT 'No Active quarters are in the database for the current year yet';

Calculate Days Present and absent from the table

I have a table name Emp_mon_day which consists Employees Present and Absent details.
What I want is
I need for these 9 employees, information about days present and days absent for each employees from Emp_mon_day table merged in to below query
QUERY
SELECT e.comp_mkey,
e.status,
e.resig_date,
dt_of_leave,
e.emp_name,
e.date_of_joining,
e.emp_card_no,
a.pl_days,
pl_days_opening,
a.month1,
a.month2,
a.month3,
a.month4,
a.month5,
a.month6,
a.month7,
a.month8,
a.month9,
a.month10,
a.month11,
a.month12,
a.month1 + a.month2 + a.month3 + a.month4 + a.month5 + a.month6 + a.month7 + a.month8 + a.month9 + +a.month10 + a.month11 + a.month12 AS pl_sum
FROM p_leave_allocation AS a
INNER JOIN
emp_mst AS e
ON a.emp_card_no = e.emp_card_no
WHERE a.year = 2016
AND (datediff(MONTH, e.date_of_joining, CONVERT (DATETIME, getdate(), 103)) >= 6
AND datediff(MONTH, e.date_of_joining, CONVERT (DATETIME, getdate(), 103)) <= 36)
AND (e.resig_date IS NULL
OR (e.dt_of_leave IS NOT NULL
AND e.dt_of_leave >= CONVERT (DATETIME, getdate(), 103)))
AND e.status IN ('A', 'S')
AND e.comp_mkey IN (7, 110)
AND a.Year = 2016;
The above query gives me data as below
[![Image data][1]][1]
Column details for Emp_mon_day is below
[![enter image description here][2]][2]
You can try the below query:
SELECT e.comp_mkey, e.status, e.resig_date, dt_of_leave, e.emp_name,
e.date_of_joining, e.emp_card_no, a.pl_days, pl_days_opening, a.month1,
a.month2, a.month3, a.month4, a.month5, a.month6, a.month7, a.month8,
a.month9, a.month10, a.month11, a.month12,
a.month1 + a.month2 + a.month3 + a.month4 + a.month5 + a.month6 + a.month7 + a.month8 + a.month9 + +a.month10 + a.month11 + a.month12 AS pl_sum,
m.[DaysAbsent],m.[DaysPresent]
FROM p_leave_allocation AS a
INNER JOIN
emp_mst AS e
ON a.emp_card_no = e.emp_card_no
INNER JOIN
(
SELECT
comp_mkey,emp_mkey,[month],[year],
SUM(CASE WHEN data ='AB' THEN 1 ELSE 0 END) AS [DaysAbsent],
SUM(CASE WHEN data ='P' THEN 1 ELSE 0 END) AS [DaysPresent]
FROM
(
SELECT comp_mkey,emp_mkey,[month],[year],[Day1],[Day2],[Day3],[Day4],[Day5]
--,...
FROM Emp_mon_day
) source
UNPIVOT
(
data FOR day IN ([Day1],[Day2],[Day3],[Day4],[Day5]) -- dynamic query can generate all days data
)up
GROUP BY comp_mkey, emp_mkey,[month],[year]
) AS m
ON m.comp_mkey=e.Comp_mkey and m.emp_mkey=e.mkey
--- ABOVE CRITERIA NEEDS TO BE CHECKED
WHERE a.year = 2016
AND (datediff(MONTH, e.date_of_joining, CONVERT (DATETIME, getdate(), 103)) >= 6
AND datediff(MONTH, e.date_of_joining, CONVERT (DATETIME, getdate(), 103)) <= 36)
AND (e.resig_date IS NULL
OR (e.dt_of_leave IS NOT NULL
AND e.dt_of_leave >= CONVERT (DATETIME, getdate(), 103)))
AND e.status IN ('A', 'S')
AND e.comp_mkey IN (7, 110)
AND a.Year = 2016;
Explanation:
We've added another INNER JOIN to the existing query to get collated data of DaysPresent and DaysAbsent
To optimize this further, I'd suggest you directly apply following WHERE clause to source set
WHERE comp_mkey IN (7, 110) AND Year = 2016;

How to Combine dates and time in separate fields in SQL

Table :
schedule_id job_id next_run_date next_run_time
------------------------------------------------------
221 D23EA7B2 20151005 90000
222 18EDFB21 20151020 90000
242 90283725 20151001 170000
239 4B69C670 20151011 90000
Result :
schedule_id job_id next_run_date_Time
--------------------------------------------
221 D23EA7B2 2015-10-05 09:00 AM
222 18EDFB21 2015-10-20 09:00 AM
242 90283725 2015-10-01 05:00 PM
239 4B69C670 2015-10-11 09:00 AM
How to join next_run_date and next_run_time together as a single column?
My Query used in SSRS 2008
SELECT c.Name AS ReportName,[LastRunTime],
'Next Run Date' = CASE next_run_date WHEN 0 THEN null ELSE
substring(convert(varchar(15),next_run_date),1,4) + '/' +
substring(convert(varchar(15),next_run_date),5,2) + '/' +
substring(convert(varchar(15),next_run_date),7,2)
END,
--Need to add next_run_date_Time here
FROM
dbo.[Catalog] c
INNER JOIN dbo.[Subscriptions] S ON c.ItemID = S.Report_OID
INNER JOIN dbo.ReportSchedule R ON S.SubscriptionID = R.SubscriptionID
INNER JOIN msdb.dbo.sysjobs J ON Convert(nvarchar(128),R.ScheduleID) = J.name
INNER JOIN msdb.dbo.sysjobschedules JS ON J.job_id = JS.job_id
ORDER BY S.LastRunTime DESC
Assuming both are varchar, try this:
SELECT schedule_id, job_id,
CONVERT(datetime, next_run_date, 112)
+ CONVERT(time,
SUBSTRING(next_run_time, 1, LEN(next_run_time) - 4) + ':'
+ LEFT(RIGHT(next_run_time, 4), 2) + ':'
+ RIGHT(next_run_time, 2),
114) AS next_run_date_Time
FROM my_table
Here is a fiddle
If those fields are numbers, you can convert them in a sub-query first and then apply the same query above:
SELECT schedule_id, job_id,
CONVERT(datetime, next_run_date, 112)
+ CONVERT(time,
SUBSTRING(next_run_time, 1, LEN(next_run_time) - 4) + ':'
+ LEFT(RIGHT(next_run_time, 4), 2) + ':'
+ RIGHT(next_run_time, 2),
114) AS next_run_date_Time
FROM (SELECT schedule_id, job_id
, CAST(next_run_date AS VARCHAR(8)) AS next_run_date
, CAST(next_run_time AS VARCHAR(6)) AS next_run_time
FROM my_table) AS t
Here is a fiddle
EDIT you can update your query to use this concept like this:
SELECT c.Name AS ReportName,[LastRunTime],
CONVERT(datetime, next_run_date, 112)
+ CONVERT(time,
SUBSTRING(next_run_time, 1, LEN(next_run_time) - 4) + ':'
+ LEFT(RIGHT(next_run_time, 4), 2) + ':'
+ RIGHT(next_run_time, 2),
114) AS 'Next Run Date'
FROM
dbo.[Catalog] c
INNER JOIN dbo.[Subscriptions] S ON c.ItemID = S.Report_OID
INNER JOIN dbo.ReportSchedule R ON S.SubscriptionID = R.SubscriptionID
INNER JOIN msdb.dbo.sysjobs J ON Convert(nvarchar(128),R.ScheduleID) = J.name
INNER JOIN (SELECT schedule_id, job_id
, CAST(next_run_date AS VARCHAR(8)) AS next_run_date
, CAST(next_run_time AS VARCHAR(6)) AS next_run_time
FROM msdb.dbo.sysjobschedules) AS JS ON J.job_id = JS.job_id
ORDER BY S.LastRunTime DESC
Here is one way to do it:
-- Create sample table and data
CREATE TABLE tbl (
next_run_date char(8),
next_run_time varchar(6)
)
INSERT INTO tbl VALUES
(20151005, 93020),
(20151001, 170000)
using cte1 to pad next_run_time with a leading zero if needed,
and using cte2 to break the string to a "normal" time representation:
;with cte1 as
(
select next_run_date,
right('000000'+ next_run_time, 6) as run_time_base
FROM tbl
), cte2 as
(
select next_run_date,
left(run_time_base, 2) + ':' +
substring(run_time_base, 3, 2) + ':' +
right(run_time_base, 2) as run_time
from cte1
)
select cast(next_run_date as datetime) + cast(run_time as datetime) as run_datetime
from cte2
-- clean up
drop table tbl
Results:
run_datetime
-----------------------
2015-10-05 09:30:20.000
2015-10-01 17:00:00.000
I am assuming here that next_run_date & next_run_time, I have written query here which will give your desired output..
select
schedule_id
,job_id
,convert(datetime,
convert(varchar, convert(datetime, next_run_date, 112), 111)
+ ' ' + substring(REPLICATE('0',6-LEN(next_run_time)) + next_run_time, 1, 2)
+ ':' + substring(REPLICATE('0',6-LEN(next_run_time)) + next_run_time, 3, 2)
+ ':' + substring(REPLICATE('0',6-LEN(next_run_time)) + next_run_time, 5, 2)
) as next_run_date_Time
from TableName
Here is the sql fiddle
I think the simplest option, assuming both are varchars to begin with, is:
dateadd(HH, cast(left(time, len(time) - 4) as int), cast(date as datetime))
Convert the varchar date to a datetime format, which will default to midnight on the date, then add the number of hours specified by the time. The syntax above assumes times are always on the hour - if not, add minutes as necessary.
Once in datetime, you can use convert to specify any display format you like.

SQL - How to find currently running job steps through TSQL

I am writing a query to find currently running job in SQL (I know we can view it in Job Active Monitor, but I've a need to do in TSQL). Though I can query sysjobactivity table to find currently running job, it's nowhere telling what job step is running (because my job might have more than 1 step).
Query I used:
SELECT s.name AS [JOB_NAME],
'' AS [STEP_ID],
'' AS STEP_NAME,
'Processing' AS STATUS,
sja.run_requested_date AS START_TIME,
null AS END_DATE,
convert(varchar, (getdate() - sja.run_requested_date), 8) AS Duration
FROM sysjobactivity sja, sysjobs s
WHERE sja.job_id = s.job_id
AND sja.run_requested_date > getdate() - 1
AND sja.stop_execution_date IS NULL
Please help me finding the step ID & Step name in which the job is currently progressing.
I think below script help to get SQL Jobs with current execution step, try this
msdb.dbo.sp_help_job #execution_status = 1
DECLARE #StepCount INT
SELECT #StepCount = COUNT(1)
FROM msdb.dbo.sysjobsteps
WHERE job_id = '0523333-5C24-1526-8391-AA84749345666' --JobID
SELECT
[JobName]
,[JobStepID]
,[JobStepName]
,[JobStepStatus]
,[RunDateTime]
,[RunDuration]
FROM
(
SELECT
j.[name] AS [JobName]
,Jh.[step_id] AS [JobStepID]
,jh.[step_name] AS [JobStepName]
,CASE
WHEN jh.[run_status] = 0 THEN 'Failed'
WHEN jh.[run_status] = 1 THEN 'Succeeded'
WHEN jh.[run_status] = 2 THEN 'Retry (step only)'
WHEN jh.[run_status] = 3 THEN 'Canceled'
WHEN jh.[run_status] = 4 THEN 'In-progress message'
WHEN jh.[run_status] = 5 THEN 'Unknown'
ELSE 'N/A'
END AS [JobStepStatus]
,msdb.dbo.agent_datetime(run_date, run_time) AS [RunDateTime]
,CAST(jh.[run_duration]/10000 AS VARCHAR) + ':' + CAST(jh.[run_duration]/100%100 AS VARCHAR) + ':' + CAST(jh.[run_duration]%100 AS VARCHAR) AS [RunDuration]
,ROW_NUMBER() OVER
(
PARTITION BY jh.[run_date]
ORDER BY jh.[run_date] DESC, jh.[run_time] DESC
) AS [RowNumber]
FROM
msdb.[dbo].[sysjobhistory] jh
INNER JOIN msdb.[dbo].[sysjobs] j
ON jh.[job_id] = j.[job_id]
WHERE
j.[name] = 'ProcessCubes' --Job Name
AND jh.[step_id] > 0
AND CAST(RTRIM(run_date) AS DATE) = CAST(GETDATE() AS DATE) --Current Date
) A
WHERE
[RowNumber] <= #StepCount
AND [JobStepStatus] = 'Failed'
Try this:
SELECT distinct
cast([sJOB].[job_id] as varchar(max)) AS execution_id
, [sJSTP].[step_name] AS executable_name
, [sJOB].[name] AS package_name
, CASE [sJSTP].[run_date]
WHEN 0 THEN NULL
ELSE
CAST(
CAST([sJSTP].[run_date] AS CHAR(8))
+ ' '
+ STUFF(
STUFF(RIGHT('000000' + CAST([sJSTP].[run_time] AS VARCHAR(6)), 6)
, 3, 0, ':')
, 6, 0, ':')
AS DATETIME)
END AS start_time,
dateadd(ss, run_duration, CASE [sJSTP].[run_date]
WHEN 0 THEN NULL
ELSE
CAST(
CAST([sJSTP].[run_date] AS CHAR(8))
+ ' '
+ STUFF(
STUFF(RIGHT('000000' + CAST([sJSTP].[run_time] AS VARCHAR(6)), 6)
, 3, 0, ':')
, 6, 0, ':')
AS DATETIME)
END) end_time
-- , [sJSTP].[run_duration] [looptijd in minuten]
, CASE [sJSTP].[run_status]
WHEN 0 THEN 'Failed'
WHEN 1 THEN 'Success'
WHEN 2 THEN 'Retry'
WHEN 3 THEN 'Cancelled'
WHEN 5 THEN 'Unknown'
END AS execution_result_description
FROM
[msdb].[dbo].[sysjobhistory] AS [sJSTP]
INNER JOIN [msdb].[dbo].[sysjobs] AS [sJOB]
ON [sJSTP].[job_id] = [sJOB].[job_id]
inner join [msdb].[dbo].[sysjobsteps] steps
ON [sJSTP].[job_id] = [steps].[job_id]
where [sJSTP].[run_date] <> 0
and CASE [sJSTP].[run_date]
WHEN 0 THEN NULL
ELSE
CAST(
CAST([sJSTP].[run_date] AS CHAR(8))
+ ' '
+ STUFF(
STUFF(RIGHT('000000' + CAST([sJSTP].[run_time] AS VARCHAR(6)), 6)
, 3, 0, ':')
, 6, 0, ':')
AS DATETIME)
END between dateadd(hh, -20, getdate()) and getdate()
and [sJSTP].[step_name] not in ('(Job outcome)')
order by start_time desc
Additionally I use this query to see step result from a running SSIS job. However, this only shows you the finished steps, not the running ones. I still have to find an SQL to see the currently running step and merge it with this one.
select distinct
cast(e.execution_id as varchar(max)),
e.executable_name,
e.package_name,
CONVERT(datetime, es.start_time) AS start_time
, CONVERT(datetime, es.end_time) AS end_time
, datediff(mi, es.start_time, es.end_time) [running time]
, case es.execution_result
when 0 then 'Success'
when 1 then 'Failed'
when 2 then 'Completion'
when 3 then 'Cancelled'
else cast(es.execution_result as varchar(max)) end as execution_result_description
from ssisdb.catalog.executables e
left join ssisdb.catalog.executable_statistics es
on e.executable_id = es.executable_id
and e.execution_id = es.execution_id
order by 6 desc