SQL display date on fixed frequency - sql

I am trying to create a query which displays dates in a specific interval.For example if I have an hire_date and the min(hire_date) is 2009-01-01 and the max(hire_date) is 2009-03-1, then the table should display hire_date in frequency of 2 days apart. Like:
+---------+
hire_date
+---------+
2009-01-01
2009-01-03
2009-01-05
2009-01-07
..........
The table named Employee_Service contains:
ID Number
Hire_date Timestamp
Name Varchar
The current query that I use is:
select trunc(hire_date) "Date",
count(id) "Num Empl Opened"
from Employee_Service
group by trunc(hire_date)
order by trunc(hire_date)
Is there any way to build a query for this.

You can try:
select trunc(hire_date) "Date",
count(id) "Num Empl Opened"
from Employee_Service
where mod(to_char(hire_date,'dd'),2)<>0
and trunc(hire_date) between to_date('2009-01-01','yyyy-mm-dd') and to_date('2009-03-01','yyyy-mm-dd')
group by trunc(hire_date)
order by trunc(hire_date)

Try this:
WITH DAYS AS
(SELECT TRUNC(TO_DATE('2009-03-01','YYYY-mm-dd')) - ROWNUM*2 D
FROM ALL_OBJECTS
WHERE ROWNUM < 365)
SELECT
DAYS.D
FROM
DAYS WHERE D >= TO_DATE('2009-01-01','YYYY-mm-dd');
Demo

I guess this query like this would help you
select to_date('01/05/2014','DD/MM/YYYY') + level -1 HIRE_DATE
from dual
WHERE MOD(LEVEL,2) <> 0
connect by level <= to_date('01/06/2014','DD/MM/YYYY') - to_date('01/05/2014','DD/MM/YYYY') +1;
WHERE '01/05/2014' is taken as from date and '01/06/2014' is taken as to date.
So your query would be:
IF hire_date is a VARCHAR in DD/MM/YYYY format
select to_date(min(hire_date),'DD/MM/YYYY') + level -1 HIRE_DATE
from dual
WHERE MOD(LEVEL,2) <> 0
connect by level <= to_date(max(hire_date),'DD/MM/YYYY') - to_date(min(hire_date),'DD/MM/YYYY') +1;
Or,
IF HIRE_DATE IS A DATE FIELD,
select (min(hire_date)) + level -1 HIRE_DATE
from dual
WHERE MOD(LEVEL,2) <> 0
connect by level <= (max(hire_date)) - (min(hire_date)) +1;
EDIT:
Max and Min cannot be used with Group by. So use below format:
with cte as
(
select max(hire_date) max_date, min(hire_date) min_date from my_table
where --- all conditions here
and rownum=1
)
select (min_date + level -1) hire_dates
from dual
WHERE MOD(LEVEL,2) <> 0
connect by level <= ( max_date - min_date +1 );
with cte as
(
select max(hire_date) max_date, min(hire_date) min_date from Employee_Service
)
select (min_date + level -1) "Date", count(id) "Num Empl Opened"
from Employee_Service , cte
WHERE MOD(LEVEL,2) <> 0
group by (min_date + level -1)
connect by level <= ( max_date - min_date +1 )
ORDER BY (min_date + level -1);

Related

How can I get all dates in a query if there is not data in that dates?

