count rows before time - sql

i have the following situation. every row has a timestamp when it was written on table. now i want to evaluate per day how many rows have been inserted before 5 am and how many after. how can that be done??

You can use the HH24 format to get the hour in 24-hour time:
select trunc(created_Date) as the_day
,sum(case when to_number(to_char(created_Date,'HH24')) < 5 then 1 else 0 end) as before_five
,sum(case when to_number(to_char(created_Date,'HH24')) >= 5 then 1 else 0 end) as after_five
from yourtable
group by trunc(created_Date)
Per USER's comment on 5:10, to show timestamps just before and after 5:
select trunc(created_Date) as the_day
,sum(case when to_number(to_char(created_Date,'HH24')) < 5 then 1 else 0 end) as before_five
,sum(case when to_number(to_char(created_Date,'HH24')) >= 5 then 1 else 0 end) as after_five
from (
-- one row januar 1 just after 5:00 a.m.
select to_Date('01/01/2015 05:10:12','dd/mm/yyyy hh24:mi:ss') as created_date from dual
union all
-- one row Januar 2 just before 5:00 a.m.
select to_Date('02/01/2015 04:59:12','dd/mm/yyyy hh24:mi:ss') as created_date from dual
)
group by trunc(created_Date);
THE_DAY, BEFORE_FIVE, AFTER_FIVE
02/01/2015, 1, 0
01/01/2015, 0, 1

Assuming your timestamp is a DATE column:
select trunc(date_written) as day
, count (case when (date_written-trunc(date_written))*24 < 5 then 1 end) before_5_count
, count (case when (date_written-trunc(date_written))*24 >= 5 then 1 end) after_5_count
from mytable
group by trunc(date_written)

select to_char(time_column, 'dd/mm/yyyy'),
sum( decode ( greatest(extract(hour from time_column), 5), extract(hour from time_column), 1, 0)) post_5,
sum( decode ( greatest(extract(hour from time_column), 5), extract(hour from time_column), 0, 1)) pre_5
from test_time
group by to_char(time_column, 'dd/mm/yyyy')

Related

Oracle SQL Show all month of a year, with or without value ORA-01841

