How to use case when in sql query - sql

query="SELECT a.id, b.id
FROM tab_a a, tab_b b
WHERE a.ref = b.ref
AND a.amount = -b.amount
AND NOT a.tot AND NOT b.tot
AND a.a_id = %(a_id)s AND b.a_id = %(a_id)s
{p_id_condition}
ORDER BY a.date desc"
i am trying
first try to match the ref, but if no pair found, try to match the amount

Would something like this work:
SELECT
a.id,
b.id
FROM tab_a a
INNER JOIN tab_b b
ON a.ref = b.ref OR
a.amount = -b.amount
WHERE
NOT a.tot AND
NOT b.tot AND
a.a_id = %(a_id)s AND
b.a_id = %(a_id)s
{p_id_condition}
ORDER BY a.date DESC
I rewrote your query using explicit join syntax, which isolated and revealed the two join conditions you mentioned in your question. Then I changed the and to an or, which would seem to be what you want.

Related

SQL Join - If it doesn't find any, try another parameters

Let's suppose we have the following query:
SELECT *
FROM tableA a
LEFT JOIN tableB b ON b.number = a.number
AND b.name = a.name
AND b.place = a.place
If that LEFT JOIN doesn't find nothing, I need to change the ON parameters to something like:
SELECT *
FROM tableA a
LEFT JOIN tableB b ON b.number = a.number
AND b.person = a.person
That second LEFT JOIN only needs to run if the first one doesn't return nothing. How can I achieve that behaviour?
I have already tried with an OR statement, but doesn't work because the first LEFT JOIN always need to be checked first, and not at the same time that the second.
Perhaps the simplest method is union all:
SELECT a.*, b.*
FROM tableA a JOIN
tableB b
ON b.number = a.number AND
b.name = a.name AND
b.place = a.place
UNION ALL
SELECT a.*, b.*
FROM tableA a JOIN
tableB b
ON b.number = a.number AND
b.person = a.person
WHERE NOT EXISTS (SELECT 1
FROM tableB b
WHERE b.number = a.number AND
b.name = a.name AND
b.place = a.place
);

Combine two sql queries into a single table

I have two SQL queries where uses an inner join first to match based on a condition, and the other does not. Ultimately, I would like the difference between the columns created by each query. How can I do this?
I have tried unioning and joining the queries as in some similar posts, but it won't work. I wonder if the issue is around the joins within each query.
Query 1 :
SELECT A.date, COUNT(DISTINCT A.id)
FROM A
INNER JOIN B
ON A.id = B.id AND A.date = B.date
AND B.col1 = 'value1'
LEFT JOIN C on C.key = A.key
WHERE A.col1 = 'value2'
AND C.category = 'cat1'
GROUP BY 1
ORDER BY 1 DESC
Query 2 :
SELECT A.date, COUNT(DISTINCT A.id)
FROM A
LEFT JOIN C on C.key = A.key
WHERE A.col1 = 'value2'
AND C.category = 'cat1'
GROUP BY 1
ORDER BY 1 DESC
Your left join of c is actually turned to an inner join because it's used in a NULL excluding expression in the WHERE clause. So you can directly inner join c and left join b. Then you can use a case in one count() to count only the instances where a row from b was joined. Subtract that value from another count() counting all occurrences to get difference.
SELECT a.date,
count(DISTINCT a.id)
-
count(DISTINCT CASE
WHEN b.id IS NOT NULL THEN
a.id
END)
FROM a
INNER JOIN c
ON c.key = a.key
AND c.category = 'cat1'
LEFT JOIN b
ON a.id = b.id
AND a.date = b.date
AND b.col1 = 'value1'
WHERE a.col1 = 'value2'
GROUP BY 1
ORDER BY 1 DESC;
SELECT A.date, COUNT(DISTINCT A.id)
FROM A
INNER JOIN B
ON A.id = B.id AND A.date = B.date
AND B.col1 = 'value1'
LEFT JOIN C on C.key = A.key
WHERE A.col1 = 'value2'
AND C.category = 'cat1'
GROUP BY 1
ORDER BY 1 DESC
UNION
SELECT A.date, COUNT(DISTINCT A.id)
FROM A
LEFT JOIN C on C.key = A.key
WHERE A.col1 = 'value2'
AND C.category = 'cat1'
GROUP BY 1
ORDER BY 1 DESC
A simple way is to JOIN the two queries, using the date column, which is available in both queries :
SELECT x.date, x.cnt, y.cnt, y.cnt - x.cnt
FROM
(
SELECT A.date, COUNT(DISTINCT A.id) AS cnt
FROM A
INNER JOIN B ON A.id = B.id AND A.date = B.date AND B.col1 = 'value1'
LEFT JOIN C on C.key = A.key
WHERE A.col1 = 'value2' AND C.category = 'cat1'
GROUP BY 1
) AS x
INNER JOIN (
SELECT A.date, COUNT(DISTINCT A.id) AS cnt
FROM A
LEFT JOIN C on C.key = A.key
WHERE A.col1 = 'value2' AND C.category = 'cat1'
GROUP BY 1
) AS y ON x.date = y.date
ORDER BY 1 DESC
You might want to adapt the join type according to your data layout :
LEFT JOIN if all dates are available in the first subquery but may be missing in the second subquery
RIGHT JOIN if the situation is the other way around
FULL OUTER JOIN if you want all available dates from both ends
If you choose any of the above option, you would need to use COALESCE to prevent the substraction to return NULL when one of the terms is NULL.