Currently, I have a ORACLE query that gives me the following data:
As you can see, there is not data between the 11th and the 12th, and I would need to get this result if there is not data:
I currently have this query:
select
TRUNC(FECHA_PEDIDO, 'dd') AS FECHA,
PEDIDO,
COUNT(TRUNC(FECHA_PEDIDO, 'dd')) AS CANTIDAD
FROM PEDIDOS_TIENDA
GROUP BY TRUNC(FECHA_PEDIDO, 'dd'), PEDIDO
ORDER BY TRUNC(FECHA_PEDIDO, 'dd') ASC
I'm a little lost, can you help me?
You want something like:
SELECT c.fecha,
t.pedido,
COALESCE(t.cantidad, 0) AS cantidad
FROM (
SELECT min_fecha + LEVEL - 1 AS fecha
FROM (
SELECT MIN(TRUNC(FECHA_PEDIDO, 'dd')) AS min_fecha,
MAX(TRUNC(FECHA_PEDIDO, 'dd')) AS max_fecha
FROM PEDIDOS_TIENDA
)
CONNECT BY LEVEL - 1 <= max_fecha - min_fecha
) c
LEFT OUTER JOIN
(
SELECT TRUNC(FECHA_PEDIDO, 'dd') AS FECHA,
PEDIDO,
COUNT(TRUNC(FECHA_PEDIDO, 'dd')) AS CANTIDAD
FROM PEDIDOS_TIENDA
GROUP BY TRUNC(FECHA_PEDIDO, 'dd'), PEDIDO
ORDER BY TRUNC(FECHA_PEDIDO, 'dd') ASC
) t
ON c.fecha = t.fecha
ORDER BY fecha, pedido
Which, for the sample data:
CREATE TABLE PEDIDOS_TIENDA ( fecha_pedido, pedido ) AS
SELECT DATE '2020-12-09', 'Aleron' FROM DUAL UNION ALL
SELECT DATE '2020-12-09', 'Aleron' FROM DUAL UNION ALL
SELECT DATE '2020-12-10', 'Bugia' FROM DUAL UNION ALL
SELECT DATE '2020-12-10', 'Focos' FROM DUAL UNION ALL
SELECT DATE '2020-12-10', 'Ruedas' FROM DUAL UNION ALL
SELECT DATE '2020-12-10', 'Focos' FROM DUAL UNION ALL
SELECT DATE '2020-12-11', 'Llantas' FROM DUAL UNION ALL
SELECT DATE '2020-12-13', 'Llantas' FROM DUAL;
Outputs:
FECHA
PEDIDO
CANTIDAD
09-DEC-20
Aleron
2
10-DEC-20
Bugia
1
10-DEC-20
Focos
2
10-DEC-20
Ruedas
1
11-DEC-20
Llantas
1
12-DEC-20
0
13-DEC-20
Llantas
1
db<>fiddle here
You can use following query . using table t2, calculate the dates between the smallest date and the largest date, and then right outer join it to the main table.
SELECT t2.dt AS fecha,t1.pedido,COALESCE(t1.cantidad,0)
FROM mytable t1
RIGHT OUTER JOIN
(SELECT (SELECT MIN(fecha) FROM mytable) + level - 1 dt
FROM DUAL CONNECT BY level <= ((SELECT MAX(fecha) FROM mytable) - (SELECT MIN(fecha) FROM mytable) + 1)) t2
ON t1.fecha = t2.dt
ORDER BY t2.dt
demo in db<>fiddle

Improve query to be less repetitive

Is there a way to improve this query? I see two problems here -
Repetitive code
Hard coded strings
The first CTE calculates count based on 18 months. The second CTE calculates count based on 12 months.
with month_18 as (
select proc_cd, count(*) as month_18 from
(
select distinct patient, proc_cd from
service
where proc_cd = '35'
and month_id >= (select month_id from annual)
and month_id <= '202009' --This month should be 18 months from the month above
and length(patient) > 1
) a
group by proc_cd
),
month_12 as
(
select proc_cd, count(*) as month_12 from
(
select distinct patient_id, proc_cd from
service
where proc_cd = '35'
and month_id >= '201910'
and month_id <= '202009' --This month should be 12 months from the month above
and length(patient) > 1
) a
group by proc_cd
)
select a.*, b.month_12 from
month_18 a
join month_12 b
on a.proc_cd = b.proc_cd
If I understand correctly, you can use conditional aggregation:
select proc_cd,
count(distinct patient) filter (where month_id >= (select month_id from annual) and month_id <= '202009') as month_18,
count(distinct patient) filter (where month_id >= '201910' and month_id <= '202009')
from service
where proc_cd = 35 and
length(patient) > 1
group by proc_cd;
If you have to deal with date arithmetic on the month ids, you can convert to a date, do the arithmetic and convert back to a string:
select to_char(to_date(month_id, 'YYYYMM') - interval '12 month', 'YYYYMM')
from (values ('202009')) v(month_id);

How to continous count by using SQL Oracle

