SQL working week in Oracle - sql

I need Oracle SQL that returns the 'working' week number in year:
no overflowing weeks from one year to another
each week starts from monday
first few days in year are week 01
So the result should be:
2015-12-28 - MON - week 53
2015-12-29 - TUE - week 53
2015-12-30 - WED - week 53
2015-12-31 - THU - week 53
===
2016-01-01 - FRI - week 01 - reseting yearly week counter
2016-01-02 - SAT - week 01
2016-01-03 - SUN - week 01
---
2016-01-04 - MON - week 02 - monday start of new week
2016-01-05 - TUE - week 02
...
2016-12-31 - SAT - week 53
===
2017-01-01 - SUN - week 01 - reseting yearly week counter
2017-01-02 - MON - week 02 - monday start of new week
...

W - week number in a month
WW - week number in a year, week 1 starts at 1st of Jan
IW - week number in a year, according to ISO standard
For your requirement, you need to use combination of IW and WW format. You could combine them using a CASE expression.
If you want to generate the list of dates for entire year, then you could use the row generator method.
SQL> WITH sample_data AS(
2 SELECT DATE '2015-12-28' + LEVEL -1 dt FROM dual
3 CONNECT BY LEVEL <= 15
4 )
5 -- end of sample_data mimicking real table
6 SELECT dt,
7 TO_CHAR(dt, 'DY') DAY,
8 NVL(
9 CASE
10 WHEN dt < DATE '2016-01-01'
11 THEN TO_CHAR(dt, 'IW')
12 WHEN dt >= next_day(TRUNC(DATE '2016-01-01', 'YYYY') - 1, 'Monday')
13 THEN TO_CHAR(dt +7, 'IW')
14 END, '01') week_number
15 FROM sample_data;
DT DAY WEEK_NUMBER
---------- --- -----------
2015-12-28 MON 53
2015-12-29 TUE 53
2015-12-30 WED 53
2015-12-31 THU 53
2016-01-01 FRI 01
2016-01-02 SAT 01
2016-01-03 SUN 01
2016-01-04 MON 02
2016-01-05 TUE 02
2016-01-06 WED 02
2016-01-07 THU 02
2016-01-08 FRI 02
2016-01-09 SAT 02
2016-01-10 SUN 02
2016-01-11 MON 03
15 rows selected.
NOTE:
The value 15 to generate 15 rows and the dates are hard-coded above just for demonstration using the WITH clause since OP did not provide the test case with create and insert statements. In reality, you need to use your table and column names.

An approach could be counting the number of days of the year and divide by 7, with some logic to handle the beginning and the end ot the week and of the year:
with test(date_) as
(
select to_date('23122016', 'ddmmyyyy') + level -1 from dual connect by level < 30
)
SELECT date_,
floor( to_number( to_char(
greatest( least(
trunc(date_, 'iw')+6 ,
add_months( trunc(date_, 'YEAR'),12) -1
),
trunc(date_, 'yyyy')),
'ddd'
)
) /7 +1
) week
FROM test
The LEAST is used to avoid going to the next year, while the GREATEST is useful to avoid going to the previous one.

