Display data even for null values stored procedure - oracle

I made this query to show some aggregate data.
I am also interested in showing elements of a table that are not affected. After several useless tests I try to ask you for help.
create or replace PROCEDURE PROVA
(
NOME IN VARCHAR2,
COGNOME IN VARCHAR2,
CARTA IN VARCHAR2,
output OUT SYS_REFCURSOR
)
AS
BEGIN
OPEN output FOR
SELECT
dati.nome "NOME",
dati.cognome "COGNOME",
dati.carta "CARTA",
dati.profilo "PROFILO",
dati.s_denominazione "DENOMINAZIONE",
CASE
WHEN SUM(numacc) > 0 THEN
'TRUE'
ELSE
'FALSE'
END "ABILITATO",
SUM(numacc) "NUMERO ACCESSI"
FROM
(
SELECT
d.codicepunto,
d.s_denominazione,
a.carta carta,
c.nome nome,
c.cog cognome,
c.profilo profilo,
coalesce(a.numacc, 0) numacc
FROM
tabellaA a
JOIN tabellaB b
ON a.id = b.id
RIGHT OUTER JOIN TABELLAC c
ON a.carta = c.carta
RIGHT OUTER JOIN TABELLAD d
ON b.punto = d.punto
WHERE
(NOME IS NULL or c.nome = NOME)
AND
(COGNOME IS NULL or c.cog = COGNOME)
AND
(CARTA IS NULL or a.carta = CARTA)
AND
sysdate BETWEEN a.datinivalacc AND a.datfinvalacc
) dati
GROUP BY
dati.s_denominazione,
dati.carta,
dati.nome,
dati.cognome,
dati.profilo
ORDER BY
dati.s_denominazione;
END ;
This is the result:
NOME
COGNOME
CARTA
PROFILO
DENOMINAZIONE
ABILITATO
NUMERO ACCESSI
Albert
Weah
010101
1
Torino
TRUE
10
Albert
Weah
010101
1
Milano
TRUE
20
(null)
(null)
(null)
(null)
Napoli
FALSE
0
(null)
(null)
(null)
(null)
Roma
FALSE
0
This is what I would like to achieve:
NOME
COGNOME
CARTA
PROFILO
DENOMINAZIONE
ABILITATO
NUMERO ACCESSI
Albert
Weah
010101
1
Torino
TRUE
10
Albert
Weah
010101
1
Milano
TRUE
20
Albert
Weah
010101
1
Napoli
FALSE
0
Albert
Weah
010101
1
Roma
FALSE
0
What am I missing?
The input parameters are all optional.
Thanks in advance

Related

How to generate 18 digit code using cursor based on some conditions in Oracle

I have a table from which I want to generate 18 digit code.
Below is the 18 digit code sample which I want.
R-AP-AP01-SMT-4567
Also for generating the above sample code, here is the data and its logic:
R - Fix value
AP – (2 digit state code from STATE column)
EAST- (From ZONE_NAME column from query below
SMT – (From FORMAT_CODE column from below query)
4567 – (From Store Code column from below query)
SELECT STATE, STORE_CODE, ZONE_NAME FROM TBL_RRSOC_STORE_INFO;
AND
select FORMAT_CODE from TBL_SITE_STORE_FORMAT;
How can it be achieved?
Update
Below is the table description
Table name:- TBL_RRSOC_STORE_INFO
Name Null Type
--------------------------- -------- --------------
RRSOC_ID NOT NULL NUMBER
STORE_CODE NOT NULL NVARCHAR2(55)
STATE NVARCHAR2(55)
SLP_STATE NVARCHAR2(100)
FORMAT_GROUP NVARCHAR2(100)
Table name:- TBL_SITE_STORE_FORMAT
Name Null Type
------------ ---- -------------
ID VARCHAR2(20)
STORE_FORMAT VARCHAR2(100)
ISACTIVE VARCHAR2(3)
FORMAT_GROUP VARCHAR2(100)
FORMAT_CODE VARCHAR2(50)
The way you put it, you'd join those tables somehow (cross join is as good as any other, as you didn't explain it better) and concatenate column values.
Something like this:
SQL> with
2 tbl_rrsoc_store_info (state, store_code, zone_name) as
3 (select 'AP', 'EAST', 'SMT' from dual union all
4 select 'NY', 'WEST', 'XYZ' from dual
5 ),
6 tbl_site_store_format (format_code) as
7 (select 4567 from dual)
8 --
9 select 'R' ||'-'|| r.state ||'-'|| r.store_code ||'-'|| r.zone_name ||'-'|| s.format_code result
10 from tbl_rrsoc_store_info r cross join tbl_site_store_format s;
RESULT
--------------------
R-AP-EAST-SMT-4567
R-NY-WEST-XYZ-4567
SQL>
Function returns a value; you didn't explain how it should look like (which parameters it accepts) so I chose to pass state, presuming it is unique within the table.
Sample data:
SQL> select * From tbl_rrsoc_store_info;
ST STOR ZON
-- ---- ---
AP EAST SMT
NY WEST XYZ
SQL> select * from tbl_site_store_format;
FORMAT_CODE
-----------
4567
Function:
SQL> create or replace function f_test (par_state in varchar2)
2 return varchar2
3 is
4 retval varchar2(18);
5 begin
6 select 'R' ||'-'|| r.state ||'-'|| r.store_code ||'-'|| r.zone_name ||'-'|| s.format_code
7 into retval
8 from tbl_rrsoc_store_info r cross join tbl_site_store_format s
9 where r.state = par_state;
10
11 return retval;
12 end;
13 /
Function created.
Testing:
SQL> select r.state, f_test(r.state) result
2 from tbl_rrsoc_store_info r;
ST RESULT
-- --------------------
AP R-AP-EAST-SMT-4567
NY R-NY-WEST-XYZ-4567
SQL>

