Here are the dates available in my Record_Date (Date) column in Attendance table in Oracle 10g.You can find the dates 04/06/2016 08/06/2016 16/06/2016 23/06/2016 29/06/2016 are missing in the sequence.
**Record_Date**
01/06/2016
02/06/2016
03/06/2016
05/06/2016
06/06/2016
07/06/2016
09/06/2016
10/06/2016
12/06/2016
13/06/2016
14/06/2016
15/06/2016
17/06/2016
18/06/2016
19/06/2016
20/06/2016
21/06/2016
22/06/2016
24/06/2016
25/06/2016
26/06/2016
27/06/2016
28/06/2016
30/06/2016
01/07/2016
I just need a query to find the missing dates in the specific month (and later also in the Year).
Kindly show me an approach
You can use this one:
WITH all_days AS
(SELECT DATE '2016-06-01' + LEVEL-1 AS the_day
FROM dual
CONNECT BY DATE '2016-06-01' + LEVEL-1 <= DATE '2016-06-30')
SELECT the_day
FROM all_days
WHERE the_day <>ALL (SELECT Record_Date FROM Attendance);
Or, if you like to have it more dynamically:
WITH all_days AS
(SELECT START_DATE + LEVEL AS the_day
FROM dual
CROSS JOIN
(SELECT
TRUNC(MIN(Record_Date), 'MM') -1 AS START_DATE,
TRUNC(LAST_DAY(MAX(Record_Date))) AS END_DATE
FROM Attendance)
CONNECT BY START_DATE + LEVEL <= END_DATE)
SELECT the_day
FROM all_days
WHERE the_day <>ALL (SELECT Record_Date FROM Attendance);
Note, <>ALL is the same as NOT IN - it's just my personal preference.
with
nums(num )
as
(select 0 from dual
union all
select num + 1 from nums
where num < (select max(col) from qtable)- (select min(col) from qtable)
),
date_btwn(dat)
as(select num + (select min(col) from qtable) from nums)
select dat from date_btwn
minus
select col from qtable;
Inline views nums will generate all numbers to add from start date. date_btwn contains all dates between start date and end date in table. We are excluding the dates in our table using minus.
You need to generate all dates and you have to find missing ones. Below with cte i have done it
Using CTE and not inQuery :
with calendar as (
select rownum - 1 as daynum,
to_date('1-jun-2016') + (rownum - 1) as monthdate
from dual )
select monthdate as monthdate
from calendar
where monthdate not in (
SELECT
Record_Date
FROM Attendance )
and monthdate >= to_date('1-jun-2016') /* CHANGE as per need */
and monthdate < to_date('1-jul-2016') /* CHANGE as per need */
Or Using CTE and left join Query :
with calendar as (
select rownum - 1 as daynum,
to_date('1-jun-2016') + (rownum - 1) as monthdate
from dual )
select monthdate as monthdate
from calendar C left join Attendance A on A.Record_Date=c.monthdate
where A.Record_Date is null
and monthdate >= to_date('1-jun-2016') /* CHANGE as per need */
and monthdate < to_date('1-jul-2016') /* CHANGE as per need */
as per datecolumn format change it to validformat into selection query first then use this query.
Related
Question for the SQL gurus. I have a table with 3 columns. [Date, Meter, Quality], where there will only be one line per date for each meter. As an example:
SELECT * FROM MyDB WHERE Meter = 'MeterX' AND Date > '1-AUG-2022' AND Date <= '5-AUG-2022' ORDER BY Date;
I would query much larger date ranges so would usually miss if there is a date missing. Is there a way that I can have a value returned in the Quality column like "Missing" if that partiqular day is missing from the database? This means that I also need the missing date in the Date column. I also only have read access, so no creating temp tables to join with.
Thank you.
Use a PARTITIONed OUTER JOIN to a row-generator:
SELECT c.day,
m.meter,
COALESCE(m.quality, 0) AS quality
FROM (
SELECT DATE '2022-08-01' + (LEVEL - 1) AS day
FROM DUAL
CONNECT BY DATE '2022-08-01' + (LEVEL - 1) <= DATE '2022-08-05'
) c
LEFT OUTER JOIN MyDB m
PARTITION BY (m.meter)
ON (c.day <= m."DATE" and m."DATE" < c.day + 1)
WHERE m.Meter = 'MeterX'
ORDER BY c.day;
Which, for the sample data:
CREATE TABLE mydb ("DATE", meter, quality) AS
SELECT DATE '2022-08-01', 'MeterX', 42 FROM DUAL UNION ALL
SELECT DATE '2022-08-02', 'MeterX', 23 FROM DUAL UNION ALL
SELECT DATE '2022-08-04', 'MeterX', 7 FROM DUAL UNION ALL
SELECT DATE '2022-08-05', 'MeterX', 99 FROM DUAL;
Outputs:
DAY
METER
QUALITY
01-AUG-22
MeterX
42
02-AUG-22
MeterX
23
03-AUG-22
MeterX
0
04-AUG-22
MeterX
7
05-AUG-22
MeterX
99
db<>fiddle here
for postgres this could work for you
with date_dimension as (
SELECT dd::DATE
FROM generate_series
( '2022-08-01'::timestamp
, '2022-08-05'::timestamp
, '1 day'::interval) dd
)
select *
from my_table
left join date_dimension on date_dimension.dd = my_table.Date
where Meter = 'MeterX'
and Date > '2022-08-01'
and Date <= '2022-08-05'
order by Date;
I Need To Generate Dates During Specific Time Period ( Using Oracle SQL Developer 12& 18).
/
I Found Bellow Solution:
SELECT TO_DATE('01/01/2020','DD/MM/YYYY') + (ROWNUM - 1) DATEGEN
FROM ALL_OBJECTS
WHERE TO_DATE('01/01/2020','DD/MM/YYYY') + (ROWNUM -1) <= TO_DATE('01/11/2020','DD/MM/YYYY')
I Get Daily Dates (As Bellow):
01-JAN-20 12.00.00 AM
02-JAN-20 12.00.00 AM
03-JAN-20 12.00.00 AM
04-JAN-20 12.00.00 AM
05-JAN-20 12.00.00 AM
06-JAN-20 12.00.00 AM
I Need To Get Dates With Gap 5 days or 3 days (Example):
01-JAN-20 12.00.00 AM
05-JAN-20 12.00.00 AM
10-JAN-20 12.00.00 AM
15-JAN-20 12.00.00 AM
Any Suggestions?!
Thanks in Advance.
You can use the hiearchy query with simple logic as follows:
SELECT DATE '2020-01-01' + ( LEVEL * 5 ) - 1
FROM DUAL CONNECT BY
LEVEL <= ( DATE '2020-11-01' - DATE '2020-01-01' ) / 5;
Please note that in your question first two dates have 4 days of difference and in my solution, the First date will be 05-01-2020
You could use a standad recursive query:
with cte (dt) as (
select date '2020-01-01' from dual
union all
select dt + interval '5' day from cte where dt < date '2020-01-15'
)
select * from cte order by dt
You can adjust the boundaries as needed, or move them to another cte:
with
params (startdt, enddt) as (
select date '2020-01-01', date '2020-01-15' from dual
),
cte (dt, enddt) as (
select startdt dt, enddt from params
union all
select dt + interval '5' day, enddt from cte where dt < enddt
)
select dt from cte order by dt
Note that the intervals between the dates are not constant in your sample data (there are 4 days between the first two dates, then 5 days.
You might want to adjust the where clause of the recursive member depending on your actual requirement. Here, the last day may be greater the January 15th. If you don't want that, then change the where clause to:
where dt + interval '5' day < enddt
Hello i need to get transaction count and total amount for transactions in last quarter.
i use following to get data for a quarter (last 90 days)
WITH date_range as
(SELECT TRUNC(sysdate) - 90 + level AS week_day
FROM dual
CONNECT BY ROWNUM <= 90),
the_data
AS (SELECT TRUNC(systemdate) AS log_date, count(*) AS num_obj,status AS log_status, nvl(sum(
CASE
WHEN VERSION = '1.1'
THEN nvl(amount/100,'0.0')
ELSE nvl(amount,'0.0')
END), 0) AS totalamount
from transactionlog where ((merchantcode in (
SELECT regexp_substr('MERC0003','[^,]+', 1, LEVEL) FROM dual
connect by regexp_substr('MERC0003', '[^,]+', 1, level) is not null ) OR 'MERC0003' IS NULL) AND status = 'xxxx')
GROUP BY TRUNC(systemdate),status)
SELECT TO_CHAR(dr.week_day,'DD/MM/YYYY HH:MI AM') AS TXNDATE, NVL(trans_log.num_obj,0) as TXNCOUNT,trans_log.log_status,trans_log.totalamount
FROM date_range dr LEFT OUTER JOIN the_data trans_log
on trans_log.log_date = dr.week_day
ORDER BY dr.week_day DESC ;
From above, I get 90 days record , as in 90 rows containing transaction count and amount for 90 days,
i need to get data in terms of weeks in quarter. That is, 12 rows containing data for each week which has transaction count and amount in last 12 weeks.
You can use TRUNC( date_value ,'IW' ) to truncate a date to the start of the ISO week (Monday 00:00) and add multiples of a week in your date range and perform the same truncation in your query:
WITH date_range( week_day ) as (
SELECT TRUNC( sysdate - 90, 'IW' ) + ( level - 1 ) * INTERVAL '7' DAY
FROM dual
CONNECT BY TRUNC( sysdate - 90, 'IW' ) + ( level - 1 ) * INTERVAL '7' DAY <= SYSDATE
),
the_data ( log_date, num_obj, log_status, totalamount ) AS (
SELECT TRUNC(systemdate, 'IW'),
-- rest of your query
GROUP BY TRUNC(systemdate, 'IW'),
status
)
SELECT *
FROM date_range dr
LEFT OUTER JOIN the_data trans_log
on trans_log.log_date = dr.week_day
ORDER BY dr.week_day DESC;
I have a few fields in a database that look like this:
trip_id
start_date
end_date
start_station_name
end_station_name
I need to write a query that shows all the stations with no activity on a particular day in the year 2015. I wrote the following query but it's not giving the right output:
select
start_station_name,
extract(date from start_date) as dt,
count(*)
from
trips_table
where
(
start_date >= timestamp('2015-01-01')
and
start_date < timestamp('2016-01-01')
)
group by
start_station_name,
dt
order by
count(*)
Can someone help come up with the right query? Thanks in advance!
Below is for BigQuery Standard SQL
It assumes start_date and end_date are of DATE type
It also assumes that all days in between start_date and end_date are "dedicated" to station in start_station_name field, which most likely not what is expected but question is missing details here thus such an assumption
#standardSQL
WITH days AS (
SELECT day
FROM UNNEST(GENERATE_DATE_ARRAY('2015-01-01', '2015-12-31')) AS day
),
stations AS (
SELECT DISTINCT start_station_name AS station
FROM `trips_table`
)
SELECT s.*
FROM (SELECT * FROM stations CROSS JOIN days) AS s
LEFT JOIN (SELECT * FROM `trips_table`,
UNNEST(GENERATE_DATE_ARRAY(start_date, end_date)) AS day) AS a
ON s.day = a.day AND s.station = a.start_station_name
WHERE a.day IS NULL
You can test/play it with below simple/dummy data
#standardSQL
WITH `trips_table` AS (
SELECT 1 AS trip_id, DATE '2015-01-01' AS start_date, DATE '2015-12-01' AS end_date, '111' AS start_station_name UNION ALL
SELECT 2, DATE '2015-12-10', DATE '2015-12-31', '111'
),
days AS (
SELECT day
FROM UNNEST(GENERATE_DATE_ARRAY('2015-01-01', '2015-12-31')) AS day
),
stations AS (
SELECT DISTINCT start_station_name AS station
FROM `trips_table`
)
SELECT s.*
FROM (SELECT * FROM stations CROSS JOIN days) AS s
LEFT JOIN (SELECT * FROM `trips_table`,
UNNEST(GENERATE_DATE_ARRAY(start_date, end_date)) AS day) AS a
ON s.day = a.day AND s.station = a.start_station_name
WHERE a.day IS NULL
ORDER BY station, day
the output is like below
station day
111 2015-12-02
111 2015-12-03
111 2015-12-04
111 2015-12-05
111 2015-12-06
111 2015-12-07
111 2015-12-08
111 2015-12-09
Use recursion for this purpose: try this SQL SERVER
WITH sample AS (
SELECT CAST('2015-01-01' AS DATETIME) AS dt
UNION ALL
SELECT DATEADD(dd, 1, dt)
FROM sample s
WHERE DATEADD(dd, 1, dt) < CAST('2016-01-01' AS DATETIME)
)
SELECT * FROM sample
Where CAST(sample.dt as date) NOT IN (
SELECT CAST(start_date as date)
FROM tablename
WHERE start_date >= '2015-01-01 00:00:00'
AND start_date < '2016-01-01 00:00:00'
)
Option(maxrecursion 0)
If you want the station data with it then you can use left join as :
WITH sample AS (
SELECT CAST('2015-01-01' AS DATETIME) AS dt
UNION ALL
SELECT DATEADD(dd, 1, dt)
FROM sample s
WHERE DATEADD(dd, 1, dt) < CAST('2016-01-01' AS DATETIME)
)
SELECT * FROM sample
left join tablename
on CAST(sample.dt as date) = CAST(tablename.start_date as date)
where sample.dt>= '2015-01-01 00:00:00' and sample.dt< '2016-01-01 00:00:00' )
Option(maxrecursion 0)
For mysql, see this fiddle. I think this would help you....
SQL Fiddle Demo
I have a SQL query which displays count, date, and time.
This is what the output looks like:
And this is my SQL query:
select
count(*),
to_char(timestamp, 'MM/DD/YYYY'),
to_char(timestamp, 'HH24')
from
MY_TABLE
where
timestamp >= to_timestamp('03/01/2016','MM/DD/YYYY')
group by
to_char(timestamp, 'MM/DD/YYYY'), to_char(timestamp, 'HH24')
Now, in COUNT column, I want to display 0 if the count doesn't exist for that hour. So on 3/2/2016 at 8am, the count was 6. Then at 9am the count was 0 so that row didn't get displayed. I want to display that row. And at 10am & 11am, the counts are displayed then it just goes to next day.
So how do I display count of 0? I want to display 0 count for each day every hour doesn't matter if it's 0 or 6 or whatever. Thanks :)
Use a partition outer join:
SELECT m.day,
h.hr,
COALESCE( freq, 0 ) AS freq
FROM ( SELECT LEVEL - 1 AS hr
FROM DUAL
CONNECT BY LEVEL <= 24
) h
LEFT OUTER JOIN
( SELECT COUNT(*) AS freq,
TO_CHAR( "timestamp", 'mm/dd/yyyy' ) AS day,
EXTRACT( HOUR FROM "timestamp" ) AS hr
FROM MY_TABLE
WHERE "timestamp" >= TIMESTAMP '2016-03-01 00:00:00'
GROUP BY
TO_CHAR( "timestamp", 'mm/dd/yyyy' ),
EXTRACT( HOUR FROM "timestamp" )
) m
PARTITION BY ( m.day, m.hr )
ON ( m.hr = h.hr );
Use a cte to generate numbers for all the hours in a day. Then cross join the result with all the possible dates from the table. Then left join on the cte which has all date and hour combinations, to get a 0 count when a row is absent for a particular hour.
with nums(n) as (select 1 from dual
union all
select n+1 from nums where n < 24)
,dateshrscomb as (select n,dt
from nums
cross join (select distinct trunc(timestamp) dt from my_table
where timestamp >= to_timestamp('03/01/2016','MM/DD/YYYY')
) alldates
)
select count(trunc(m.timestamp)), d.dt, d.n
from dateshrscomb d
left join MY_TABLE m on to_char(m.timestamp, 'HH24') = d.n
and trunc(m.timestamp) = d.dt
and m.timestamp >= to_timestamp('03/01/2016','MM/DD/YYYY')
group by d.dt, d.n
with cteHours(h) as (select 0 from dual
union all
select h+1 from cteHours where h < 24)
, cteDates(d) AS (
SELECT
trunc(MIN(timestamp)) as d
FROM
My_Table
WHERE
timestamp >= to_timestamp('03/01/2016','MM/DD/YYYY')
UNION ALL
SELECT
d + 1 as d
FROM
cteDates
WHERE
d + 1 <= (SELECT trunc(MAX(timestamp)) FROM MY_TABLE)
)
, datesNumsCross (d,h) AS (
SELECT
d, h
FROM
cteDates
CROSS JOIN cteHours
)
select count(*), to_char(d.d, 'MM/DD/YYYY'), d.h
from datesNumsCross d
LEFT JOIN MY_TABLE m
ON d.d = trunc(m.timestamp)
AND d.h = to_char(m.timestamp, 'HH24')
group by d.d, d.h
#VPK is doing a good job at answering, I just happened to be writing this at the same time as his last edit to generate a date hour cross join. This solution differs from his in that it will get all dates between your desired max and min. Where as his will get only the dates within the table so if you have a day missing completely it would not be represented in his but would in this one. Plus I did a little clean up on the joins.
Here is one way to do that.
Using Oracle's hierarchical query feature and level psuedo column, generate the dates and hours.
Then do an outer join of above with your data.
Need to adjust the value of level depending upon your desired range (This example uses 120). Start date needs to be set as well. It is ( trunc(sysdate, 'hh24')-2/24 ) in this example.
select nvl(c1.cnt, 0), d1.date_part, d1.hour_part
from
(
select
to_char(s.dt - (c.lev)/24, 'mm/dd/yyyy') date_part,
to_char(s.dt - (c.lev)/24, 'hh24') hour_part
from
(select level lev from dual connect by level <= 120) c,
(select trunc(sysdate, 'hh24')-2/24 dt from dual) s
where (s.dt - (c.lev)/24) < trunc(sysdate, 'hh24')-2/24
) d1
full outer join
(
select
count(*) cnt,
to_char(timestamp, 'MM/DD/YYYY') date_part,
to_char(timestamp, 'HH24') hour_part
from
MY_TABLE
where
timestamp >= to_timestamp('03/01/2016','MM/DD/YYYY')
group by
to_char(timestamp, 'MM/DD/YYYY'), to_char(timestamp, 'HH24')
) c1
on d1.date_part = c1.date_part
and d1.hour_part = c1.hour_part