SQL - Minimum from a JOIN - sql

I am joining several tables. From the joined table I need to select a record with the minimum value in one column. The where clause contains some additional conditions. How can this be achieved without having to list the whole join twice in the select and in the where clause to identify the minimum?
I mean - from the result of the join, I need to select one record that fullfills some conditions and that also includes a minimum in a specific column. It is in Teradata but I am asking about the general principle.
I have something like this. It works, but is ugly as the join is included twice.
SELECT TABLE1.X, TABLE2.Y, TABLE3.Z
FROM TABLE1
INNER JOIN TABLE2
ON TABLE1.A = TABLE2.B
INNER JOIN TABLE3
ON TABLE2.C=TABLE3.D
WHERE TABLE3.M =
(SELECT MIN(TABLE3.M)
FROM TABLE1
INNER JOIN TABLE2
ON TABLE1.A = TABLE2.B
INNER JOIN TABLE3
ON TABLE2.C=TABLE3.D
WHERE TABLE1.K=123 AND TABLE2.L=456
)
Thanks, R.

In a comment you say you only need one row as your output.
In which case, use ORDER BY and LIMIT 1
SELECT TABLE1.X, TABLE2.Y, TABLE3.Z
FROM TABLE1
INNER JOIN TABLE2
ON TABLE1.A = TABLE2.B
INNER JOIN TABLE3
ON TABLE2.C=TABLE3.D
WHERE TABLE1.K=123 AND TABLE2.L=456
ORDER BY TABLE3.M
LIMIT 1
Edit: (To use min() to fulfil unstated requirements...)
SELECT
X, Y, Z
FROM
(
SELECT
TABLE1.X,
TABLE2.Y,
TABLE3.Z,
TABLE3.M,
MIN(TABLE3.M) OVER () AS MIN_M
FROM
TABLE1
INNER JOIN
TABLE2
ON TABLE1.A = TABLE2.B
INNER JOIN
TABLE3
ON TABLE2.C = TABLE3.D
WHERE
TABLE1.K = 123
AND TABLE2.L = 456
)
AS FILTERED
WHERE
MIN_M = M
Even if I was going to use window functions for this, I'd use ROW_NUMBER() OR RANK() rather than using MIN(). Without a clear reason WHY you feel this MUST use it, yet still be DRY, efficient and maintainable, this constraint appears not only pointless, but misguided.

Use min window function as follows:
Select x, y, z from
(SELECT TABLE1.X, TABLE2.Y, TABLE3.Z,
Min(TABLE3.M) over () as mn,
TABLE3.M
FROM TABLE1
INNER JOIN TABLE2
ON TABLE1.A = TABLE2.B
INNER JOIN TABLE3
ON TABLE2.C=TABLE3.D
Where TABLE1.K=123 AND TABLE2.L=456 ) t
Where m = mn

If I am following correctly, you can use qualify:
SELECT TABLE1.X, TABLE2.Y, TABLE3.Z
FROM TABLE1 INNER JOIN
TABLE2
ON TABLE1.A = TABLE2.B INNER JOIN
TABLE3
ON TABLE2.C = TABLE3.D
QUALIFY TABLE3.M = MIN(CASE WHEN TABLE1.K = 123 AND TABLE2.L = 456 THEN TABLE3.M END) OVER ();

Related

How to conditionally disable a join in SQL?

Oracle, specifically. I need to do the following in SQL in a declarative query, without defining a variable or if condition. Consider the following SQL:
SELECT *
FROM table1
JOIN table2 on table2.a = table1.a
WHERE table1.d = 4
AND c = (select c from table3 where b=3);
The catch is, if there is NOT a match in table3, then instead of table3 limiting the rows , the table3 condition should not apply (effectly just removing the last line of the above query)
How can I do that in a single SQL statement without defining a variable first?
You could add a NOT EXISTS clause to effectively remove the c = ... condition:
SELECT *
FROM table1
JOIN table2 on table2.a = table1.a
WHERE table1.d = 4
AND (NOT EXISTS (SELECT c FROM table3 WHERE b=3)
OR c = (SELECT c FROM table3 WHERE b=3)
)
The subquery returns at most one value. Here are two ways that you can handle this without an additional subquery:
c = all (select t3.c from table3 t3 where t3.b = 3)
This works because all matches to an empty list.
c = (select coalesce(max(t3.c), ?.c) from table3 t3 where t3.b = 3)
The ? is for the outer table reference. This works because max() will return null if there are no matches. The coalesce() then replaces the value with a matching value. Note: This assumes that the outer c is not null.
You would want to consider using left join on the table that contains the col c, as follows
SELECT *
FROM table1
JOIN table2
on table2.a = table1.a
LEFT JOIN (select distinct c
from table3
where b=3
)tbl_3
ON table1.c=tbl_3.c /*i assumed that the column c is in table1*/
WHERE table1.d = 4

Joining on one column and if no match join on another

I am attempting to use multiple columns in my join like this:
FROM Table1 t
INNER JOIN Table2 s ON t.number = s.number OR t.letter = s.letter
Both of these tables have several hundred thousand rows of data and it is running infinitely.
Any ideas?
You mean something like:
FROM Table1 t
INNER JOIN Table2 s ON case
when t.number = s.number then 1
when t.letter = s.letter then 1
else 0 end = 1
The first matching condition wins.
One possibility is to use left join and fix the rest of the query:
FROM Table1 t LEFT JOIN
Table2 sn
ON t.number = sn.number LEFT JOIN
Table2 sl
ON t.letter = sl.letter and sn.number is null
For performance, you want indexes on Table2(number) and Table2(letter).
ORs are usually produce bad performance. I would go for:
SELECT *
FROM Table1 t
INNER JOIN Table2 s ON t.number = s.number
UNION
SELECT *
FROM Table1 t
INNER JOIN Table2 s ON t.letter = s.letter

