How to concat two columns and comparing the value with other table - oracle

I'm trying to concatenate two columns and compare this value to a "not in" condition, from another select, but I'm not succeeding.
My last attempt:
SELECT valorA || SubStr(valorB,3,2) AS al,hours FROM table_A
where valorA LIKE '%RR%'
AND hours > To_Date('24/09/2021 12:00','dd/mm/yyyy HH24:mi')
AND al NOT IN (
SELECT vr
FROM table_B )
Its returnin error: ORA-00904: "AL": invalid identifier

As Del said (like that name!), you cannot reference a column alias in the same query block. Choices are
rephrase the concatenation in the predicate (as Del pointed out):
SQL> SELECT valorA || SubStr(valorB,3,2) AS al,hours FROM table_A
2 where valorA LIKE '%RR%'
3 AND hours > To_Date('24/09/2021 12:00','dd/mm/yyyy HH24:mi')
4 AND valorA || SubStr(valorB,3,2) NOT IN (
5 SELECT vr
6* FROM table_B );
no rows selected
Use a subquery, either subquery factoring or embedded subquery. How many of the predicates you push down should depend on whether you're reusing the same query block potentially multiple times in the same stmt ... . Should not matter to the Optimizer.
-- subquery factoring
SQL> with q as
2 (SELECT valorA || SubStr(valorB,3,2) AS al,hours FROM table_A
3 where valorA LIKE '%RR%'
4 AND hours > To_Date('24/09/2021 12:00','dd/mm/yyyy HH24:mi'))
5 select al, hours from q
6 where al NOT IN (
7 SELECT vr
8* FROM table_B );
no rows selected
-- subquery factoring, less predicates pushed down
SQL> with q as
2 (SELECT valorA || SubStr(valorB,3,2) AS al,hours FROM table_A
3 where valorA LIKE '%RR%')
4 select al, hours from q
5 where hours > To_Date('24/09/2021 12:00','dd/mm/yyyy HH24:mi')
6 and al NOT IN (
7 SELECT vr
8* FROM table_B );
no rows selected
-- normal, embedded subquery
SQL> select al, hours from
2 (SELECT valorA || SubStr(valorB,3,2) AS al,hours FROM table_A
3 where valorA LIKE '%RR%')
4 where hours > To_Date('24/09/2021 12:00','dd/mm/yyyy HH24:mi')
5 and al NOT IN (
6 SELECT vr
7* FROM table_B );
no rows selected

Related

No hard coding in where clause in oracle

[I have created a table as shown in figure
CREATE TABLE TABLE1 (CAL_YEAR VARCHAR2(4), NAME VARCHAR2(1))
INSERT INTO TABLE1 VALUES(‘2020’, ’A’)
INSERT INTO TABLE1 VALUES(‘2020’, ’B’)
INSERT INTO TABLE1 VALUES(‘2020’, ’C’)
INSERT INTO TABLE1 VALUES(‘2020’, ’D’)
INSERT INTO TABLE1 VALUES(‘2021’, ’E’)
INSERT INTO TABLE1 VALUES(‘2021’, ’F’)
Let us assume I am querying the statement in this year like
SELECT * FROM TABLE1 WHERE CAL_YEAR= TO_CHAR((SYSDATE),'YYYY');
so I get present year values.
What I need is if the present year data not present in the table then I need to use previous year data i.e. 2020 data...How can I do this in the where clause itself]1
You can simply get the maximum year that is less than or equal the year you query for in a subquery.
SELECT *
FROM table1
WHERE cal_year = (SELECT max(cal_year)
FROM table1
WHERE cal_year <= 2020);
You want to show the previous year, if no row for the current year exists? Simply select all years until now, order by year, take the last one.
SELECT *
FROM table1
WHERE cal_year <= EXTRACT(YEAR FROM SYSDATE)
ORDER BY cal_year DESC
FETCH FIRST ROW WITH TIES;
Optionally (if you're on a database version which doesn't support FETCH FIRST ROW WITH TIES Thorsten suggested):
with year 2021 in the table:
SQL> select * from table1
2 where cal_year = case when (select max(1) from table1
3 where exists (select null from table1
4 where cal_year = to_char(sysdate, 'yyyy')
5 )
6 ) = 1 then to_char(sysdate, 'yyyy')
7 else to_char(add_months(sysdate, -12), 'yyyy')
8 end;
CAL_ N
---- -
2021 E
2021 F
Without this year:
SQL> delete from table1 where cal_year = 2021;
2 rows deleted.
SQL>
SQL> select * from table1
2 where cal_year = case when (select max(1) from table1
3 where exists (select null from table1
4 where cal_year = to_char(sysdate, 'yyyy')
5 )
6 ) = 1 then to_char(sysdate, 'yyyy')
7 else to_char(add_months(sysdate, -12), 'yyyy')
8 end;
CAL_ N
---- -
2020 A
2020 B
2020 C
2020 D
SQL>
What does it do?
lines #2 - 5: SELECT* (within CASE) returns 1 if EXISTS says that there's at least one row whose CAL_YEAR = this year
if that's so (i.e. 1 has been returned), then CAL_YEAR is compared to this year (to_char(sysdate, 'yyyy'))
otherwise, if SELECT returns something else (most probably NULL), CAL_YEAR is compared to previous year (that's what to_char(add_months(sysdate, -12), 'yyyy') does - subtracts 12 months from today's date and extracts year from it)

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.

