query to obtain maximum 3 results - oracle

I have a table with some fileds like these:
**Date Name Table Direction Code**
13/01/1978 Jacks xxxx stret ... 1
13/01/1978 John xxxx xxxxx 0
...........
12/01/1978 Dave xxxx xxxxxx 0
12/01/1978 Suse xxxxx xxxxxx 0
...........
11/01/1978 Mickey xxxx xxxxx 1
11/01/1978 Suse xxxxxx xxxxxxx 2
11/01/1978 Nune xxxxxx xxxxxxx 2
.....
09/01/1978 ..... ...... ....... 1
08/01/1978 ..... ...... ....... 0
21/01/1978 ..... ...... ....... 1
.....
I would like to extract the three first ocurrences grouping by date descendent and detect if the field code have an value '1' in any row with the same date. The result that I would like is:
The first date is 21/01/1978, and have code 1
The second date is 13/01/1978 and have code 1
The third date is 12/01/1978 and don't have code 1
I'm trying with rownum but not works. Could you help me please? Thanks and sorry for my English!

I don't know why rownum didn't work for u, maybe becuase you tried to put in in the inner query before the order by(you first have to order and only then use rownum becuase every row will get another rownum)
SELECT Date,code from (
(SELECT Date,max(Code) as code from YourTable
GROUP BY Date
ORDER BY Date DESC
)
WHERE rownum <= 3

Try this Hope it helps
SELECT DISTINCT B.DT,
DECODE(B.VL,1,' has code 1',999,' dont have code 1') inf
FROM
(SELECT A.DT,
CASE
WHEN A.CODE = 1
THEN 1
ELSE 999
END VL,
ROW_NUMBER() OVER(PARTITION BY A.DT ORDER BY A.CODE DESC) RN
FROM
(SELECT '01/03/2016' AS DT,1 AS CODE FROM DUAL
UNION ALL
SELECT '01/03/2016' AS DT,0 AS CODE FROM DUAL
UNION ALL
SELECT '05/03/2016' AS DT,0 AS CODE FROM DUAL
UNION ALL
SELECT '05/03/2016' AS DT,0 AS CODE FROM DUAL
UNION ALL
SELECT '06/03/2016' AS DT,1 AS CODE FROM DUAL
)A
) B
WHERE B.RN = 1;

you can do it in quite a few ways, one of them would be like:
select date,
decode(date_cnt, 0, 'dont have code = 1', 'have code = 1)
from (
select t.date,
max(t.code) as date_cnt
from table_name t
group by date
order by date desc)
where rownum <=3
this way you first sort them by date in the inner query aswell as prepare the info about having a code = 1. The outer query will fetch first 3 rows and change the codes to desired info (if 0 => no code, else there is code)

Related

count returns <<air>> when there's nothing to group by

Here's my SQL:
select
count(*)
from
sysdba.dw_cmap_arf_tmp
left join SYSDBA.TABLE1 rrc
on rrc.part_work_order = pwo
left join sysdba.TABLE2 R
on rrc.run_number = R.RUN_NUMBER
where
upper(run_type) like '%FEE%'
group by
pwo;
When there's nothing to group by, count returns --air--. It's null nor is it blank. I have modified the above SQL as the following to prove.
select
'>>' || count(*) || '<<' as blah
from
sysdba.dw_cmap_arf_tmp
left join SYSDBA.TABLE1 rrc
on rrc.part_work_order = pwo
left join sysdba.TABLE2 R
on rrc.run_number = R.RUN_NUMBER
where
upper(run_type) like '%ANNEAL%'
group by
pwo;
When I, however, write a statement above to perform an update, I got null. So tried coalesce, but got the same thing.
Does anyone know what I can do to replace --air-- with null or 0? Thanks!
P.S. I have did something research, but couldn't find anything...apologize in advance, if there's already a similar question out there.
Thanks!
This is what you currently have (based on Scott's EMP table): as there's no department 50, you get no rows selected (which is the air you're talking about, I presume).
SQL> with your_current_query as
2 (select count(*) cnt
3 from emp
4 where deptno = &deptno
5 group by job
6 )
7 select cnt
8 from your_current_query;
Enter value for deptno: 50
no rows selected
Just to show that it actually returns something if there are some data there:
SQL> /
Enter value for deptno: 30
CNT
----------
4
1
1
SQL>
OK; now, to do something with the situation where there are no rows selected, use union with a "dummy" row selected from the DUAL table:
SQL> with your_current_query as
2 (select count(*) cnt
3 from emp
4 where deptno = &deptno
5 group by job
6 )
7 select cnt
8 from your_current_query
9 -- add this: if YOUR_CURRENT_QUERY doesn't return anything, union it with
10 -- a select from dual
11 union all
12 select 0
13 from dual
14 where 0 = (select count(*) from your_current_query);
Enter value for deptno: 50
CNT
----------
0
SQL>
So: even though there are no employees in department 50, you got 0 as the result.
Again, to show what happens when there are some rows:
SQL> /
Enter value for deptno: 30
CNT
----------
4
1
1
SQL>
Finally, your query - rewritten - would look like this:
with your_current_query as
(select
count(*) cnt
from
sysdba.dw_cmap_arf_tmp
left join SYSDBA.TABLE1 rrc
on rrc.part_work_order = pwo
left join sysdba.TABLE2 R
on rrc.run_number = R.RUN_NUMBER
where
upper(run_type) like '%FEE%'
group by
pwo
)
select cnt from your_current_query
union all
select 0
from dual
where 0 = (select count(*) from your_Current_query);
See if it helps.

PL/SQL: Need help creating calculated columns based on conditions. to be done in select query

I am trying to count all the distinct ids based on conditions. But I am unable to figure out where I am going wrong with the syntax. The logic is
COUNTD(IF ([column_name1] = 1) THEN [DATAPAGEID] END)
This is the formula I used in Tableau. However when writing it in a PL/SQL query as
Select FT.NAME, COUNT(DISTINCT FT.pageID IF FT."column_name" = 1 )
as total_expected
FROM
( Sub Query) FT
Group by FT.Name
Order by FT.Name
Needless to say its throwing errors. Now I can write separate queries which can give me each number using a where condition. For example, if I wanted a count of distinct pageid where column_name1 = 1, I would write something like this
Select FT.SITENAME, COUNT(DISTINCT DATAPAGEID) as Datapage
from
(sub query)
WHERE FT."column_name" = 1
but the problem with that is that I have other calculated columns in the query which will all need to be part of the same row. To illustrate here's what the table would look like
name Calculated_Column1 Calculated_Column2 Calculated_column3
abc 781 811 96.54%
pqr 600 800 75.00%
where calculated_column3 is the result of 781/811. Therefore I can't have a new query for each column. I thought using an if condition when calculating columns will solve this, but I can't get the syntax right somehow.
Therefore, I need to know how can I create conditional calculated columns within the select query. If I have not explained this well, please let me know and I will try to clarify further.
You can use a CASE block inside the count (DISTINCT ) as shown.
SELECT FT.NAME,
COUNT(DISTINCT
CASE
WHEN DATAPAGEID = 1
THEN 1
ELSE 0
END ) Calculated_Column1,
COUNT(DISTINCT
CASE
WHEN DATAPAGEID = 2
THEN 1
ELSE 0
END ) Calculated_Column2,
( COUNT(DISTINCT
CASE
WHEN DATAPAGEID = 1
THEN 1
ELSE 0
END ) / COUNT(DISTINCT
CASE
WHEN DATAPAGEID = 2
THEN 1
ELSE 0
END ) ) * 100||'%' Calculated_Column3
FROM
( SELECT 'abc' name, 1 DATAPAGEID FROM dual
UNION ALL
SELECT 'abc' name, 1 DATAPAGEID FROM dual
UNION ALL
SELECT 'pqr' name, 2 DATAPAGEID FROM dual
UNION ALL
SELECT 'pqr' name, 2 DATAPAGEID FROM dual
UNION ALL
SELECT 'pqr' name, 3 DATAPAGEID FROM dual
) FT
GROUP BY FT.Name
ORDER BY FT.Name;
Output is
abc 1 1 100%
pqr 1 2 50%

How to breakdown data by month and showing zero for months with no data?

Using information in Table A, how can I produce results in Table B below?
Table A:
CASE_ID DATE_EFF COPAY STATUS
1 11/04/2016 10 A
1 11/20/2016 5 A
1 11/23/2016 5 R
1 12/01/2016 1 A
1 12/10/2016 2 A
1 12/12/2016 10 A
1 12/31/2016 50 R
For the above CASE_ID, we have dates in Nov 2016 and Dec 2016 only, however, I want to produce a breakdown of this CASE_ID for a period of 6 months as below where for each month the copays are summed where applicable as per the DATE_EFF and for the months that are not within the above dates, a zero is entered. Also, only records with copays with a status of 'A' are summed for any month -- so those with status of 'R' are ignored in the summation. For example, based on data in Table A above, the intended results are as follow:
Table B:
CASE_ID MONTH TOTAL_COPAY
1 01/2017 0
1 12/2016 13
1 11/2016 15
1 10/2016 0
1 09/2016 0
1 08/2016 0
I have below as a possible solution[using a with clause], but can this be achieved without the use of the 'with' clause?
Possible Solution:
WITH
XRF AS
( SELECT CASE_ID, COPAY, DATE_EFF
FROM Table_A WHERE STATUS = 'A'
)
SELECT F.CASE_ID, ST, NVL(SUM(F.COPAY),0) TOTAL_COPAY FROM XRF F PARTITION BY (F.CASE_ID)
RIGHT OUTER JOIN (SELECT '12/2016' ST FROM DUAL UNION ALL
SELECT '11/2016' FROM DUAL UNION ALL
SELECT '10/2016' FROM DUAL UNION ALL
SELECT '09/2016' FROM DUAL UNION ALL
SELECT '08/2016' FROM DUAL UNION ALL
SELECT '07/2016' FROM DUAL) STS
ON (TO_CHAR(LAST_DAY((F.DATE_EFF)),'MM/YYYY') = STS.ST)
GROUP BY F.CASE_ID, ST ORDER BY F.CASE_ID, ST DESC
;
UPDATE AND SOLUTION:
Using the above query, I believe I am have answered my own question by implementing it as below -- not sure though if using this method is expensive when you have millions of records of such CASE_IDs. Any thoughts?
SELECT F.CASE_ID, ST, NVL(SUM(F.COPAY),0) TOTAL_COPAY FROM (SELECT CASE_ID, COPAY, DATE_EFF FROM TABLE_A WHERE STATUS = 'A') F PARTITION BY (F.CASE_ID)
RIGHT OUTER JOIN (SELECT '12/2016' ST FROM DUAL UNION ALL
SELECT '11/2016' FROM DUAL UNION ALL
SELECT '10/2016' FROM DUAL UNION ALL
SELECT '09/2016' FROM DUAL UNION ALL
SELECT '08/2016' FROM DUAL UNION ALL
SELECT '07/2016' FROM DUAL) STS
ON (TO_CHAR(LAST_DAY((F.DATE_EFF)),'MM/YYYY') = STS.ST)
GROUP BY F.CASE_ID, ST ORDER BY F.CASE_ID, ST DESC
;

Pl/Sql Approach to write complex procedure Best Practices

I am relatively new to Pl Sql and have to write a procedure which does following, It has got 6-7 different queries which are as follows :
Select manager, count(*) bank from abc ..........
select manager , count(*) retail from abc .......
Now each count is for a different department , while manager may be same . So problem is that I am looking for a best approach which helps me to store
the data in separate table which has following structure:
Manager : Count Bank : Count Retail : Count xyz ...........
How do I make sure using Pl sql that each manager has right count for all the columns(bank/retai/xyz) , the first thing is how to store multiple different query result and process it . I think we can use cursor but I need to research on it , also this procedure should be high performance .
Please suggest.
Thanks
XslGuy
This is easily achievable with conditional sums. Hopefully the below will give you enough of an idea of what you need to do:
with sample_data as (select 1 id, 1 dept, 10 val from dual union all
select 2 id, 1 dept, 20 val from dual union all
select 3 id, 2 dept, 30 val from dual union all
select 4 id, 3 dept, 40 val from dual union all
select 5 id, 1 dept, 50 val from dual union all
select 6 id, 3 dept, 60 val from dual union all
select 7 id, 2 dept, 70 val from dual union all
select 8 id, 4 dept, 80 val from dual)
-- end of creating a subquery that contains some sample data. See sql below:
select sum(case when dept = 1 then val end) dept_1_total,
sum(case when dept = 2 then val end) dept_2_total,
sum(case when dept = 3 then val end) dept_3_total
from sample_data
where dept in (1, 2, 3);
DEPT_1_TOTAL DEPT_2_TOTAL DEPT_3_TOTAL
------------ ------------ ------------
80 100 100

Find only particular days between two dates

I have an Oracle table with data like below:
1. ID DATE
2. 12 02/11/2013
3. 12 02/12/2013
4. 13 02/11/2013
5. 13 02/12/2013
6. 13 02/13/2013
7. 13 02/14/2013
8. 14 02/11/2013
9. 14 02/12/2013
10. 14 02/13/2013
I need to find only those ID who has only Monday, Tuesday and Wednesday dates, so here only ID = 14 should be returned. I am using Oracle and dates are in format MM/DD/YYYY.
Please advice.
Regards,
Nitin
If date column is DATE datatype, then you can
select id
from your_table
group by id
having sum(case
when to_char(date_col,'fmday')
in ('monday','tuesday','wednesday') then 1
else 99
end) = 3;
EDIT: Corected the above code at the igr's observation
But this is ok only if you don't have a day twice for the same id.
If the column is varchar2 then the condition becomes to_char(to_date(your_col,'mm/dd/yyyy'),'fmday') in ...
A more robust code would be:
select id
from(
select id, date_col
from your_table
group by id, date_col
)
group by id
having sum(case
when to_char(date_col,'fmday', 'NLS_DATE_LANGUAGE=ENGLISH')
in ('monday','tuesday','wednesday') then 1
else 99
end) = 3;
select id
from (
select
id,
sum (case when to_char(dt, 'D', 'nls_territory=AMERICA') between 1 and 3 then 1 else -1 end) AS cnt
from t
group by id
)
where cnt=3
NOTE: I assumed (id,dt) is unique - no two lines with same id and date.
do something like
SELECT * FROM your_table t
where to_char(t.DATE, 'DY') in ('whatever_day_abbreviation_day_you_use');
alternatively if you prefer you could use day numbers like:
SELECT * FROM your_table t
where to_number(to_char(d.ts, 'D')) in (1,2,3);
if you'd like to avoid ID repetition add DISTINCTION
SELECT DISTINCT ID FROM your_table t
where to_number(to_char(d.ts, 'D')) in (1,2,3);

Resources