CASE in BETWEEN in WHERE - case

I need to put a CASE statement in my WHERE clause, and that CASE statement contains a BETWEEN. But, I'm unable to get this working:
SELECT ticker, trending_rank
FROM (SELECT a.ticker, t.trending_rank
FROM (SELECT ticker FROM template7_data WHERE TRUNC(date_inserted) = TRUNC(SYSDATE)
MINUS
SELECT ticker FROM template7_data WHERE TRUNC(SYSDATE) BETWEEN TRUNC(date_excluded) AND TRUNC(date_released)) a, trending_tickers t
WHERE a.ticker = t.ticker
AND CASE
WHEN category_id = 516 THEN trending_rank BETWEEN 1 AND 200
ELSE trending_rank BETWEEN 100 AND 300
END
ORDER BY trending_rank)
WHERE rownum <= 5;
Any help would be great.

Related

substr instr length for loop

good morning guys i have a problem with code i work on Health Care and complain code must be checkbox but they ask for Report that contain the treatment code which is gonna appear in database like this 1:15:2:3 etc so i need to calculate each code separate
i have to count until i get ":" then i need to take the number which can be 1 or 2 digit then making inner join with the other table
can anyone help me to fix this function and the problem in the loop and get the number for each one
create or replace function hcc_get_tcd_codes (p_id in number )
return varchar2 is
x number := 0 ;
y number := 0 ;
z number ;
code1 number ;
code_name varchar2(15);
begin
for i in 0 .. x
loop
select length(t.tcd_codes ) into x from hcc_patient_sheet t where t.id = p_id ; --- (9)العدد كامل
select instr(tcd_codes, ':') into y from hcc_patient_sheet t where t.id = p_id ; ---- عدد الكود الاو(3)ل
select instr(tcd_codes, ':')+1 + y into z from hcc_patient_sheet t where t.id = p_id ; --عدد الكود كامل +1
enter code here
i := x -y ;
select substr(t.tcd_codes,z, instr(tcd_codes, ':')-1) into code1
--,select substr(t.tcd_codes, 0, instr(tcd_codes, ':')-1) as code2
from Hcc_Patient_Sheet t
where t.id = 631 ;
select t.alt_name into code_name from hcc_complaint_codes t where t.code = code1 ;
select instr(tcd_codes, ':') into y from hcc_patient_sheet t where t.id = p_id ; ---- عدد الكود الاول
return code_name ;
end loop ;
end;
Often with frequent sounding string processing issues, a wheel has already been invented, and even packaged.
select * from table(apex_string.split('THIS:IS:GREAT',':'));
Partial SUBSTR doesn't seem to be the best option; I'd suggest you to split that colon-separated-values string into row as follows:
SQL> with test (col) as
2 (select '1:15:2:3' from dual)
3 select regexp_substr(col, '[^:]+', 1, level) one_value
4 from test
5 connect by level <= regexp_count(col, ':') + 1;
ONE_VALUE
--------------------------------
1
15
2
3
SQL>
and use such an option in your query; something like this:
select ...
into ...
from some_table t
where t.id in (select regexp_substr(that_string, '[^:]+', 1, level) one_value
from dual
connect by level <= regexp_count(that_string, ':') + 1
);
If it has to be row-by-row, use the above option as a source for the cursor FOR loop, as
for cur_r in (select regexp_substr(that_string, '[^:]+', 1, level) one_value
from dual
connect by level <= regexp_count(that_string, ':') + 1
)
loop
do_something_here
end loop;

Total Number and Total Value pl/sql