I found the answer myself, TO_CHAR(date,'IW') format is of no use because the very first week in a year according to this standard (ISO) can start after the New Year but also before it (look at TO_CHAR(TO_DATE('2014-12-31','YYYY-MM-DD'),'IW')=01 the first week that belongs to the next year!)
| DAY | WW | IW | MY
===========+=====+====+====+====
2014-12-28 | SUN | 52 | 52 | 52
2014-12-29 | MON | 52 | 01 | 53
2014-12-30 | TUE | 52 | 01 | 53
2014-12-31 | WED | 52 | 01 | 53
2015-01-01 | THU | 53 | 01 | 53
... | ... | .. | .. | ..
2016-12-31 | THU | 53 | 53 | 01
2016-01-01 | FRI | 01 | 53 | 01
2016-01-02 | SAT | 01 | 53 | 01
2016-01-03 | SUN | 01 | 53 | 01
2016-01-04 | MON | 01 | 01 | 02
2016-01-05 | TUE | 01 | 01 | 02
2016-01-06 | WED | 01 | 01 | 02
2016-01-07 | THU | 01 | 01 | 02
2016-01-08 | FRI | 02 | 01 | 02
The logic is quite simple, let's look at the very first day in year and its offset from monday. If current day is bigger than this first day offset then week number should be incremented by 1.
The number of very first day (offset from monday) is calculated with:
TO_CHAR(TO_DATE(TO_CHAR(dt,'YYYY')||'0101','YYYYMMDD'),'D'))
So the final SQL statement is
WITH DATES AS
(
SELECT DATE '2014-12-25' + LEVEL -1 dt FROM DUAL CONNECT BY LEVEL <= 500
)
SELECT dt,TO_CHAR(dt,'DY') DAY,TO_CHAR(dt,'WW') WW,TO_CHAR(dt,'IW') IW,
CASE WHEN TO_CHAR(dt,'D')<TO_CHAR(TO_DATE(TO_CHAR(dt,'YYYY')||'0101','YYYYMMDD'),'D') THEN
LPAD(TO_CHAR(dt,'WW')+1,2,'0')
ELSE
TO_CHAR(dt,'WW')
END MY
FROM dates
Of course, one can create a function for that purpose like:
CREATE OR REPLACE FUNCTION WorkingWeek(dt IN DATE) RETURN CHAR
IS
BEGIN
IF(TO_CHAR(dt,'D')<TO_CHAR(TO_DATE('0101'||TO_CHAR(dt,'YYYY'),'DDMMYYYY'),'D')) THEN
RETURN LPAD(TO_CHAR(dt,'WW')+1,2,'0');
ELSE
RETURN TO_CHAR(dt,'WW');
END IF;
END WorkingWeek;
/

Related

SQL-Aggregate Timeseries Table (HourOfDay, Val) to Average Value of HourOfDay by Weeekday (fi. Avg of Mondays 10:00-11:00, 11:00-12:00,...,Tue...)

So far I made an SQL query that provides me with a table containing the amount of customers handled for each hour of the day - given a arbitrary start and an end datetime value (from Grafana interface). The result might be over many weeks. My goal is to implement an hourly heatmap by weekday with averaged values.
How do I aggregate those customer per hour to show the average value of that hours per weekday?
So let's say I got 24 values per day over 19 days. How do I aggregate so I get 24 values for each mon, tue, wed, thu, fri, sat, sun - each hour representing the average value for those days?
Also only use data of full weeks, so strip leading and trailing days, that are not part of a fully represented week (so same amount of individual weekdays representing an average value).
Here is a segment on how the return of my SQL query looks so far. (hour of each day, number of customers):
...
2021-12-13 11:00:00 | 0
2021-12-13 12:00:00 | 3
2021-12-13 13:00:00 | 4
2021-12-13 14:00:00 | 4
2021-12-13 15:00:00 | 7
2021-12-13 16:00:00 | 17
2021-12-13 17:00:00 | 12
2021-12-13 18:00:00 | 18
2021-12-13 19:00:00 | 15
2021-12-13 20:00:00 | 8
2021-12-13 21:00:00 | 10
2021-12-13 22:00:00 | 1
2021-12-13 23:00:00 | 0
2021-12-14 00:00:00 | 0
2021-12-14 01:00:00 | 0
2021-12-14 02:00:00 | 0
2021-12-14 03:00:00 | 0
2021-12-14 04:00:00 | 0
2021-12-14 05:00:00 | 0
2021-12-14 06:00:00 | 0
2021-12-14 07:00:00 | 0
2021-12-14 08:00:00 | 0
2021-12-14 09:00:00 | 0
2021-12-14 10:00:00 | 12
2021-12-14 11:00:00 | 12
2021-12-14 12:00:00 | 19
2021-12-14 13:00:00 | 11
2021-12-14 14:00:00 | 11
2021-12-14 15:00:00 | 12
2021-12-14 16:00:00 | 9
2021-12-14 17:00:00 | 2
...
So (schematically, example data) startDate 2021-12-10 11:00 to endDate 2021-12-31 17:00
-------------------------------
...
Mon 2021-12-13 12:00 | 3
Mon 2021-12-13 13:00 | 4
Mon 2021-12-13 14:00 | 4
...
Mon 2021-12-20 12:00 | 1
Mon 2021-12-20 13:00 | 6
Mon 2021-12-20 13:00 | 2
...
Mon 2021-12-27 12:00 | 2
Mon 2021-12-27 13:00 | 2
Mon 2021-12-27 13:00 | 3
...
-------------------------------
into this:
strip leading fri 10., sat 11., sun 12.
strip trailing tue 28., wen 29., thu 30., fri 31.
average hours per weekday
-------------------------------
...
Mon 12:00 | 2
Mon 13:00 | 4
Mon 14:00 | 3
...
Tue 12:00 | x
Tue 13:00 | y
Tue 13:00 | z
...
-------------------------------
My approach so far:
WITH CustomersPerHour as (
SELECT dateadd(hour, datediff(hour, 0, Systemdatum),0) as DayHour, Count(*) as C
FROM CustomerList
WHERE CustomerID > 0
AND Datum BETWEEN '2021-12-010T11:00:00Z' AND '2021-12-31T17:00:00Z'
AND EntryID IN (62,65)
AND CustomerID IN (SELECT * FROM udf_getActiveUsers())
GROUP BY dateadd(hour, datediff(hour, 0, Systemdatum), 0)
)
-- add null values on missing data/insert missing hours
SELECT DATEDIFF(second, '1970-01-01', dt.Date) AS time, C as Customers
FROM dbo.udf_generateHoursTable('2021-12-03T18:14:56Z', '2022-03-13T18:14:56Z') as dt
LEFT JOIN CustomersPerHour cPh ON dt.Date = cPh.DayHour
ORDER BY
time ASC
Hi simpliest solution is just do what you have written in example. Create custom base for aggregation.
So first step is to prepare your data in aggregated table with Date & Hour precision & customer count.
Then create base.
This is example of basic idea:
-- EXAMPLE
SELECT
DATENAME(WEEKDAY, GETDATE()) + ' ' + CAST(DATEPART(HOUR, GETDATE()) + ':00' AS varchar(8))
-- OUTPUT: Sunday 21:00
You can concatenate data and then use it in GROUP BY clause.
Adjust this query for your use case:
SELECT
DATENAME(WEEKDAY, <DATETIME_COL>) + ' ' + CAST(DATEPART(HOUR, <DATETIME_COL>) AS varchar(8)) + ':00' as base
,SUM(...) as sum_of_whatever
,AVG(...) as avg_of_whatever
FROM <YOUR_AGG_TABLE>
GROUP BY DATENAME(WEEKDAY, <DATETIME_COL>) + ' ' + CAST(DATEPART(HOUR, <DATETIME_COL>) AS varchar(8)) + ':00'
This create base exactly as you wanted.
You can use this logic to create other desired agg. bases.

