I'm SELECTing some aggregate data and grouping on the date and a particular field. I want to display all values in that field and a count for those values even if there was no data matching that field on that day. Eg
Date MyField Count
2009-09-25 A 2
2009-09-25 B 0
2009-09-24 A 1
2009-09-24 B 1
The Oracle SQL I currently have to do this is akin to the following:
SELECT today,
mytable.myfield,
COUNT(
CASE WHEN fields.myfield = mytable.myfield AND
date >= today AND
date < tomorrow
THEN 1
END
)
FROM (
SELECT TRUNC(SYSDATE) + 1 - LEVEL AS today,
TRUNC(SYSDATE) + 2 - LEVEL AS tomorrow
FROM DUAL
CONNECT BY LEVEL <= 30
),
(
/* This is the part that seems inefficient */
SELECT DISTINCT myfield
FROM mytable
WHERE myfield IN ('A', 'B')
) fields,
mytable
GROUP BY today, mytable.myfield
ORDER BY today DESC, mytable.myfield ASC
My concern is that I know exactly which values I want to display for myfield
, and it seems inefficient to have a SELECT
query that accesses mytable
. I was wondering if there's some way I could do something like this in that sub-query:
SELECT ('A', 'B') AS myfield
FROM DUAL
I'm using an older version of Oracle where WITH
clauses do not work.
You would have to get them as different rows, not different columns. So you'll end up with
select 'A' from dual
union
select 'B' from dual
In that case, the query should be equivalent as long as there are rows in mytable
with fields 'A' and 'B'. If ever there aren't, then your subquery will return rows that the original subquery would not.
Why don't you upgrade your Oracle Version? The with-clause is added first to Oracle 9.2 (2002). Are you still using Oracle 8?
You don't have a join between the FIELDS sub-query and MYTABLE, so your resultset will contain a row for every value of MYFIELD for the last thirty days.
However, rather than adding that join, why not ditch the sub-query and just filter on MYTABLE.MYFIELD? Also, if you are concerned about performance you should bound the date in a WHERE
clause, otherwise you will process every row in MYTABLE.
select today
, myfield
, count ( case when trunc(somedate) = today then 1 end ) as ab_count
from ( select trunc(sysdate) + 1 - level as today
from dual
connect by level <= 30 )
, mytable
where myfield in ('A', 'B')
and somedate >= trunc(sysdate) - 30
group by today, myfield
order by today desc, myfield asc
/
edit
I have run your original query and my revised one against some test data. You will just have to take my word for it that the two resulsets were in fact identical - or try it yourself :)
Your query returns:
TODAY M AB_COUNT
----------- - ----------
26-SEP-2009 A 0
26-SEP-2009 B 0
25-SEP-2009 A 2
25-SEP-2009 B 2
24-SEP-2009 A 2
24-SEP-2009 B 0
...
29-AUG-2009 A 1
29-AUG-2009 B 2
28-AUG-2009 A 1
28-AUG-2009 B 0
60 rows selected.
SQL>
My query returns:
TODAY M AB_COUNT
----------- - ----------
26-SEP-2009 A 0
26-SEP-2009 B 0
25-SEP-2009 A 2
25-SEP-2009 B 2
24-SEP-2009 A 2
24-SEP-2009 B 0
...
29-AUG-2009 A 1
29-AUG-2009 B 2
28-AUG-2009 A 1
28-AUG-2009 B 0
60 rows selected.
SQL>
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.