I have a problem with which I despair, I have data distributed over days, and would like to display this for the entire year in months and once in weeks.
My problem with the months that I get in the select my data displayed (for January, September) but I want that all months for a selected year are displayed, even if they are empty. For this I have made myself a "WITH" (copied) and now try to join this, but get an ORA-01841 error.
And how do I implement the whole construct to display only the weeks.
WITH MONAT_ZAEHLER (MZ) AS
(
SELECT
TO_CHAR(ADD_MONTHS(TO_DATE('01.2022','MM.YYYY'),LEVEL -1),'Month', 'NLS_DATE_LANGUAGE = GERMAN') AS GRD_ROW_ID
FROM
DUAL
CONNECT BY LEVEL <= 12
)
SELECT
TO_CHAR(GEN_DATUM,'Month', 'NLS_DATE_LANGUAGE = GERMAN') AS GRD_ROW_ID
, COUNT( DISTINCT CASE
WHEN LP_BELEGUNG.ART = 1 THEN LP_BELEGUNG.LP_BELEGUNG_ID
ELSE NULL
END ) AS "1"
, COUNT( DISTINCT CASE
WHEN LP_BELEGUNG.ART = 2 THEN LP_BELEGUNG.LP_BELEGUNG_ID
ELSE NULL
END ) AS "2"
, COUNT( DISTINCT CASE
WHEN LP_BELEGUNG.ART = 3 THEN LP_BELEGUNG.LP_BELEGUNG_ID
ELSE NULL
END ) AS "3"
, COUNT( DISTINCT CASE
WHEN LP_BELEGUNG.ART = 99 THEN LP_BELEGUNG.LP_BELEGUNG_ID
ELSE NULL
END ) AS "99"
FROM
LP_BELEGUNG
FULL OUTER JOIN MONAT_ZAEHLER ON TRUNC(LP_BELEGUNG.GEN_DATUM, 'Month') = MONAT_ZAEHLER.MZ
WHERE
TO_CHAR(GEN_DATUM, 'YYYY') = '2022'
GROUP BY
TO_CHAR(GEN_DATUM,'Month', 'NLS_DATE_LANGUAGE = GERMAN')
The error is because you're converting the month to a name string in the CTE, then trying to convert it again for the GRD_ROW_ID alias.
The solution is basically the same as your previous question, but now you want the CTE to have one row per month - which you are doing, but you should leave it as a date type in the CTE, not convert it to a string there:
with cte (dt) as (
select add_months(date '2022-01-01', level - 1)
from dual
connect by level <= 12
)
... then convert that actual date value to a string:
SELECT
TO_CHAR(cte.dt, 'Month', 'NLS_DATE_LANGUAGE = GERMAN') AS GRD_ROW_ID
...
... and outer join to your actual table as before, using a date range:
FROM
cte
LEFT JOIN
LP_BELEGUNG
ON
LP_BELEGUNG.GEN_DATUM >= cte.dt AND LP_BELEGUNG.GEN_DATUM < add_months(cte.dt, 1)
GROUP BY
cte.dt
ORDER BY
cte.dt
... this time looking for values where the the GEN_DATUM is greater than or equal to cte.dt value (again, as before), which is midnight on the first day of the first day of the month; and less than add_months(cte.dt, 1), which is midnight on the first day of the first day of the following month. So for January, that will be >= 2022-01-01 00:00:00 and < 2022-02-01 00:00:00, which is all possible dates and times during that month.
GRD_ROW_ID
ANZAHL_ART_1
ANZAHL_ART_2
ANZAHL_ART_3
ANZAHL_ART_4
Januar
0
0
0
0
Februar
0
0
0
0
März
0
0
0
0
April
0
0
0
0
Mai
0
0
0
0
Juni
0
0
0
0
Juli
0
0
0
0
August
0
0
0
0
September
1
1
1
7
Oktober
0
0
0
0
November
0
0
0
0
Dezember
0
0
0
0
fiddle
To get a row for every week of the year you would do something similar again, but in blocks of 7 days:
with cte (dt) as (
select date '2022-01-01' + 7 * (level - 1)
from dual
connect by level <= 53
)
SELECT
TO_CHAR(cte.dt, 'YYYY-WW') AS GRD_ROW_ID
...
FROM
cte
LEFT JOIN
LP_BELEGUNG
ON
LP_BELEGUNG.GEN_DATUM >= cte.dt AND LP_BELEGUNG.GEN_DATUM < cte.dt + 7
AND LP_BELEGUNG.GEN_DATUM < add_months(trunc(cte.dt, 'YYYY'), 12)
GROUP BY
cte.dt
ORDER BY
cte.dt
which has an extra check in the join to stop it including data from week 53 which is actually in the following year - which I'm guessing you woudl want to do.
fiddle

Is there a way to join the below 2 queries

I'm trying to join the below 2 queries.. though both of the below queries use the same tables, I'm unable to get the correct result..
In this query I'm checking for entries present in table 1 which would satisfy the condition m1.condition is 1, and for that entry a query is made to table 2 where even after 5 minutes there is no entry in table 2,then get the count of that entries.
The date check you see for 30 min is to get all the entries in table which are processed half an hour before.
SELECT count(*) AS TOTALCOUNT,
SELECT TO_CHAR(amount, '$999,999,999,999,999.99') AS TOTALVALUE
from table1 m1
LEFT JOIN table2 m ON m.id=m1.id
where m1.condition='1'
and amount BETWEEN 1000 and 25000
and (m1.DATE <= (select to_char((select systimestamp - interval '0 00:05' day to minute from dual),'dd-MON-yy HH.MI.SS AM TZD') from dual))
and m1.DATE <= (select to_char((select systimestamp - interval '0 00:30' day to minute from dual),'dd-MON-yy HH.MI.SS AM TZD') from dual)
and m1.DATE <= systimestamp
similarly in below query in table 2 there are some conditions.. so based on that I'm performing some actions.
SELECT COALESCE(SUM (CASE when m.f = 'Converted' then 1 else 0 END),0) AS CCOUNT,
COALESCE(SUM (CASE when m.f = 'Do Not Convert' then 1 else 0 END),0) AS NCOUNT,
count(m. id) AS TOTALCOUNT,
TO_CHAR(COALESCE (SUM(CASE when m.f = 'C' then (amount) END),0), '$999,999,999,999,999.99') AS CONVERTED,
TO_CHAR(COALESCE (SUM(CASE when m.f = 'D' then (amount) END),0), '$999,999,999,999,999.99') AS NONCONVERTED,
TO_CHAR(COALESCE (SUM(CASE when m.f <> '0' then (amount) END),0), '$999,999,999,999,999.99') AS TOTAL
FROM table1 ml
JOIN table2 m ON m.id=ml.id
and amount BETWEEN 1000 and 25000
and m1.DATE <= (select to_char((select systimestamp - interval '0 30:00' day to minute from dual),'dd-MON-yy HH.MI.SS AM TZD') from dual)
and m1.DATE < systimestamp;
I have to combine both the above queries.. but I'm unable to do.
The only real difference between filters is that m1.condition = '1', so just add
count(case condition when '1' then 1 end) as condition_1_totalcount,
sum(case condition when '1' then amount end) as condition_1_totalvalue
to your second query. In my short test it looks OK:
-- sample data
with
table1(id, date_, condition) as (
select 1, date '1990-02-19', '1' from dual union all
select 2, date '1990-02-19', '7' from dual union all
select 3, date '1990-02-19', '1' from dual union all
select 4, date '1990-02-19', '1' from dual ),
table2(id, f, amount) as (
select 1, 'C', 3000 from dual union all
select 2, 'D', 3500 from dual union all
select 3, 'D', 4000 from dual union all
select 4, 'D', 5000 from dual )
-- query
select sum (case when m.f = 'Converted' then 1 else 0 end) ccount,
sum (case when m.f = 'Do Not Convert' then 1 else 0 end) as ncount,
count(m.id) as totalcount,
sum(case when m.f = 'C' then (amount) end) as converted,
sum(case when m.f = 'D' then (amount) end) as nonconverted,
sum(case when m.f <> '0' then (amount) end) as total,
count(case condition when '1' then 1 end) as condition_1_totalcount,
sum(case condition when '1' then amount end) as condition_1_totalvalue
from table1 ml
join table2 m on m.id = ml.id
where ml.date_ <= systimestamp - interval '30' minute
and amount between 1000 and 25000
I do not fully understand why is this 5 min part in first query. Rows older than 30 minutes are also older than 5 min, so it is superfluous as pointed in comments. And older than current timestamp. If you need to differentiate values this way you can also place this condition in case whens.

