How to avoid repetition of conditions in EXISTS clause in a UNION - sql

I have this SQL in DB2 and want to avoid repeating the conditions in the EXISTS clause in the second UNION, as the conditions can be fairly large. How do I do that? Any help is greatly appreciated.
select id from table t where t.given_name = 'good' and t.time = 1 and exists
(select 1 from table t1 where t1.id = t.id and t1.surname = 'OK') union
select id from table t where t.given_name = 'good' and t.time = 2 and not exists
(select 1 from table t1 where t1.id = t.id and t1.surname = 'OK')

I think this could be also achieve via where clause only
where given_name = 'good' and
(times = 1 and surname = 'OK') or
(times = 2 and surname <> 'OK')

Why are you using union? How about just doing this?
select id
from table t
where t.given_name = 'good' and
t.time in (1, 2) and
exists (select 1 from table t1 where t1.id = t.id and t1.surname = 'OK');
If id could have duplicates, use select distinct in the outer query.'
EDIT:
I think I misread the original query. The logic would be:
select id
from table t
where t.given_name = 'good' and
( (t.time = 1 and exists (select 1 from table t1 where t1.id = t.id and t1.surname = 'OK')
) or
(t.time = 2 and not exists (select 1 from table t1 where t1.id = t.id and t1.surname = 'OK')
)
)

Use a WITH clause to remove redundancy
with t2 as (select * from t1 where surname = 'OK')
select id from table t where t.given_name = 'good' and t.time = 1 and exists
(select 1 from table t2 where t2.id = t.id) union
select id from table t where t.given_name = 'good' and t.time = 2 and not exists
(select 1 from table t2 where t2.id = t.id)
;
and you can do the same for the other table too if needed
with t2 as (select * from t1 where surname = 'OK')
, tt as (select * from t where given_name = 'good')
select id from table tt where tt.time = 1 and exists
(select 1 from table t2 where t2.id = tt.id) union
select id from table tt where tt.time = 2 and not exists
(select 1 from table t2 where t2.id = tt.id)
;

Related

Select another column of matching record - Oracle SQL

I have this query:
SELECT
t1.*,
(
SELECT
MIN(t2.e_nm)
FROM
table2 t2
WHERE
t2.c_type = t1.c_type
AND t2.h_level = t1.h_level
AND t2.loop = t1.loop
AND t2.e_id = t1.e_id
HAVING
COUNT(*) = 1
) AS e_nm
FROM
table1 t1
ORDER BY
t1.f_name,
t1.line_num;
When e_nm gets selected from table2 as second parameter, I also want to grab another column of matching record - seq_nm from table1.
How can I do it in the above query?
If the count(*) = 1, then you can use a join with aggregation
SELECT t1.*, t2.e_nm, t2.x
FROM table1 t1 LEFT JOIN
(SELECT t2.c_type, t2.h_level, t2.loop, t2.e_id,
MIN(t2.e_nm) as e_nm, MIN(x) as x
FROM table2 t2
GROUP BY t2.c_type, t2.h_level, t2.loop, t2.e_id
HAVING COUNT(*) = 1
) t2
ON t2.c_type = t1.c_type AND
t2.h_level = t1.h_level AND
t2.loop = t1.loop AND
t2.e_id = t1.e_id
ORDER BY t1.f_name, t1.line_num;
This works because you have the COUNT(*) = 1, so only one row matches. If you didn't, you could still use KEEP:
MIN(x) KEEP (DENSE_RANK FIRST ORDER B t2.e_num ASC) as x

Execute different statements in one query on the condition

Say we have two Users with ID = 1 and ID = 2
I know the ID of the current User and I need to execute different select statements depending on the ID.
if ID = 1
select a from table1
else if ID = 2
select b from table2
else
select c from table3
Is there a way to put this logic into single SQL query?
You can use union with appropriate where conditions.
select a from table1 where id = 1
union all
select b from table2 where id = 2
union all
select c from table3 where id not in (1,2)
or if the tables can be joined
select
case when t1.id = 1 then t1.a
when t2.id = 2 then t2.b
else t3.c end
from table1 t1
join table2 t2 on t1.id = t2.id
join table3 t3 on t1.id = t3.id