Select default in case of no value returned

I am trying to get some default value in my resultset if query does not return anything. I am trying nvl for the same but it is not returning the expected default value. To simulate, Consider following query,
select nvl(null, '10') from dual where 1=0;
I want to get 10 in case of given condition is not true and query does not return any value. However above query not returning any row.
Your query returns zero rows. NVL() isn't going to change that (*).
The correct solution is for the program which executes the query to handle NO_DATA_FOUND exception rather than fiddling the query.
However, you need a workaround so here is one using two sub-queries, one for your actual query, one to for the default.
When your_query returns an empty set you get this:
SQL> with your_qry as
2 ( select col1 from t42 where 1=0 )
3 , dflt as
4 ( select 10 as col1 from dual )
5 select col1
6 from your_qry
7 union all
8 select col1
9 from dflt
10 where not exists (select * from your_qry );
COL1
----------
10
SQL>
And when it returns a row you get this:
SQL> with your_qry as
2 ( select col1 from t42 )
3 , dflt as
4 ( select 10 as col1 from dual )
5 select col1
6 from your_qry
7 union all
8 select col1
9 from dflt
10 where not exists (select * from your_qry );
COL1
----------
12
13
SQL>
The WITH clause is optional here, it just makes it easier to write the query without duplication. This would have the same outcome:
select col1
from t42
where col0 is null
union all
select 10
from dual
where not exists (select col1
from t42
where col0 is null)
;
(*) Okay, there are solutions which use NVL() or COALESCE() with aggregations to do this. They work with single column projections in a single row as this question poses, but break down when the real query has more than one row and/or more than one column. Aggregations change the results.
So this looks alright ...
SQL> with cte as (
2 select 'Z' as col0, 12 as col1 from dual where 1=0 union all
3 select 'X' as col0, 13 as col1 from dual where 1=0 )
4 select
5 nvl(max(col0), 'Y') as col0, nvl(max( col1), 10) as col1
6 from cte;
COL0 COL1
---------- ----------
Y 10
SQL>
... but this not so much:
SQL> with cte as (
2 select 'Z' as col0, 12 as col1 from dual union all
3 select 'X' as col0, 13 as col1 from dual )
4 select
5 nvl(max(col0), 'Y') as col0, nvl(max( col1), 10) as col1
6 from cte;
COL0 COL1
---------- ----------
Z 13
SQL>
May be something like this is what you need
You could change WHERE clause (in this case WHERE COL > 1) similarly in both places.
WITH T(COL) AS(
SELECT 1 FROM DUAL UNION ALL
SELECT 2 FROM DUAL UNION ALL
SELECT 3 FROM DUAL
)
SELECT COL FROM T WHERE COL > 1
UNION ALL
SELECT 10 AS COL FROM DUAL WHERE NOT EXISTS( SELECT 1 FROM T WHERE COL > 1)
You can use aggregation. An aggregation query always returns one row:
select coalesce(max(null), '10')
from dual
where 1 = 0;
I prefer coalesce() to nvl() because coalesce() is the ANSI standard function. But, nvl() would work here just as well.

Ora-00932 - expected NUMBER got -