I made a query within a cursor that calculates the total number of cancellations, cancels or returned items and the total value. However, I cannot get the right value for the totals of the items that been cancelled, return or all cancelled
create or replace PROCEDURE NUM_OF_RET_CAN(PRAM_DATE IN DATE)
AS
CURSOR CUR2 IS
SELECT I.CONDITION, I.DEL_DATE, SUM(DE.QUANTITY) NUMBER_OF_PRO,
SUM(NVL(DE.QUANTITY,0) * NVL (P.COSTS,0)) TOTAL
FROM ITEMS I, DE_DETAILS DE, PARTS P
WHERE DE.PRO_NO = P.PRO_NO
AND I.ITEMS_NO = DE.ITEM_NO
AND TO_CHAR(I.DEL_DATE, 'mm-yyyy') = TO_CHAR(PRAM_DATE, 'mm-yyyy')
GROUP BY I.CONDITION, I.DEL_DATE;
CAL_CUR CUR2%ROWTYPE;
BEGIN
OPEN CUR2;
FETCH CUR2 INTO CAL_CUR;
IF VARCUR1.CONDITION ='CANCELL' THEN
DBMS_OUTPUT.PUT_LINE('CANCELLED: '||CAL_CUR.NUMBER_OF_PRO );
DBMS_OUTPUT.PUT_LINE('Total: '|| CAL_CUR.TOTAL);
ELSIF VARCUR1.CONDITION ='ORDER RETURNED' THEN
DBMS_OUTPUT.PUT_LINE('RETURNED : '||
CAL_CUR.NUMBER_OF_PRO);
DBMS_OUTPUT.PUT_LINE('Total : '|| CAL_CUR.TOTAL);
ELSIF VARCUR1.CONDITION = 'ALL ORDERS ARE CANCELLED!' THEN
DBMS_OUTPUT.PUT_LINE('ALL CANCELLATIONS : '||
CAL_CUR.NUMBER_OF_PRO );
DBMS_OUTPUT.PUT_LINE('Total : '|| CAL_CUR.TOTAL);
ELSE
DBMS_OUTPUT.PUT_LINE('No records for this month');
END IF;
CLOSE CUR2;
END NUM_OF_RET_CAN;
If I run the select Query without using the cursor or the procedure I get this result:
CONDITION DEL_DATE NUMBER_OF_PRO TOTAL
------------ ------------- ------------------- ---------
ALL ORDERS ARE CANCELLED! 12-JAN-16 4 99.96
ALL ORDERS ARE CANCELLED! 10-JAN-16 2 44.98
Expected Answer
CONDITION DEL_DATE NUMBER_OF_PRO TOTAL
------------ ------------- ------------------- ---------
ALL ORDERS ARE CANCELLED! JAN-16 6 144.94
Any help would be much appreciated
Sounds like you just need to group by month rather than day, eg:
select i.condition,
trunc(i.del_date, 'mm') del_date,
sum(de.quantity) number_of_pro,
sum(nvl(de.quantity,0) * nvl (p.costs,0)) total
from items i
inner join de_details de on (i.items_no = de.item_no)
inner join parts p on (de.pro_no = p.pro_no)
where trunc(i.del_date, 'mm') = trunc(pram_date, 'mm')
group by i.condition,
trunc(i.del_date, 'mm');
A few notes:
You'll note that I have converted your old-style joins into ANSI join syntax.
You had aliased the items table as "pros" but elsewhere in the query, the alias "i" was referenced. I believe this is what the alias for the items table should have been, so I updated it accordingly.
I've converted your to_char(... 'mm-yyyy')s to trunc(... 'mm') as I believe that comparing columns in the same format as they're defined is more meaningful (leading to easier maintenance etc).
I'm not sure why you're getting the "not a group by expression" error, since it's working for me:
with items as (select 1 items_no, 'a' condition, sysdate -1 del_date from dual union all
select 2 items_no, 'a' condition, sysdate del_date from dual union all
select 3 items_no, 'a' condition, sysdate + 31 del_date from dual),
de_details as (select 1 item_no, 10 quantity, 1 pro_no from dual union all
select 2 item_no, 20 quantity, 2 pro_no from dual union all
select 3 item_no, 40 quantity, 1 pro_no from dual),
parts as (select 1 pro_no, 100 costs from dual union all
select 2 pro_no, 200 costs from dual)
----- end of mimicking your tables with data in them
select i.condition,
trunc(i.del_date, 'mm') del_date,
sum(de.quantity) number_of_pro,
sum(nvl(de.quantity,0) * nvl (p.costs,0)) total
from items i
inner join de_details de on (i.items_no = de.item_no)
inner join parts p on (de.pro_no = p.pro_no)
--where trunc(i.del_date, 'mm') = trunc(pram_date, 'mm')
group by i.condition,
trunc(i.del_date, 'mm');
CONDITION DEL_DATE NUMBER_OF_PRO TOTAL
--------- --------- ------------- ----------
a 01-JAN-16 30 5000
a 01-FEB-16 40 4000
Are you sure that you added the trunc(..., 'mm') to the colunn in both the select and group by colunn lists?
Here's how I'd write the procedure, assuming that I needed to loop through each of the rows returned by the query and display them via dbms_output:
create or replace procedure num_of_ret_can (pram_date in date)
as
cursor cur2 is
select i.condition,
trunc(i.del_date, 'mm') del_date,
sum(de.quantity) number_of_pro,
sum(nvl(de.quantity,0) * nvl (p.costs,0)) total
from items i
inner join de_details de on (i.items_no = de.item_no)
inner join parts p on (de.pro_no = p.pro_no)
where trunc(i.del_date, 'mm') = trunc(pram_date, 'mm')
group by i.condition,
trunc(i.del_date, 'mm');
v_counter number := 0;
begin
for cal_cur in cur2
loop
v_counter := v_counter + 1;
if cal_cur.condition ='CANCELL' then -- are you sure? Not CANCEL?
dbms_output.put_line('CANCELLED: '||cal_cur.number_of_pro );
dbms_output.put_line('Total: '|| cal_cur.total);
elsif cal_cur.condition ='ORDER RETURNED' then
dbms_output.put_line('RETURNED : '||cal_cur.number_of_pro);
dbms_output.put_line('Total : '||cal_cur.total);
elsif cal_cur.condition = 'ALL ORDERS ARE CANCELLED!' then
dbms_output.put_line('ALL CANCELLATIONS : '||cal_cur.number_of_pro );
dbms_output.put_line('Total : '||cal_cur.total);
end if;
end loop;
if v_counter = 0 then
dbms_output.put_line('No records for this month');
end if;
end num_of_ret_can;
/

