CASE WHEN to choose which list of values to bring back ORACLE SQL - sql

I am trying to use a case when to enforce some data access security that's not standard.
The basics of what I am trying to do keep causing the "more than one row" error.
I want to use 2 different subqueries with the result of the case when determining which list will be returned.
Example logic
SELECT CASE
WHEN condition 1 is true THEN (SUBQUERY 1)
WHEN condition 2 is true THEN (SUBQUERY 2)
END AS ACCESS
FROM DUAL
Both subqueries on there own work fine and fetch back correct values, put them into the brackets above and it fails.
SUBQUERY 1
SELECT DISTINCT FND.PK1_START_VALUE
FROM FND_GRANTS FG
JOIN FND_OBJECTS_VL FO
ON FO.OBJECT_ID = FG.OBJECT_ID
JOIN FND_FORM_FUNCTIONS_VL FFF
ON FO.OBJECT_ID = FFF.OBJECT_ID
JOIN FND_MENU_ENTRIES FME
ON FME.MENU_ID = FG.MENU_ID AND FFF.FUNCTION_ID = FME.FUNCTION_ID
LEFT JOIN FND_OBJECT_INSTANCE_SETS_VL FOIS
ON FG.INSTANCE_SET_ID = FOIS.INSTANCE_SET_ID
JOIN FND_SESSION_ROLES FSR
ON FG.ROLE_NAME = FSR.ROLE_NAME
JOIN FND_SESSIONS FS
ON FS.SESSION_ID = FSR.SESSION_ID
LEFT JOIN FND_FLEX_VALUES_VL FLV
ON FLV.DESCRIPTION = SUBSTR(FG.NAME, 1, CASE WHEN INSTR(FG.NAME,'Area')< 1 THEN INSTR(FG.NAME,'Cost')-2)
ELSE (INSTR(FG.NAME,'Area')-2)
END)
JOIN FND_TREE_NODE FND
ON FLV.FLEX_VALUE = FND.PARENT_PK1_VALUE
WHERE FG.ROLE_NAME IN (:P_ROLE)----:P_ROLE is a list of VALUES fetching back the users system access.
AND FND.TREE_STRUCTURE_CODE = 'GL_ACCT_FLEX'
AND FND.TREE_CODE ='Cost Centre-T'
AND FND.DEPTH ='4'
ORDER BY FND.PK1_START_VALUE ASC)
SUBQUERY 2----idea here is the user has none of the roles in P_ROLE and therefore has all access to all sections
SELECT DISTINCT FND.PK1_START_VALUE
FROM FND_TREE_NODE FND
WHERE FND.TREE_STRUCTURE_CODE IN('GL_ACCT_FLEX')
AND FND.TREE_CODE IN('Cost Centre-T')
AND FND.DEPTH IN('4')
NOTE some sensitive data has been redacted from the query and replaced

One (simple) option is to UNION them:
select columns
from subquery_1
where condition = 1
union all
select columns
from subquery_2
where condition = 2

Hmmm . . . I think you want union all:
SELECT q1.*
FROM (SUBQUERY 1) q1
WHERE condition1
UNION ALL
SELECT q2.*
FROM (SUBQUERY 2) q1
WHERE (NOT condition 1) AND -- may not be necessary
(condition 2) is true ;
Note: This assumes that the column(s) returned by each subquery are compatible -- in both number and types. The name in the result set is taken from the first subquery.

You can do something like the below using CASE
WITH sub1
AS (SELECT ROWNUM rw,
data
FROM sub_query1),
sub2
AS (SELECT ROWNUM rw,
data
FROM sub_query2)
SELECT CASE
WHEN condition1 = 1 THEN a1.data
WHEN condition1 = 2 THEN b1.data
ELSE NULL
END case1
FROM sub1 a1
full outer join sub2 b1
ON a1.rw = b1.rw

Related

Oracle SQL XOR condition with > 14 tables

