How to return 0 incase of empty result in oracle select query? - oracle

I have a Query where there is number datatype column. In case of empty resultset O/P is blank. I want 0 in that case.
My query is
select nvl(infodata,0)
from table1
where group_id = 111
and appid in (select max(id)
from table1, table2
where status = 'A');
Now my inner query is returning null in that case. NVL seems to be not working on infodata column. Please help

Outcome of such a query is no rows selected.
For example (based on Scott's EMP and DEPT tables as I don't have yours, and you didn't post test case), this query returns nothing:
SQL> select e.deptno
2 from emp e
3 where e.ename = 'Littlefoot';
no rows selected
If it is used as a subquery, the result is equal to what you currently have:
SQL> select nvl(d.dname, 'x') result
2 from dept d
3 where d.deptno in (select e.deptno
4 from emp e
5 where e.ename = 'Littlefoot'
6 );
no rows selected
But, if you apply aggregation (see line #1), then you'd get what you wanted:
SQL> select nvl(max(d.dname), 'x') result
2 from dept d
3 where d.deptno in (select e.deptno
4 from emp e
5 where e.ename = 'Littlefoot'
6 );
RESULT
--------------
x
As I said: it works on my test case; might, or might not on yours. If you post sample data, we'd have something to work with.

Related

List all tables,numrows,numcol and primary key for table if any in oracle

I want to QUERY to generate a table like below:
Tablename||noofrows||noofcolumns||PRIMARKEYCOL(IF ANY for the table)
xyz. 590. 11. xyz_id
bcd. 934 15 null
...
...
So far I was able to do this until now in 2 query:
Query 1:
select a.table_name,count_rows(a.table_name) total_rows,count(b.column_name) total_cols from user_tables a,
,user_tab_columns b
where a.table_name =b.table_name
and a.table_name not like('amp%')
group by a.table_name;
note:Count_rows() is function to calculate rows as stats are not up to date
query 2:
select b.table_name b.column_name PRIMKEY_COL FROM user_constraints a,user_cons_columns b
where
a.constraint_type = 'P'
and a.constraint_name=b.constraint_name
and a.table_name=b.table_name
and b.table_name not like ('amp%');
Problem
I need to merge this table to one query (as shown in example above) so that I can represent the data in one table. My issue in clubbing the table is, with joins and how to make sure table without any primary keys are represent because if I just directly give constraint type ='p' in the where clause of the join I see that it only shows table with Primarykeys I am not able to figure this out.
Primary key can have more than just a single column, so - if you'd want to return them all, you'd either have to "aggregate" them, somehow (listagg looks like a natural choice, but - in 12c - you can't fetch distinct list of columns which you might need because of joining those tables duplicates appear).
As you already use a function to return number of rows (you'd probably rather regularly collect statistics, though, but yes - if tables are frequently modified (inserts and deletes), counting on-the-fly is the way to go.
So, alternative approach with 2 functions and 1 simple query. Yes, I know - context switching and stuff, but - see if it does any good.
This is what you already have (see if your and my code look similar):
SQL> create or replace function count_rows (par_table_name in varchar2)
2 return number
3 is
4 -- return number of rows in PAR_TABLE_NAME
5 retval number;
6 begin
7 execute immediate 'select count(*) from ' ||
8 dbms_assert.sql_object_name(par_table_name) into retval;
9 return retval;
10 end;
11 /
Function created.
This is new (to simplify fetching distinct list of primary key columns):
SQL> create or replace function pk_cols (par_table_name in varchar2)
2 return varchar2
3 is
4 -- return list of primary key columns for PAR_TABLE_NAME, sorted by column's
5 -- position within the primary key constraint
6 retval varchar2(100);
7 begin
8 select listagg(b.column_name, ', ') within group (order by b.position)
9 into retval
10 from user_constraints a join user_cons_columns b on b.constraint_name = a.constraint_name
11 where upper(a.table_name) = dbms_assert.sql_object_name(upper(par_table_name))
12 and a.constraint_type = 'P';
13 return retval;
14 end;
15 /
Function created.
Finally, that simple query I mentioned:
SQL> select a.table_name,
2 count_rows(a.table_name) total_rows,
3 max(a.column_id) total_cols,
4 pk_cols(a.table_name) primkey_cols
5 from user_tab_columns a
6 group by a.table_name;
TABLE_NAME TOTAL_ROWS TOTAL_COLS PRIMKEY_COLS
--------------- ---------- ---------- -------------------------
DEPT 4 3 DEPTNO
SO_TEST 0 1
TEST 7 3 ORD, TERMREGIONAL
EMP 14 8 EMPNO
TRAINING 0 5 TRAINING_PLACE_ID
TEMP_SE 10 3
<snip>
Just join the two queries:
select tab_cols.*, tab_keys.* from
(select a.table_name,count_rows(a.table_name) total_rows,count(b.column_name) total_cols from user_tables a,
,user_tab_columns b
where a.table_name =b.table_name
and a.table_name not like('amp%')
group by a.table_name) tab_cols,
(select b.table_name b.column_name PRIMKEY_COL FROM user_constraints a,user_cons_columns b
where
a.constraint_type = 'P'
and a.constraint_name=b.constraint_name
and a.table_name=b.table_name
and b.table_name not like ('amp%')) tab_keys
where tab_cols.table_name = tab_keys.table_name(+);

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.

Trying to update table but i always get ora-01427

So i am trying to update my Vorschlagspakete table with some values i got from other tables. Precisely there are 3 values i want to write into the main table. Atm it looks like this:
update vorschlagspakete
set (paketid, verkaufsstelleid) = (
select k.paketid, k.verkaufsstelleid
from Konfiguration k, bewertung b
where k.konfigurationsid = b.konfigurationsid
group by k.paketid, k.verkaufsstelleid
having avg(b.sterne) >= 5);
But every time i tried this it results into ora-01427.
Error you got, ORA-01427, means too many rows (were returned by subquery). For example based on Scott's schema (as I don't have your tables), it looks like this:
SQL> update emp e set
2 (e.ename, e.job) = (select d.dname, d.loc from dept d);
(e.ename, e.job) = (select d.dname, d.loc from dept d)
*
ERROR at line 2:
ORA-01427: single-row subquery returns more than one row
Why wouldn't it work? Because subquery returns more than a single row!
SQL> select d.dname, d.loc from dept d;
DNAME LOC
-------------- -------------
ACCOUNTING NEW YORK
RESEARCH DALLAS
SALES CHICAGO
OPERATIONS BOSTON
SQL>
So, how would you put all those values into a single row of the EMP table? That won't work, obviously, so you have to do something to restrict number of rows. How? Well, it depends.
sometimes DISTINCT helps, e.g.
select distinct d.dname, d.loc from dept d
sometimes additional WHERE condition helps, e.g.
select d.dname, d.loc from dept d
where d.location = 'NEW YORK'
sometimes joining with the table to be updated helps, e.g.
select d.dname, d.loc from dept d where d.deptno = e.deptno
which leads to
SQL> update emp e set
2 (e.ename, e.job) = (select d.dname, d.loc from dept d where d.deptno = e.deptno);
14 rows updated.
What should you do? I don't know, we don't have your data. See whether something written above helps.

Update Statement with two tables -Oracle

I have two tables and I need to Update the second table with a value from first table based on a common column.
I used the below statement
UPDATE emp
2 SET ename = ( SELECT dname
3 FROM dept
4 WHERE emp.deptno = dept.deptno)
5 WHERE EXISTS
6 ( SELECT dname
7 FROM dept
8 WHERE emp.deptno = dept.deptno);
But am getting the error
ORA-01427 - Single row subquery returns more than one row.
Can you plese help me out?
SELECT dname
FROM dept
WHERE emp.deptno = dept.deptno
query must return single record.
check with
SELECT count(*), dname
FROM dept
group by dname
having count(*) > 1
order by 1 desc
or use
SELECT dname
FROM dept
WHERE emp.deptno = dept.deptno
and rownum = 1
You need to check if 1st subquery returns only 1 value bcz if this subquery returns more then 1 row you want to update 1 field ename with 2 values from row and that's why i guess you have this error
Your subquery to dept table is probably not returning one row. Add min or max operation to get one row from dept for each row from emp. Each setting value must be nonambigous.
UPDATE emp
SET ename = ( SELECT min dname
FROM dept
WHERE emp.deptno = dept.deptno)
WHERE EXISTS
( SELECT dname
FROM dept
WHERE emp.deptno = dept.deptno);
Remark1: in default SCOTT schema deptno is primary key, so no error would be encountered.
Remark2: shouldn't your question to be placed in serverfault.com?

Delete with Left Join in Oracle 10g

I have the following code that works fine in MS SQL Server:
delete grp
from grp
left join my_data
on grp.id1 = my_data.id1
and grp.id2 = my_data.id2
and grp.id3 = my_data.id3
and grp.id4 = my_data.id4
where my_data.id1 is NULL
Basically, I want to delete all occurrence that can be found in grp and don't have any equivalence in my_data. Sadly, it doesn't work in Oracle 10g. I tried using the old syntax for left join (+) but it doesn't work either. Like this:
delete grp
from grp,
my_data
where grp.id1 = my_data.id1 (+)
and grp.id2 = my_data.id2 (+)
and grp.id3 = my_data.id3 (+)
and grp.id4 = my_data.id4 (+)
and my_data.id1 is NULL
A IN clause would works if I didn't have multiple keys but I don't see how I could use it with my data. So, what is the alternative?
Shannon's solution is the way to go: use the operator NOT IN (or NOT EXISTS).
You can however delete or update a join in Oracle, but the synthax is not the same as MS SQL Server:
SQL> DELETE FROM (SELECT grp.*
2 FROM grp
3 LEFT JOIN my_data ON grp.id1 = my_data.id1
4 AND grp.id2 = my_data.id2
5 AND grp.id3 = my_data.id3
6 AND grp.id4 = my_data.id4
7 WHERE my_data.id1 IS NULL);
2 rows deleted
Additionally, Oracle will only let you update a join if there is no ambiguity as to which base row will be accessed by the statement. In particular, Oracle won't risk an update or a delete (the statement will fail) if there is a possibility that a row may appear twice in the join. In this case, the delete will only work if there is a UNIQUE constraint on my_data(id1, id2, id3, id4).
Tables and data:
SQL> create table grp (id1 number null, id2 number null, id3 number null, id4 number null);
Table created.
SQL> create table my_data (id1 number null, id2 number null, id3 number null, id4 number null);
Table created.
SQL> insert into grp values (1, 2, 3, 4);
1 row created.
SQL> insert into grp values (10, 20, 30, 40);
1 row created.
SQL> insert into grp values (1, 2, 30, 40);
1 row created.
SQL> insert into my_data values (1, 2, 3, 4);
1 row created.
SQL> commit;
Commit complete.
Using in. Note Do not use if the IDs in the subquery can be null. Not in of null never returns true.
SQL> delete grp where (id1, id2, id3, id4) not in (select id1, id2, id3, id4 from my_data);
2 rows deleted.
SQL> select * from grp;
ID1 ID2 ID3 ID4
---------- ---------- ---------- ----------
1 2 3 4
Using exists
SQL> rollback;
Rollback complete.
SQL> delete grp where not exists (select * from my_data where grp.id1 = my_data.id1 and grp.id2 = my_data.id2 and grp.id3 = my_data.id3 and grp.id4 = my_data.id4);
2 rows deleted.
SQL> select * from grp;
ID1 ID2 ID3 ID4
---------- ---------- ---------- ----------
1 2 3 4
SQL>
If you want to ensure there is no ambiguity in what's being deleted, you could change Vincent's solution to:
delete from grp where rowid in
(
select
grp.rowid
from
grp left outer join my_data on
grp.id1 = my_data.id1
and grp.id2 = my_data.id2
and grp.id3 = my_data.id3
and grp.id4 = my_data.id4
where
my_data.id1 is NULL
)
Either Vincent's answer https://stackoverflow.com/a/3675205 does not work at all, or it does not work in Oracle 12c. That answer should be improved by specifying the lowest or highest version of Oracle where this works. The proof:
SELECT * FROM v$version where banner like 'Oracle%';
/*
Oracle Database 12c Standard Edition Release 12.2.0.1.0 - 64bit Production
*/
create table a (id int);
create table b (id int);
insert into a select 1 from dual union select 2 from dual;
insert into b select 1 from dual union select 2 from dual union select 3 from dual;
select * from a right join b on b.id = a.id;
/*
1 1
2 2
null 3
*/
delete from (
select b.*
from b
inner join a on a.id = b.id
)
/*
Error at Command Line : 7 Column : 13
Error report -
SQL Error: ORA-01752: cannot delete from view without exactly one key-preserved table
01752. 00000 - "cannot delete from view without exactly one key-preserved table"
*Cause: The deleted table had
- no key-preserved tables,
- more than one key-preserved table, or
- the key-preserved table was an unmerged view.
*Action: Redefine the view or delete it from the underlying base tables.
*/
delete from b
where rowid in (
select b.rowid
from b
inner join a on a.id = b.id
)
/*
2 rows deleted.
*/
select * from a right join b on b.id = a.id
/*
null 3
*/
drop table a;
drop table b;
Bottom line is, use WHERE ROWID IN () at least in 12c.
I can't add a comment because it need 50 reps,so I add a answer here.
I tested Vincent's delete from query, that syntax can't let you delete what you want,at least it's not a common use for all the delete join cases.
At first I create a table using oracle default user scott:
create table emp1 as select * from emp where sal<2000;
I want to delete the records from emp where empno in emp1(just a simple test),so I used this delete from query:
delete from (select a.* from emp a join emp1 b on a.empno=b.empno);
No matter what the table or join order is,left join or inner join,no matter what where clause I use,the sql will delete the corresponding records in emp1.
So I think this delete from query can not let you delete from a specified table.
Loop a cursor will be a better way for these cases.

Resources