My SQL Query is working on one date, but I want start date to end date - sql

I am using SQL Server 2005
I have two tables:
CheckInOut
TR BadgeNum USERID Dated Time CHECKTYPE
------- --------- ------ ----------------------- ----------------------- ----------
2337334 4 1 2018-04-01 00:00:00.000 2018-04-14 10:10:58.000 I
2337334 4 1 2018-04-01 00:00:00.000 2018-04-14 18:10:00.000 O
2337334 4 1 2018-04-02 00:00:00.000 2018-04-14 10:00:10.000 I
2337335 4 1 2018-04-02 00:00:00.000 2018-04-14 18:14:27.000 O
2337336 4 1 2018-04-03 00:00:00.000 2018-04-14 10:22:10.000 I
2337334 4 1 2018-04-03 00:00:00.000 2018-04-14 18:03:11.000 O
2337337 44 5 2018-04-01 00:00:00.000 2018-04-14 09:27:03.000 I
2337337 44 5 2018-04-01 00:00:00.000 2018-04-14 18:27:42.000 O
2337337 44 5 2018-04-02 00:00:00.000 2018-04-14 10:00:50.000 I
2337337 44 5 2018-04-02 00:00:00.000 2018-04-14 18:02:25.000 O
2337337 44 5 2018-04-03 00:00:00.000 2018-04-14 08:58:36.000 I
2337337 44 5 2018-04-03 00:00:00.000 2018-04-14 18:12:18.000 O
UserInfo
Tr UserID BadgeNumber Name
----- ------- ----------- --------------
13652 44 5 SAMIA NAZ
13653 4 1 Waqar Yousufzai
I need to calculate presence hours for each day for each user. My below query is working fine for given day. But I need to calculate for a given range. How do I get expected result?
Select isnull(max(ch.userid), 0)As 'ID'
,isnull(max(ch.badgenum), 0)as 'Badge#'
,isnull(max(convert(Char(10), ch.dated, 103)), '00:00')as 'Date'
,isnull(max(ui.name),'Empty')as 'Name'
,isnull(min(convert(VARCHAR(26), ch.time, 108)), '00:00') as 'Time In'
,case when min(ch.time) = max(ch.time) then '' else isnull(max(convert(VARCHAR(26), ch.time, 108)), '00:00') end as 'TimeOut'
,case when min(ch.time) = max(ch.time) then 'Absent' else 'Present' end as 'Status'
,isnull(CONVERT(varchar(3),DATEDIFF(minute,min(ch.time), max(ch.time))/60) + ' hrs and ' +
RIGHT('0' + CONVERT(varchar(2),DATEDIFF(minute,min(ch.time),max(ch.time))%60),2) + 'Min' , 0) as 'Total Hrs'
From CHECKINOUT ch left Join userinfo ui on ch.badgenum = ui.badgenumber
Where ch.Dated between '2018-04-01' and '2018-04-03' GROUP BY ch.badgenum
Query result
ID Badge# Date Name Time In TimeOut Status Total Hrs
--- ------ ---------- --------------- -------- ---------- -------- -----------------
4 1 03/04/2018 Waqar Yousufzai 11:33:34 18:24:23 Present 30 hrs and 14Min
82 3 03/04/2018 TANVEER ANSARI 09:37:14 19:18:22 Present 32 hrs and 37Min
13 4 03/04/2018 07:19:26 09:30:17 Present 21 hrs and 49Min
44 5 03/04/2018 SAMIA NAZ 08:53:15 18:25:21 Present 33 hrs and 24Min
28 7 03/04/2018 Anees Ahmad 08:34:57 22:00:38 Present 61 hrs and 25Min
46 8 03/04/2018 Shazia - OT 08:10:41 16:15:05 Present 32 hrs and 01Min
Expected result
ID Badge# Date Name Time In TimeOut Status Total Hrs
--- ------ ---------- --------------- -------- ---------- -------- -----------------
4 1 01/04/2018 Waqar Yousufzai 10:30:00 18:00:00 Present 7 hrs and 30Min
4 1 02/04/2018 Waqar Yousufzai 10:30:00 18:00:00 Present 7 hrs and 30Min
4 1 03/04/2018 Waqar Yousufzai 10:00:00 18:00:00 Present 8 hrs and 00Min
44 5 01/04/2018 SAMIA 08:00:00 18:00:00 Present 10 hrs and 00Min
44 5 02/04/2018 SAMIA 08:30:00 18:00:00 Present 9 hrs and 30Min
44 5 03/04/2018 SAMIA 08:00:00 18:00:00 Present 10 hrs and 00Min

