SQL - Grouping results by custom 24 hour period - sql

I need to create an Oracle 11g SQL report showing daily productivity: how many units were shipped during a 24 hour period. Each period starts at 6am and finishes at 5:59am the next day.
How could I group the results in such a way as to display this 24 hour period? I've tried grouping by day, but, a day is 00:00 - 23:59 and so the results are inaccurate.
The results will cover the past 2 months.
Many thanks.

group by trunc(your_date - 1/4)

Days are whole numbers in oracle so 6 am will be 0.25 of a day
so :
select
trunc(date + 0.25) as period, count(*) as number
from table
group by trunc(date + 0.25 )
I havent got an oracle to try it on at the moment.

Well, you could group by a calculated date.
So, add 6 hours to the dates and group by that which would then technically group your dates correctly and produce the correct results.

Assuming that you have a units column or similar on your table, perhaps something like this:
SQL Fiddle
SELECT
TRUNC(us.shipping_datetime - 0.25) + 0.25 period_start
, TRUNC(us.shipping_datetime - 0.25) + 1 + (1/24 * 5) + (1/24/60 * 59) period_end
, SUM(us.units) units
FROM units_shipped us
GROUP BY TRUNC(us.shipping_datetime - 0.25)
ORDER BY 1
This simply subtracts 6 hours (0.25 of a day) from each date. If the time is earlier than 6am, the subtraction will make it fall prior to midnight, and when the resultant value is truncated (time element is removed, the date at midnight is returned), it falls within the grouping for the previous day.
Results:
| PERIOD_START | PERIOD_END | UNITS |
-----------------------------------------------------------------------
| April, 22 2013 06:00:00+0000 | April, 23 2013 05:59:00+0000 | 1 |
| April, 23 2013 06:00:00+0000 | April, 24 2013 05:59:00+0000 | 3 |
| April, 24 2013 06:00:00+0000 | April, 25 2013 05:59:00+0000 | 1 |
The bit of dynamic maths in the SELECT is just to help readability of the results. If you don't have a units column to SUM() up, i.e. each row represents a single unit, then substitute COUNT(*) instead.

Related

How to convert separate year and month column into a single date and get the difference between two dates in terms of months/days

After joining two tables in google bigquery, I ended up with a table which have two sets of year and month in four separate columns. First two year and month columns should form one date and the second pair for another date. I need to convert each of those two sets of year and month in to two single dates, and then get the difference between those two dates in terms of months or days.
Example of the table is provided below:
year month year month
0 2013 12 2014 2
1 2014 5 2014 9
2 2015 6 2015 8
If anyone can help code this in bigquery, would be really helpful.
Thanks in advance.
#standardSQL
WITH `project.dataset.table` AS (
SELECT 2013 year1, 12 month1, 2014 year2, 2 month2 UNION ALL
SELECT 2014, 5, 2014, 9 UNION ALL
SELECT 2015, 6, 2015, 8
)
SELECT
DATE(year1, month1, 1) date1,
DATE(year2, month2, 1) date2,
DATE_DIFF(DATE(year2, month2, 1), DATE(year1, month1, 1), DAY) diff_in_days
FROM `project.dataset.table`
with result
Row date1 date2 diff_in_days
1 2013-12-01 2014-02-01 62
2 2014-05-01 2014-09-01 123
3 2015-06-01 2015-08-01 61
To get the difference in months, you don't need to convert to dates. Just use arithmetic:
select (year1 * 12 + month1) - (year2 * 12 + month2)
So you can use the DATE(YEAR,MONTH,DAY) function two times passing the data that you've got on both columns and passing 1 as the day since it doesn't matter, then use DATE_DIFF(date_expression, date_expression, date_part) passing the dates that you got from those functions and the DATE PART that you want to get as a return, it accepts :
DAY,WEEK, ISOWEEK,MONTH,QUARTER,YEAR and ISOYEAR.

postgresql query to get counts between 12:00 and 12:00