Need help in output for Oracle PLSQL function

Am currently working on oracle PLSQL function to list the project numbers, titles and the names of employees who work on each project.
For this function, I am required to obtain an output as such:
[Fragmented example]
1001 Computation: Alvin, Peter
1002 Study methods: Bob, Robert
1003 Racing car: Robert
Here is the output I am currently having.
SQL> execute PROJECTGROUPS;
1001 Computation: Alvin
1001 Computation: Ami
1001 Computation: Michael
1001 Computation: Peter
1001 Computation: Wendy
1002 Study methods: Bob
1002 Study methods: Robert
1003 Racing car: Bob
1003 Racing car: Robert
1004 Football: Douglass
1004 Football: Eadger
1005 Swimming: Douglass
1005 Swimming: Eadger
1006 Training: Aban
1006 Training: Carl
[Current Code]
SQL> set echo on
SQL> set feedback on
SQL> set linesize 100
SQL> set pagesize 200
SQL> set serveroutput on
SQL> --
SQL> -- Task 01
SQL> --
SQL> CREATE OR REPLACE PROCEDURE PROJECTGROUPS
2 IS
3 Previous_pnum PROJECT.P#%type := -1;
4 --
5 begin
6 for currentRow IN(select p.P#, p.PTITLE, e.NAME
7 from PROJECT p LEFT OUTER JOIN EMPLOYEE e
8 on p.d# = e.d#
9 WHERE p.P# IN(1001,1002,1003,1004,1005,1006)
10 ORDER BY p.P#, p.PTITLE, e.NAME)
11 --
12 --
13 loop
14 if currentRow.P# is not null then
15 dbms_output.put_line(currentRow.P# || ' ' || currentRow.PTITLE || ': ' || currentRow.NAME);
16 end if;
17 Previous_pnum := currentRow.P#;
18 end loop;
19 dbms_output.put_line(NULL);
20 END;
21 /
Procedure created.
What you need is listagg function.
Here is the SQL -
SELECT p.P#
,p.PTITLE
,listagg(e.NAME, ',') within group (order by e.name) employee_names
FROM PROJECT p
,EMPLOYEE e
WHERE p.P# IN(1001,1002,1003,1004,1005,1006)
AND p.d# = e.d#(+)
GROUP
BY p.P#
,p.PTITLE
ORDER
BY p.P#
,p.PTITLE
LISTAGG() function to return the string in order to print directly with alias (str), and print such as DBMS_OUTPUT.PUT_LINE( str ) :
SELECT p.p#||' '||p.ptitle||': '||LISTAGG( e.name, ',' ) WITHIN GROUP ( ORDER BY e.name ) AS str
FROM project p
LEFT JOIN employee e
ON p.d# = e.d#
WHERE p.p# BETWEEN 1001 AND 1006
GROUP BY p.p#, p.ptitle
ORDER BY p.p#, p.ptitle;
we don't know your sample data set. Perhaps you'd need INNER JOIN depending on the data. Btw, it seems need to fix the data structure of project table. The column d# should be moved to another(the third) table I think.

Oracle- Join with not equal condition

I want to join a select in select in select etc. with a table where I don't have to find the customer_no from selects result in the table.
Also, for optimization and reduction execution time how should I wrote the script?
ex.
SELECT DISTINCT customer_no,
str_data_expirare_1,
data_expirare_buletin_1,
str_data_expirare_2,
data_expirare_buletin_2
FROM (SELECT customer_no,
str_data_expirare_1,
data_expirare_buletin_1,
str_data_expirare_2,
data_expirare_buletin_2
FROM (SELECT customer_no,
str_data_expirare_1,
normalize_date (str_data_expirare_trim_1)
AS data_expirare_buletin_1,
str_data_expirare_2,
normalize_date (str_data_expirare_trim_2)
AS data_expirare_buletin_2
FROM (SELECT customer_no,
str_data_expirare_1,
REGEXP_REPLACE (str_data_expirare_1,
'[[:punct:]]',
'')
AS str_data_expirare_trim_1,
str_data_expirare_2,
REGEXP_REPLACE (str_data_expirare_2,
'[[:punct:]]',
'')
AS str_data_expirare_trim_2
FROM (SELECT Q1.customer_no,
Q1.set_act_id_1,
NVL (SUBSTR (set_act_id_1,
INSTR (set_act_id_1,
'+',
1,
5)
+ 1,
LENGTH (set_act_id_1)),
'NULL')
AS str_data_expirare_1,
Q1.set_act_id_2,
NVL (SUBSTR (set_act_id_2,
INSTR (set_act_id_2,
'+',
1,
5)
+ 1,
LENGTH (set_act_id_2)),
'NULL')
AS str_data_expirare_2
FROM STAGE_CORE.IFLEX_CUSTOMERS Q1
WHERE Q1.set_act_id_1 IS NOT NULL/*AND Q1.PERS_LAST_NAME LIKE 'LACAN'*/
)))
WHERE str_data_expirare_1 <> 'NULL'
AND data_expirare_buletin_1 - SYSDATE < 0
AND ( data_expirare_buletin_2 =
TO_DATE ('30-DEC-99', 'DD-MM-YYYY')
OR data_expirare_buletin_2 < SYSDATE)) T1
JOIN STAGE_CORE.IFLEX_CUSTOMERS_REPRES T2
ON T1.CUSTOMER_NO = T2.representative
JOIN STAGE_CORE.VW_IFLEX_ACCOUNTS_IBAN t3
ON t3.Cust_key <> T2.representative
WHERE T2.relationship <> 'EXTERN' AND T2.REPRESENTATIVE <> T2.REF_NO
The query has to find all people with identity card expired which are authorized on someone else account (from a bank) and doesn't have any account opened.
STAGE_CORE.IFLEX_CUSTOMERS_REPRES - the table with all accounts and
authoried on accounts
JOIN STAGE_CORE.VW_IFLEX_ACCOUNTS_IBAN - the table with all accounts
and details
STAGE_CORE.IFLEX_CUSTOMERS - the table with all customers
Sample data:
VW_IFLEX_ACCOUNTS_IBAN
UNIT_ID CUST_KEY ACCOUNT_NO IBAN AC_OPEN_DATE ACC_ISOPEN
51 875497 xxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxx 29/02/2016 O
51 875497 xxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxx 08/03/2016 C
51 875497 xxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxx 01/03/2016 O
751 875497 xxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxx 12/08/2011 C
466 875497 xxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxx 22/01/2013 O
IFLEX_CUSTOMERS_REPRES
REF_NO CATEGORY TITULAR REPRESENTATIVE RELATIONSHIP
875497 C 875497 535555 UNICA
875497 C 875497 565796 UNICA
875497 C 875497 875497 PRIMARY-MEANS I AM AUTHORIZED ON MY ACCOUNT
875497 C 875497 875497 UNICA-MEANS I AM AUTHORIZED ON MY ACCOUNT
875497 C 875497 0O00000 EXTERN -- MEANS EVERYONE CAN PUT MONEY ON MY ACCOUNT
IFLEX_CUSTOMERS
CUSTOMER_NO LEGACY_CUSTOMER_NUMBER_KEY UNIT_ID CUST_CATEGORY CUST_CLASSIFICATION CUST_TYPE KYC_CUST_TYPE CIF_SINCE FISCAL_ID FULL_NAME FISCAL_RESIDENCE_COUNTRY NIF_EXPIRED_DATE NIF SET_ACT_ID_1 SET_ACT_ID_2
875497 875497 xxx RETAIL 80 I R 254911 2548614654 LACAN MARIA ADRIANA RO 01/03/2016 CI+XX+8646844+SPCLEP SIMM+01-Oct-2015+06-Dec-2022 +FN+FN+++
OUTPUT:
CUSTOMER_NO STR_DATA_EXPIRARE_1 DATA_EXPIRARE_BULETIN_1 STR_DATA_EXPIRARE_2 DATA_EXPIRARE_BULETIN_2
535555 29-Apr-16 29-Apr-16 NULL 30-Dec-99
0Jxx1ds0 09-Sep-16 09-Sep-16 NULL 30-Dec-99
2xx8ds 24-Mar-16 24-Mar-16 NULL 30-Dec-99
2756719 20-Feb-17 20-Feb-17 NULL 30-Dec-99
this customers must not have any account to this bank
To be frank I'm going to ignore that sprawling chunk of code and answer the question using the focused, reproducible test case you should have posted :)
What you need is an anti-join. Use the left join syntax to connect Customers with Accounts then filter results where the Account key is null. This gives you Customers without Accounts. But inner join on Customers and Representatives to ensure that the set of Customers is restricted to those who are also Representatives.
select c.customer_no
, c.expired_date
from customers c
inner join customers_repres cr
on cr.representative_id = c.customer_no
left outer join accounts_iban ai
on ai.cust_key = c.customer_no
where c.expired_date is not null -- ?? indicates expired ID ??
and ai.cust_key is null
SOLVED:
select * from STAGE_CORE.IFLEX_CUSTOMERS c
join
STAGE_CORE.IFLEX_CUSTOMERS_REPRES cr
on c.CUSTOMER_NO=cr.REPRESENTATIVE
left join STAGE_CORE.VW_IFLEX_ACCOUNTS_IBAN a
on a.cust_key=cr.REPRESENTATIVE
where cr.RELATIONSHIP<>'PRIMARY'
and a.CUST_KEY is not null

Oracle procedure or function to return multiple values

I need to write a procedure or a function which returns the count of status, age and type which should satisfy the below criteria
select * from ABC
where ABC_id = 2001
and ABC_LEVEL_ID = 1 --status
and ABC_REQUEST_DATE < sysdate --age
and ABC_TYPE_ID = 5; --type
If ABC_ID = 2001 and ABC_LEVEL_ID = 1
THEN return COUNT(STATUS)
If ABC_ID = 2001 and ABC_REQUEST_DATE < SYSDATE
THEN return COUNT(AGE)
If ABC_ID = 2001 and ABC_TYPE_ID = 5
THEN return COUNT(TYPE)
All three values should be OUT parameters which are passed to front end application.
You can use a CASE expression to your query to include those constraint like
select *,
case when ABC_ID = 2001 and ABC_LEVEL_ID = 1 then COUNT(STATUS) else null end as testcol1,
case when ABC_ID = 2001 and ABC_REQUEST_DATE < SYSDATE then COUNT(AGE) else null end as testcol2,
case when ABC_ID = 2001 and ABC_TYPE_ID = 5 then COUNT(TYPE) else null end as testcol3
from ABC
where ABC_id = 2001
and ABC_LEVEL_ID = 1 --status
and ABC_REQUEST_DATE < sysdate --age
and ABC_TYPE_ID = 5; --type
Per Comment: modified query (to include #jeffrykemps earlier edit)
select *,
case when ABC_LEVEL_ID = 1 then COUNT(STATUS) end as testcol1,
case when ABC_REQUEST_DATE < SYSDATE then COUNT(AGE) end as testcol2,
case when ABC_TYPE_ID = 5 then COUNT(TYPE) end as testcol3
from ABC
where ABC_id = 2001
and ABC_LEVEL_ID = 1 --status
and ABC_REQUEST_DATE < sysdate --age
and ABC_TYPE_ID = 5; --type
I think it would make more sense if your WHERE clause uses OR operations rather than AND. It's easy to do the counts in the projection of the query, using CASE statements.
As the code belongs to a stored procedure you need to select into something. Here I've assumed direct assignment to the OUT parameters. However, if you code contains additional requirements you should populate local variables instead, and assign them to the OUT parameters at the end of the procedure.
create or replace procedure get_counts
( p_out_status_count out pls_integer
, p_out_age_count out pls_integer
, p_out_type_count out pls_integer
as
begin
select
count (case ABC_LEVEL_ID = 1 then 1 else null end),
count (case ABC_REQUEST_DATE < SYSDATE then 1 else null end),
count (case ABC_TYPE_ID = 5 then 1 else null end)
into p_out_status_count
, p_out_age_count
, p_out_type_count
from ABC
where ABC_id = 2001
and (ABC_LEVEL_ID = 1 --status
or ABC_REQUEST_DATE < sysdate --age
or ABC_TYPE_ID = 5); -- type
end get_counts;
Also you might want to paramterize the ABC_ID. In which case the procedure's signature might be:
create or replace procedure get_counts
( p_abc_id in abc.abc_id%type
, p_out_status_count out pls_integer
, p_out_age_count out pls_integer
, p_out_type_count out pls_integer
)
and the WHERE clause would be
....
from ABC
where ABC_id = p_abc_id
....

oracle: counting number of null fields in a row

I have a table that has 5 "optional" fields. I'd like to find out how many rows have all 5 null, how many have 1 field non-null, etc.
I've tried a couple of things, like:
select
count(*),
( (if field1 is null then 1 else 0) +
(if field2 is null then 1 else 0) +
etc.
but of course that doesn't work.
Ideally, I'm looking for output that's something like
Nulls Cnt
0 200
1 345
...
5 40
Is there an elegant solution?
The keyword is not if, it is case, and you must use end to end the case statement.
Here is a query that can suit you:
WITH subQuery AS
(
SELECT My_Table.*, (CASE WHEN My_Table.field1 IS NULL THEN 1 ELSE 0 END +
CASE WHEN My_Table.field2 IS NULL THEN 1 ELSE 0 END +
CASE WHEN My_Table.field3 IS NULL THEN 1 ELSE 0 END +
CASE WHEN My_Table.field4 IS NULL THEN 1 ELSE 0 END +
CASE WHEN My_Table.field5 IS NULL THEN 1 ELSE 0 END ) NumberOfNullFields
FROM My_Table
)
SELECT NumberOfNullFields, COUNT(*)
FROM subQuery
GROUP BY NumberOfNullFields;
While there is nothing wrong with the case WHEN counting, I just wanted to see if there was another way.
WITH SAMPLEDATA AS(--just generate some data with nulls for 5 cols
select level ,
(case when mod(level,2) = 0 then 1 else null end) colA,
(case when mod(level,3) = 0 then 1 else null end) colB,
(case when mod(level,5) = 0 then 1 else null end) colC,
(case when mod(level,7) = 0 then 1 else null end) colD,
(case when mod(level,11) = 0 then 1 else null end) colE
from dual
connect by level < 1000
), --utilize the count(Aggregate)'s avoidance of nulls to our summation advantage
nullCols as(
SELECT COUNT(COLA) aNotNull
,cOUNT(*)-COUNT(COLA) aNull
,count(colB) bNotNull
,cOUNT(*)-count(colB) bNull
,count(colc) cNotNull
,cOUNT(*)-count(colc) cNull
,count(cold) dNotNull
,cOUNT(*)-count(cold) dNull
,count(cole) eNotNull
,cOUNT(*)-count(cole) eNull
, cOUNT(*) TotalCountOfRows
from SAMPLEDATA )
SELECT (select count(*) from sampledata where cola is null and colb is null and colc is null and cold is null and cole is null) allIsNull
,nullCols.*
FROM nullCols;
ALLISNULL ANOTNULL ANULL BNOTNULL BNULL CNOTNULL CNULL DNOTNULL DNULL ENOTNULL ENULL TOTALCOUNTOFROWS
---------------------- ---------------------- ---------------------- ---------------------- ---------------------- ---------------------- ---------------------- ---------------------- ---------------------- ---------------------- ---------------------- ----------------------
207 499 500 333 666 199 800 142 857 90 909 999
this utilizes the
If expression in count(expression)
evaluates to null, it is not counted:
as noted from here
This method, as is obvious above, does not 'eloquently' sum all null columns well. Just wanted to see if this was possible without the CASE logic.

Resources