I have a question on sql desgin.
Context:
I have a table called t_master and 13 other tables (lets call them a,b,c... for simplicity) where it needs to compared.
Logic:
t_master will be compared to table 'a' where t_master.gen_val =
a.value.
If record exist in t_master, retrieve t_master record, else retrieve 'a' record.
I do not need to retrieve the records if it exists in both tables (t_master and a) - XOR condition
Repeat this comparison with the remaining 12 tables.
I have some idea on doing this, using WITH to subquery the non-master tables (a,b,c...) first with their respective WHERE clause.
Then use XOR statement to retrieve the records.
Something like
WITH a AS (SELECT ...),
b AS (SELECT ...)
SELECT field1,field2...
FROM t_master FULL OUTER JOIN a FULL OUTER JOIN b FULL OUTER JOIN c...
ON t_master.gen_value = a.value
WHERE ((field1 = x OR field2 = y ) AND NOT (field1 = x AND field2 = y))
AND ....
.
.
.
.
Seeing that I have 13 tables that I need to full outer join, is there a better way/design to handle this?
Otherwise I would have at least 2*13 lines of WHERE clause which I'm not sure if that will have impact on the performance as t_master is sort of a log table.
**Assume I cant change any schema.
Currently I'm not sure if this SQL will working correctly yet, so I'm hoping someone can guide me in the right direction regarding this.
update from used_by_already's suggestion:
This is what I'm trying to do (comparison between 2 tables first, before I add more, but I am unable to get values from ATP_R.TBL_HI_HDR HI_HDR as it is in the NOT EXISTS subquery.
How do i overcome this?
SELECT LOG_REPO.UNIQ_ID,
LOG_REPO.REQUEST_PAYLOAD,
LOG_REPO.GEN_VAL,
LOG_REPO.CREATED_BY,
TO_CHAR(LOG_REPO.CREATED_DT,'DD/MM/YYYY') AS CREATED_DT,
HI_HDR.HI_NO R_VALUE,
HI_HDR.CREATED_BY R_CREATED_BY,
TO_CHAR(HI_HDR.CREATED_DT,'DD/MM/YYYY') AS R_CREATED_DT
FROM ATP_COMMON.VW_CMN_LOG_GEN_REPO LOG_REPO JOIN ATP_R.TBL_HI_HDR HI_HDR ON LOG_REPO.GEN_VAL = HI_HDR.HI_NO
WHERE NOT EXISTS
(SELECT NULL
FROM ATP_R.TBL_HI_HDR HI_HDR
WHERE LOG_REPO.GEN_VAL = HI_HDR.HI_NO
)
UNION ALL
SELECT LOG_REPO.UNIQ_ID,
LOG_REPO.REQUEST_PAYLOAD,
LOG_REPO.GEN_VAL,
LOG_REPO.CREATED_BY,
TO_CHAR(LOG_REPO.CREATED_DT,'DD/MM/YYYY') AS CREATED_DT,
HI_HDR.HI_NO R_VALUE,
HI_HDR.CREATED_BY R_CREATED_BY,
TO_CHAR(HI_HDR.CREATED_DT,'DD/MM/YYYY') AS R_CREATED_DT
FROM ATP_R.TBL_HI_HDR HI_HDR JOIN ATP_COMMON.VW_CMN_LOG_GEN_REPO LOG_REPO ON HI_HDR.HI_NO = LOG_REPO.GEN_VAL
WHERE NOT EXISTS
(SELECT NULL
FROM ATP_COMMON.VW_CMN_LOG_GEN_REPO LOG_REPO
WHERE HI_HDR.HI_NO = LOG_REPO.GEN_VAL
)
Full outer joins used to exclude all matching rows can be an expensive query. You don't supply much detail, but perhaps using NOT EXISTS would be simpler and maybe it will produce a better explain plan. Something along these lines.
select
cola,colb,colc
from t_master m
where not exists (
select null from a where m.keycol = a.fk_to_m
)
and not exists (
select null from b where m.keycol = b.fk_to_m
)
and not exists (
select null from c where m.keycol = c.fk_to_m
)
union all
select
cola,colb,colc from a
where not exists (
select null from t_master m where a.fk_to_m = m.keycol
)
union all
select
cola,colb,colc from b
where not exists (
select null from t_master m where b.fk_to_m = m.keycol
)
union all
select
cola,colb,colc from c
where not exists (
select null from t_master m where c.fk_to_m = m.keycol
)
You could union the 13 a,b,c ... tables to simplify the coding, but that may not perform so well.