You shouldn't do aggregation on date value, it must be part of grouping. Get time out and time in using conditional aggregation. And count total hours worked. Your query should be something like:
select
BadgeNum, USERID, Dated, Name
, right('0' + cast(datediff(mi, [in], [out]) / 60 as varchar(10)), 2) + ':'
+ right('0' + cast(datediff(mi, [in], [out]) % 60 as varchar(10)), 2)
from (
select
ch.BadgeNum, ch.USERID, dated = cast(ch.Dated as date), ui.Name
, [in] = min(case when ch.CHECKTYPE = 'I' then ch.Time end)
, [out] = min(case when ch.CHECKTYPE = 'O' then ch.Time end)
from
CheckInOut ch
left join UserInfo ui on ch.USERID = ui.badgenumber
where
ch.Dated >= '20180401'
and ch.Dated < '20180404'
group by ch.BadgeNum, ch.USERID, cast(ch.Dated as date), ui.Name
) t

Related

Count median days per ID between one zero and the first transaction after the last zero in a running balance

I have a running balance sheet showing customer balances after inflows and (outflows) by date. It looks something like this:
ID DATE AMOUNT RUNNING AMOUNT
-- ---------------- ------- --------------
10 27/06/2019 14:30 100 100
10 29/06/2019 15:26 -100 0
10 03/07/2019 01:56 83 83
10 04/07/2019 17:53 15 98
10 05/07/2019 15:09 -98 0
10 05/07/2019 15:53 98.98 98.98
10 05/07/2019 19:54 -98.98 0
10 07/07/2019 01:36 90.97 90.97
10 07/07/2019 13:02 -90.97 0
10 07/07/2019 16:32 39.88 39.88
10 08/07/2019 13:41 50 89.88
20 08/01/2019 09:03 890.97 890.97
20 09/01/2019 14:47 -91.09 799.88
20 09/01/2019 14:53 100 899.88
20 09/01/2019 14:59 -399 500.88
20 09/01/2019 18:24 311 811.88
20 09/01/2019 23:25 50 861.88
20 10/01/2019 16:18 -861.88 0
20 12/01/2019 16:46 894.49 894.49
20 25/01/2019 05:40 -871.05 23.44
I have attempted using lag() but I seem not to understand how to use it yet.
SELECT ID, MEDIAN(DIFF) MEDIAN_AGE
FROM
(
SELECT *, DATEDIFF(day, Lag(DATE, 1) OVER(ORDER BY ID), DATE
)AS DIFF
FROM TABLE 1
WHERE RUNNING AMOUNT = 0
)
GROUP BY ID;
The expected result would be:
ID MEDIAN_AGE
-- ----------
10 1
20 2
Please help in writing out the query that gives the expected result.
As already pointed out, you are using syntax that isn't valid for Oracle, including functions that don't exist and column names that aren't allowed.
You seem to want to calculate the number of days between a zero running-amount and the following non-zero running-amount; lead() is probably easier than lag() here, and you can use a case expression to only calculate it when needed:
select id, date_, amount, running_amount,
case when running_amount = 0 then
lead(date_) over (partition by id order by date_) - date_
end as diff
from your_table;
ID DATE_ AMOUNT RUNNING_AMOUNT DIFF
---------- -------------------- ---------- -------------- ----------
10 2019-06-27 14:30:00 100 100
10 2019-06-29 15:26:00 -100 0 3.4375
10 2019-07-03 01:56:00 83 83
10 2019-07-04 17:53:00 15 98
10 2019-07-05 15:09:00 -98 0 .0305555556
10 2019-07-05 15:53:00 98.98 98.98
10 2019-07-05 19:54:00 -98.98 0 1.2375
10 2019-07-07 01:36:00 90.97 90.97
10 2019-07-07 13:02:00 -90.97 0 .145833333
10 2019-07-07 16:32:00 39.88 39.88
10 2019-07-08 13:41:00 50 89.88
20 2019-01-08 09:03:00 890.97 890.97
20 2019-01-09 14:47:00 -91.09 799.88
20 2019-01-09 14:53:00 100 899.88
20 2019-01-09 14:59:00 -399 500.88
20 2019-01-09 18:24:00 311 811.88
20 2019-01-09 23:25:00 50 861.88
20 2019-01-10 16:18:00 -861.88 0 2.01944444
20 2019-01-12 16:46:00 894.49 894.49
20 2019-01-25 05:40:00 -871.05 23.44
Then use the median() function, rounding if desired to get your expected result:
select id, median(diff) as median_age, round(median(diff)) as median_age_rounded
from (
select id, date_, amount, running_amount,
case when running_amount = 0 then
lead(date_) over (partition by id order by date_) - date_
end as diff
from your_table
)
group by id;
ID MEDIAN_AGE MEDIAN_AGE_ROUNDED
---------- ---------- ------------------
10 .691666667 1
20 2.01944444 2
db<>fiddle

