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

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

Related

Select all days in a week, when calendarweek is given

certainly something very simple, but for an application I would like to know how, if I know the calendar week, I can display the first to the last day of the week per row.
Currently, I am only shown the day in which content is present.
I would like to have 7 days displayed (as date, not necessarily with name) whether they are empty or not.
SELECT
MIN( TO_CHAR(LP_BELEGUNG.GEN_DATUM,'DD.MM.YYYY')) AS GRD_ROW_ID
, COUNT( DISTINCT
CASE
WHEN LP_BELEGUNG.ART = 1 THEN LP_BELEGUNG.LP_BELEGUNG_ID
ELSE NULL
END ) AS ANZAHL_ART_1
, COUNT( DISTINCT
CASE
WHEN LP_BELEGUNG.ART = 2 THEN LP_BELEGUNG.LP_BELEGUNG_ID
ELSE NULL
END ) AS ANZAHL_ART_2
, COUNT( DISTINCT
CASE
WHEN LP_BELEGUNG.ART = 3 THEN LP_BELEGUNG.LP_BELEGUNG_ID
ELSE NULL
END ) AS ANZAHL_ART_3
, COUNT( DISTINCT
CASE
WHEN LP_BELEGUNG.ART = 99 THEN LP_BELEGUNG.LP_BELEGUNG_ID
ELSE NULL
END ) AS ANZAHL_ART_4
FROM
LP_BELEGUNG
WHERE
TO_CHAR(LP_BELEGUNG.GEN_DATUM, 'WW') = 37 --the calendar week
If you want one row per day, for a week number in a given year, then you can generate all the dates in that week and use an outer join to look for matching rows in your table, if there are any.
Unfortunately Oracle doesn't supply a simple way to get a date from a week number, but based on how the WW element is defined you can start from the first day of the year and add the appropriate number of days to get the start of the week:
select trunc(sysdate, 'YYYY') + (7 * 37) - 7 from dual;
TRUNC(SYSDATE,'YYYY')+(7*37)-7
10-SEP-22
... where 37 is the week number, and I've assumed you're looking at the current year (if not, use a fixed date like date '2022-01-01' instead of trunc(sysdate)).
You can then get all the days in that week with a hierarchical query:
select trunc(sysdate, 'YYYY') + (7 * 37) + level - 8
from dual
connect by level <= 7;
TRUNC(SYSDATE,'YYYY')+(7*37)+LEVEL-8
10-SEP-22
11-SEP-22
12-SEP-22
13-SEP-22
14-SEP-22
15-SEP-22
16-SEP-22
Then use those values in an inline view or CTE, and left-join to your table using a date range (to allow for non-midnight times but still allowing an index on that column to be used), grouping by the date:
with cte (dt) as (
select trunc(sysdate, 'YYYY') + (7 * 37) + level - 8
from dual
connect by level <= 7
)
SELECT
TO_CHAR(cte.dt, 'DD.MM.YYYY') AS GRD_ROW_ID
, COUNT( DISTINCT
CASE
WHEN LP_BELEGUNG.ART = 1 THEN LP_BELEGUNG.LP_BELEGUNG_ID
ELSE NULL
END ) AS ANZAHL_ART_1
, COUNT( DISTINCT
CASE
WHEN LP_BELEGUNG.ART = 2 THEN LP_BELEGUNG.LP_BELEGUNG_ID
ELSE NULL
END ) AS ANZAHL_ART_2
, COUNT( DISTINCT
CASE
WHEN LP_BELEGUNG.ART = 3 THEN LP_BELEGUNG.LP_BELEGUNG_ID
ELSE NULL
END ) AS ANZAHL_ART_3
, COUNT( DISTINCT
CASE
WHEN LP_BELEGUNG.ART = 99 THEN LP_BELEGUNG.LP_BELEGUNG_ID
ELSE NULL
END ) AS ANZAHL_ART_4
FROM
cte
LEFT JOIN
LP_BELEGUNG
ON
LP_BELEGUNG.GEN_DATUM >= cte.dt AND LP_BELEGUNG.GEN_DATUM < cte.dt + 1
GROUP BY
cte.dt
ORDER BY
cte.dt
With some sample data to mimic your original result, that gives:
GRD_ROW_ID
ANZAHL_ART_1
ANZAHL_ART_2
ANZAHL_ART_3
ANZAHL_ART_4
10.09.2022
0
0
0
0
11.09.2022
0
0
0
0
12.09.2022
0
0
0
0
13.09.2022
0
0
0
0
14.09.2022
0
0
0
0
15.09.2022
0
0
0
0
16.09.2022
1
0
0
7
fiddle
Here is a set of dates counted and divided to days of the week using to_char and pivot.
select *
from
(
select dt
,to_char(dt, 'D') as dow
from t
) t
pivot (count(dt) for dow in('1', '2', '3', '4', '5', '6', '7')) p
'1'
'2'
'3'
'4'
'5'
'6'
'7'
1
1
0
0
1
3
1
Fiddle
Use conditional aggregation:
SELECT TO_CHAR(MIN(GEN_DATUM),'DD.MM.YYYY') AS GRD_ROW_ID,
COUNT( DISTINCT
CASE
WHEN ART = 1
AND TRUNC(gen_datum) - TRUNC(gen_datum, 'WW') = 0
THEN LP_BELEGUNG_ID
END
) AS ANZAHL_ART_1_DAY1,
COUNT( DISTINCT
CASE
WHEN ART = 1
AND TRUNC(gen_datum) - TRUNC(gen_datum, 'WW') = 1
THEN LP_BELEGUNG_ID
END
) AS ANZAHL_ART_1_DAY2,
-- ...
COUNT( DISTINCT
CASE
WHEN ART = 1
AND TRUNC(gen_datum) - TRUNC(gen_datum, 'WW') = 6
THEN LP_BELEGUNG_ID
END
) AS ANZAHL_ART_1_DAY7,
COUNT( DISTINCT
CASE
WHEN ART = 2
AND TRUNC(gen_datum) - TRUNC(gen_datum, 'WW') = 0
THEN LP_BELEGUNG_ID
END
) AS ANZAHL_ART_2_DAY1,
COUNT( DISTINCT
CASE
WHEN ART = 2
AND TRUNC(gen_datum) - TRUNC(gen_datum, 'WW') = 1
THEN LP_BELEGUNG_ID
END
) AS ANZAHL_ART_2_DAY2,
-- ...
COUNT( DISTINCT
CASE
WHEN ART = 2
AND TRUNC(gen_datum) - TRUNC(gen_datum, 'WW') = 6
THEN LP_BELEGUNG_ID
END
) AS ANZAHL_ART_2_DAY7,
COUNT( DISTINCT
CASE
WHEN ART = 3
AND TRUNC(gen_datum) - TRUNC(gen_datum, 'WW') = 0
THEN LP_BELEGUNG_ID
END
) AS ANZAHL_ART_3_DAY1,
COUNT( DISTINCT
CASE
WHEN ART = 3
AND TRUNC(gen_datum) - TRUNC(gen_datum, 'WW') = 1
THEN LP_BELEGUNG_ID
END
) AS ANZAHL_ART_3_DAY2,
-- ...
COUNT( DISTINCT
CASE
WHEN ART = 3
AND TRUNC(gen_datum) - TRUNC(gen_datum, 'WW') = 6
THEN LP_BELEGUNG_ID
END
) AS ANZAHL_ART_3_DAY7,
COUNT( DISTINCT
CASE
WHEN ART = 99
AND TRUNC(gen_datum) - TRUNC(gen_datum, 'WW') = 0
THEN LP_BELEGUNG_ID
END
) AS ANZAHL_ART_4_DAY1,
COUNT( DISTINCT
CASE
WHEN ART = 99
AND TRUNC(gen_datum) - TRUNC(gen_datum, 'WW') = 1
THEN LP_BELEGUNG_ID
END
) AS ANZAHL_ART_4_DAY2,
-- ...
COUNT( DISTINCT
CASE
WHEN ART = 99
AND TRUNC(gen_datum) - TRUNC(gen_datum, 'WW') = 6
THEN LP_BELEGUNG_ID
END
) AS ANZAHL_ART_4_DAY7
FROM LP_BELEGUNG
WHERE TO_CHAR(GEN_DATUM, 'WW') = 37

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.

