I'm trying to figure out how to get the same results using joins, as I would using a not exists condition.
For example, if I had the following two tables:
TABLE 1
--------------
|ID | EXT_ID |
|1 | A |
|2 | B |
|3 | C |
--------------
TABLE 2
-------------------------
|EXT_ID | TB1_ID |PRIMARY|
|A | 1 |1 |
|A | 1 |0 |
|B | 2 |0 |
|B | 2 |0 |
-------------------------
If I'm looking to find the records from table 1 that do not have a primary flag of 1 in table 2, for records that actually have a child in table 2 (to exclude orphans), I could simply write the following (expected to return only ID 2 from table 1):
SELECT TB1.ID FROM Table1 TB1
JOIN Table2
ON Table1.EXT_ID = Table2.EXT_ID
WHERE Table2.Primary = 0
AND NOT EXISTS
(
SELECT * FROM Table2 TB2
WHERE TB1.ID = TB2.TB1_ID
AND TB2.PRIMARY = 1
)
Is there a way to do this with joins? And if so, would there be much efficiency in using the not exists vs. a join?
Thanks in advance!
EDIT: fixed tables.
with x as (select Ext_ID, Tb1_ID, Sum(Primary) as sum_primary
from Table2 group by Ext_ID,Tb1_ID)
SELECT TB1.ID
FROM Table1 TB1 JOIN x
ON Table1.EXT_ID = x.EXT_ID
where x.sum_primary = 0
You can use a CTE for this.
SELECT
TB1.ID, TB1.EXT_ID
FROM
TABLE1 TB1
JOIN TABLE2 TB2 ON TB1.ID = TB2.TB1_ID AND TB1.EXT_ID = Table2.EXT_ID
GROUP BY
TB1.ID, TB1.EXT_ID
HAVING MAX(TB2.[PRIMARY]) = 0
Related
Why isn't EXCEPT returning expected results? I feel like this should work, but it returns zero rows. However, if I do a count of order-buid-tie rows in T3 that aren't in t2, I get thousands of results and vice versa.
Manual review
--first table (T3) without T2 match --178k
select count(*) --178K
from LAB3.Report_stg st3
left join LAB2.Report_stg st2
on st3.order_num = st2.order_num and st3.buid= st2.buid and st3.tie_nbr = st2.tie_num
where st2.tie_num is null
--second table (t2) without match in first (T3)
select count(*) --12.5M
from LAB2.Report_stg st2
left join LAB3.Report_stg st3
on st3.order_num = st2.order_num and st3.buid = st2.buid and st3.tie_num = st2.tie_num
where st3.tie_num is null
Let's try EXCEPT. I expect 178k rows here and get zero
--to avoid cartesian join
--select all mismatched tie_nums
--unless (EXCEPT if) that tie_num exists as a matched set between the two tables
select t3.order_num, t3.buid, t3.tie_num as t3_tie
from LAB3.Report_stg t3
left join LAB2.Report_stg t2
on t3.order_num = t2.order_num and t3.buid = t2.buid and t3.tie_num <> t2.tie_num
except
(select st3.order_num, st3.buid, st3.tie_num
from LAB3.Report_stg st3
left join LAB2.Report_stg st2
on st3.order_num = st2.order_num and st3.buid = st2.buid and st3.tie_num = st2.tie_num
)
Argh, I figured it out! So, the second part of the clause (inside the EXCEPT SELECT) needs to be an INNER JOIN to achieve the desired effect. Again, what I was trying to pull was all the Ties in T3 on the same T2 Orders that don't have a match between the tables.
--to avoid cartesian join
--select all mismatched tie_nums
--unless (EXCEPT if) that tie_num exists as a matched set between the two tables
select t3.order_num, t3.buid, t3.tie_num as t3_tie
from LAB3.Report_stg t3
left join LAB2.Report_stg t2
on t3.order_num = t2.order_num and t3.buid = t2.buid and t3.tie_num <> t2.tie_num
except
(select st3.order_num, st3.buid, st3.tie_num
from LAB3.Report_stg st3
INNER join LAB2.Report_stg st2
on st3.order_num = st2.order_num and st3.buid = st2.buid and st3.tie_num = st2.tie_num
)
Example:
T3
| Order_Num | Tie_Num |
| -------- | -------------- |
| 123 | 1 |
| 123 | 2 |
| 123 | 3 |
| 456 | 1|
| 456 | 4|
T2
| Order_Num | Tie_Num |
| -------- | -------------- |
| 123 | 2 |
| 123 | 4 |
| 123 | 5 |
| 456 | 1|
| 456 | 3|
Results using query above
T3
| Order_Num | Tie_Num |
| -------- | -------------- |
| 123 | 1 |
| 123 | 3 |
| 456 | 4|
So, ideally, I'd union this with an inverse to get the order-ties in T2 that aren't in t3 into the same result set. But then that means I've just written a manual FULL OUTER JOIN
SMH
I have MS SQL database table like this
TableA
+----+-----------+--------+
|ID | Table2_FK | Value |
+----+-----------+--------+
|1 | 7 | X |
|2 | 7 | Y |
|3 | 8 | X |
|4 | 8 | Z |
|5 | 9 | W |
|6 | 9 | M |
|5 | 10 | X |
|6 | 10 | Z |
+----+-----------+--------+
I want to make query to get list of Table2_FKs if I pass X and Z in query for Values. In this example 8 and 10 is the result
It can be more than 2 values
You can do this with group by and having:
select table2_fk
from t
where value in ('X', 'Z')
group by table2_fk
having count(*) = 2;
If the values can be duplicated for a key value, then use count(distinct value) = 2. The "2" is the number of values in the IN list.
Try this:
select distinct Table2_FK
from TableA
where value in ('X','Z');
You can use query as below:
Select distinct table2_fk from (
Select *, Ct = count(id) over (partition by table2_fk) from yourtable
) a
Where a.[Value] in ('X','Z') and a.Ct >= 2
you can use a query like below
select
distinct Table2_FK
from TableA a
where exists (
select 1 1 from TableA b where b.value ='X' and a.Table2_FK =b.Table2_FK
)
and exists (
select 1 1 from TableA c where c.value ='Z' and a.Table2_FK =c.Table2_FK
)
Assume I have the following activity table:
id type value flag
------|------|-------|------|
1 |A | 13 | 1 |
2 |B | 29 | |
3 |C | 11 | |
4 |A | 78 | |
5 |X | 91 | |
6 |C | 2 | |
7 |B | 14 | 1 |
I want to select rows that any row with the same type has the flag 1 or the type is X. In this case, I would like to get:
id type value flag
------|------|-------|------|
1 |A | 13 | 1 |
4 |A | 78 | |
2 |B | 29 | |
7 |B | 14 | 1 |
5 |X | 91 | |
I can do an INNER JOIN to get the result like:
SELECT
"activities".*
FROM "activities"
INNER JOIN activities AS a2
ON activities.type = a2.type
AND a2.flag = 1
OR activities.type = 'X'
AND activities.id = a2.id
However, this is slow when the amount of records becomes large, especially when I need to do a COUNT(*) on top of the result. I wonder how I can rewrite the query and make it more performant.
I am using Postgres. Thanks!
Here are 3 ways to do it. Choose one which will be work faster on your dataset. To speed up these queries you have to create indexes on type and flag fields.
select a.* from activities a
JOIN (select distinct type FROM activities where type='X' or flag=1) t
ON a.type=t.type;
select a.* from activities a
where type='X'
or EXISTS(SELECT * FROM activities WHERE type=a.type AND flag=1);
select a.* from activities a
where type='X'
or type IN (SELECT type FROM activities WHERE flag=1)
SQLFiddle demo
Try this solution
SELECT id,
type,
value,
flag
FROM (SELECT *,
SUM(CASE WHEN flag = 1 THEN 1 ELSE 0 END) OVER (PARTITION BY type) AS flag_occurence_for_type
FROM activities) t
WHERE type = 'X' OR flag_occurence_for_type > 0
table A must be outer-joinned with B.
A
id | ...
---|-----
1 |
2 |
3 |
---|-----
B
id| a_id | b | ...
--|------|----------
1 |1 | special |
2 |1 | normal |
3 |2 | normal |
4 |2 | normal |
--------------------
so I want:
A.outerjoin(B)
like this:
a_id | b.id | ...
-----|------|--------
2 | 3 |normal |
2 | 4 |normal |
3 | None |
-----|------|
what should i use to filter entire group of B with the same a_id having at least one "special" value?
I dont want lose outerjoin - None for a.id = 3.
my first idea is to use nested select, but it's not optimal.
select distinct a.*
from a join b on a.id=b.a_id
where b.b='special';
excludes a groups with 'special'
select a.*
from a outer join
(select distinct a.*
from a join b on a.id=b.a_id
where b.b='special') excluded on a.id=excluded.id
having excluded.id is null;
leaves only valid a rows.
select *
from (select a.*
from a outer join
(select distinct a.*
from a join b on a.id=b.a_id
where b.b='special') excluded on a.id=excluded.id
having excluded.id is null) outer join b on a.id=b.a_id;
valid rows outer joined with b
I have a table as such:
|testNr |date |
|1 | 2014-01-01 |
|2 | 2014-01-03 |
|3 | 2014-01-03 |
And another one like:
|finalID | testNr | from_date |to_date
|1 | 1 | 2013-12-01 |2013-12-20
|2 | 1 | 2013-12-25 |2014-01-05
|3 | 2 | 2014-01-01 |2014-01-05
I want to lookup the finalID from the second table and join it with the first. It is imporant that the date in the first column is between the date range in the second column.
I would like to end up with:
|testNr |date | finalID
|1 | 2014-01-01 | 2
|2 | 2014-01-03 | 3
|3 | 2014-01-03 | NULL
I am using SQL server. Any ideas on how to approach this?
I think this is what you want
select t1.testNr, t1.date, t2.finalID
from table1 t1 left join table2 t2
on t1.testNr=t2.testNr and t1.date between t2.from_date and t2.to_date
fiddle
Use the following
SELECT t1.*, t2.finalID
FROM table1 t1
LEFT JOIN table2 t2 on t1.testNR=t2.testNR and t1.[date] between t2.from_date and t2.to_date
SQLFIddle
SELECT T1.*,T2.FINALID FROM TABLE1 T1
LEFT JOIN TABLE2 T2 ON T1.TESTNR=T2.TESTNR
AND T1.DATE BETWEEN T2.FROM_DATE AND T2.TO_DATE