How to deduplicate table rows with the same date and keep the row with the most current date stamp?

A client (e-commerce store) doesn't possess a very well-built database. For instance, there are many users with a lot of shopping orders (=different IDs) for exactly the same products and on the same day. It is obvious that these seemingly multiple orders are in many cases just one unique order. At least that's what we have decided to work with to simplify the issue. (I am trying to do a basic data analytics.)
My table might look like this:
| Email | OrderID | Order_date | TotalAmount |
| ----------------- | --------- | ---------------- | ---------------- |
|customerA#gmail.com| 1 |Jan 01 2021 1:00PM| 2000 |
|customerA#gmail.com| 2 |Jan 01 2021 1:03PM| 2000 |
|customerA#gmail.com| 3 |Jan 01 2021 1:05PM| 2000 |
|customerA#gmail.com| 4 |Jan 01 2021 1:10PM| 2000 |
|customerA#gmail.com| 5 |Jan 01 2021 1:14PM| 2000 |
|customerA#gmail.com| 6 |Jan 03 2021 3:55PM| 3000 |
|customerA#gmail.com| 7 |Jan 03 2021 4:00PM| 3000 |
|customerA#gmail.com| 8 |Jan 03 2021 4:05PM| 3000 |
|customerB#gmail.com| 9 |Jan 04 2021 2:10PM| 1000 |
|customerB#gmail.com| 10 |Jan 04 2021 2:20PM| 1000 |
|customerB#gmail.com| 11 |Jan 04 2021 2:30PM| 1000 |
|customerB#gmail.com| 12 |Jan 06 2021 5:00PM| 5000 |
|customerC#gmail.com| 13 |Jan 09 2021 3:00PM| 4000 |
|customerC#gmail.com| 14 |Jan 09 2021 3:06PM| 4000 |
And my desired result would look like this:
| Email | OrderID | Order_date | TotalAmount |
| ----------------- | --------- | ---------------- | ---------------- |
|customerA#gmail.com| 5 |Jan 01 2021 1:14PM| 2000 |
|customerA#gmail.com| 8 |Jan 03 2021 4:05PM| 3000 |
|customerA#gmail.com| 11 |Jan 04 2021 2:30PM| 1000 |
|customerA#gmail.com| 12 |Jan 06 2021 5:00PM| 5000 |
|customerA#gmail.com| 14 |Jan 09 2021 3:06PM| 4000 |
I would guess this might be a common problem, but is there a simple solution to this?
Maybe there is, but I certainly don't seem to come up with one any time soon. I'd like to see even a complex solution, btw :-)
Thank you for any kind of help you can provide!
Do you mean this?
WITH
indata(Email,OrderID,Order_ts,TotalAmount) AS (
SELECT 'customerA#gmail.com', 1,TO_TIMESTAMP( 'Jan 01 2021 01:00PM','Mon DD YYYY HH12:MIAM'),2000
UNION ALL SELECT 'customerA#gmail.com', 2,TO_TIMESTAMP( 'Jan 01 2021 01:03PM','Mon DD YYYY HH12:MIAM'),2000
UNION ALL SELECT 'customerA#gmail.com', 3,TO_TIMESTAMP( 'Jan 01 2021 01:05PM','Mon DD YYYY HH12:MIAM'),2000
UNION ALL SELECT 'customerA#gmail.com', 4,TO_TIMESTAMP( 'Jan 01 2021 01:10PM','Mon DD YYYY HH12:MIAM'),2000
UNION ALL SELECT 'customerA#gmail.com', 5,TO_TIMESTAMP( 'Jan 01 2021 01:14PM','Mon DD YYYY HH12:MIAM'),2000
UNION ALL SELECT 'customerA#gmail.com', 6,TO_TIMESTAMP( 'Jan 03 2021 03:55PM','Mon DD YYYY HH12:MIAM'),3000
UNION ALL SELECT 'customerA#gmail.com', 7,TO_TIMESTAMP( 'Jan 03 2021 04:00PM','Mon DD YYYY HH12:MIAM'),3000
UNION ALL SELECT 'customerA#gmail.com', 8,TO_TIMESTAMP( 'Jan 03 2021 04:05PM','Mon DD YYYY HH12:MIAM'),3000
UNION ALL SELECT 'customerB#gmail.com', 9,TO_TIMESTAMP( 'Jan 04 2021 02:10PM','Mon DD YYYY HH12:MIAM'),1000
UNION ALL SELECT 'customerB#gmail.com',10,TO_TIMESTAMP( 'Jan 04 2021 02:20PM','Mon DD YYYY HH12:MIAM'),1000
UNION ALL SELECT 'customerB#gmail.com',11,TO_TIMESTAMP( 'Jan 04 2021 02:30PM','Mon DD YYYY HH12:MIAM'),1000
UNION ALL SELECT 'customerB#gmail.com',12,TO_TIMESTAMP( 'Jan 06 2021 05:00PM','Mon DD YYYY HH12:MIAM'),5000
UNION ALL SELECT 'customerC#gmail.com',13,TO_TIMESTAMP( 'Jan 09 2021 03:00PM','Mon DD YYYY HH12:MIAM'),4000
UNION ALL SELECT 'customerC#gmail.com',14,TO_TIMESTAMP( 'Jan 09 2021 03:06PM','Mon DD YYYY HH12:MIAM'),4000
)
,
-- need a ROW_NUMBER() to identify the last row within the day (order descending to get 1.
-- can't filter by an OLAP function, so in a fullselect, and WHERE cond in the final SELECT
with_rank AS (
SELECT
*
, ROW_NUMBER() OVER(PARTITION BY email,DAY(order_ts) ORDER BY order_ts DESC) AS rank
FROM INDATA
)
SELECT
*
FROM with_rank
WHERE rank = 1;
-- out Email | OrderID | Order_ts | TotalAmount | rank
-- out ---------------------+---------+---------------------+-------------+------
-- out customerA#gmail.com | 5 | 2021-01-01 13:14:00 | 2000 | 1
-- out customerA#gmail.com | 8 | 2021-01-03 16:05:00 | 3000 | 1
-- out customerB#gmail.com | 11 | 2021-01-04 14:30:00 | 1000 | 1
-- out customerB#gmail.com | 12 | 2021-01-06 17:00:00 | 5000 | 1
-- out customerC#gmail.com | 14 | 2021-01-09 15:06:00 | 4000 | 1

How to change the default day of week and timestamp using date_trunc in snowflake sql

I have a timestamp variable as input, and I want to group the data by week, with a week defined as being between saturday 21:00:00 and saturday 20:59:59. I am querying from a snowflake database.
My data looks like this:
employee_id | shift_started_at | hours_worked
1 | '2018-09-12 08:00:00' | 2
2 | '2018-09-10 22:00:00' | 8
1 | '2018-09-18 08:00:00' | 3
I am trying something like this:
alter session set week_start = 6;
SELECT dateadd('hour',21,date_trunc('week',shift_started_at)) as week_starts_at,
min(shift_started_at) as first_shift_of_week,
max(shift_started_at) as last_shift_of_week,
sum(hours_worked)
FROM table
group by 1;
But even though this query gives me the right date for week_starts_at, the min and max select statements show that the group by statement is ignoring the dateadd function. In short, my weeks are being counted from midnight to midnight on saturday. Any advice on how to change the default timestamp used by date_trunc? Thank you!
The problem is that you're applying date_trunc(week..) before adjusting the time by hours. One solution would be:
first, move the shift times by 3 hours forward, so 9pm shift starts on Sunday midnight
then truncate to a week, with Sunday being the first day of the week
then move the result back 3 hours, to 21 on Saturday
Here's an example:
alter session set week_start = 7, timestamp_output_format='YYYY-MM-DD HH24:MI:SS DY';
create or replace table x(t timestamp);
insert into x values('2018-09-14 08:00:00'),('2018-09-15 20:59:59'),('2018-09-15 21:00:00'),('2018-09-16 23:00:00'), ('2018-09-22 20:59:59'),('2018-09-22 21:00:00');
select t, dateadd(hour, 3, t), date_trunc(week, dateadd(hour, 3, t)), dateadd(hour, -3, date_trunc(week, dateadd(hour, 3, t))) from x;
-------------------------+-------------------------+---------------------------------------+----------------------------------------------------------+
T | DATEADD(HOUR, 3, T) | DATE_TRUNC(WEEK, DATEADD(HOUR, 3, T)) | DATEADD(HOUR, -3, DATE_TRUNC(WEEK, DATEADD(HOUR, 3, T))) |
-------------------------+-------------------------+---------------------------------------+----------------------------------------------------------+
2018-09-14 08:00:00 Fri | 2018-09-14 11:00:00 Fri | 2018-09-09 00:00:00 Sun | 2018-09-08 21:00:00 Sat |
2018-09-15 20:59:59 Sat | 2018-09-15 23:59:59 Sat | 2018-09-09 00:00:00 Sun | 2018-09-08 21:00:00 Sat |
2018-09-15 21:00:00 Sat | 2018-09-16 00:00:00 Sun | 2018-09-16 00:00:00 Sun | 2018-09-15 21:00:00 Sat |
2018-09-16 23:00:00 Sun | 2018-09-17 02:00:00 Mon | 2018-09-16 00:00:00 Sun | 2018-09-15 21:00:00 Sat |
2018-09-22 20:59:59 Sat | 2018-09-22 23:59:59 Sat | 2018-09-16 00:00:00 Sun | 2018-09-15 21:00:00 Sat |
2018-09-22 21:00:00 Sat | 2018-09-23 00:00:00 Sun | 2018-09-23 00:00:00 Sun | 2018-09-22 21:00:00 Sat |
-------------------------+-------------------------+---------------------------------------+----------------------------------------------------------+
The last column you can use for grouping, and things should work.

Count duration of events and group in 24 hours intervals SQL

I have table with data of events contains two dates:
1) Event Start Date (eg. 2017-03-01 05:30:00)
2) Event End Date (eg. 2017-03-01 06:10:00)
I need create report with structure, where data are sum all events in given year-day and hour grouped in 24h time interval, like that:
Two rows:
Id | Start date | End date
Event 1 | 2017-03-01 07:45 | 2017-03-01 08:15
Event 2 | 2017-03-01 08:25 | 2017-03-01 08:40
And Query result:
Year-Month-Day | Hours | (Activity time in seconds or minutes - here minutes)
2017-03-01 | 00 | 0
2017-03-01 | 01 | 0
2017-03-01 | 02 | 0
2017-03-01 | 03 | 0
2017-03-01 | 04 | 0
2017-03-01 | 05 | 0
2017-03-01 | 06 | 0
2017-03-01 | 07 | 15
2017-03-01 | 08 | 30
2017-03-01 | 09 | 00
...
Is elegant way to do this in Oracle SQL? I wrote some psudocode in first moment, (where I count diff between start and end time, check how many hours were affected, and assign to right interval) , but maybe exist more native and better performance solution - like aggregate functions or etc?
Thanks for help.
Here is a query that does what you need.
However note that this will only work for events that finish in 1 million minutes i.e. about 2 years.
WITH event
AS (SELECT 'Event 1' AS id,
'2017-03-01 07:45' AS start_date,
'2017-03-01 08:15' AS end_date
FROM DUAL
UNION ALL
SELECT 'Event 2' AS id,
'2017-03-01 08:25' AS start_date,
'2017-03-01 08:40' AS end_date
FROM DUAL),
add_mins
AS (SELECT LEVEL
- 1
AS add_min
FROM DUAL
CONNECT BY LEVEL <= 1000000),
hrs_in_day
AS (SELECT LEVEL
- 1
AS hr
FROM DUAL
CONNECT BY LEVEL <= 24),
all_days_hrs
AS (SELECT *
FROM (SELECT TO_CHAR (
(first_start_day
+ LEVEL
- 1),
'YYYY-MM-DD'
)
AS curr_day
FROM (SELECT MIN (curr_day) AS first_start_day,
MAX (curr_day) AS last_end_day
FROM (SELECT TO_TIMESTAMP (start_date, 'YYYY-MM-DD HH24:MI')
AS curr_day
FROM event
UNION
SELECT TO_TIMESTAMP (end_date, 'YYYY-MM-DD HH24:MI')
AS curr_day
FROM event))
CONNECT BY (first_start_day
+ LEVEL
- 1) < last_end_day),
hrs_in_day)
SELECT hl.curr_day AS year_month_day,
LPAD (hl.hr, 2, '0') AS hours,
COUNT (h.curr_hr) AS activity_duration_in_min
FROM all_days_hrs hl
LEFT JOIN
(SELECT id,
start_time,
end_time,
curr_time,
TO_CHAR (curr_time, 'YYYY-MM-DD') AS year_month_day,
EXTRACT (HOUR FROM curr_time) AS curr_hr
FROM (SELECT id,
start_time,
end_time,
b.add_min,
start_time
+ NUMTODSINTERVAL (b.add_min, 'minute')
AS curr_time
FROM (SELECT id,
start_time,
end_time,
EXTRACT (DAY FROM dur_interval) * 1440
+ EXTRACT (HOUR FROM dur_interval) * 60
+ EXTRACT (MINUTE FROM dur_interval)
AS duration_in_min
FROM (SELECT id,
start_time,
end_time,
(end_time
- start_time)
AS dur_interval
FROM (SELECT id,
TO_TIMESTAMP (
start_date,
'YYYY-MM-DD HH24:MI'
)
AS start_time,
TO_TIMESTAMP (
end_date,
'YYYY-MM-DD HH24:MI'
)
AS end_time
FROM event))) a,
add_mins b
WHERE b.add_min < a.duration_in_min)) h
ON (hl.curr_day = h.year_month_day
AND hl.hr = h.curr_hr)
GROUP BY hl.curr_day,
hl.hr
ORDER BY year_month_day NULLS FIRST,
hl.hr;
It is a bit slow though. I haven't spent any time thinking about performance.
But it works. Here is the output.
Year-Month-day | Hours | Activity_Duration_in_min
2017-03-01 | 00 | 0
2017-03-01 | 01 | 0
2017-03-01 | 02 | 0
2017-03-01 | 03 | 0
2017-03-01 | 04 | 0
2017-03-01 | 05 | 0
2017-03-01 | 06 | 0
2017-03-01 | 07 | 15
2017-03-01 | 08 | 30
2017-03-01 | 09 | 0
2017-03-01 | 10 | 0
2017-03-01 | 11 | 0
2017-03-01 | 12 | 0
2017-03-01 | 13 | 0
2017-03-01 | 14 | 0
2017-03-01 | 15 | 0
2017-03-01 | 16 | 0
2017-03-01 | 17 | 0
2017-03-01 | 18 | 0
2017-03-01 | 19 | 0
2017-03-01 | 20 | 0
2017-03-01 | 21 | 0
2017-03-01 | 22 | 0
2017-03-01 | 23 | 0
And if we change the end date of 'Event 2' to 2017-03-02 (i.e. the event lasted one day and 15 min and 40 min in the 8th hour) we can see that the output changes to reflect the 48 hour duration.
Year-Month-day | Hours | Activity_Duration_in_min
2017-03-01 | 00 | 0
2017-03-01 | 01 | 0
2017-03-01 | 02 | 0
2017-03-01 | 03 | 0
2017-03-01 | 04 | 0
2017-03-01 | 05 | 0
2017-03-01 | 06 | 0
2017-03-01 | 07 | 15
2017-03-01 | 08 | 50
2017-03-01 | 09 | 60
2017-03-01 | 10 | 60
2017-03-01 | 11 | 60
2017-03-01 | 12 | 60
2017-03-01 | 13 | 60
2017-03-01 | 14 | 60
2017-03-01 | 15 | 60
2017-03-01 | 16 | 60
2017-03-01 | 17 | 60
2017-03-01 | 18 | 60
2017-03-01 | 19 | 60
2017-03-01 | 20 | 60
2017-03-01 | 21 | 60
2017-03-01 | 22 | 60
2017-03-01 | 23 | 60
2017-03-02 | 00 | 60
2017-03-02 | 01 | 60
2017-03-02 | 02 | 60
2017-03-02 | 03 | 60
2017-03-02 | 04 | 60
2017-03-02 | 05 | 60
2017-03-02 | 06 | 60
2017-03-02 | 07 | 60
2017-03-02 | 08 | 40
2017-03-02 | 09 | 0
2017-03-02 | 10 | 0
2017-03-02 | 11 | 0
2017-03-02 | 12 | 0
2017-03-02 | 13 | 0
2017-03-02 | 14 | 0
2017-03-02 | 15 | 0
2017-03-02 | 16 | 0
2017-03-02 | 17 | 0
2017-03-02 | 18 | 0
2017-03-02 | 19 | 0
2017-03-02 | 20 | 0
2017-03-02 | 21 | 0
2017-03-02 | 22 | 0
2017-03-02 | 23 | 0