SQL Subquery Having COUNT(var) turns 0 to NULLs

I have written a SQL query with a subquery to include counts. When the count is 0, and I try to filter out the 0, it turns the 0's to NULLs and keeps the rows, and vice versa. The result is that I can't filter out the 0's, which was the purpose of including the counts.
SELECT distinct
a
,b
,
(SELECT
count(id)
FROM seq_stud
WHERE scs.SequenceID = seq_stud.SequenceID
and seq_stud.EndDate is null
HAVING count(id) <> 0
) As t1
FROM sp
INNER JOIN p on sp.ProgramID = p.ProgramID
...etc.
Does anyone know why this is happening and how I can filter out the 0 counts?
You don't filter in the SELECT clause. If you don't want rows that have no match in seq_stud, then use WHERE:
WHERE EXISTS (SELECT 1
FROM seq_stud ss
WHERE scs.SequenceID = ss.SequenceID and ss.EndDate is null
)
I would remove the HAVING statement altogether. You need to put that in the WHERE clause. Otherwise, it will return null, as you found.
SELECT distinct a, b,
(SELECT count(id)
FROM seq_stud
WHERE scs.SequenceID = seq_stud.SequenceID
and seq_stud.EndDate is null
) As t1
FROM sp
INNER JOIN p on sp.ProgramID = p.ProgramID
WHERE t1 > 0
I just figured this out. The Select subquery should be included as a WHERE statement
Using having count() in exists clause

SQL Join / Union

I have two statements that I want to merge into one output.
Statement One:
select name from auxiliary_variable_inquiry
where inquiry_idbr_code = '063'
Returns the following list of names:
Name
------------
Affiliates
NetBookValue
Parents
Worldbase
Statement Two:
select name, value from auxiliary_variable_value
where inquiry_idbr_code = '063'
and ru_ref = 20120000008
and period = 200912
Returns the following:
Name Value
-------------------
Affiliates 112
NetBookValue 225.700
I would like to have an output like this:
Name Value
-------------------
Affiliates 112
NetBookValue 225.700
Parents 0
Worldbase 0
So basically, if the second query only returns 2 names and values, I'd still like to display the complete set of names from the first query, with no values. If all four values were returned by both queries, then all four would be displayed.
Sorry I must add, im using Ingres SQL so im unable to use the ISNULL function.
You can do a left join. This ensures that all records from the first table will stay included. Where value is null, no child record was found, and we use coalesce to display 0 in these cases.
select i.name, COALESCE(v.Value,0) from auxiliary_variable_inquiry i
left join auxiliary_variable_value v
on v.inquiry_idbr_code = i.inquiry_idbr_code
and v.ru_ref = 20120000008
and v.period = 200912
where i.inquiry_idbr_code = '063'
I'd recommend a self-JOIN using the LEFT OUTER JOIN syntax. Include your 'extra' conditions from the second query in the JOIN condition, while the first conditions stay in the WHERE, like this:
select a.name, CASE WHEN b.Value IS NULL THEN 0 ELSE b.Value END AS Value
from
auxiliary_variable_inquiry a
LEFT JOIN
auxiliary_variable_inquiry b ON
a.name = b.name and -- replace this with your real ID-based JOIN
a.inquiry_idbr_code = b.inquiry_idbr_code AND
b.ru_ref = 20120000008 AND
b.period = 200912
where a.inquiry_idbr_code = '063'
if i got right, you should use something like:
SELECT i.NAME,
v.NAME,
v.value
FROM auxiliary_variable_inquiry i
LEFT JOIN auxiliary_variable_value v
ON i.inquiry_idbr_code = v.inquiry_idbr_code
WHERE v.ru_ref = 20120000008
AND v.period = 200912

Link tables based on column value

