I have table with data like:
Id | Start | End | Used
----------------------------------------
1 | 27-04-17 2:00 |27-04-17 0:00 | 1:30
---------------------------------------
2 | 27-04-17 2:00 |27-04-17 0:00 | 23:00
---------------------------------------
3 | 27-04-17 2:00 |27-04-17 0:00 | 1:00
---------------------------------------
4 | 28-04-17 2:00 |28-04-17 0:00 | 0:30
---------------------------------------
5 | 30-04-17 2:00 |30-04-17 0:00 | 3:30
---------------------------------------
I want to set it like
Id | Start | End | Used
----------------------------------------
1 | 27-04-17 2:00 |27-04-17 3:30 | 1:30
---------------------------------------
2 | 27-04-17 3:30 |28-04-17 2:30 | 23:00
---------------------------------------
3 | 28-04-17 2:30 |28-04-17 3:30 | 1:00
---------------------------------------
4 | 28-04-17 3:30 |28-04-17 4:00 | 0:30
---------------------------------------
5 | 30-04-17 2:00 |30-04-17 4:30 | 2:30
---------------------------------------
I want to set End time of previous id as Start time of next id, where Start time of first id is set by user.End time is just sum of Start time and Used Time I am using vb.net data table and SQL server for database. To set value in first row I am using SQL function DATEADD(), through which two hours are added and same function for adding time in end date.
I want to copy my End date of previous row in next row, so that whole calculation works properly.Can I do it through SQL Only? or I will need a function to do it in Vb.net Data table from where it will be used for reports. Note: Id sequence can be changed Thanks for help.
Below recursive CTE logic will give you the desired output.
create table #tmp
(
ID int,
starttime datetime2,
endtime datetime2,
used varchar(5)
)
insert into #tmp values
(1,'17-Apr-2017 2:00','17-Apr-2017','1:30'),
(2,'17-Apr-2017 2:00','17-Apr-2017','2:00'),
(3,'17-Apr-2017 2:00','17-Apr-2017','1:00'),
(4,'17-Apr-2017 2:00','17-Apr-2017','0:30'),
(5,'28-Apr-2017 2:00','28-Apr-2017','3:30')
;with CTE as (
select ID,starttime,endtime,used,levels FROM
(select row_number() over (partition by cast(starttime as date) order by starttime) RID,ID,starttime,dateadd(hour,cast(substring(used,1,charindex(':',used)-1) as int),dateadd(mi,cast(right(used,2) as int),starttime))
endtime,used,0 levels
from #tmp ) T
where RID=1
union all
select T.ID,C.endtime,dateadd(hour,cast(substring(T.used,1,charindex(':',T.used)-1) as int),dateadd(mi,cast(right(T.used,2) as int),C.endtime))
endtime,T.used,C.levels+1
from CTE C inner join #tmp T on T.ID=C.ID+1
where datediff(d,C.starttime,T.starttime)=0
)
select ID,StartTime,EndTime,Used from CTE order by ID
drop table #tmp
Related
I need to create a report in SQL Server Reporting Service. The source table/query data is structured as follows:
CNum | EmpNo | TDate | TimeIn | TimeOut
100 | 2 | 12/4/2019 | 7:00 AM | 12:00 PM
100 | 2 | 12/4/2019 | 12:30 PM | 3:30 PM
100 | 2 | 12/5/2019 | 7:00 AM | 12:00 PM
100 | 2 | 12/5/2019 | 12:30 PM | 3:30 PM
I need the report output to be displayed as follows (or something similar, just need to show the TDate as columns and any related time entries based on the CNum as rows).
CNum | 12/4/2019 | 12/5/2019 |
100 | 7:00 AM | 7:00 AM |
| 12:00 PM | 12:00 PM |
100 | 12:30 PM | 12:30 PM |
| 3:30 PM | 3:30 PM |
I have tried using the Matrix Tablix but this forces the group to only return on record per day, when there may be multiple. My goal is to write a SQL Query (CTE or PIVOT) which will give me the report data in the correct format so I will not have to get crazy in the report designer.
I am familiar with SQL but for some reason I cannot get any query to output (Pivot) and include both records for the day.
Any help/guidance will be much appreciated.
You can do this easily in SSRS with a small change to your dataset query.
I reproduced your sample data with the following
DECLARE #t TABLE(CNum int, EMpNo int, TDate Date, TimeIn Time, [Timeout] Time)
INSERT INTO #t VALUES
(100, 2, '2019/12/04', '07:00', '12:00'),
(100, 2, '2019/12/04', '12:30', '15:30'),
(100, 2, '2019/12/05', '07:00', '12:00'),
(100, 2, '2019/12/05', '12:30', '15:30')
SELECT *, ROW_NUMBER() OVER(PARTITION BY TDate, Cnum ORDER BY TimeIn) as RowN FROM #t
Note: I added the RowN column which gives each row a unique number within each TDate and CNum. We add this to the CNum group in the matrix (so it groups by CNum then RowN)
Here's the final design including the row and column groups (Column group is just by TDate)
To get the 2nd row I right clicked the [TimeIn] 'cell' and did "Insert Row = > Inside Group - Below"
The final output looks like this
I think, you may take it fwd from Efficiently convert rows to columns in sql server
Here is the answer :
With CTE as (
Select
CNum,
TDate,
TimeIn as [Time],
'In' as [Action]
From TimeTable
Union All
Select
CNum,
TDate,
[TimeOut] as [Time],
'Out' as [Action]
From TimeTable
)
Select
*
From CTE
Pivot(min([Time]) for TDate in ([2019-12-04],[2019-12-05])) as pivot_table
union all
Select
*
From CTE
Pivot(max([Time]) for TDate in ([2019-12-04],[2019-12-05])) as pivot_table
I am building a schedule app, which uses a predefined API from my school. Each lesson has it's times declared for each hour in a single row. A short example:
Table lessons:
Subject Id Start End
English 111 09:30 10:30
English 111 10:30 11:30
Dutch 120 12:30 13:30
Java 109 14:30 15:30
English 111 15:30 16:30
To retrieve the lessons, and to show them inside my app, I use the following query:
SELECT MIN(start), MAX(end) FROM lessons ORDER BY Start GROUP BY Id
It works pretty well, however when a student has the same two lessons with the same Id on a day, in this case English, the SQL query will show it as:
English 09:30 - 16:30
In this case we have a problem, since English doesn't take 6 hours, and I want to show it as:
English 09:30 - 11:30
English 15:30 - 16:30
So my question is:
What query should I use to only GROUP BY lessons when the start or end values equals another row with the same id, to avoid wrong times?, I could do this programmaticly but I really prefer to do this using SQL.
EDIT:
I am grouping by, because I don't want to show each lesson as a seperate row, because my school defines every lessonhour as a single row.
This will do the job
with cte as
(
select subject,id,start,end
from mytable t
where not exists
(
select null
from mytable t2
where t2.id = t.id
and t2.end = t.start
)
union all
select t.subject,t.id,cte.start,t.end
from cte
join mytable t
on t.id = cte.id
and t.start = cte.end
)
select subject,id,start,max(end) as end
from cte
group by subject,id,start
+---------+-----+-------+-------+
| subject | id | start | end |
+---------+-----+-------+-------+
| Dutch | 120 | 12:30 | 13:30 |
+---------+-----+-------+-------+
| English | 111 | 09:30 | 11:30 |
+---------+-----+-------+-------+
| English | 111 | 15:30 | 16:30 |
+---------+-----+-------+-------+
| Java | 109 | 14:30 | 15:30 |
+---------+-----+-------+-------+
Using Postgresql 9.4, I am trying to craft a query on time series log data that logs new values whenever the value updates (not on a schedule). The log can update anywhere from several times a minute to once a day.
I need the query to accomplish the following:
Filter too much data by just selecting the first entry for the timestamp range
Fill in sparse data by using the last reading for the log value. For example, if I am grouping the data by hour and there was an entry at 8am with a log value of 10. Then the next entry isn't until 11am with a log value of 15, I would want the query to return something like this:
Timestamp | Value
2015-07-01 08:00 | 10
2015-07-01 09:00 | 10
2015-07-01 10:00 | 10
2015-07-01 11:00 | 15
I have got a query that accomplishes the first of these goals:
with time_range as (
select hour
from generate_series('2015-07-01 00:00'::timestamp, '2015-07-02 00:00'::timestamp, '1 hour') as hour
),
ranked_logs as (
select
date_trunc('hour', time_stamp) as log_hour,
log_val,
rank() over (partition by date_trunc('hour', time_stamp) order by time_stamp asc)
from time_series
)
select
time_range.hour,
ranked_logs.log_val
from time_range
left outer join ranked_logs on ranked_logs.log_hour = time_range.hour and ranked_logs.rank = 1;
But I can't figure out how to fill in the nulls where there is no value. I tried using the lag() feature of Postgresql's Window functions, but it didn't work when there were multiple nulls in a row.
Here's a SQLFiddle that demonstrates the issue:
http://sqlfiddle.com/#!15/f4d13/5/0
your columns are log_hour and first_vlue
with time_range as (
select hour
from generate_series('2015-07-01 00:00'::timestamp, '2015-07-02 00:00'::timestamp, '1 hour') as hour
),
ranked_logs as (
select
date_trunc('hour', time_stamp) as log_hour,
log_val,
rank() over (partition by date_trunc('hour', time_stamp) order by time_stamp asc)
from time_series
),
base as (
select
time_range.hour lh,
ranked_logs.log_val
from time_range
left outer join ranked_logs on ranked_logs.log_hour = time_range.hour and ranked_logs.rank = 1)
SELECT
log_hour, log_val, value_partition, first_value(log_val) over (partition by value_partition order by log_hour)
FROM (
SELECT
date_trunc('hour', base.lh) as log_hour,
log_val,
sum(case when log_val is null then 0 else 1 end) over (order by base.lh) as value_partition
FROM base) as q
UPDATE
this is what your query return
Timestamp | Value
2015-07-01 01:00 | 10
2015-07-01 02:00 | null
2015-07-01 03:00 | null
2015-07-01 04:00 | 15
2015-07-01 05:00 | nul
2015-07-01 06:00 | 19
2015-07-01 08:00 | 13
I want this result set to be split in groups like this
2015-07-01 01:00 | 10
2015-07-01 02:00 | null
2015-07-01 03:00 | null
2015-07-01 04:00 | 15
2015-07-01 05:00 | nul
2015-07-01 06:00 | 19
2015-07-01 08:00 | 13
and to assign to every row in a group the value of first row from that group (done by last select)
In this case, a method for obtaining the grouping is to create a column which holds the number of
not null values counted until current row and split by this value. (use of sum(case))
value | sum(case)
| 10 | 1 |
| null | 1 |
| null | 1 |
| 15 | 2 | <-- new not null, increment
| nul | 2 |
| 19 | 3 | <-- new not null, increment
| 13 | 4 | <-- new not null, increment
and now I can partion by sum(case)
I have got a table:
---------------------------------------------------
***************************************************
worker | job | starttime | endtime
---------------------------------------------------
w1 | j1 | 2014-01-02 08:00 | 2014-01-02 10:00
---------------------------------------------------
w1 | j2 | 2014-01-02 08:00 | 2014-01-02 11:00
---------------------------------------------------
w1 | j3 | 2014-01-02 09:00 | 2014-01-02 12:00
---------------------------------------------------
w1 | j4 | 2014-01-02 12:00 | 2014-01-02 13:00
---------------------------------------------------
w1 | j1 | 2014-01-02 12:00 | 2014-01-02 13:00
I want the result as
w1's j1 is 2 hours
w1's j2 is 1 hours
w1's j3 is 1 hours its like extra time
w1's j4 is 1 hours
For example, When preparing the cake, coat flour and eggs at the same time but coat eggs takes longer time.
finding total time is easy but how can I separate them ?
Is it possible code it in sql?
Have a look at DATEDIFF
Returns the count (signed integer) of the specified datepart
boundaries crossed between the specified startdate and enddate.
(This is also true for timeparts)
select worker,job, DATEDIFF(HOUR,endtime,starttime) as diff from mytable
I edited my answer to handle the case where the end time is equal to a previous end time.
So here's something like what you want. We take the max end time where
the job is not the current one and
the end time is less than the current row's end time
and compare that the current row's end time to determine how long the current row extends the previous job.
If that response is null, the row is the first row in the job, and in that case we just use the difference between start and end.
If the end time is equal to a previous end time, determined because the row_number is greater than one when partitioned over end time, we output zero as the time since that is the differential between this row and the previous one.
Please note that the "time" data type is limited to 24 hours so it is probably not the appropriate data type for the output. I just used it because it produces nice looking results for the times in the example.
declare #times table (
rowId int identity,
worker varchar(10),
job varchar(10),
starttime datetime,
endtime datetime
)
insert into #times (worker,job,starttime,endtime)
select 'w1','j1','2014-01-02 08:00','2014-01-02 9:25'
union select 'w1','j2','2014-01-02 08:00','2014-01-02 11:00'
union select 'w1','j3','2014-01-02 09:00','2014-01-02 12:00'
union select 'w1','j4','2014-01-02 12:00','2014-01-02 14:22'
union select 'w1','j5','2014-01-02 12:00','2014-01-02 14:22'
select *,
case when
ROW_NUMBER() over (partition by endtime order by starttime,endtime) > 1 then cast('0:00' as time)
else
coalesce(
cast((endtime - (select max(endtime) from #times where endtime < t.endtime)) as time),
cast((endtime - starttime) as time)
)
end as differential3
from #times t
We have events with a date range:
Event | Begin | End
------|-------|------
a | 11:30 | 12:15
b | 10:30 | 13:15
c | 11:30 | 13:30
Visualized as a timetable:
a) |---|
b) |---------------|
c) |-----------|
|-----|-----|-----|-----|
10:00 11:00 12:00 13:00 14:00
We want an efficient query for counting ongoing events on given timestamps. In this example we want them per hour. Like this:
Time | OnGoing
-----------------|--------
2014-02-06 10:00 | 0
2014-02-06 11:00 | 1
2014-02-06 12:00 | 3
2014-02-06 13:00 | 2
2014-02-06 14:00 | 0
You can create a driver table with all hours, then join to that:
;WITH cal AS (SELECT CAST('10:00:00' AS TIME) dt
UNION ALL
SELECT DATEADD(hour,1,dt)
FROM cal
WHERE dt < '14:00:00')
SELECT dt, COUNT(DISTINCT Event) OnGoing
FROM cal a
LEFT JOIN Table1 b
ON a.dt BETWEEN b.[Begin] AND b.[End]
GROUP BY dt
Demo: SQL Fiddle
Adjust the range in the cal cte to fit your preferences. I notice your sample output shows a datetime, so you could cast begin and end as TIME in your join and add a DATE portion to your select and group by, or you could alter the cte to be full datetime. If spanning more than 100 units in your cte, you'll need to add OPTION (MAXRECURSION 0) to the very end of your query.