How to get maximum column values across a row in Teradata sql?

I have a table named cnst_chrctrstc_abc with 10 columns (equ_gender1 - bb_population_flag) each row which contain numeric values (count) .
I want to get maximum 5 values out of each row across those 10 numeric columns.
The query I have looks something like the following ..
SEL
FROM
(
SEL
SUM(CASE WHEN COALESCE(act.equ_gender1,'') = COALESCE(inact.equ_gender1,'') THEN 0 ELSE 1 END ) AS equ_gender1_chg_cnt,
SUM(CASE WHEN COALESCE(act.exp_ex_bmyr1,'') = COALESCE(inact.exp_ex_bmyr1,'') THEN 0 ELSE 1 END ) AS exp_ex_bmyr1_chg_cnt,
SUM(CASE WHEN COALESCE(act.equ_age1,'') = COALESCE(inact.equ_age1,'') THEN 0 ELSE 1 END ) AS equ_age1_chg_cnt,
SUM(CASE WHEN COALESCE(act.maritalstatus1,'') = COALESCE(inact.maritalstatus1,'') THEN 0 ELSE 1 END ) AS maritalstatus1_chg_cnt,
SUM(CASE WHEN COALESCE(act.person_type1,'') = COALESCE(inact.person_type1,'') THEN 0 ELSE 1 END ) AS person_type1_chg_cnt,
SUM(CASE WHEN COALESCE(act.homeowner,'') = COALESCE(inact.homeowner,'') THEN 0 ELSE 1 END ) AS homeowner_chg_cnt,
SUM(CASE WHEN COALESCE(act.dwelling_size,'') = COALESCE(inact.dwelling_size,'') THEN 0 ELSE 1 END ) AS dwelling_size_chg_cnt,
SUM(CASE WHEN COALESCE(act.lengthofresidence,'') = COALESCE(inact.lengthofresidence,'') THEN 0 ELSE 1 END ) AS lengthofresidence_chg_cnt,
SUM(CASE WHEN COALESCE(act.childrenage0_18,'') = COALESCE(inact.childrenage0_18,'') THEN 0 ELSE 1 END ) AS childrenage0_18_chg_cnt,
SUM(CASE WHEN COALESCE(act.bb_population_flag,'') = COALESCE(inact.bb_population_flag,'') THEN 0 ELSE 1 END ) AS bb_population_flag
FROM
(SEL * FROM arc_mdm_Tbls.cnst_chrctrstc_abc WHERE load_id=1024 AND cnst_chrctrstc_end_dt='9999-12-31' (DATE))act
LEFT JOIN
(SEL * FROM arc_mdm_Tbls.cnst_chrctrstc_abc WHERE load_id=1024 AND cnst_chrctrstc_end_dt<'9999-12-31' (DATE)
QUALIFY ROW_NUMBER() OVER (PARTITION BY cnst_mstr_id ORDER BY cnst_chrctrstc_strt_ts DESC)=1
)inact
ON act.cnst_mstr_id = inact.cnst_mstr_id
)X
I know SEL GREATEST would produce the maximum value out of each row . But I want 5 top values and assign a rank to them.
Something like for some row first five columns may hold the top 5 values and for some last five i.e. homeowner to bb_population_flag may hold the top 5 values.
so if the columns and values from cnst_chrctrstc_abc look something like the following
cdi_batch_id | a | b | c | d | e | f | g | h | i |j
1024 |116|105|102|100|117|119|108|104|101|121
so the select query should return me columns j,f,e,a,g having the top 5 values.
And then I would assign a rank to them accordingly .
Should it be done using unpivot or something ?
Thanks in advance.
Yes, you need to unpivot your result.
Before TD14.10 you will need a list of those column names, either as a table
create table ColumnList (col varchar(128));
Insert into ColumnList('equ_gender1' );
Insert into ColumnList('exp_ex_bmyr1' );
Insert into ColumnList('equ_age1' );
Insert into ColumnList('maritalstatus1' );
Insert into ColumnList('person_type1' );
Insert into ColumnList('homeowner' );
Insert into ColumnList('dwelling_size' );
Insert into ColumnList('lengthofresidence' );
Insert into ColumnList('childrenage0_18' );
Insert into ColumnList('bb_population_flag');
or on-thy-fly using a bulky
with ColumnList as
(
select * from (select 'equ_gender1' as Col) as dt union all
select * from (select 'exp_ex_bmyr1' as Col) as dt union all
select * from (select 'equ_age1' as Col) as dt union all
select * from (select 'maritalstatus1' as Col) as dt union all
select * from (select 'person_type1' as Col) as dt union all
select * from (select 'homeowner' as Col) as dt union all
select * from (select 'dwelling_size' as Col) as dt union all
select * from (select 'lengthofresidence' as Col) as dt union all
select * from (select 'childrenage0_18' as Col) as dt union all
select * from (select 'bb_population_flag' as Col) as dt
)
Then you CROSS JOIN to unpivot:
select
col,
case col
when 'equ_gender1' then equ_gender1
when 'exp_ex_bmyr1' then exp_ex_bmyr1
when 'equ_age1' then equ_age1
when 'maritalstatus1' then maritalstatus1
when 'person_type1' then person_type1
when 'homeowner' then homeowner
when 'dwelling_size' then dwelling_size
when 'lengthofresidence' then lengthofresidence
when 'childrenage0_18' then childrenage0_18
when 'bb_population_flag' then bb_population_flag
end as Counts,
rank() over (order by Counts desc) as rnk
FROM
(
your current select
) as dt
cross join ColumnList
qualify rnk <= 5
In TD14.10 you could utilize the TD_UNPIVOT function:
SELECT Col, rank() over (order by Counts desc) as rnk
from TD_UNPIVOT(
ON (
your current select
)
USING
VALUE_COLUMNS('Counts')
UNPIVOT_COLUMN('Col')
COLUMN_LIST('equ_gender1'
,'exp_ex_bmyr1'
,'equ_age1'
,'maritalstatus1'
,'person_type1'
,'homeowner'
,'dwelling_size'
,'lengthofresidence'
,'childrenage0_18'
,'bb_population_flag')
) dt
qualify rnk <= 5;
Edit:
Additionally you might replace your LEFT JOIN with a single OLAP-function. Depending on the number of rows per cnst_mstr_id this might be more efficient as you need a ROW_NUMBER anyway:
SEL
SUM(CASE WHEN COALESCE(equ_gender1,'') = COALESCE(last_equ_gender1,'') THEN 0 ELSE 1 END ) AS equ_gender1_chg_cnt,
...
FROM
( SELECT
min(equ_gender1) OVER (PARTITION BY cnst_mstr_id ORDER BY cnst_chrctrstc_strt_ts DESC rows between 1 following and 1 following) as equ_gender1,
...
FROM arc_mdm_Tbls.cnst_chrctrstc_abc
WHERE load_id=1024
qualify cnst_chrctrstc_end_dt= date '9999-12-31'
)act