Problem in formatting result set in desired format

I have devices which are installed in a different location with different area ID I am returning the device activity status of different areas on an hourly basis in the following format.
AreaId | UpdatedOn | DeviceStatus
1 | 2018-08-08 00:00:00.000 | Active
1 | 2018-08-08 01:00:00.000 | Active
1 | 2018-08-08 02:00:00.000 | Active
2 | 2018-08-08 00:00:00.000 | Inactive
2 | 2018-08-08 01:00:00.000 | Active
2 | 2018-08-08 02:00:00.000 | Active
3 | 2018-08-08 00:00:00.000 | Active
3 | 2018-08-08 01:00:00.000 | Inactive
3 | 2018-08-08 02:00:00.000 | Inactive
As its clearly visible that Device 1 was active throughout. Device 2 was not active from 00.00 to 01.00 Hrs
and device 3 was not active from 01.00 to 02.00 and 02.00 to 03.00 Hrs.
I want to show this data in the following format.
Status of devices for 8 August 2018 0:00 Hrs to 03:00 Hrs
Areaid | Status
1 | Active
2 | Not Active between 00:00 hrs to 01:00 hrs
3 | Not Active between 01:00 hrs to 02:00 hrs and 02:00 hrs to 03:00 hrs
for all the Areas.
How can I achieve this?
You can try to use STUFF function in subquery to get DeviceStatus = 'Inactive' row data, then outer join base on AreaId table
SELECT t2.AreaId, coalesce(Status,'Active') Status
FROM (
SELECT distinct AreaId,DeviceStatus,
STUFF((
SELECT ' and ' + CONVERT(VARCHAR(5),UpdatedOn,108) + ' hrs' + ' to ' + CONVERT(VARCHAR(5),DATEADD(HOUR,1,UpdatedOn),108) + ' hrs'
FROM T tt
WHERE tt.AreaId = t1.AreaId and tt.DeviceStatus = t1.DeviceStatus
FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)')
,1,4,'') Status
FROM T t1
WHERE DeviceStatus = 'Inactive'
) t1 RIGHT JOIN
(
SELECT distinct AreaId
FROM T
) t2
on t1.AreaId = t2.AreaId
sqlfiddle
Result
AreaId Status
1 Active
2 00:00 hrs to 01:00 hrs
3 01:00 hrs to 02:00 hrs and 02:00 hrs to 03:00 hrs
You can try below using lag() function
DEMO
select id,concat('Not Active between ', format(cast(prevd as datetime),' hh:mm '), 'hrs to ', format(cast(d as datetime),' hh:mm '), 'hrs') from
(select *,
LAG (d, 1, 0) OVER (PARTITION BY id ORDER BY d) prevd,
LAG (p, 1, 0) OVER (PARTITION BY id ORDER BY d) prevp
from cte1
)a where prevp<>'0' and p<>prevp
Output:
id status
1 Not Active between 01:00 hrs to 02:00 hrs
2 Not Active between 01:00 hrs to 02:00 hrs

Get temperature from live data if available, else avg over historical data