Oracle SQL - How to get counts based up on dates into multiple columns in ORACLE

I have data like this in a Oracle table
REGID SESSION_START_DATETIME USAGEID
1 7/11/2016 1
1 6/10/2016 1
1 6/09/2016 1
1 5/04/2016 1
1 5/04/2016 1
1 5/04/2016 1
I need the output like
REGID 0-30_days_usagecount 31-60_days_usagecount 61-90_days_usagecount
1 1 2 3
usagecount is basically count(usage_id)... how to write a query for this problem?
Please help
Here is a way to do this using the PIVOT operator.
with
inputs (REGID, SESSION_START_DATETIME, USAGEID) as (
select 1 , to_date('7/11/2016', 'mm/dd/yyyy'), 1 from dual union all
select 1 , to_date('6/10/2016', 'mm/dd/yyyy'), 1 from dual union all
select 1 , to_date('6/09/2016', 'mm/dd/yyyy'), 1 from dual union all
select 1 , to_date('5/04/2016', 'mm/dd/yyyy'), 1 from dual union all
select 1 , to_date('5/04/2016', 'mm/dd/yyyy'), 1 from dual union all
select 1 , to_date('5/04/2016', 'mm/dd/yyyy'), 1 from dual
)
select * from (
select regid, session_start_datetime,
case when trunc(sysdate) - session_start_datetime between 0 and 30
then '0-30_days_usagecount'
when trunc(sysdate) - session_start_datetime between 31 and 60
then '31-60_days_usagecount'
when trunc(sysdate) - session_start_datetime between 61 and 90
then '61-90_days_usagecount'
end
as col
from inputs
)
pivot ( count(session_start_datetime)
for col in ( '0-30_days_usagecount', '31-60_days_usagecount',
'61-90_days_usagecount'
)
)
;
REGID '0-30_days_usagecount' '31-60_days_usagecount' '61-90_days_usagecount'
---------- ---------------------- ----------------------- -----------------------
1 1 2 3
1 row selected.
SELECT regid,
SUM(
CASE
WHEN session_start_dt <= (sysdate - 30)
THEN usage_id
ELSE 0
END) T1,
SUM(
CASE
WHEN session_start_dt > (sysdate - 30)
AND session_start_date <= (sysdate -60)
THEN usage_id
ELSE 0
END) T2,
SUM(
CASE
WHEN session_start_dt > (sysdate - 60)
AND session_start_date <= (sysdate -90)
THEN usage_id
ELSE 0
END) T3
FROM temp
GROUP BY regid;
Try this
select (select count(USAGEID) from tablename where DATEDIFF(day, SESSION_START_DATETIME, CURDATE()) < 31 and REGID = A. REGID) AS 0-30_days_usagecount, (select count(USAGEID) from tablename where DATEDIFF(day, SESSION_START_DATETIME, CURDATE()) > 30 and DATEDIFF(day, SESSION_START_DATETIME, CURDATE()) < 61 and REGID = A. REGID) AS 31-60_days_usagecount, (select count(USAGEID) from tablename where DATEDIFF(day, SESSION_START_DATETIME, CURDATE()) > 60 and DATEDIFF(day, SESSION_START_DATETIME, CURDATE()) < 91 and REGID = A. REGID) AS 61-90_days_usagecount from tablename A group by A.REGID