SQL Server : Insert blank line in query

This may be so last year but I'm using SQL Server 2005
stmpdate intime
----------------------
2014-10-08 08:04:43
2014-10-09 07:57:13
2014-10-10 07:57:14
2014-10-16 07:79:56
2014-10-17 07:45:56
I have this table. It keeps check-in time of the employee, but this employee didn't check-in everyday in the month. So what I want it to be is something like this
stmpdate intime
1 2014-10-01
2 2014-10-02
3 2014-10-03
4 2014-10-04
5 2014-10-05
6 2014-10-06
7 2014-10-07
8 2014-10-08 08:04:43
9 2014-10-09 07:57:13
10 2014-10-10 07:57:14
11 2014-10-11
12 2014-10-12
13 2014-10-13
14 2014-10-14
15 2014-10-15
16 2014-10-16 07:59:56
17 2014-10-17 07:45:56
18 2014-10-18
19 2014-10-19
20 2014-10-20
21 2014-10-21
22 2014-10-22
23 2014-10-23
24 2014-10-24
25 2014-10-25
26 2014-10-26
27 2014-10-27
28 2014-10-28
29 2014-10-29
30 2014-10-30
31 2014-10-31
I tried to make a temp table which contains every date in the month, and then left join it with the first table I mentioned, but it seemed to not work.
declare #datetemp table (
stmpdate varchar(10)
);
insert into #datetemp
SELECT '2014-10-01'
UNION ALL
SELECT '2014-10-02'
UNION ALL
SELECT '2014-10-03'
....
and
SELECT dtt.stmpdate, intime
FROM #datetemp dtt left join v_dayTimesheet
on dtt.stmpdate=v_dayTimesheet.stmpdate
WHERE (emp_no = '001234567')
here is the result of query above
stmpdate intime
2014-10-08 08:04:43
2014-10-09 07:57:13
2014-10-10 07:57:14
2014-10-16 07:59:56
2014-10-17 07:45:56
and here is the result of select * from #datetemp
2014-10-01
2014-10-02
2014-10-03
2014-10-04
2014-10-05
2014-10-06
2014-10-07
2014-10-08
2014-10-09
2014-10-10
2014-10-11
2014-10-12
2014-10-13
2014-10-14
2014-10-15
2014-10-16
2014-10-17
2014-10-18
2014-10-19
2014-10-20
2014-10-21
2014-10-22
2014-10-23
2014-10-24
2014-10-25
2014-10-26
2014-10-27
2014-10-28
2014-10-29
2014-10-30
2014-10-31
you're filtering for only where emp_no has a value. if they didn't check in, it won't return on that row because you just have date info and no employee number. so you have to allow for equal or null.
SELECT dtt.stmpdate, intime
FROM #datetemp dtt
left outer join v_dayTimesheet
on dtt.stmpdate=v_dayTimesheet.stmpdate
WHERE emp_no = '001234567' or emp_no is null
also, for your dates... check this out: http://www.sqlservercurry.com/2010/03/generate-start-and-end-date-range-using.html
DECLARE
#StartDate datetime = '2010-01-01',
#EndDate datetime = '2010-03-01'
;WITH datetemp as
(
SELECT #StartDate as stmpdate
UNION ALL
SELECT DATEADD(day, 1, stmpdate)
FROM datetemp
WHERE DATEADD(day, 1, stmpdate) <= #EndDate
)
SELECT stmpdate
FROM datetemp;
you would then select from datetemp as a normal table. beware, though, a common table expression can only be used once and immediately following the with statement.
just trust me on this one... run this query and see how your blank lines occur:
SELECT dtt.stmpdate, intime, emp_no
FROM #datetemp dtt
left outer join v_dayTimesheet
on dtt.stmpdate=v_dayTimesheet.stmpdate
WHERE emp_no = '001234567' or emp_no is null
all these lines will return with emp_no = 001234567
stmpdate intime
2014-10-08 08:04:43
2014-10-09 07:57:13
2014-10-10 07:57:14
2014-10-16 07:59:56
2014-10-17 07:45:56
and all your blank lines will have null as emp_no.
I got my answer!!
SELECT dtt.stmpdate, intime
FROM #datetemp dtt left join
(
SELECT stmpdate, intime
FROM v_dayTimesheet
WHERE (emp_no = '001234567')
) as vdayTimesheet
on sparedate.stmpdate=vdayTimesheet.stampdate
ORDER BY stmpdate
this is what I want, thanks everyone
SQL Query:
SQLFIDDLEExample
SELECT t2.dt,
isnull(t1.intime, '') intime
FROM
(
SELECT DATEADD(day,number,'2014-10-01') dt
FROM master..spt_values
WHERE Type = 'P'
AND DATEADD(day,number,'2014-10-01') >= '2014-10-01'
AND DATEADD(day,number,'2014-10-01') < '2014-11-01'
) t2
LEFT JOIN Table1 t1
ON t1.stmpdate = t2.dt
Result:
| DT | INTIME |
|--------------------------------|----------|
| October, 01 2014 00:00:00+0000 | |
| October, 02 2014 00:00:00+0000 | |
| October, 03 2014 00:00:00+0000 | |
| October, 04 2014 00:00:00+0000 | |
| October, 05 2014 00:00:00+0000 | |
| October, 06 2014 00:00:00+0000 | |
| October, 07 2014 00:00:00+0000 | |
| October, 08 2014 00:00:00+0000 | 08:04:43 |
| October, 09 2014 00:00:00+0000 | 07:57:13 |
| October, 10 2014 00:00:00+0000 | 07:57:14 |
| October, 11 2014 00:00:00+0000 | |
| October, 12 2014 00:00:00+0000 | |
| October, 13 2014 00:00:00+0000 | |
| October, 14 2014 00:00:00+0000 | |
| October, 15 2014 00:00:00+0000 | |
| October, 16 2014 00:00:00+0000 | 07:79:56 |
| October, 17 2014 00:00:00+0000 | 07:45:56 |
| October, 18 2014 00:00:00+0000 | |
| October, 19 2014 00:00:00+0000 | |
| October, 20 2014 00:00:00+0000 | |
| October, 21 2014 00:00:00+0000 | |
| October, 22 2014 00:00:00+0000 | |
| October, 23 2014 00:00:00+0000 | |
| October, 24 2014 00:00:00+0000 | |
| October, 25 2014 00:00:00+0000 | |
| October, 26 2014 00:00:00+0000 | |
| October, 27 2014 00:00:00+0000 | |
| October, 28 2014 00:00:00+0000 | |
| October, 29 2014 00:00:00+0000 | |
| October, 30 2014 00:00:00+0000 | |
| October, 31 2014 00:00:00+0000 | |