开发者

oracle case or decode? - how to handle no recs

开发者 https://www.devze.com 2023-03-21 19:43 出处:网络
How can I make sure in this case statement that if I get now rows selected that I set that my result=1?

How can I make sure in this case statement that if I get now rows selected that I set that my result=1? I want a value of 1 if I have no records or a count of 0. I would also want a null date to be sysdate by default.

Here is what I have so far. It works with (sysdate-1), but when I tested it with (sysdate-0) or =sysdate (today's date), I was getting no records. So I want to be able to handle null values too.

How can I change this query to do that? or would I use something like DECODE?

select 
     to_date(year||'/'||month||'/'||day, 'YYYY/MM/DD') as mydt,
     case when count(*) = 0 then 1 else 0 end result
from Table1 
where 
   to_date(year||'/'||month||'/'||day, 'YYYY/MM/DD') >= trunc(sysdate)-1
GROUP BY 
    to_date(year||'/'||month||'/'||day, 'YYYY/MM/DD')

Here is my table desc. Perhaps, I should go off of timestamp instead? There is also a value of counts which may help change this query or not. So what i'm looking for is coming up with a value of 1 or 0 with a timestamp if that's possible.

SQL> desc Table1
 Name                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 YEAR                           NUMBER
 QUARTER                        NUMBER
 MONTH                          NUMBER
 DAY                            NUMBER
 HOUR                           NUMBER
 TIMESTAMP                      NUMBER
 CNT_N                              NUMBER
 ACTN_N                             NUMBER 
 ADDR_C            开发者_JS百科           VARCHAR2(255)


You will never get a count(*) of zero with a group by. If there are no rows, you won't get any groups and if there are rows then any group you get will show the number of rows.

If you have 3 rows for January 1st and another 3 for January 3rd, you will get :

2011/01/01 3
2011/01/03 3

You won't get a row for January 2nd. Are you wanting to generate that row ? Is there a maximum number of rows that you want returned ?


I think you can do modify your query like below to assign the sysdate to null values

select
       nvl(to_date(year||'/'||month||'/'||day, 'YYYY/MM/DD') ,sysdate) as mydt
from Table1  
where
       nvl(to_date(year||'/'||month||'/'||day, 'YYYY/MM/DD') ,sysdate) 
                      >= trunc(sysdate)-1 
GROUP BY      nvl(to_date(year||'/'||month||'/'||day, 'YYYY/MM/DD') ,sysdate)

but your second requirement is not very much understood.... please clarify what exectly you want with count(*)

if you want to assign 1 for null date count then you can add following line in select statment

case 
when nvl(to_date(year||'/'||month||'/'||day, 'YYYY/MM/DD') ,sysdate) = sysdate then
     1
else
count(*)-- or 0 ???!!!!!


It looks like you need to generate the date(s) and then see how may records match each one. Assuming you don't have any data in the future you can do something like:

select trunc(sysdate) + level - (:range + 1) as dt
from dual
connect by level <= (:range + 1);

which with :range set to 1 gives two dates:

DT
---------
19-JUL-11
20-JUL-11

You can then have outer join to that list of dates:

with tmp_dt as (
    select trunc(sysdate) + level - (:range + 1) as dt
    from dual
    connect by level <= (:range + 1)
)
select td.dt as mydt,
    case when count(t.year) = 0 then 1 else 0 end as result
from tmp_dt td
left join table1 t on t.year = extract(year from td.dt)
    and t.month = extract(month from td.dt)
    and t.day = extract(day from td.dt)
group by td.dt
order by td.dt;

If I only have any data in the table for 19-Jul-11, I get:

MYDT          RESULT
--------- ----------
19-JUL-11          0
20-JUL-11          1

If you do have data in the future this won't show it; range is how many days to look in the past. If you know there's a limit of, say, seven days you can use connect by level <= (:range + 1) + 7 or have a second variable, but it rather depends on what you want to see.

I've swapped the join around a bit to avoid doing a date conversion for every row in the table, extracting the relevant part of the generated date instead. I hope you have a reason for storing the date components in separate fields rather than as a date.


If you're only looking for data from today, just change the date generator:

with tmp_dt as (select trunc(sysdate) as dt from dual)
select td.dt as mydt,
     case when count(t.year) = 0 then 1 else 0 end result
from tmp_dt td
left join table1 t on t.year = extract(year from td.dt)
    and t.month = extract(month from td.dt)
    and t.day = extract(day from td.dt)
group by td.dt;

If you always want yesterday, it would be select trunc(sysdate) - 1 as dt from dual, etc.


May be you can encapsulate your whole query within the NVL function like this


SELECT NVL(TO_CHAR((select to_date(year || '/' || month || '/' || day,
                                  'YYYY/MM/DD') as mydt
                     from Table1
                    where to_date(year || '/' || month || '/' || day,
                                  'YYYY/MM/DD') >= trunc(sysdate) - 1
                    GROUP BY to_date(year || '/' || month || '/' || day,
                                     'YYYY/MM/DD')),
                   'DD/MM/YYYY HH:MI:SS AM'),
           '1')
  FROM DUAL

I have removed the case statement from the query Instead , what this query will do is, in case your query does return any value, it will do so without interference. However, in case the query returns NOTHING, then the NVL function will take over and return 1 I have used the TO_CHAR function to maintain the datatype of both the arguments in the NVL function. Both the date returned and the value for NVL are character based

Hope it helps

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号