Join selections

How I can join this queries to single select (without temp table)?
SELECT t2.value,
t1.value
FROM table0 t1
INNER JOIN table1 t3 on t1.idrelation = t3.id and t3.idparent=#id
INNER JOIN table2 t2 on t2.idversion = t3.idchild and t2.name = 'FOO'
ORDER BY t1.value
SELECT SELECT COALESCE(t4._NAME+','+'')
FROM table1 t1
JOIN table1 t2 on t2.idparent = t1.idchild
JOIN table1 t3 on t3.idparent = t2.idchild
JOIN table3 t4 on t4._ID = t3.idchild
WHERE t1.idparent = #id
AND t4._TYPE ='TXT_CAT'
Something like this would help (once you can tell the columns on which you want to join):
select *
from
(
QUERY 1
) q1
join
(
QUERY 2
) q2
on q1.key1 = q2.key2
If you want to join them, there has to be common or uncommon ground.
Putting the 2 selects together is the easy part when you join the resultant sets, but unless you want all permutations of Ans1 and Ans2, then it is wise to determine some sort of where clause to allow it to be more effecient, and narrow it down for you.
If you give more table information, We could be able to adjust and give you something further, but that is the best we can do without taking arbitrary guesses as to what you are trying to accomplish here.
One thing i can part unto you:
Select A.name, B.id from (Select A.name, A.date from A) join (Select B.id, B.date from B) on A.date = B.date;

sql, outer join

I have two tables, linked with an outer join. The relationship between the primary and secondary table is a 1 to [0..n]. The secondary table includes a timestamp column indicating when the record was added. I only want to retrieve the most recent record of the secondary table for each row in the primary. I have to use a group by on the primary table due to other tables also part of the SELECT. There's no way to use a 'having' clause though since this secondary table is not part of the group.
How can I do this without doing multiple queries?
For performance, try to touch the table least times
Option 1, OUTER APPLY
SELECT *
FROM
table1 a
OUTER APPY
(SELECT TOP 1 TimeStamp FROM table2 b
WHERE a.somekey = b.somekey ORDER BY TimeStamp DESC) x
Option 2, Aggregate
SELECT *
FROM
table1 a
LEFT JOIN
(SELECT MAX(TimeStamp) AS maxTs, somekey FROM table2
GROUP BY somekey) x ON a.somekey = x.somekey
Note: each table is mentioned once, no correlated subqueries
Something like:
SELECT a.id, b.*
FROM table1 a
INNER JOIN table2 b ON b.parentid = a.id
WHERE b.timestamp = (SELECT MAX(timestamp) FROM table2 c WHERE c.parentid = a.id)
Use LEFT JOIN instead of INNER JOIN if you want to show rows for IDs in table1 without any matches in table2.
select *
from table1 left outer join table2 a on
table1.id = a.table1_id
where
not exists (select 1 from table2 b where a.table1_id = b.table1_id and b.timestamp > a.timestamp)
The quickest way I know of is this:
SELECT
A.*,
B.SomeField
FROM
Table1 A
INNER JOIN (
SELECT
B1.A_ID,
B1.SomeField
FROM
Table2 B1
LEFT JOIN Table2 B2 ON (B1.A_ID=B2.A_ID) AND (B1.TimeStmp < B2.TimeStmp)
WHERE
B2.A_ID IS NULL
) B ON B.A_ID = A.ID

Semantic difference between join queries

I have two queries that I thought meant the same thing, but I keep getting different results and I was hoping someone could explain how these are different:
1.
select *
from table1 a
left join table2 b on a.Id = b.Id and a.val = 0
where b.Id is null
2.
select *
from table1 a
left join table2 b on a.Id = b.Id
where b.Id is null
and a.val = 0
The point of the query is to find the rows that are in table1 and val = 0 that are not in table2.
I'm using sql server 2008 as well, but I doubt that this should matter.
When considering left joins think of them as having 3 conceptual stages.
The join filter is applied
The left rows are added back in
the where clause is applied.
You will then see why you get different results.
That also explains why this returns results
select o.*
from sys.objects o
left join sys.objects o2 on o.object_id=o2.object_id and 1=0
And this doesn't.
select o.*
from sys.objects o
left join sys.objects o2 on o.object_id=o2.object_id
where 1=0
SELECT * from TABLE1 t1
WHERE Val = 0
AND NOT EXISTS(SELEct 1 from Table2 t2 Where t1.Id = t2.Id)
If you remove the WHERE clause entirely, using a LEFT OUTER JOIN means that all the rows from the table on the left hand side will appear, even if they don't satisfy the JOIN criteria. For example, no rows satisfy the expression 1 = 0 however this:
SELECT *
FROM table1 AS a
LEFT OUTER JOIN table2 AS b
ON a.Id = b.Id
AND 1 = 0;
still results in all rows in table1 being returned where the id values match. Simply put, that's the way OUTER JOINs work.
The WHERE clause is applied after the JOIN, therefore this
SELECT *
FROM table1 AS a
LEFT OUTER JOIN table2 AS b
ON a.Id = b.Id
WHERE 1 = 0;
will return no rows.