I have been running the below query without issue:
with Nums (NN) as
(
select 0 as NN
from dual
union all
select NN+1 -- (1)
from Nums
where NN < 30
)
select null as errormsg, trunc(sysdate)-NN as the_date, count(id) as the_count
from Nums
left join
(
SELECT c1.id, trunc(c1.c_date) as c_date
FROM table1 c1
where c1.c_date > trunc(sysdate) - 30
UNION
SELECT c2.id, trunc(c2.c_date)
FROM table2 c2
where c2.c_date > trunc(sysdate) -30
) x1
on x1.c_date = trunc(sysdate)-Nums.NN
group by trunc(sysdate)-Nums.NN
However, when I try to pop this in a proc for SSRS use:
procedure pr_do_the_thing (RefCur out sys_refcursor)
is
oops varchar2(100);
begin
open RefCur for
-- see above query --
;
end pr_do_the_thing;
I get
Error(): PL/SQL: ORA-00932: inconsistent datatypes: expected NUMBER got -
Any thoughts? Like I said above, as a query, there is no issue. As a proc, the error appears at note (1) int eh query.
This seems to be bug 18139621 (see MOS Doc ID 2003626.1). There is a patch available, but if this is the only place you encounter this, it might be simpler to switch to a hierarchical query:
with Nums (NN) as
(
select level - 1
from dual
connect by level <= 31
)
...
You could also calculate the dates inside the CTE (which also fails with a recursive CTE):
with Dates (DD) as
(
select trunc(sysdate) - level + 1
from dual
connect by level <= 31
)
select null as errormsg, DD as the_date, count(id) as the_count
from Dates
left join
(
SELECT c1.id, trunc(c1.c_date) as c_date
FROM table1 c1
where c1.c_date > trunc(sysdate) - 30
UNION
SELECT c2.id, trunc(c2.c_date)
FROM table2 c2
where c2.c_date > trunc(sysdate) -30
) x1
on x1.c_date = DD
group by DD;
I'd probably organise it slightly differently, so the subquery doesn't limit the date range directly:
with dates (dd) as
(
select trunc(sysdate) - level + 1
from dual
connect by level <= 31
)
select errormsg, the_date, count(id) as the_count
from (
select null as errormsg, d.dd as the_date, c1.id
from dates d
left join table1 c1 on c1.c_date >= d.dd and c1.c_date < d.dd + 1
union all
select null as errormsg, d.dd as the_date, c2.id
from dates d
left join table2 c2 on c2.c_date >= d.dd and c2.c_date < d.dd + 1
)
group by errormsg, the_date;
but as always with these things, check the performance of each approach...
Also notice that I've switched from union to union all. If an ID could appear more than once on the same day, in the same table or across both tables, then the counts will be different - you need to decide whether you want to count them once or as many times as they appear. That applies to your original query too.

scalar subquery has an aggregate operation

My oracle version is 10.2.
It's very strange when a scalar subquery has an aggregate operation.
my table named t_test looked like this;
t_id t_name
1 1
2 1
3 2
4 2
5 3
6 3
query string looked like this;
select t1.t_id,
(select count(t_name)
from (select t2.t_name
from t_test t2
where t2.t_id=t1.t_id
group by t2.t_name)) a
from t_test t1
this query's result is,
t_id a
1 3
2 3
3 3
4 3
5 3
6 3
which is very weird,
take t1.t_id=1 for example,
select count(t_name)
from (select t2.t_name
from t_test t2
where t2.t_id=1
group by t2.t_name)
the result is 1,
somehow,the 'where' operator doesn't work,the result is exactly the same as I put my query like this:
select t1.t_id,
(select count(t_name)
from (select t2.t_name
from t_test t2
group by t2.t_name)) a
from t_test t1
why?
Can you post a cut-and-paste from SQL*Plus showing exactly what query you're running? The query you posted does not appear to be valid-- the alias t1 is not going to be valid in the subquery where you're referencing it. That makes me suspect that you're simplifying the problem to post here but you've accidentally left something important out.
SQL> ed
Wrote file afiedt.buf
1 with x as (
2 select 1 id, 1 name from dual union all
3 select 2,1 from dual union all
4 select 3,2 from dual union all
5 select 4,2 from dual union all
6 select 5,3 from dual union all
7 select 6,3 from dual
8 )
9 select t1.id
10 ,(select count(b.name)
11 from (select t2.name
12 from x t2
13 where t2.id = t1.id
14 group by t2.name) b) a
15* from x t1
SQL> /
where t2.id = t1.id
*
ERROR at line 13:
ORA-00904: "T1"."ID": invalid identifier
Presumably, it would be much more natural to write the query like this (assuming you really want to use a scalar subquery) where t1 is going to be a valid alias in the scalar subquery.
SQL> ed
Wrote file afiedt.buf
1 with x as (
2 select 1 id, 1 name from dual union all
3 select 2,1 from dual union all
4 select 3,2 from dual union all
5 select 4,2 from dual union all
6 select 5,3 from dual union all
7 select 6,3 from dual
8 )
9 select t1.id
10 ,(select count(t2.name)
11 from x t2
12 where t2.id = t1.id) cnt
13* from x t1
SQL> /
ID CNT
---------- ----------
1 1
2 1
3 1
4 1
5 1
6 1
6 rows selected.

Resources