Regarding my table as below
WORKING_CALENDAR_TABLE
===================================================
EMPLOYEE ID | DATE | WORKING DAY (0: Holiday; 1: WORKING DAY)
===================================================
02661 2017/12/01 1
02661 2017/12/02 1
02661 2017/12/03 0
02661 2017/12/04 0
02661 2017/12/05 0
02661 2017/12/06 1
02661 2017/12/07 1
02661 2017/12/08 1
02661 2017/12/09 1
When 2017/12/10, my expected result as below
===================================================
EMPLOYEE ID | CONTINOUS WORKING DAY
===================================================
02661 4
IF WE USE SQL ORACLE, CAN WE UTILIZE SQL ORACLE to got this result ?
One way of doing it can be by:
Getting the last day before your reference date (2017/12/10) where the employee didnĀ“t work.
Counting the rows after the date in 1. and before your reference date. Forcibly, each row represents a working day for the employee.
Here's the code with some comments:
Select employee_id, count(*) as continuous_days from mytable
where employee_id = '02661' and date > (select max(date)
where employee_id ='02661' and working_day = 0 and date date <
'2017/12/10') and date <
'2017/12/10' group by employee_id
/* (select max(date)
where employee_id ='02661' and working_day = 0 and date <
'2017/12/10') gets the last day where the employee didn't work before the reference date. Each row from a date after max(date) represent a working day for the employee because all of them are going to to have working_day = 1*/
Something to improve here:
In the case the employee had no holidays before a reference date then
select max(date)
where employee_id ='02661' and working_day = 0 and date date <
'2017/12/10' will return null so you could use COALESCE to prevent errors and in the case of a null, you get some other value in return. I think that in your case it would suffice with a very early date. You could use it this way:
COALESCE( (select max(date)
where employee_id ='02661' and working_day = 0 and date <
'2017/12/10'), '1900-01-01')
and the complete query would be:
select employee_id, count(*) as continuous_days from mytable
where employee_id = '02661' and date > COALESCE( (select max(date)
where employee_id ='02661' and working_day = 0 and date <
'2017/12/10'), '1900-01-01') and date <
'2017/12/10' group by employee_id
For a given date, the most general solution is:
select employee_id, count(*)
from t
where employee_id = '02661' and
date < date '2017-12-10' and
date > (select max(t2.date)
from t t2
where t2.employee_id = t.employee_id and t2.date < date '2017-12-10'
);
You can try this query:
SELECT T.EMPLOYEE_ID,
COUNT(0) AS CONTINUOUS_DAYS
FROM WORKING_CALENDAR_TABLE T
WHERE T.WORK_DATE BETWEEN (SELECT MAX(WORK_DATE) + 1
FROM WORKING_CALENDAR_TABLE I
WHERE I.EMPLOYEE_ID = T.EMPLOYEE_ID
AND I.WORK_DAY = 0)
AND DATE '2017-12-10' + 1
GROUP BY T.EMPLOYEE_ID

select records with a 6 month interval

I want to select with an oracle sql statement the records with a 6 month time interval.
Example
01/06/2011 AMOUNT
01/12/2011 AMOUNT
01/06/2012 AMOUNT
01/12/2012 AMOUNT
And so on
How can I do this with oracle sql?
select ADD_MONTHS(trunc(sysdate), (rownum - 1) * 6) some_date
from dual
connect by level <= 5;
SOME_DATE
-----------
18.04.2014
18.10.2014
18.04.2015
18.10.2015
18.04.2016
WITH got_r_num AS
(
SELECT t.* -- OR WHATEVER YOU WANT
, DENSE_RANK () OVER ( PARTITION BY TRUNC (created_date, 'MONTH')
ORDER BY TRUNC (created_date) -- DESC
) AS r_num
FROM test_table
WHERE MOD ( MONTHS_BETWEEN ( TRUNC (SYSDATE)
, TRUNC (created_date)
)
, 6
) = 0
)
SELECT * -- or list all columns except r_num
FROM got_r_num
WHERE r_num = 1
;
Have a look here please .
If you need to sum of all records of 6 months in the AMOUNT field:
You can subquery with sum function and query with CONNECT BY LEVEL
SELECT x AS l_date,
(
SELECT sum(your_data)
FROM your_table
WHERE table_date >= x
AND table_date < add_months(x,6)
)AMOUNT
FROM(
SELECT add_months(to_date('01/06/2011','dd/mm/yyyy'),(LEVEL-1)*6) x
FROM dual
CONNECT BY LEVEL <= 4
);