Sort numbers with 0 as the last priority, PL/SQL keyword for priority

I have:
PROCEDURE A
(
inId IN NUMBER,
RC1 OUT SYS_REFCURSOR
) IS
tMessage VARCHAR2(128);
BEGIN
OPEN RC1 FOR
SELECT * FROM
(
SELECT
a.company,
SUM(a.holding_balance) balance
FROM TableNameEntries A
WHERE
A.BATCH_ID = inId
GROUP BY a.company
)
ORDER BY balance DESC ;
EXCEPTION
WHEN OTHERS THEN
tMessage :='Exception ' || SQLCODE || ': ' || SQLERRM;
OPEN RC1 FOR
SELECT tMessage FROM DUAL;
END A;
For the balance column, I have values like -1, 0,1. Currently it's sorted like 1 0 -1, but I don't care about the zeros, so I want it to be like -1,1,0 or 1,-1,0
How do I do it in PL/SQL? I tried:
SELECT
a.company,
SUM(a.holding_balance) balance
FROM REC.CAG_GL_ENTRIES A
WHERE
A.BATCH_ID = 201311
order by case priority when 0 then 2 else 1 END priority
But it's saying PRIORITY is invalid identifier.
How can I get this to work?
Try:
ORDER BY CASE balance
WHEN 0 THEN null
ELSE balance
END
DESC NULLS LAST
there are two ways in your case (second is more common, first is more clear):
SELECT a.company,
SUM(a.holding_balance) balance
FROM REC.CAG_GL_ENTRIES A
WHERE A.BATCH_ID = 201311
order by abs(balance) desc;
SELECT a.company,
SUM(a.holding_balance) balance
FROM REC.CAG_GL_ENTRIES A
WHERE A.BATCH_ID = 201311
order by decode(balance,1,1,-1,1,2);
ORDER BY SIGN(ABS(balance)) DESC, balance
ABS() turns all the negatives to positives. SIGN() turns 0 to 0, >0 to 1 (and <0 to -1). Combined with DESC this puts all the zeros at the end and all the non-zeros above them in no particular order.
Then I sorted by the balance again to order the non-zeros.
You can also use
order by balance * balance desc