filter based on variable, if no value in variable apply NO filters

Based on a filter I would like to apply that to what to filter.
For example if I have a variable called p_filter that could have the values 'A' or 'B' I would like to filter specific where clauses based on that.
SELECT *
FROM t1
WHERE
id NOT IN (select id from t2 WHERE t1.id = t2.id(+) ) --only apply this filter if p_filter = 'A'
AND id NOT IN (select id from t3 WHERE t1.id = t3.id(+) ) --only apply this filter if p_filter = 'B'
If the variable though is null, I would like for it to apply NO filters. I tried something like this but it does not work:
SELECT *
FROM t1
WHERE
CASE WHEN :p_filter != 'A' THEN 1 WHEN id NOT IN (select id from t2 WHERE t1.id = t2.id(+) ) THEN 1 ELSE 0 END = 1
AND CASE WHEN :p_filter != 'B' THEN 1 WHEN id NOT IN (select id from t3 WHERE t1.id = t3.id(+) ) THEN 1 ELSE 0 END = 1;
UPDATE: I am so stupid, I meant if null apply no filters!
Just do:
WHERE
--only apply this filter if p_filter = 'A'
p_filter = 'A'
AND
id NOT IN (select id from t2 WHERE t1.id = t2.id(+) )
OR
--only apply this filter if p_filter = 'B'
p_filter = 'B'
AND
id NOT IN (select id from t3 WHERE t1.id = t3.id(+) )
OR
-- If the variable though is null, I would like for it to apply both filters
p_filter IS NULL
AND
id NOT IN (select id from t2 WHERE t1.id = t2.id(+) )
AND
id NOT IN (select id from t3 WHERE t1.id = t3.id(+) )
---- EDIT ---------
I am so sorry, I am stupid, I meant to say if null apply NO filters! –
user2924127
In that case just remove the last condition, and add OR p_filter IS NULL:
WHERE
--only apply this filter if p_filter = 'A'
p_filter = 'A'
AND
id NOT IN (select id from t2 WHERE t1.id = t2.id(+) )
OR
--only apply this filter if p_filter = 'B'
p_filter = 'B'
AND
id NOT IN (select id from t3 WHERE t1.id = t3.id(+) )
-- I am so sorry, I am stupid, I meant to say if null apply NO filters!
OR
p_filter IS NULL
To do this "OR" the negative of your condition like so:
SELECT *
FROM t1
WHERE
(:p_filter IS NULL OR :p_filter!='A' OR id NOT IN (select id from t2 WHERE t1.id = t2.id(+) ))
AND (:p_filter IS NULL OR :p_filter!='B' OR id NOT IN (select id from t3 WHERE t1.id = t3.id(+) ))
SELECT *
FROM t1
WHERE (
(p_filter = 'A' and id NOT IN (select id from t2 WHERE t1.id = t2.id(+) ))
or
(p_filter = 'B' and id NOT IN (select id from t3 WHERE t1.id = t3.id(+) ))
or
(p_filter is null)
)

CASE statement in update