Quantity of data per month

I have to calculate the number of cars a day - of every month
I tried to do this using the Datediff formula
But I can't add the segmentation of each month either.
Attached script table:
create table TABLE_A(Code FLOAT,DateIn datetime,dateOut datetime,Garage varchar(30)
)
insert into Table_A (Code,DateIn,dateOut,Garage) values
('1','2018-06-07 00:00:00.000','2018-12-19 00:00:00.000','X'),
('2','2018-05-30 00:00:00.000','2018-12-19 00:00:00.000','Y'),
('3','2018-08-08 00:00:00.000','2018-11-18 00:00:00.000','Z'),
('4','2018-12-30 00:00:00.000','2018-12-30 00:00:00.000','Y'),
('5','2018-09-16 00:00:00.000','2018-10-19 00:00:00.000','Y'),
('6','2018-05-08 00:00:00.000','2018-08-28 00:00:00.000','Z'),
('7','2018-01-29 00:00:00.000','2018-07-31 00:00:00.000','Z'),
('8','2018-05-24 00:00:00.000','2018-09-10 00:00:00.000','X'),
('9','2018-05-02 00:00:00.000','2018-06-30 00:00:00.000','Y'),
('10','2018-07-05 00:00:00.000','2018-12-09 00:00:00.00','Z')
And this is the structure of the query result that should be:(Columns:Year,month,Garage-Number of vehicles per day by month)
Year month X Y Z
2018 1
2018 2
2018 3
2018 4
2018 5
2018 6
2018 7
2018 8
2018 9
2018 10
2018 11
2018 12
Thanks for the help.
You can first generate the list of months and year and then can left join your table to that -
WITH MONTHS AS (SELECT 1 MNTHS
UNION ALL
SELECT 2
UNION ALL
SELECT 3
UNION ALL
SELECT 4
UNION ALL
SELECT 5
UNION ALL
SELECT 6
UNION ALL
SELECT 7
UNION ALL
SELECT 8
UNION ALL
SELECT 9
UNION ALL
SELECT 10
UNION ALL
SELECT 11
UNION ALL
SELECT 12),
YEAR AS (SELECT 2018 YEAR)
SELECT YEAR,
MNTHS,
SUM(CASE WHEN Garage = 'X' THEN 1 ELSE 0 END) AS X,
SUM(CASE WHEN Garage = 'Y' THEN 1 ELSE 0 END) AS Y,
SUM(CASE WHEN Garage = 'Z' THEN 1 ELSE 0 END) AS Z
FROM (SELECT * FROM MONTHS M CROSS JOIN YEAR Y) YEARS
LEFT JOIN TABLE_A T ON YEARS.MNTHS = MONTH(T.DateIn)
AND YEARS.YEAR = YEAR(T.DateIn)
GROUP BY YEAR(DateIn),
MONTH(DateIn),
MNTHS,
YEAR
ORDER BY YEAR,
MNTHS
Here is the fiddle
The following query should do what you want, the Recursive CTE part is used to figure out dates between each DayIn and DayOut. Once we have the complete list of dates, in the main query we do a conditional aggregation to find out the DISTINCT number of cars in the garage each month,
CREATE TABLE TABLE_A (Code FLOAT,DateIn DATETIME,dateOut DATETIME,Garage VARCHAR(30))
INSERT INTO Table_A (Code,DateIn,dateOut,Garage) VALUES
('1','2018-06-07 00:00:00.000','2018-12-19 00:00:00.000','X'),
('2','2018-05-30 00:00:00.000','2018-12-19 00:00:00.000','Y'),
('3','2018-08-08 00:00:00.000','2018-11-18 00:00:00.000','Z'),
('4','2018-12-30 00:00:00.000','2018-12-30 00:00:00.000','Y'),
('5','2018-09-16 00:00:00.000','2018-10-19 00:00:00.000','Y'),
('6','2018-05-08 00:00:00.000','2018-08-28 00:00:00.000','Z'),
('7','2018-01-29 00:00:00.000','2018-07-31 00:00:00.000','Z'),
('8','2018-05-24 00:00:00.000','2018-09-10 00:00:00.000','X'),
('9','2018-05-02 00:00:00.000','2018-06-30 00:00:00.000','Y'),
('10','2018-07-05 00:00:00.000','2018-12-09 00:00:00.00','Z')
/** Main Query Starts Here **/
;WITH CTE ([Code],[DateIn],[DateOut],[Garage]) AS (
SELECT [Code], [DateIn], [DateOut], [Garage]
FROM TABLE_A WHERE [DateIn] <= [DateOut]
UNION ALL
SELECT [Code], DATEADD(DAY, 1, [DateIn]), [DateOut], [Garage]
FROM CTE
WHERE [DateIn] < [DateOut])
SELECT
YEAR([DateIn]) AS [Year]
,MONTH([DateIn]) AS [Month]
,COUNT( DISTINCT CASE WHEN [Garage] = 'X' THEN T.t1 ELSE NULL END) AS X
,COUNT( DISTINCT CASE WHEN [Garage] = 'Y' THEN T.t1 ELSE NULL END) AS Y
,COUNT( DISTINCT CASE WHEN [Garage] = 'Z' THEN T.t1 ELSE NULL END) AS Z
FROM CTE
CROSS APPLY (VALUES (CONVERT(VARCHAR(4),YEAR([DateIn])) + CONVERT(VARCHAR(2),MONTH([DateIn])) + CONVERT(VARCHAR(20),[Code]))) AS T(t1)
GROUP BY YEAR([DateIn]), MONTH([DateIn])
ORDER BY [Year], [Month]
OPTION (MAXRECURSION 0)
The result is as below,
Year Month X Y Z
2018 1 0 0 1
2018 2 0 0 1
2018 3 0 0 1
2018 4 0 0 1
2018 5 1 2 2
2018 6 2 2 2
2018 7 2 1 3
2018 8 2 1 3
2018 9 2 2 2
2018 10 1 2 2
2018 11 1 1 2
2018 12 1 2 1
You can generate the dates using a recursive subquery.
Then you have multiple ways to calculate the summary data. One simple method uses apply:
with dates as (
select convert(date, '2018-01-01') as dte, 1 as lev
union all
select dateadd(month, 1, dte), lev + 1
from dates
where lev < 12
)
select year(d.dte), month(d.dte), s.*
from dates d outer apply
(select sum(case when a.Garage = 'X' then 1 else 0 end) as x,
sum(case when a.Garage = 'Y' then 1 else 0 end) as y,
sum(case when a.Garage = 'Z' then 1 else 0 end) as z
from table_a a
where a.datein <= d.dte and a.dateout >= d.dte
) s;
Your question is a little vague on the exact calculation. This calculates the number of cars in each garage on the first of each month.
Here is a db<>fiddle.
EDIT:
Your revised question is more complicated, but since I started answering:
with dates as (
select convert(date, '2018-01-01') as dte, 1 as lev
union all
select dateadd(month, 1, dte), lev + 1
from dates
where lev < 12
)
select year(d.dte), month(d.dte),
sum(case when a.Garage = 'X'
then datediff(day,
(case when a.datein < d.dte then d.dte else datein end),
(case when a.dateout >= dateadd(month, 1, d.dte) then eomonth(d.dte) else dateout end)
) + 1
else 0
end) as x_cardays,
sum(case when a.Garage = 'Y'
then datediff(day,
(case when a.datein < d.dte then d.dte else datein end),
(case when a.dateout >= dateadd(month, 1, d.dte) then eomonth(d.dte) else dateout end)
) + 1
else 0
end) as y_cardays,
sum(case when a.Garage = 'Z'
then datediff(day,
(case when a.datein < d.dte then d.dte else datein end),
(case when a.dateout >= dateadd(month, 1, d.dte) then eomonth(d.dte) else dateout end)
) + 1
else 0
end) as z_cardays
from (select d.*, day(eomonth(dte)) as days_in_month
from dates d
) d left join
table_a a
on a.datein < dateadd(month, 1, d.dte) and a.dateout >= d.dte
group by d.dte
order by d.dte;
Overlaps with dates are a bit tricky, but you definitely want to do this with dates.
Note this doesn't calculate the average per day. You can divide by d.days_in_month if you want the average.
Here is a revised db<>fiddle.
This below script will only work if all your DateIn and DateOut for a particular record is in a single year like 2018.
Leap year Year is not considered but can be implemented.
WITH Month_Wise_Day AS
(
-- Listing/selecting 12 month manually here
-- With number of day for that month
SELECT 1 M, 31 ND UNION ALL
SELECT 2 M, 28 UNION ALL SELECT 3 M,31 UNION ALL SELECT 4 M,30 UNION ALL SELECT 5 M,31 UNION ALL
SELECT 6 M,30 UNION ALL SELECT 7 M,31 UNION ALL SELECT 8 M,31 UNION ALL SELECT 9 M,30 UNION ALL
SELECT 10 M,31 UNION ALL SELECT 11 M,30 UNION ALL SELECT 12 M,31
)
SELECT A.Year, A.Month,
SUM(CASE WHEN A.Garage = 'X' THEN A.No_of_days ELSE 0 END) AS X,
SUM(CASE WHEN A.Garage = 'Y' THEN A.No_of_days ELSE 0 END) AS Y,
SUM(CASE WHEN A.Garage = 'Z' THEN A.No_of_days ELSE 0 END) AS Z
FROM
(
SELECT A.*,
B.M AS Month,
YEAR(A.DateIn) AS Year,
CASE
WHEN MONTH(A.DateIn) = MONTH(A.DateOut) THEN DATEDIFF(DD,DateIn,DateOut) +1
WHEN B.M = MONTH(DateIn) THEN B.ND - DAY(DateIn)+1
WHEN B.M = MONTH(DateOut) THEN DAY(DateOut)
ELSE ND
END No_of_days
FROM TABLE_A A
INNER JOIN Month_Wise_Day B ON B.M BETWEEN MONTH(DateIn) AND MONTH (DateOut)
)A
GROUP BY A.Year, A.Month

