Select Case in SQL Statement - sql

I have the below SQL query that returns the following output for employee time ATTENDANCE.
Note when InOut = '0' it means in, and if InOut = '1' it means out.
Query:
SELECT
DATEPART(mi, LogTime) AS [InMin],
OutletName as [InOutletName]
FROM
[dbo].[AccessLog]
INNER JOIN
dbo.Outlets ON dbo.Outlets.OutletCode = dbo.AccessLog.TerminalID
WHERE
DATEPART(HOUR, LogTime) = '15'
AND InOut = '0'
AND CAST(LogDate AS date) = '2016-12-01'
Output:
InMin InOutletName
--------------------
47 GJ-SH1
The output I am looking for in to get OutMin, OutOutletName and this can be applied when InOut = '1'
Desired output:
InMin InOutletName OutMin OutOutletName
-----------------------------------------
47 GJ-SH1 10 GJ-SH1

I didn't get a chance to try it out in my SSMS, but required query should be something like this -
SELECT
Case When InOut = '0' Then DATEPART(mi, LogTime) End AS [InMin],
Case When InOut = '0' Then OutletName End as [InOutletName],
Case When InOut = '1' Then DATEPART(mi, LogTime) End AS [OutMin],
Case When InOut = '1' Then OutletName End as [OutOutletName]
FROM
[dbo].[AccessLog]
INNER JOIN
dbo.Outlets ON dbo.Outlets.OutletCode = dbo.AccessLog.TerminalID
WHERE
DATEPART(HOUR, LogTime) = '15'
AND CAST(LogDate AS date) = '2016-12-01';

A simple solution would be to join the AccessLog table twice:
SELECT
DATEPART(mi, ali.LogTime) AS [InMin],
DATEPART(mi, alo.LogTime) AS [OutMin],
OutletName as [InOutletName]
FROM dbo.Outlets
INNER JOIN [dbo].[AccessLog] ali
ON dbo.Outlets.OutletCode = ali.TerminalID
LEFT JOIN [dbo].[AccessLog] alo
ON dbo.Outlets.OutletCode = al0.TerminalID AND CAST(ali.LogDate AS date) = CAST(alo.LogDate AS date) AND alo.InOut = '1'
WHERE DATEPART(HOUR, LogTime) = '15'
AND ali.InOut = '0'
AND CAST(ali.LogDate AS date) = '2016-12-01'
Note that for the AccessLog representing the out date I've used a left join, so that you will also get Outlet names that hasn't logged out yet.

I just wrote it on the fly, not sure about the result but you can try it. It may help:
;with InQuery as (SELECT
DATEPART(mi, LogTime) AS [InMin],
OutletName as [InOutletName]
FROM [dbo].[AccessLog]
INNER JOIN dbo.Outlets
ON dbo.Outlets.OutletCode = dbo.AccessLog.TerminalID
WHERE DATEPART(HOUR, LogTime) = '15'
AND InOut = '0'
AND CAST(LogDate AS date) = '2016-12-01'),
OutQuery As (SELECT
DATEPART(mi, LogTime) AS [OutMin],
OutletName as [OutOutletName]
FROM [dbo].[AccessLog]
INNER JOIN dbo.Outlets
ON dbo.Outlets.OutletCode = dbo.AccessLog.TerminalID
WHERE DATEPART(HOUR, LogTime) = '15'
AND InOut = '1'
AND CAST(LogDate AS date) = '2016-12-01')
select iq.*, oq.* from InQuery iq, OutQuery oq

Related

classify interval Day and Night SQL server