oracle (+) operator in where with >(more) or <(less)

I've read about it and know how it works in this cases
select a.id
from a, b
where a.id = b.id(+)
but what about this?
select a.id
from a, b
where a.id = b.id2(+) and a.id > b.id(+)
it is not the same as
select a.id
from a
left outer join b
on a.id = b.id2
where a.id > b.id
how would I change it?
I need to rewrite it to hive but hive does not support
on a.id = b.id and a.id > b.id;
Although not exactly the same (there are some edge cases), this might do what you want:
select a.id
from a left outer join
b
on a.id = b.id2
where (a.id > b.id or b.id2 is null);
However, your expression is only picking up the id from a and it is using a left join. Hence, this is probably good enough:
select a.id
from a ;
Admittedly, it doesn't return the duplicates generated by the match to the b table, but duplicates are not usually desirable.

return pair of values instead of one value from two queries

I've got two queries that return single result.
They look something like this
// query 1
SELECT A.id FROM tableA A
INNER JOIN tableB B
ON B.id = A.id
WHERE b.status = 'ACTIVE'
// query 2
SELECT C.id FROM tableC C
WHERE c.status = 'ACTIVE'
How to combine them and make return the pair of values instead of one value from different queries? I mean to get something like [A.id, C.id]
Currently I have to use two queries in the applications and I want to combine them into one.
I think like this will do
SELECT (SELECT A.id FROM tableA A
INNER JOIN tableB B
ON B.id = A.id
WHERE b.status = 'ACTIVE'
) as 'query1',
(
SELECT C.id FROM tableC C
WHERE c.status = 'ACTIVE'
) as 'query2'
As your question is not clear, so i assume that you either needids from mentioned queries in one row or in different rows, you can use union all/union (provided that datatypes are compatible or implicitly convertible and duplicates or allowed or not) as below.
Combining Result in different rows.
SELECT A.id
FROM tableA A
INNER JOIN tableB B
ON B.id = A.id
WHERE b.status = 'ACTIVE'
union all
SELECT C.id
FROM tableC C
WHERE c.status = 'ACTIVE'
Combining Result in Single Row.
select max(id1), max(id2)
from(
SELECT A.id as id1, NULL as id2
FROM tableA A
INNER JOIN tableB B
ON B.id = A.id
WHERE b.status = 'ACTIVE'
union all
SELECT NULL, C.id
FROM tableC C
WHERE c.status = 'ACTIVE'
) t;
SAMPLE DEMO
You can run following query which work fine for me:
select t1.id as aid ,t2.id as cid
from (
SELECT A.id
FROM tableA A
INNER JOIN tableB B ON B.id = A.id
WHERE b.status = 'ACTIVE'
) t1
full outer join (
SELECT C.id
FROM tableC C
WHERE c.status = 'ACTIVE'
) t2 on t1.id=t2.id
You can join your second query with your first query as follows, so that you will get two (A.id, C.id) values in one query...
SELECT A.ID,C.ID FROM
(SELECT A.ID FROM table_A A INNER JOIN
table_B B ON A.ID=B.ID WHERE B.STATUS='A')A
INNER JOIN table_c C
ON C.ID=A.ID WHERE C.STATUS='A';

Query Logic best approach

i'm after the data obtained by my two queries plus any other data from the driving table. I'm using the following code but have a feeling my results are wrong.
select * from(
select * from tbl_a a
inner join tbl_b b on (a.id = b.id and a.col_a = b.col_b and a.col_c = '1')
union all
select * from tbl_a a
inner join tbl_b b on (a.col_a = b.col_b and a.col_c = '1')
where (1=1)
and a.id <> b.id
and a.start_time <= b.u_start_time
and a.end_time >= b.u_end_time
union all
select * from tbl_a a
where a.another_id
NOT IN ( -- either query above)
) results;
I'd just like to know if this makes sense or how I could possibly simplify some of this...
Here is query for the first 2 unions,and it is not clear what is the third union condition
SELECT *
FROM
tbl_a a
left join tbl_b b on b.id = a.id and b.col_b = a.col_a
left join tbl_b b1 on a.col_a= b1.col_b and a.id<>b1.id and a.start_time<=b1.u_start_time and a.end_time>=b1.u_end_time
WHERE
a.col_c=1
and COALESCE(b.id,b1.id) is not null