I am trying to get either live temperature for a trip, if live data is not available get an average temperature from histroical data.
I have made a simple version of my problem, with these tabels:
Trip
id departure_time arrival_time location_id
1 2018-04-07 07:00:00 2018-04-14 17:00:00 1
2 2018-04-14 07:00:00 2018-04-21 17:00:00 1
Location
id name
1 Location
Weather
id temperature date location_id
1 20 2018-04-07 1
2 20 2018-04-08 1
3 20 2018-04-09 1
4 20 2018-04-10 1
5 20 2018-04-11 1
6 20 2018-04-12 1
7 20 2018-04-13 1
8 20 2018-04-14 1
9 15 2016-04-07 1
10 15 2016-04-08 1
11 15 2016-04-09 1
12 15 2016-04-10 1
13 15 2016-04-11 1
14 15 2016-04-12 1
15 15 2016-04-13 1
16 15 2016-04-14 1
17 19 2017-04-07 1
18 19 2017-04-08 1
19 19 2017-04-09 1
20 19 2017-04-10 1
21 19 2017-04-11 1
22 19 2017-04-12 1
23 19 2017-04-13 1
24 19 2017-04-14 1
25 15 2017-04-15 1
26 15 2017-04-16 1
27 15 2017-04-17 1
28 15 2017-04-18 1
29 15 2017-04-19 1
30 15 2017-04-20 1
31 15 2017-04-21 1
32 19 2016-04-15 1
33 19 2016-04-16 1
34 19 2016-04-17 1
35 19 2016-04-18 1
36 19 2016-04-19 1
37 19 2016-04-20 1
38 19 2016-04-21 1
The problem i am having is that since these trips are last-minute trips i have "live" data for trips departing within the next week.
So i would like to get a either live forecast if available, else an avg for the temperature from the years from the previous years.
http://sqlfiddle.com/#!17/bce59/3
Here is the approach i took in order to try and solve the problem.
If any details has been forgotten please ask.
Expected result:
id departure_time arrival_time location_id temperature
1 2018-04-07 07:00:00 2018-04-14 17:00:00 1 20
1 2018-04-07 07:00:00 2018-04-14 17:00:00 1 20
1 2018-04-07 07:00:00 2018-04-14 17:00:00 1 20
1 2018-04-07 07:00:00 2018-04-14 17:00:00 1 20
1 2018-04-07 07:00:00 2018-04-14 17:00:00 1 20
1 2018-04-07 07:00:00 2018-04-14 17:00:00 1 20
1 2018-04-07 07:00:00 2018-04-14 17:00:00 1 20
1 2018-04-07 07:00:00 2018-04-14 17:00:00 1 20
2 2018-04-14 07:00:00 2018-04-21 17:00:00 1 20
2 2018-04-14 07:00:00 2018-04-21 17:00:00 1 17
2 2018-04-14 07:00:00 2018-04-21 17:00:00 1 17
2 2018-04-14 07:00:00 2018-04-21 17:00:00 1 17
2 2018-04-14 07:00:00 2018-04-21 17:00:00 1 17
2 2018-04-14 07:00:00 2018-04-21 17:00:00 1 17
2 2018-04-14 07:00:00 2018-04-21 17:00:00 1 17
2 2018-04-14 07:00:00 2018-04-21 17:00:00 1 17
Using generate_series function to make a Calendar from trip table on subquery.
Then Left JOIN on subquery by dates you might get match weather you can get it temperature. if temperature is null on w.temperature then get avg temperature
You can try this.
SELECT t.id,
t.departure_time,
t.arrival_time,
l.id as "location_id",
coalesce(w.temperature,(select FLOOR(avg(temperature)) from weather)) as "temperature"
FROM
location l inner join
(
select id,
location_id,
departure_time,
arrival_time,
generate_series(departure_time :: timestamp,arrival_time::timestamp,'1 day'::interval) as dates
from trip
) t on t.location_id = l.id LEFT JOIN weather w on t.dates::date = w.date::date
sqlfiddle:http://sqlfiddle.com/#!17/bce59/48
EDIT
You could use a CTE query get Avg by year instead of the subquery in coalesce function on select clause.
WITH weather_avg AS (
SELECT floor(avg(a)) avgTemp
from
(
SELECT
extract(YEAR from weather.date) AS YEAR,
floor(avg(weather.temperature)) a
FROM weather
group by extract(YEAR from weather.date)
) t
)
SELECT t.id,
t.departure_time,
t.arrival_time,
t.location_id as "location_id",
coalesce(w.temperature,(select avgTemp from weather_avg)) as "temperature"
FROM
(
select t.id,
t.location_id,
t.departure_time,
t.arrival_time,
generate_series(departure_time :: timestamp,arrival_time::timestamp,'1 day'::interval) as dates
from trip t inner join location l on t.location_id = l.id
) t LEFT JOIN weather w
on t.dates::date = w.date::date
sqlfiddle:http://sqlfiddle.com/#!17/bce59/76