I have a time frame :
FROMTIME:06:01:00 - TOTIME:23:59:00 = DAY
FROMTIME:23:59:01 - TOTIME:06:00:00 = Night
I have a TIME_IN and TIME_OUT by a Car .
How to classify interval time by car is DAY or Night or DayAndNight
. I use code but it's execute very long time :
Declare #From_Time_Day time
,#To_Time_Day time
,#From_Time_Night time
,#To_Time_Night time
,#Midnight time
set #From_Time_Day = (select FROM_TIME from DAY_STATUS where DAY_CHECK=1)
set #To_Time_Day = (select TO_TIME from DAY_STATUS where DAY_CHECK=1)
set #From_Time_Night = (select FROM_TIME from DAY_STATUS where DAY_CHECK=2)
set #To_Time_Night = (select TO_TIME from DAY_STATUS where DAY_CHECK=2)
set #Midnight = '00:00:00'
select * from (
select
(case when (
cast((select top 1 IO_TIME from IO_INFO where IO_STATUS= 'IN' and CA_ID = Data.CA_ID and IO_ID < Data.IO_ID order by IO_ID desc) as date)
= cast(Data.IO_TIME as date)
)
then
(
case when (
cast((select top 1 IO_TIME from IO_INFO where IO_STATUS= 'IN' and CA_ID = Data.CA_ID and IO_ID < Data.IO_ID order by IO_ID desc) as time) >= #From_Time_Day
and cast(Data.IO_TIME as time) <=#To_Time_Day
) then 'DAY'
when (
cast((select top 1 IO_TIME from IO_INFO where IO_STATUS= 'IN' and CA_ID = Data.CA_ID and IO_ID < Data.IO_ID order by IO_ID desc) as time) >= #From_Time_Night
and cast(Data.IO_TIME as time) < #Midnight
) then 'Night'
when (
cast((select top 1 IO_TIME from IO_INFO where IO_STATUS= 'IN' and CA_ID = Data.CA_ID and IO_ID < Data.IO_ID order by IO_ID desc) as time) >= #Midnight
and cast(Data.IO_TIME as time) <= #To_Time_Night
) then 'Night'
else 'DayAndNight' end
)
when (
cast((select top 1 IO_TIME from IO_INFO where IO_STATUS= 'IN' and CA_ID = Data.CA_ID and IO_ID < Data.IO_ID order by IO_ID desc) as date)
<> cast(Data.IO_TIME as date)
)
then (
case when(
(cast((select top 1 IO_TIME from IO_INFO where IO_STATUS= 'IN' and CA_ID = Data.CA_ID and IO_ID < Data.IO_ID order by IO_ID desc) as time) >= #From_Time_Night and
cast((select top 1 IO_TIME from IO_INFO where IO_STATUS= 'IN' and CA_ID = Data.CA_ID and IO_ID < Data.IO_ID order by IO_ID desc) as time) < #Midnight)
and (cast(Data.IO_TIME as time)>=#Midnight and cast(Data.IO_TIME as time)<=#To_Time_Night)
) then 'Night'
else 'DayAndNight' end
)
end
) as INTERVAL
from IO_INFO as Data where IO_STATUS = 'OUT'
Okay, seeing that this is a Tricky type of query I've lost some time to find a solution, I've Created a Table in a test database with 4 fields, ID, Car, TimeOut, TimeIn, I've inserted some rows in this table to have samples and then I've made a Query that returns the state of the period for each travel of my cars.
Here is the Query:
SELECT [ID]
,[Car]
,[TimeOut]
,[TimeIn]
, MultiDay
,OutDAyNight
,InDAyNight
, CASE WHEN Multiday = 1 THEN 'Day/Night'
WHEN OutDAyNight+InDAyNight = 1 THEN 'Day/Night'
WHEN OutDAyNight+InDAyNight = 2 THEN 'Day'
WHEN OutDAyNight+InDAyNight = 0 THEN 'Night' END AS Periods
FROM
(SELECT [ID]
,[Car]
,[TimeOut]
,[TimeIn]
, CASE WHEN DATEDIFF(hour, [TimeOut],[TimeIn]) > 18
THEN 1 ELSE 0 END AS MultiDay
, CASE WHEN DATEPART(hour, [TimeOut]) > 6 AND
DATEPART(hour, [TimeOut]) <=23 THEN 1
WHEN DATEPART(hour, [TimeOut]) = 6 AND
DATEPART(minute, [TimeOut]) > 0 THEN 1
ELSE 0 END AS OutDAyNight
, CASE WHEN DATEPART(hour, [TimeIn]) > 6 AND
DATEPART(hour, [TimeIn]) <=23 THEN 1
WHEN DATEPART(hour, [TimeIn]) = 6 AND
DATEPART(minute, [TimeIn]) > 0 THEN 1
ELSE 0 END AS InDAyNight
FROM [dbo].[TbCarInOut] ) Times
Using this double query, In the first select (the lower one in the script) I Used the Datediff function to see if the time between exit and return of the car is more than 18 hours, this surely means there is a day and night period. Then I use the Datepart function to understand if the out time is Night or Day setting a 0 for Night and 1 for day and I do the same for the time In, this way, in the second query, I can use a simple case and a sum to decide if the period is Day/Night, Day or Night.
HTH

SQL count date time

I have to concatenate the date and time portion of two fields, which I have managed to do, then I need to test if the result is < getdate()
select count(cast(cast(DischargeDatenew as date) as datetime) + cast(DischargeTime as time))as Requiredby FROM [dbo].[Main]
where Location = 'Home' and ScriptTypeID = '1' and Requiredby < GETDATE()
Unfortunately the second Requiredby comes up as an invalid column name. How can I get this query to work? Do I need a subquery?
You don't need the calculation in COUNT, simply move it to WHERE:
select count(*)
FROM [dbo].[Main]
where Location = 'Home' and ScriptTypeID = '1'
and cast(cast(DischargeDatenew as date) as datetime)
+ cast(DischargeTime as time) < GETDATE()
Yes you will need subquery:
select * from (select someExpression as Requiredby
FROM [dbo].[Main]
where Location = 'Home' and ScriptTypeID = '1') t
where Requiredby < GETDATE()
But I really think you want this:
select sum(case when cast(cast(DischargeDatenew as date) as datetime) +
cast(DischargeTime as time) < getdate() then 1
else 0 end) as Requiredby
FROM [dbo].[Main]
where Location = 'Home' and ScriptTypeID = '1'

sql join and group by generated date range

I have Table1 and I need a query to populate Table2:
Problem here is with Date column. I want to know the process of location/partner combination per day. Main issue here is that I can't pick DateCreated and make it as default date since it doesn't necessarily cover whole date range, like in this example where it doesn't have 2015-01-07 and 2015-01-09. Same case with other dates.
So, my idea is to first select dates from some table which contains needed date range and then perform calculation for each day/location/partner combination from cte but in that case I can't figure out how to make a join for LocationId and PartnerId.
Columns:
Date - CreatedItems - number of created items where Table1.DateCreated = Table2.Date
DeliveredItems - number of delivered items where Table1.DateDateOut = Table2.Date
CycleTime - number of days delivered item was in the location (DateOut - DateIn + 1)
I started with something like this but it's very like that I completely missed the point with it:
with d as
(
select date from DimDate
where date between DATEADD(DAY, -365, getdate()) and getdate()
),
cr as -- created items
(
select
DateCreated,
LocationId,
PartnerId,
CreatedItems = count(*)
from Table1
where DateCreated is not null
group by DateCreated,
LocationId,
PartnerId
),
del as -- delivered items
(
select
DateOut,
LocationId,
ParnerId,
DeliveredItems = count(*),
CycleTime = DATEDIFF(Day, DateOut, DateIn)
from Table1
where DateOut is not null
and Datein is not null
group by DateOut,
LocationId,
PartnerId
)
select
d.Date
from d
LEFT OUTER JOIN cr on cr.DateCreated = d.Date -- MISSING JOIN PER LocationId and PartnerId
LEFT OUTER JOIN del on del.DateCompleted = d.Date -- MISSING JOIN PER LocationId and PartnerId
with range(days) as (
select 0 union all select 1 union all select 2 union all
select 3 union all select 4 union all select 5 union all
select 6 /* extend as necessary */
)
select dateadd(day, r.days, t.DateCreated) as "Date", locationId, PartnerId,
sum(
case
when dateadd(day, r.days, t.DateCreated) = t.DateCreated
then 1 else 0
end) as CreatedItems,
sum(
case
when dateadd(day, r.days, t.DateCreated) = t.Dateout
then 1 else 0
end) as DeliveredItems,
sum(
case
when dateadd(day, r.days, t.DateCreated) = t.Dateout
then datediff(days, t.DateIn, t.DateOut) + 1 else 0
end) as CycleTime
from
<yourtable> as t
inner join range as r
on r.days between 0 and datediff(day, t.DateCreated, t.DateOut)
group by dateadd(day, r.days, t.DateCreated), LocationId, PartnerId;
If you only want the end dates (rather than all the dates in between) this is probably a better approach:
with range(dt) as (
select distinct DateCreated from T union
select distinct DateOut from T
)
select r.dt as "Date", locationId, PartnerId,
sum(
case
when r.dt = t.DateCreated
then 1 else 0
end) as CreatedItems,
sum(
case
when r.dt = t.Dateout
then 1 else 0
end) as DeliveredItems,
sum(
case
when r.dt = t.Dateout
then datediff(days, t.DateIn, t.DateOut) + 1 else 0
end) as CycleTime
from
<yourtable> as t
inner join range as r
on r.dt in (t.DateCreated, t.DateOut)
group by r.dt, LocationId, PartnerId;
If to specify WHERE clause? Something Like that:
WHERE cr.LocationId = del.LocationId AND
cr.PartnerId = del.PartnerId

Calculate total business working days between two dates

select count(distinct(dateadd(d, 0, datediff(d, 0,checktime)))) as workingdays
from departments,
dbo.USERINFO INNER JOIN dbo.CHECKINOUT ON
dbo.USERINFO.USERID = dbo.CHECKINOUT.USERID
where userinfo.name='Gokul Gopalakrishnan' and deptname='GEN/SUP-TBL'
and checktime>='2014-05-01' and checktime<='2014-05-30'
from the above code I am able to find total working days of employee between two dates.
workingdays
20
but now I want other column name total business days. I want to calculate total business days between two dates.
workingdays businessdays
20 21
how can i do this?
If you only want to exclude weekends then you can simply just exclude these using a conditional count by adding:
count(distinct case when datepart(weekday, getdate()) <= 5 then date end)
So your query becomes:
set datefirst 1;
select count(distinct(dateadd(d, 0, datediff(d, 0,checktime)))) as workingdays,
count(distinct case when datepart(weekday, getdate()) <= 5
then dateadd(d, 0, datediff(d, 0,checktime))
end) as weekdays
from departments,
dbo.USERINFO INNER JOIN dbo.CHECKINOUT ON
dbo.USERINFO.USERID = dbo.CHECKINOUT.USERID
where userinfo.name='Gokul Gopalakrishnan' and deptname='GEN/SUP-TBL'
and checktime>='2014-05-01' and checktime<='2014-05-30'
HOWEVER I would really recommend adding a calendar table to your database. It makes everything so easy, your query would become:
SELECT DaysWorked = COUNT(cio.Date),
WeekDaysWorked = COUNT(CASE WHEN c.IsWeekDay = 1 THEN cio.Date END),
WorkingDaysWorked = COUNT(CASE WHEN c.IsWorkingDay = 1 THEN cio.Date END),
TotalDays = COUNT(*),
TotalWeekDays = COUNT(CASE WHEN c.IsWeekDay = 1 THEN 1 END),
TotalWorkingDays = COUNT(CASE WHEN c.IsWorkingDay = 1 THEN 1 END)
FROM dbo.Calender AS c
LEFT JOIN
( SELECT DISTINCT
Date = CAST(CheckTime AS DATE)
FROM dbo.Departments AS d
CROSS JOIN dbo.userInfo AS ui
INNER JOIN dbo.CheckInOut AS cio
ON cio.UserID = ui.UserID
WHERE ui.Name = 'Gokul Gopalakrishnan'
AND d.deptname = 'GEN/SUP-TBL'
) AS cio
ON c.Date = cio.Date
WHERE d.Date >= '2014-05-01'
AND d.Date <= '2014-05-30';
This way you can define public holidays, weekends, etc. It is so much more flexible than any other solution.
EDIT
I think I misunderstood your original criteria. This should work for you with no calendar table:
SET DATEFIRST 1;
DECLARE #StartDate DATE = '2014-05-01',
#EndDate DATE = '2014-05-30';
DECLARE #Workdays INT =
(DATEDIFF(DAY, #StartDate, #EndDate) + 1)
-(DATEDIFF(WEEK, #StartDate, #EndDate) * 2)
-(CASE WHEN DATEPART(WEEKDAY, #StartDate) = 7 THEN 1 ELSE 0 END)
-(CASE WHEN DATEPART(WEEKDAY, #EndDate) = 6 THEN 1 ELSE 0 END);
SELECT WorkingDays = COUNT(DISTINCT CAST(CheckTime AS DATE)),
BusinessDays = #Workdays
FROM dbo.Departments AS d
CROSS JOIN dbo.userInfo AS ui
INNER JOIN dbo.CheckInOut AS cio
ON cio.UserID = ui.UserID
WHERE ui.Name = 'Gokul Gopalakrishnan'
AND d.deptname = 'GEN/SUP-TBL'
AND cio.CheckTime >= #StartDate
AND cio.CheckTime <= #EndDate;
following query calculate Fridays count between #FromDate and #ToDate variable
((DATEDIFF(DAY,#FromDate,#ToDate)-(6-DATEPART(dw,#FromDate)))/7)*2
Following query calculate Working day count and business day count between to date :
DECLARE #FromDate DATE = '2014-05-01',
#ToDate DATE = '2014-05-30'
SELECT COUNT(DISTINCT CAST(checktime AS Date)) as workingdays,
DATEDIFF(DAY,#FromDate,#ToDate) -
((DATEDIFF(DAY,#FromDate,#ToDate)-(6-DATEPART(dw,#FromDate)))/7)*2 AS BusinessDay
from departments,
dbo.USERINFO INNER JOIN dbo.CHECKINOUT ON
dbo.USERINFO.USERID = dbo.CHECKINOUT.USERID
where userinfo.name='Gokul Gopalakrishnan' and deptname='GEN/SUP-TBL'
and checktime>= #FromDate and checktime<=#ToDate

SQL Date Order Correction

Working on a SQL statement here and I have sort of a stupid question. Ive got this date field that spits out various dates from years and months etc. I'm trying to order them correctly but i get only the month in order. Example is:
01-05-2012
12-30-2011
12-18-2011
11-25-2011
11-24-2011
Etc.
My query is as follows:
SELECT TOP (100) PERCENT CONVERT(VARCHAR(10), A.tran_end_time, 110) AS Date
FROM dbo.ttdpur040101_CT AS A INNER JOIN
dbo.ttdpur040101_Audit AS B ON NOT (A.tran_begin_time > B.event_time_local OR
A.tran_end_time < B.event_time_local) AND (A.__$operation = 2 AND B.action_id = 'IN' OR
(A.__$operation = 3 OR
A.__$operation = 4) AND B.action_id = 'UP' OR
A.__$operation = 1 AND B.action_id = 'DL') AND B.class_type = 'U'
WHERE (B.server_principal_name = #Name)
GROUP BY CONVERT(VARCHAR(10), A.tran_end_time, 110)
ORDER BY Date
I would like to have it shown as follows:
11-24-2011
11-25-2011
12-18-2011
12-25-2011
01-08-2012
01-09-2012
etc.
Thanks
You are ordering by a Date column that has been converted to a VARCHAR(). Instead order by the original date column:
ORDER BY A.tran_end_time ASC
That's because Date is a varchar.
Try it this way:
SELECT TOP (100) PERCENT CONVERT(VARCHAR(10), A.tran_end_time, 110) AS Date
FROM dbo.ttdpur040101_CT AS A INNER JOIN
dbo.ttdpur040101_Audit AS B ON NOT (A.tran_begin_time > B.event_time_local OR
A.tran_end_time < B.event_time_local) AND (A.__$operation = 2 AND B.action_id = 'IN' OR
(A.__$operation = 3 OR
A.__$operation = 4) AND B.action_id = 'UP' OR
A.__$operation = 1 AND B.action_id = 'DL') AND B.class_type = 'U'
WHERE (B.server_principal_name = #Name)
GROUP BY CONVERT(VARCHAR(10), A.tran_end_time, 110)
ORDER BY CONVERT(DATETIME, Date, 103) ASC
SELECT CONVERT(VARCHAR(10), [Date], 110) FROM (
SELECT TOP (100) PERCENT DATEADD(dd, datediff(dd, 0, A.tran_end_time), 0) AS Date
FROM dbo.ttdpur040101_CT AS A
INNER JOIN dbo.ttdpur040101_Audit AS B ON
NOT (A.tran_begin_time > B.event_time_local OR
A.tran_end_time < B.event_time_local) AND
(A.__$operation = 2 AND B.action_id = 'IN' OR
(A.__$operation = 3 OR A.__$operation = 4) AND
B.action_id = 'UP' OR
A.__$operation = 1 AND
B.action_id = 'DL'
) AND
B.class_type = 'U'
WHERE (B.server_principal_name = #Name)
GROUP BY DATEADD(dd, datediff(dd, 0, A.tran_end_time), 0)
) t
ORDER BY [Date]
Quite important is "rounding" to date part only. Faster than converting to varchar is DATEADD(dd, datediff(dd, 0, A.tran_end_time), 0) - because it's just math that SQL Server can do much faster than varchar manipulation.