Select data grouped by time over midnight

I have a table like:
ID TIMEVALUE
----- -------------
1 06.07.15 06:43:01,000000000
2 06.07.15 12:17:01,000000000
3 06.07.15 18:21:01,000000000
4 06.07.15 23:56:01,000000000
5 07.07.15 04:11:01,000000000
6 07.07.15 10:47:01,000000000
7 07.07.15 12:32:01,000000000
8 07.07.15 14:47:01,000000000
and I want to group this data by special times.
My current query looks like this:
SELECT TO_CHAR(TIMEVALUE, 'YYYY\MM\DD'), COUNT(ID),
SUM(CASE WHEN TO_CHAR(TIMEVALUE, 'HH24MI') <=700 THEN 1 ELSE 0 END) as morning,
SUM(CASE WHEN TO_CHAR(TIMEVALUE, 'HH24MI') >700 AND TO_CHAR(TIMEVALUE, 'HH24MI') <1400 THEN 1 ELSE 0 END) as daytime,
SUM(CASE WHEN TO_CHAR(TIMEVALUE, 'HH24MI') >=1400 THEN 1 ELSE 0 END) as evening FROM Table
WHERE TIMEVALUE >= to_timestamp('05.07.2015','DD.MM.YYYY')
GROUP BY TO_CHAR(TIMEVALUE, 'YYYY\MM\DD')
and I am getting this output
day overall morning daytime evening
----- ---------
2015\07\05 454 0 0 454
2015\07\06 599 113 250 236
2015\07\07 404 139 265 0
so that is fine grouping on the same day (0-7 o'clock, 7-14 o'clock and 14-24 o'clock)
But my question now is:
How can I group over midnight?
For example count from 6-14 , 14-23 and 23-6 o'clock on next day.
I hope you understand my question. You are welcome to even improve my upper query if there is a better solution.
EDIT: It is tested now: SQL Fiddle
The key is simply to adjust the group by so that anything before 6am gets grouped with the previous day. After that, the counts are pretty straight-forward.
SELECT TO_CHAR(CASE WHEN EXTRACT(HOUR FROM timevalue) < 6
THEN timevalue - 1
ELSE timevalue
END, 'YYYY\MM\DD') AS day,
COUNT(*) AS overall,
SUM(CASE WHEN EXTRACT(HOUR FROM timevalue) >= 6 AND EXTRACT(HOUR FROM timevalue) < 14
THEN 1 ELSE 0 END) AS morning,
SUM(CASE WHEN EXTRACT(HOUR FROM timevalue) >= 14 AND EXTRACT(HOUR FROM timevalue) < 23
THEN 1 ELSE 0 END) AS daytime,
SUM(CASE WHEN EXTRACT(HOUR FROM timevalue) < 6 OR EXTRACT(HOUR FROM timevalue) >= 23
THEN 1 ELSE 0 END) AS evening
FROM my_table
WHERE timevalue >= TO_TIMESTAMP('05.07.2015','DD.MM.YYYY')
GROUP BY TO_CHAR(CASE WHEN EXTRACT(HOUR FROM timevalue) < 6
THEN timevalue - 1
ELSE timevalue
END, 'YYYY\MM\DD');
Substract 1 day from timevalue for times lower than '06:00' at first and then:
SQLFiddle demo
select TO_CHAR(day, 'YYYY\MM\DD') day, COUNT(ID) cnt,
SUM(case when '23' < tvh or tvh <= '06' THEN 1 ELSE 0 END) as midnight,
SUM(case when '06' < tvh and tvh <= '14' THEN 1 ELSE 0 END) as daytime,
SUM(case when '14' < tvh and tvh <= '23' THEN 1 ELSE 0 END) as evening
FROM (
select id, to_char(TIMEVALUE, 'HH24') tvh,
trunc(case when (to_char(timevalue, 'hh24') <= '06')
then timevalue - interval '1' day
else timevalue end) day
from t1
)
GROUP BY day
Maybe you can do it like this (with some reformatting or PIVOT):
WITH spans AS
(SELECT TIMESTAMP '2015-01-01 00:00:00' + LEVEL * INTERVAL '1' HOUR AS start_time
FROM dual
CONNECT BY TIMESTAMP '2015-01-01 00:00:00' + LEVEL * INTERVAL '1' HOUR < LOCALTIMESTAMP),
t AS
(SELECT start_time, lead(start_time, 1) OVER (ORDER BY start_time) AS end_time, ROWNUM AS N
FROM spans
WHERE EXTRACT(HOUR FROM start_time) IN (6,14,23))
SELECT N, start_time, end_time, COUNT(*) AS ID_COUNT,
DECODE(EXTRACT(HOUR FROM start_time), 6,'morning', 14,'daytime', 23,'evening') AS daytime
FROM t
JOIN YOUR_TABLE WHERE TIMEVALUE BETWEEN start_time AND end_time
GROUP BY N;
Of course, the initial time value ('2015-01-01 00:00:00' in my example) has to be lower than the least date in your table.

SQL Results group by month

I'm trying to return some results spread over a rolling 12 month period eg:
MONTH IN OUT
January 210 191
February 200 111
March 132 141
April 112 141
May 191 188
etc...
How do I spread the results over a date range, populating the first column with the month name?
IN MSSQL it would be something like:
SELECT COUNT(problem.problem_type = 'IN') AS IN,
COUNT(problem.problem_type = 'OUT') AS OUT,
DATEPART(year, DateTime) as Year,
DATEPART(month, DateTime) as Month
FROM problem
WHERE (DateTime >= dbo.FormatDateTime('2010-01-01'))
AND
(DateTime < dbo.FormatDateTime('2010-01-31'))
GROUP BY DATEPART(year, DateTime),
DATEPART(month, DateTime);
But this is against an Oracle database so DATEPART and DateTime are not available.
My Problem table is roughly:
problem_ID Problem_type IN_Date OUT_Date
1 IN 2010-01-23 16:34:29.0 2010-02-29 13:06:28.0
2 IN 2010-01-27 12:34:29.0 2010-01-29 12:01:28.0
3 OUT 2010-02-13 13:24:29.0 2010-09-29 15:04:28.0
4 OUT 2010-02-15 16:31:29.0 2010-07-29 11:03:28.0
Use:
SELECT SUM(CASE WHEN p.problem_type = 'IN' THEN 1 ELSE 0 END) AS IN,
SUM(CASE WHEN p.problem_type = 'OUT' THEN 1 ELSE 0 END) AS OUT,
TO_CHAR(datetime, 'YYYY') AS year,
TO_CHAR(datetime, 'MM') AS month
FROM PROBLEM p
WHERE p.DateTime >= TO_DATE('2010-01-01', 'YYYY-MM-DD')
AND p.DateTime < TO_DATE('2010-01-31', 'YYYY-MM-DD')
GROUP BY TO_CHAR(datetime, 'YYYY'), TO_CHAR(datetime, 'MM')
You could also use:
SELECT SUM(CASE WHEN p.problem_type = 'IN' THEN 1 ELSE 0 END) AS IN,
SUM(CASE WHEN p.problem_type = 'OUT' THEN 1 ELSE 0 END) AS OUT,
TO_CHAR(datetime, 'MM-YYYY') AS mon_year
FROM PROBLEM p
WHERE p.DateTime >= TO_DATE('2010-01-01', 'YYYY-MM-DD')
AND p.DateTime < TO_DATE('2010-01-31', 'YYYY-MM-DD')
GROUP BY TO_CHAR(datetime, 'MM-YYYY')
Reference:
TO_CHAR
TO_DATE
You probably want something like
SELECT SUM( (CASE WHEN problem_type = 'IN' THEN 1 ELSE 0 END) ) in,
SUM( (CASE WHEN problem_type = 'OUT' THEN 1 ELSE 0 END) ) out,
EXTRACT( year FROM DateTime ) year,
EXTRACT( month FROM DateTime ) month
FROM problem
WHERE DateTime >= date '2010-01-01'
AND DateTime < date '2010-01-31'
GROUP BY EXTRACT( year FROM DateTime ),
EXTRACT( month FROM DateTime )