Is it possible to pull values from 2 different tables based on the value of a column? For example, I have a table with a boolean column that either returns 0 or 1 depending on what the end user selects in our program. 0 means that I should pull in the default values. 1 means to use the user's data.
If my table Table1 looked like this:
Case ID Boolean
====================
1 0
2 1
3 1
4 0
5 0
Then I would need to pull Case IDs 1,4,and 5's corresponding data from table Default and Case IDs 3 and 4's corresponding data from table UserDef. Then I would have to take these values, combine them, and reorder them by Case ID so I can preserve the order in the resulting table.
I am fairly inexperienced with SQL but I am trying to learn. Any help or suggestions are greatly appreciated. Thank you in advance for your help.
Something like this:
SELECT
t1.CaseID
,CASE WHEN t1.Boolean = 1 THEN dt.Col1 ELSE ut.Col1 END AS Col1
,CASE WHEN t1.Boolean = 1 THEN dt.Col2 ELSE ut.Col2 END AS Col2
FROM Table1 t1
LEFT JOIN DefaultTable dt ON dt.CaseID = t1.CaseID
LEFT JOIN UserDefTable ut ON ut.CaseID = t1.CaseID
ORDER BY t1.CaseID
You join on both tables and then use CASE in SELECT to choose from which one to display data.
Option B:
WITH CTE_Combo AS
(
SELECT 0 as Boolean, * FROM Default --replace * with needed columns
UNION ALL
SELECT 1 AS Boolean, * FROM UserDef --replace * with needed columns
)
SELECT * FROM Table1 t
LEFT JOIN CTE_Combo c ON t.CaseID = c.CaseID AND t.Boolean = c.Boolean
ORDER BY t.CaseID
This might be even simpler - using CTE make a union of both tables adding artificial column, and then join CTE and your Table using both ID and flag column.
SELECT t1.CaseID,
ISNULL(td.data, tu.data) userData -- pick data from table_default
-- if not null else from table_user
FROM table1 t1
LEFT JOIN table_default td ON t1.CaseID = td.CaseID -- left join with table_default
AND t1.Boolean = 0 -- when boolean = 0
LEFT JOIN table_user tu ON t1.CaseID = tu.CaseID -- left join with table_user
AND t1.Boolean = 1 -- when boolean = 1
ORDER BY t1.CaseID

Combine two different select statements, one distinct the other not

I have two selects which are required to filter data. They are not complicated:
"SELECT * FROM StevesTable t WHERE "
"t.data1 = '%s' AND "
"t.data2 = to_date('%s','DD/MM/YYYY');",
strdata1,
dtDate.Format();
and
SELECT distinct data1 FROM anothertable ftt
join table1 tab on tab.somedata = ftt.somedata
where tab.somedata = 0
and tab.someotherdata = 1
I would like to combine these two as I need to filter the returned dataset from the first select statement by the returned field in the second (ie if a record returned in the first set does not have a data1 value which is contained in the second returned set it is invalid).
I tried to union and intersect the selects but you need the same number of columns returned and that cannot happen as these are completely different tables. When I tried to simply merge them together I found it difficult as the second select statement is a distinct select whereas the first is not.
I was wondering whether I had missed a trick somewhere for combining these sorts of selects?
What you need is a SQL sub-query:
SELECT * FROM StevesTable t
WHERE t.data1 = '%s'
AND t.data2 = to_date('%s','DD/MM/YYYY')
AND t.data1 in (select distinct data1 FROM anothertable ftt
join table1 tab on tab.somedata = ftt.somedata
where tab.somedata = 0
and tab.someotherdata = 1)
There, you check that all records in the first select have a data1 value in the second set.
You can do this using an EXISTS condition:
SELECT * FROM StevesTable t
WHERE t.data1 = '%s' AND
t.data2 = to_date('%s','DD/MM/YYYY') AND
EXISTS (select null
from anothertable ftt
join table1 tab on tab.somedata = ftt.somedata
where tab.somedata = 0 and
tab.someotherdata = 1 and
ftt.data1 = t.data1)