Select statement inside NVL - oracle

I'm trying to run the following query:
select a.*,
case when NVL (SELECT max(b.field1)
FROM b
where b.field2 = a.tbl_a_PK , 'TRUE') = 'TRUE'
then 'has no data in b'
else 'has data in b' end as b_status
from a
I checked and the select inside the nvl returns only 1 value (so there shouldn't be a problem there).
However I'm getting 'ORA-00936: missing expression'

NVL() requires 2 parameters: expression to test and default value e.g. nvl(some_field, 111). You just need to isolate query parameter by braces and provide second parameter like in this statement:
select nvl( (select 1 from dual), 34) from dual
In your variant parser expects comma after SELECT keyword and can't parse remaining string.
Exactly your statement must look like this:
select
a.*,
case when NVL(
( SELECT max(b.field1)
FROM b
where b.field2 = a.tbl_a_PK
),
'TRUE'
) = 'TRUE'
then 'has no data in b'
else 'has data in b' end as b_status
from a
Hope this helps ...
Update
In terms of performance is better to use exists rather then max :
select
a.*,
case when exists
( SELECT null
FROM b
where b.field2 = a.tbl_a_PK
and
b.field2 is not null
and
rownum = 1
),
then 'has data in b'
else 'has no data in b' end as b_status
from a

If you're searching for records in a which have/don't have associated records in b
select a.*,
case when b.field2 is null then 'has no data in b'
else 'has data in b'
as b_status
from a left outer join b
on a.tbl_a_PK = b.field2;
Should do it

the NVL(string1, replace_with) function requires 2 parameters, see docs here:
http://www.techonthenet.com/oracle/functions/nvl.php
Ora 10g docs: http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions105.htm
Since you know the problem, this query can fix it:
select a.*,
case
when (SELECT NVL(b.field2, 0) FROM b where b.field2 = a.tbl_a_PK and rownum = 1) > 0 then
'has data in b'
else
'has no data in b'
end b_status
from a
and runs faster.
You don't need max() to check if the value exists in another table, simply check if the primary key is not null.

Related

Adding filters in subquery from CTE quadruples run time

I am working on an existing query for SSRS report that focuses on aggregated financial aid data split out into 10 aggregations. User wants to be able to select students included in that aggregated data based on new vs. returning and 'selected for verification.' For the new/returning status, I added a CTE to return the earliest admit date for a student. 2 of the 10 data fields are created by a subquery. I have been trying for 3 days to get the subquery to use the CTE fields for a filter, but they won't work. Either they're ignored or I get a 'not a group by expression' error. If I put the join to the CTE within the subquery, the query time jumps from 45 second to 400 seconds. This shouldn't be that complicated! What am I missing? I have added some of the code... 3 of the chunks work - paid_something doesn't.
with stuStatus as
(select
person_uid, min(year_admitted) admit_year
from academic_study
where aid_year between :AidYearStartParameter and :AidYearEndParameter
group by person_uid)
--- above code added to get student information not originally in qry
select
finaid_applicant_status.aid_year
, count(1) as fafsa_cnt --works
, sum( --works
case
when (
package_complete_date is not null
and admit.status is not null
)
then 1
else 0
end
) as admit_and_package
, (select count(*) --does't work
from (
select distinct award_by_aid_year.person_uid
from
award_by_aid_year
where
award_by_aid_year.aid_year = finaid_applicant_status.aid_year
and award_by_aid_year.total_paid_amount > 0 )dta
where
(
(:StudentStatusParameter = 'N' and stuStatus.admit_year = finaid_applicant_status.aid_year)
OR
(:StudentStatusParameter = 'R' and stuStatus.admit_year <> finaid_applicant_status.aid_year)
OR :StudentStatusParameter = '%'
)
)
as paid_something
, sum( --works
case
when exists (
select
1
from
award_by_person abp
where
abp.person_uid = fafsa.person_uid
and abp.aid_year = fafsa.aid_year
and abp.award_paid_amount > 0
) and fafsa.requirement is not null
then 1
else 0
end
) as paid_something_fafsa
from
finaid_applicant_status
join finaid_tracking_requirement fafsa
on finaid_applicant_status.person_uid = fafsa.person_uid
and finaid_applicant_status.aid_year = fafsa.aid_year
and fafsa.requirement = 'FAFSA'
left join finaid_tracking_requirement admit
on finaid_applicant_status.person_uid = admit.person_uid
and finaid_applicant_status.aid_year = admit.aid_year
and admit.requirement = 'ADMIT'
and admit.status in ('M', 'P')
left outer join stuStatus
on finaid_applicant_status.person_uid = stuStatus.person_uid
where
finaid_applicant_status.aid_year between :AidYearStartParameter and :AidYearEndParameter
and (
(:VerifiedParameter = '%') OR
(:VerifiedParameter <> '%' AND finaid_applicant_status.verification_required_ind = :VerifiedParameter)
)
and
(
(:StudentStatusParameter = 'N' and (stuStatus.admit_year IS NULL OR stuStatus.admit_year = finaid_applicant_status.aid_year ))
OR
(:StudentStatusParameter = 'R' and stuStatus.admit_year <> finaid_applicant_status.aid_year)
OR :StudentStatusParameter = '%'
)
group by
finaid_applicant_status.aid_year
order by
finaid_applicant_status.aid_year
Not sure if this helps, but you have something like this:
select aid_year, count(1) c1,
(select count(1)
from (select distinct person_uid
from award_by_aid_year a
where a.aid_year = fas.aid_year))
from finaid_applicant_status fas
group by aid_year;
This query throws ORA-00904 FAS.AID_YEAR invalid identifier. It is because fas.aid_year is nested too deep in subquery.
If you are able to modify your subquery from select count(1) from (select distinct sth from ... where year = fas.year) to select count(distinct sth) from ... where year = fas.year then it has the chance to work.
select aid_year, count(1) c1,
(select count(distinct person_uid)
from award_by_aid_year a
where a.aid_year = fas.aid_year) c2
from finaid_applicant_status fas
group by aid_year
Here is simplified demo showing non-working and working queries. Of course your query is much more complicated, but this is something what you could check.
Also maybe you can use dbfiddle or sqlfiddle to set up some test case? Or show us sample (anonimized) data and required output for them?

Merge 3 queries into a single sub query

Is it possible to write a single query to fetch data from the 3 tables?
Below are the 3 queries that I am running to fetch data, but would need a single query.
I have tried to write the below query but the data I amgetting is wrong
(SELECT M_NB,M_GF_IND,M_HEDGE_LNK FROM TABLE#DATA#DEALCOM_DBF WHERE M_GF_IND = 'Y' ) A
(SELECT M_NB,M_GF_IND,M_HEDGE_LNK FROM TABLE#DATA#DEALIRD_DBF WHERE M_GF_IND = 'Y' ) B
(SELECT M_NB,M_GF_IND,M_HEDGE_LNK FROM TABLE#DATA#DEALSCF_DBF WHERE M_GF_IND = 'Y' ) C
I have tried to write the below query but the data I am getting is wrong
SELECT DD.M_NB, DD.M_GF_IND, DD.M_HEDGE_LNK,
FF.M_NB, FF.M_GF_IND, FF.M_HEDGE_LNK
FROM TABLE#DATA#DEALCOM_DBF DD,
TABLE#DATA#DEALIRD_DBF FF
WHERE DD.M_GF_IND = 'Y' and FF.M_GF_IND = 'Y'
Sorry if this is a bad question, I am new to SQL and thus cannot figure out. Will update if I find any alternative.
Are you looking for UNION (ALL)?
SELECT M_NB,M_GF_IND,M_HEDGE_LNK FROM TABLE#DATA#DEALCOM_DBF WHERE M_GF_IND = 'Y'
UNION ALL
SELECT M_NB,M_GF_IND,M_HEDGE_LNK FROM TABLE#DATA#DEALIRD_DBF WHERE M_GF_IND = 'Y'
UNION ALL
SELECT M_NB,M_GF_IND,M_HEDGE_LNK FROM TABLE#DATA#DEALSCF_DBF WHERE M_GF_IND = 'Y';

Oracle Case in Where clause having omitted the Else expression, results?

What does the following statement equate to when table2.level = 3 for some rows?
SELECT *
FROM table1, table2
WHERE table1.value1 =
CASE table2.id
WHEN 1 THEN table2.value1(+)
WHEN 2 THEN table2.value2(+)
END
Oracle DOCS state that "CASE returns NULL" if none of the cases evaluate to TRUE and the ELSE expression is omitted, but substituting the CASE expression by NULL yields 0 results instead.
The result, by the way, is that the query results omit the optional table2 results for rows in which table2.level = 3 as if the entire equation after AND results in TRUE (for those particular rows).
Can someone elaborate on how/why this works?
Edit: Replacing the (+) operator with the LEFT OUTER JOIN syntax helped me gain insight in what it means when the CASE expression returns NULL.
The following statements are equal if table2.id IN (1,2,3)
SELECT *
FROM table1
LEFT OUTER JOIN table2
ON table1.value1 =
CASE table2.id
WHEN 1 THEN table2.value1
WHEN 2 THEN table2.value2
END
SELECT *
FROM table1
LEFT OUTER JOIN table2
ON table1.value1 =
CASE table2.id
WHEN 1 THEN table2.value1
WHEN 2 THEN table2.value2
WHEN 3 THEN NULL
END
SELECT *
FROM table1
LEFT OUTER JOIN table2
ON table1.value1 =
CASE table2.id
WHEN 1 THEN table2.value1
WHEN 2 THEN table2.value2
ELSE NULL
END
I think that answers the question;
it does equate to NULL but only for the rows not in the CASE expression, still outer joining the table2 results when table2.id = 1 OR table2.id = 2

Select data depend with IF ELSE condition

I have a query as below :
SELECT R.*
FROM
(
WITH TABLE_X AS
(
SELECT A,B,C FROM Y
)
--PSEUDO CODE
IF (COUNT(TABLE_X.*) > 0)
THEN SELECT CONCAT(A ,B, C) FROM TABLE_X
ELSE 'No Data'
END
) R
In this case, If TABLE_X have data, the selection will return A,B,C. In others way, this will return something else like 'No data'.
Please help me clarify and suggest some soluiton on it.
Thank you for your attention.
If there is at most one row in TABLE_X, then you can use aggregation:
WITH TABLE_X AS (
SELECT A, B, C FROM Y
)
SELECT (CASE WHEN COUNT(*) = 0 THEN 'No Data'
ELSE MAX(A || B || C)
END) as col
FROM TABLE_X;
Alternatively, use UNION ALL:
WITH TABLE_X AS (
SELECT A, B, C FROM Y
)
SELECT A || B || C as col
FROM TABLE_X
UNION ALL
SELECT 'No Data'
FROM Dual
WHERE NOT EXISTS (SELECT 1 FROM TABLE_X);
Looking too you query i think you simply need
select nvl( A||B,||C , 'No data')
from y

Why does CASE evalulate the ELSE clause?

Oracle 11g: Consider the following two CASE Statements
SEARCHED CASE
with null_table as (select null as null_set from dual )
select
case
when (null_set is null) then 'NULL INDEED'
else 'NOTNULL?'
end as AM_I_NULL_OR_NOT
from null_table
SIMPLE CASE
with null_table as (select null as null_set from dual )
select
case null_set
when (null) then 'NULL INDEED'
else 'NOTNULL?'
end as AM_I_NULL_OR_NOT
from null_table
The Searched CASE evaluates null_set as expected, yet, Simple CASE appears not to do so.
Questions:
How do I perform a Simple CASE evaluation on null_set ?
Why does the Simple CASE evaluate as it does?
In Oracle, null cannot be compared against using the = operator.
For example:
select * from dual where null = null; -- No result
select * from dual where 1 = 1; -- gives a result
So what you can do is use NVL to replace null by another value :
with null_table as (select null as null_set from dual )
select
case nvl(null_set,'X')
when 'X' then 'NULL INDEED'
else 'NOTNULL?'
end as AM_I_NULL_OR_NOT
from null_table
(make sure your field can never be equal to the value, X in the example.)

Resources