I have the following query that works fine, but it is giving me counts for a single, whole day (00:00 to 23:59 UTC). For example, it's giving me counts for all of January 1 2017 (00:00 to 23:59 UTC).
My dataset lends itself to be queried from 12:00 UTC to 12:00 UTC. For example, I'm looking for all counts from Jan 1 2017 12:00 UTC to Jan 2 2017 12:00 UTC.
Here is my query:
SELECT count(DISTINCT ltg_data.lat), cwa, to_char(time, 'MM/DD/YYYY')
FROM counties
JOIN ltg_data on ST_contains(counties.the_geom, ltg_data.ltg_geom)
WHERE cwa = 'MFR'
AND time BETWEEN '1987-06-01'
AND '1992-08-1'
GROUP BY cwa, to_char(time, 'MM/DD/YYYY');
FYI...I'm changing the format of the time so I can use the results more readily in javascript.
And a description of the dataset. It's thousands of point data that occurs within various polygons every second. I'm determining if the points are occurring withing the polygon "cwa = MFR" and then counting them.
Thanks for any help!
I see two approaches here.
first, join generate_series(start_date::timestamp,end_date,'12 hours'::interval) to get count in those generate_series. this would be more correct I believe. But it has a major minus - you have to lateral join it against existing data set to use min(time) and max(time)...
second, a monkey hack itself, but much less coding and less querying. Use different time zone to make 12:00 a start of the day, eg (you did not give the sample, so I generate content of counties with generate_series with 2 hours interval as sample data):
t=# with counties as (select generate_series('2017-09-01'::timestamptz,'2017-09-04'::timestamptz,'2 hours'::interval)
g)
select count(1),to_char(g,'MM/DD/YYYY') from counties
group by to_char(g,'MM/DD/YYYY')
order by 2;
count | to_char
-------+------------
12 | 09/01/2017
12 | 09/02/2017
12 | 09/03/2017
1 | 09/04/2017
(4 rows)
so for UTC time zone there are 12 two hours interval rows for days above, due to inclusive nature of generate_series in my sample, 1 row for last days. in general: 37 rows.
Now a monkey hack:
t=# with counties as (select generate_series('2017-09-01'::timestamptz,'2017-09-04'::timestamptz,'2 hours'::interval)
g)
select count(1),to_char(g at time zone 'utc+12','MM/DD/YYYY') from counties
group by to_char(g at time zone 'utc+12','MM/DD/YYYY')
order by 2;
count | to_char
-------+------------
6 | 08/31/2017
12 | 09/01/2017
12 | 09/02/2017
7 | 09/03/2017
(4 rows)
I select same dates for different time zone, switching it exactly 12 hours, getting first day starting at 31 Aug middday, not 1 Sep midnight, and the count changes, still totalling 37 rows, but grouping your requested way...
update
for your query I'd try smth like:
SELECT count(DISTINCT ltg_data.lat), cwa, to_char(time at time zone 'utc+12', 'MM/DD/YYYY')
FROM counties
JOIN ltg_data on ST_contains(counties.the_geom, ltg_data.ltg_geom)
WHERE cwa = 'MFR'
AND time BETWEEN '1987-06-01'
AND '1992-08-1'
GROUP BY cwa, to_char(time at time zone 'utc+12', 'MM/DD/YYYY');
also if you want to apply +12 hours logic to where clause - add at time zone 'utc+12' to "time" comparison as well

Determine a specific fortnight based on anchor dates

I have 2 x bi-weekly periods that were defined by 2 starting dates 1 week apart. For example, Group 1 started on 2016-01-15 and Group 2 started on 2016-01-22.
By bi-weekly, I mean a rolling period lasting 2 weeks.
How can I determine if the current date is in week 1 of Group 1 or is in week 1 of Group 2?
By way of example, today's date is 2016-04-04 so this would be day 1 of Group 2 and day 8 of Group 1, therefore I would like to a query to return 'Group 2'.
DATEDIFF calculates the difference between two dates. Divide it by 14 days and take the remainder (%).
If remainder is less than 7, then it is closer to that starting date.
Since you know that your starting dates are 1 week apart you really need to check only one starting date.
DECLARE #VarStartGroup1 date = '2016-01-15';
DECLARE #VarStartGroup2 date = '2016-01-22';
DECLARE #VarCurrentDate date = '2016-04-04';
SELECT
DATEDIFF(day, #VarStartGroup1, #VarCurrentDate) AS TotalDays1,
DATEDIFF(day, #VarStartGroup2, #VarCurrentDate) AS TotalDays2,
DATEDIFF(day, #VarStartGroup1, #VarCurrentDate) % 14 AS DayNumberInGroup1,
DATEDIFF(day, #VarStartGroup2, #VarCurrentDate) % 14 AS DayNumberInGroup2,
CASE WHEN DATEDIFF(day, #VarStartGroup1, #VarCurrentDate) % 14 < 7
THEN 'Group1' ELSE 'Group2' END AS Result
;
Result
+------------+------------+-------------------+-------------------+--------+
| TotalDays1 | TotalDays2 | DayNumberInGroup1 | DayNumberInGroup2 | Result |
+------------+------------+-------------------+-------------------+--------+
| 80 | 73 | 10 | 3 | Group2 |
+------------+------------+-------------------+-------------------+--------+
I included intermediate calculations in the result to help understand what is going on.

Using sum function with a condition based on a returned value

I have a set of given month with a number of hours related to each of it
DATE HOURS
8/1/2013 3
9/1/2013 8
10/1/2013 2
11/1/2013 4
12/1/2013 1
I need to return the sum of hours for everything that is in the past including current month, in the example below, starting in august, sum would be august only. For september, I'd need august + september
DATE HOURS SUM
8/1/2013 3 3
9/1/2013 8 11
10/1/2013 2 13
11/1/2013 4 17
12/1/2013 1 18
I am not sure how to proceed, since the date condition is different for each line.
If anyone can help on this, it'd be greatly appreciated
You can do this in most SQL dialects using a correlated subquery (or a non-equijoin, but I find the subquery cleaner):
select date, hours,
(select sum(t2.hours)
from t t2
where t2.date <= t.date
) as cum
from t;
Many SQL engines also support the cumulative sum function, which would typically look like this:
select date, hours sum(hours) over (order by date) as cum
from t

How to sum total amount for every month in a year?

I have a database in SQL Server 2012 and there is a table with dates in D.M.YYYY format like below:
ID | Date(date type) | Amount(Numeric)
1 3.4.2013 16.00
1 12.4.2013 13.00
1 2.5.2013 9.50
1 18.5.2013 10.00
I need to sum the total amount for every month in a given year. For example:
ID | Month | TotalAmount
1 1 0.00
...
1 4 29.00
1 5 19.50
I thought what I needed was to determine the number of days in a month, so I created a function which is described in determine the number of days, and it worked. After that I tried to compare two dates(date type) and got stuck; there are some examples out there, but all of them about datetime.
Is this wrong? How can I accomplish this?
I think you just want an aggregation:
select id, month(date) as "month", sum(amount) as TotalAmount
from t
where year(date) = 2013
group by id, month(date)