how to find the date difference in hours between two records with nearest datetime value and it must be compared in same group

How to find the date difference in hours between two records with nearest datetime value and it must be compared in same group?
Sample Data as follows:
Select * from tblGroup
Group FinishedDatetime
1 03-01-2009 00:00
1 13-01-2009 22:00
1 08-01-2009 03:00
2 01-01-2009 10:00
2 13-01-2009 20:00
2 10:01-2009 10:00
3 27-10-2008 00:00
3 29-10-2008 00:00
Expected Output :
Group FinishedDatetime Hours
1 03-01-2009 00:00 123
1 13-01-2009 22:00 139
1 08-01-2009 03:00 117
2 01-01-2009 10:00 216
2 13-01-2009 20:00 82
2 10:01-2009 10:00 82
3 27-10-2008 00:00 48
3 29-10-2008 00:00 48
Try this:
Select t1.[Group], DATEDIFF(HOUR, z.FinishedDatetime, t1.FinishedDatetime)
FROM tblGroup t1
OUTER APPLY(SELECT TOP 1 *
FROM tblGroup t2
WHERE t2.[Group] = t1.[Group] AND t2.FinishedDatetime<t1.FinishedDatetime
ORDER BY FinishedDatetime DESC)z

Transposing SQLite rows and columns with average per hour

I have a table in SQLite called param_vals_breaches that looks like the following:
id param queue date_time param_val breach_count
1 c a 2013-01-01 00:00:00 188 7
2 c b 2013-01-01 00:00:00 156 8
3 c c 2013-01-01 00:00:00 100 2
4 d a 2013-01-01 00:00:00 657 0
5 d b 2013-01-01 00:00:00 23 6
6 d c 2013-01-01 00:00:00 230 12
7 c a 2013-01-01 01:00:00 100 0
8 c b 2013-01-01 01:00:00 143 9
9 c c 2013-01-01 01:00:00 12 2
10 d a 2013-01-01 01:00:00 0 1
11 d b 2013-01-01 01:00:00 29 5
12 d c 2013-01-01 01:00:00 22 14
13 c a 2013-01-01 02:00:00 188 7
14 c b 2013-01-01 02:00:00 156 8
15 c c 2013-01-01 02:00:00 100 2
16 d a 2013-01-01 02:00:00 657 0
17 d b 2013-01-01 02:00:00 23 6
18 d c 2013-01-01 02:00:00 230 12
I want to write a query that will show me a particular queue (e.g. "a") with the average param_val and breach_count for each param on an hour by hour basis. So transposing the data to get something that looks like this:
Results for Queue A
Hour 0 Hour 0 Hour 1 Hour 1 Hour 2 Hour 2
param avg_param_val avg_breach_count avg_param_val avg_breach_count avg_param_val avg_breach_count
c xxx xxx xxx xxx xxx xxx
d xxx xxx xxx xxx xxx xxx
is this possible? I'm not sure how to go about it. Thanks!
SQLite does not have a PIVOT function but you can use an aggregate function with a CASE expression to turn the rows into columns:
select param,
avg(case when time = '00' then param_val end) AvgHour0Val,
avg(case when time = '00' then breach_count end) AvgHour0Count,
avg(case when time = '01' then param_val end) AvgHour1Val,
avg(case when time = '01' then breach_count end) AvgHour1Count,
avg(case when time = '02' then param_val end) AvgHour2Val,
avg(case when time = '02' then breach_count end) AvgHour2Count
from
(
select param,
strftime('%H', date_time) time,
param_val,
breach_count
from param_vals_breaches
where queue = 'a'
) src
group by param;
See SQL Fiddle with Demo