Total Count of Active Employees by Date

I have in the past written queries that give me counts by date (hires, terminations, etc...) as follows:
SELECT per.date_start AS "Date",
COUNT(peo.EMPLOYEE_NUMBER) AS "Hires"
FROM hr.per_all_people_f peo,
hr.per_periods_of_service per
WHERE per.date_start BETWEEN peo.effective_start_date AND peo.EFFECTIVE_END_DATE
AND per.date_start BETWEEN :PerStart AND :PerEnd
AND per.person_id = peo.person_id
GROUP BY per.date_start
I was now looking to create a count of active employees by date, however I am not sure how I would date the query as I use a range to determine active as such:
SELECT COUNT(peo.EMPLOYEE_NUMBER) AS "CT"
FROM hr.per_all_people_f peo
WHERE peo.current_employee_flag = 'Y'
and TRUNC(sysdate) BETWEEN peo.effective_start_date AND peo.EFFECTIVE_END_DATE
Here is a simple way to get started. This works for all the effective and end dates in your data:
select thedate,
SUM(num) over (order by thedate) as numActives
from ((select effective_start_date as thedate, 1 as num from hr.per_periods_of_service) union all
(select effective_end_date as thedate, -1 as num from hr.per_periods_of_service)
) dates
It works by adding one person for each start and subtracting one for each end (via num) and doing a cumulative sum. This might have duplicates dates, so you might also do an aggregation to eliminate those duplicates:
select thedate, max(numActives)
from (select thedate,
SUM(num) over (order by thedate) as numActives
from ((select effective_start_date as thedate, 1 as num from hr.per_periods_of_service) union all
(select effective_end_date as thedate, -1 as num from hr.per_periods_of_service)
) dates
) t
group by thedate;
If you really want all dates, then it is best to start with a calendar table, and use a simple variation on your original query:
select c.thedate, count(*) as NumActives
from calendar c left outer join
hr.per_periods_of_service pos
on c.thedate between pos.effective_start_date and pos.effective_end_date
group by c.thedate;
If you want to count all employees who were active during the entire input date range
SELECT COUNT(peo.EMPLOYEE_NUMBER) AS "CT"
FROM hr.per_all_people_f peo
WHERE peo.[EFFECTIVE_START_DATE] <= :StartDate
AND (peo.[EFFECTIVE_END_DATE] IS NULL OR peo.[EFFECTIVE_END_DATE] >= :EndDate)
Here is my example based on Gordon Linoff answer
with a little modification, because in SUBSTRACT table all records were appeared with -1 in NUM, even if no date was in END DATE = NULL.
use AdventureWorksDW2012 --using in MS SSMS for choosing DATABASE to work with
-- and may be not work in other platforms
select
t.thedate
,max(t.numActives) AS "Total Active Employees"
from (
select
dates.thedate
,SUM(dates.num) over (order by dates.thedate) as numActives
from
(
(
select
StartDate as thedate
,1 as num
from DimEmployee
)
union all
(
select
EndDate as thedate
,-1 as num
from DimEmployee
where EndDate IS NOT NULL
)
) AS dates
) AS t
group by thedate
ORDER BY thedate
worked for me, hope it will help somebody
I was able to get the results I was looking for with the following:
--Active Team Members by Date
SELECT "a_date",
COUNT(peo.EMPLOYEE_NUMBER) AS "CT"
FROM hr.per_all_people_f peo,
(SELECT DATE '2012-04-01'-1 + LEVEL AS "a_date"
FROM dual
CONNECT BY LEVEL <= DATE '2012-04-30'+2 - DATE '2012-04-01'-1
)
WHERE peo.current_employee_flag = 'Y'
AND "a_date" BETWEEN peo.effective_start_date AND peo.EFFECTIVE_END_DATE
GROUP BY "a_date"
ORDER BY "a_date"