Pl/SQL - oracle 9i - Manual Pivoting

We have a table which has three columns in it:
Customer_name, Age_range, Number_of_people.
1 1-5 10
1 5-10 15
We need to return all the number of people in different age ranges as rows of a single query. If we search for customer #1, the query should just return one row:
Header- Age Range (1-5) Age Range (5-10)
10 15
We needed to get all the results in a single row; When I query for customer 1, the result should be only number of people in a single row group by age_range.
What would be the best way to approach this?
You need to manually perform a pivot:
SELECT SUM(CASE WHEN age_range = '5-10'
THEN number_of_people
ELSE NULL END) AS nop5,
SUM(CASE WHEN age_range = '10-15'
THEN number_of_people
ELSE NULL END) AS nop10
FROM customers
WHERE customer_name = 1;
There are easy solutions with 10g and 11g using LISTGAGG, COLLECT, or other capabilities added after 9i but I believe that the following will work in 9i.
Source (http://www.williamrobertson.net/documents/one-row.html)
You will just need to replace deptno with customer_name and ename with Number_of_people
SELECT deptno,
LTRIM(SYS_CONNECT_BY_PATH(ename,','))
FROM ( SELECT deptno,
ename,
ROW_NUMBER() OVER (PARTITION BY deptno ORDER BY ename) -1 AS seq
FROM emp )
WHERE connect_by_isleaf = 1
CONNECT BY seq = PRIOR seq +1 AND deptno = PRIOR deptno
START WITH seq = 1;
DEPTNO CONCATENATED
---------- --------------------------------------------------
10 CLARK,KING,MILLER
20 ADAMS,FORD,JONES,SCOTT,SMITH
30 ALLEN,BLAKE,JAMES,MARTIN,TURNER,WARD
3 rows selected.
This will create a stored FUNCTION which means you can access it at any time.
CREATE OR REPLACE FUNCTION number_of_people(p_customer_name VARCHAR2)
RETURN VARCHAR2
IS
v_number_of_people NUMBER;
v_result VARCHAR2(500);
CURSOR c1
IS
SELECT Number_of_people FROM the_table WHERE Customer_name = p_customer_name;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO v_number_of_people;
EXIT WHEN c1%NOTFOUND;
v_result := v_result || v_number_of_people || ' ' || CHR(13);
END;
END;
To run it, use:
SELECT number_of_people(1) INTO dual;
Hope this helps, and please let me know if there are any errors, I didn't testrun the function myself.
Just do
select Number_of_people
from table
where Customer_name = 1
Are we missing some detail?

Resources