count rows before time

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')

Including a row in the result of a SELECT for which the select didnt recover any data

I'm trying write a query to retrieve the following data :
TYPE | TOTAL | 0_10 DAYS | 10_20 DAYS | .......
X 300 100 200 .......
Y 0 0 0 .......
Z 600 50 120 .......
I have to group all my entries by type and count the number of entries of each type for each date range and add them up as a total.
My problem is the need to display rows of zeros for the types for which I don't retrieve any data. Basically the type column always displays a fixed amount of types. So far I have tried using 'UNION ALL' but then the rows of zeros will always show. Here is my query :
SELECT TYPE AS "ORDERS",
Count(*) AS "TOTAL",
Nvl(Sum(CASE
WHEN ( DATE_ORDER BETWEEN SYSDATE - 10 AND SYSDATE ) THEN 1
ELSE 0
END), 0) AS "0_10_DAYS",
Nvl(Sum(CASE
WHEN ( DATE_ORDER BETWEEN SYSDATE - 20 AND SYSDATE - 11 ) THEN
1
ELSE 0
END), 0) AS "10_20_DAYS",
Nvl(Sum(CASE
WHEN ( DATE_ORDER BETWEEN SYSDATE - 30 AND SYSDATE - 21 ) THEN
1
ELSE 0
END), 0) AS "20_30_DAYS",
Nvl(Sum(CASE
WHEN ( DATE_ORDER <= To_date(SYSDATE - 30) ) THEN 1
ELSE 0
END), 0) AS "PLUS_30_DAYS"
FROM T_ORDERS
WHERE TYPE = 'X'
OR TYPE = 'Y'
OR TYPE = 'Z'
GROUP BY TYPE
UNION ALL
SELECT TYPE AS "ORDERS",
0 AS "TOTAL",
0 AS "0_10_DAYS",
0 AS "10_20_DAYS",
0 AS "20_30_DAYS",
0 AS "PLUS_30_DAYS"
FROM T_ORDERS
WHERE TYPE IS NOT NULL
GROUP BY TYPE;
I'm new to SQL so bear with me if any of the answers to questions on this topic solves mine but I can't seem to work it out. If something is unclear please write it in comment box.
Try:
SELECT TYPE AS "ORDERS",
Count(DATE_ORDER) AS "TOTAL",
Nvl(Sum(CASE
WHEN (DATE_ORDER BETWEEN SYSDATE - 10 AND SYSDATE) THEN 1
ELSE 0
END), 0) AS "0_10_DAYS",
Nvl(Sum(CASE
WHEN (DATE_ORDER BETWEEN SYSDATE - 20 AND SYSDATE - 11) THEN 1
ELSE 0
END), 0) AS "10_20_DAYS",
Nvl(Sum(CASE
WHEN (DATE_ORDER BETWEEN SYSDATE - 30 AND SYSDATE - 21) THEN 1
ELSE 0
END), 0) AS "20_30_DAYS",
Nvl(Sum(CASE
WHEN (DATE_ORDER <= To_date(SYSDATE - 30)) THEN 1
ELSE 0
END), 0) AS "PLUS_30_DAYS"
FROM (SELECT TYPE, DATE_ORDER
FROM T_ORDERS
WHERE TYPE IN ('X', 'Y', 'Z')
UNION ALL
SELECT DECODE(LEVEL, 1,'X', 2,'Y', 3,'Z') TYPE, NULL DATE_ORDER
FROM DUAL
CONNECT BY LEVEL <= 3
) SQ
GROUP BY TYPE
you can use an OUTER JOIN to achive the desired result
with types
AS
(
SELECT 'X' as t_name FROM dual
UNION ALL
SELECT 'Y' as t_name FROM dual
UNION ALL
SELECT 'Z' as t_name FROM dual
)
SELECT
types.t_name AS "ORDERS",
SUM(CASE WHEN T_ORDERS.TYPE IS NULL THEN 0 ELSE 1 END ) AS "TOTAL",
Nvl(Sum(CASE
WHEN ( DATE_ORDER BETWEEN SYSDATE - 10 AND SYSDATE ) THEN 1
ELSE 0
END), 0) AS "0_10_DAYS",
Nvl(Sum(CASE
WHEN ( DATE_ORDER BETWEEN SYSDATE - 20 AND SYSDATE - 11 ) THEN
1
ELSE 0
END), 0) AS "10_20_DAYS",
Nvl(Sum(CASE
WHEN ( DATE_ORDER BETWEEN SYSDATE - 30 AND SYSDATE - 21 ) THEN
1
ELSE 0
END), 0) AS "20_30_DAYS",
Nvl(Sum(CASE
WHEN ( DATE_ORDER <= To_date(SYSDATE - 30) ) THEN 1
ELSE 0
END), 0) AS "PLUS_30_DAYS"
FROM
types
LEFT OUTER JOIN T_ORDERS ON (types.t_name = T_ORDERS.TYPE )
GROUP BY types.t_name
or if you already have all types in a TYPES table you can use this table instead of WITH
SELECT
types.t_name AS "ORDERS",
SUM(CASE WHEN T_ORDERS.TYPE IS NULL THEN 0 ELSE 1 END ) AS "TOTAL",
Nvl(Sum(CASE
WHEN ( DATE_ORDER BETWEEN SYSDATE - 10 AND SYSDATE ) THEN 1
ELSE 0
END), 0) AS "0_10_DAYS",
Nvl(Sum(CASE
WHEN ( DATE_ORDER BETWEEN SYSDATE - 20 AND SYSDATE - 11 ) THEN
1
ELSE 0
END), 0) AS "10_20_DAYS",
Nvl(Sum(CASE
WHEN ( DATE_ORDER BETWEEN SYSDATE - 30 AND SYSDATE - 21 ) THEN
1
ELSE 0
END), 0) AS "20_30_DAYS",
Nvl(Sum(CASE
WHEN ( DATE_ORDER <= To_date(SYSDATE - 30) ) THEN 1
ELSE 0
END), 0) AS "PLUS_30_DAYS"
FROM
types
LEFT OUTER JOIN T_ORDERS ON (types.t_name = T_ORDERS.TYPE )
WHERE
types.t_name IN ( 'X', 'Y','Z')
GROUP BY types.t_name