I have a scenario here where i am running 2 separate update queries.
How can I combine those into a single query by use of case?
UPDATE TABLE1 SET ACTV_IND = 0
WHERE NAME IN (
select NAME
from TABLE1
where SID = 'child'
group by NAME
having MAX(CAST(ACTV_IND AS INT)) =0
)
AND SID = 'parent'
UPDATE TABLE1 SET ACTV_IND = 1
WHERE NAME IN (
select NAME
from TABLE1
where SID = 'child'
group by NAME
having MAX(CAST(ACTV_IND AS INT)) =1
)
AND SID = 'parent'
Q1: I think this might be the solution
ACTV_IND = MAX(CAST(ACTV_IND AS INT))
Q2: It is possible to use join for update
UPDATE T1 SET T1.C1 = :val1 FROM TABLE1 T1 join TABLE T2 ON T1.KEY1 = T2.KEY2
I would try something this like this, assuming that MAX(CAST(ACTV_IND AS INT)) will return 0 or 1
WITH DATA_SOURCE (NAME, VAL) AS (
select NAME, MAX(CAST(ACTV_IND AS INT)) VAL from TABLE1 where SID = 'child' group by NAME
)
UPDATE TABLE1
SET ACTV_IND = DS.VAL
FROM TABLE1 T1 JOIN DATA_SOURCE DS ON T1.NAME = DS.NAME
WHERE T1.SID = 'parent'
GO

Select distinct rows that contain a given set of data

I have a following table:
bid | data
1 | a
1 | b
1 | c
2 | a
3 | c
3 | a
I want to select all bids that contain given set of data.
For example, all bids that 'contains' data "a" and "b" (result should be bid 1), or ones that contain "a" and "c" (1 and 3).
Only solution I could think of is kind of nasty, so I would appreciate some help/suggestions.
My first try:
select bid from my_table as t1 where
exists (select * from my_table t2 where
t2.bid = t1.bid and
t2.data='a'
)
and
exists (select * from my_table t2 where
t2.bid = t1.bid and
t2.data='b'
)
group by bid;
Thanks.
select t1.bid
from table_1 t1
inner join table_1 t2 on t1.bid = t2.bid
where t1.data = 'a' and t2.data = 'c'
By the way:
all bids that 'contains' data "a" and "b" (result should be bid 1)
--> bid 2 also contains data 'a' and 'b'
While I would not recommend this solution for only two variable lookups it's rate of growth for query cost when matching on more variables increases very slowly as opposed to doing an inner join for each match. As a disclaimer I realize that if pipe is a valid field or there are xml encoded charcters that this break.
select e.bid
from myTable e
cross apply ( select '|'+ i.data + '|'
from myTable i
where e.bid = i.bid
for xml path('')) T(v)
where v like '%|A|%' and v like '%|B|%' --and v like '%|C|%'.....
group by e.bid
as a side not about other options your answer could be simplified into
select bid from my_table as t1 where
exists (select * from my_table t2 where
t2.bid = t1.bid and
t2.data='a'
)
and t1.data = 'c'
group by bid;
This is roughly an equivalent of christian's answer. The optimizer will most likely treat these the same.
select distinct t1.bid
from table_1 t1
inner join table_1 t2 on t1.bid = t2.bid
where t1.data = 'a' and t2.data = 'c'
With a subquery, count the number of right occurences you have in your table.
SELECT DISTINCT m.bid
FROM myTable m
WHERE (
SELECT COUNT(1)
FROM myTable m2
WHERE (m2.data = 'a'
OR m2.data = 'b')
AND m.bid = m2.bid
) = 2
Maybe not the best answer but:
select bid from mytable where data = 'a'
intersect
select bid from mytable where data = 'c'
Uses exists:
declare #t table(bid int, data char)
insert #t values(1,'a'),(1,'b'),(1,'c'),(2,'b'),(2,'a'),(3,'c'),(3,'a')
select distinct t1.bid
from #t t1
where exists(
select 1
from #t t2
where t2.bid = t1.bid and t2.data = 'a'
)
and exists(
select 1
from #t t2
where t2.bid = t1.bid and t2.data = 'b'
)
XML PATH and XQuery version:
select distinct t.bid
from
(
select *
, (
select *
from #t t2
where t2.bid = t1.bid
for xml path, root('root'), type
) [x]
from #t t1
) t
where t.x.exist('root[*/data[text() = "a"] and */data[. = "b"]]') = 1