SQL Join Question - sql

I have an interesting question for an SQL Join. I have 2 tables, examples below:
Table1: ID (int), Value(string)
Table2: ID (int), ForeignID(int), (Value)
The field ForeignID in Table2 is the foreign key of the ID in Table1. For a given entry in Table1, I have multiple entries in Table2 as follows:
Table1:
ID, Value
0, "Hello World"
1, "Bonjour"
Table2:
ID, ForeignID, Value
0, 0, "First entry"
1, 0, "Second entry"
2, 1, "Third entry"
If I do an inner join such as
SELECT Table1.Value, Table2. Value FROM
Table1 INNER JOIN Table2 ON Table1.ID = Table2.ForeignID
I would get
Hello world, First entry
Hello world, Second entry
Bonjour, Third entry
Is there a way to only get the TOP entry in Table2 such as:
Hello world, First entry
Bonjour, Third entry

This works too:
SELECT Table1.value
, Table2.value
FROM Table1
INNER JOIN Table2 ON Table1.id = Table2.foreignID
INNER JOIN (
SELECT MIN(ID) AS ID, ForeignID
FROM Table2
GROUP BY ForeignID
) MinID ON Table2.foreignid = MinID.foreignid
AND Table2.id = MinID.id

SELECT
t1.Value,
(
SELECT TOP 1 t2.Value FROM Table2 t2
WHERE t2.ForeignID = t1.ID
ORDER BY t2.ID ASC
)
FROM Table1 t1

SELECT Table1.Value,
(SELECT TOP 1 Table2.Value
FROM Table2
WHERE Table2.ForeignID = Table1.ID)
FROM Table1

Since you tagged SQL, here is the ANSI SQL query (which should work on SQL Server 2005+) :
WITH TT (ID, ForeignID, Value, N) AS (
SELECT ID, ForeignID, Value,
ROW_NUMBER() OVER(PARTITION BY ForeignID ORDER BY ID)
FROM Table2
)
SELECT T1.Value, T2.Value
FROM Table1 T1
INNER JOIN TT T2
ON T1.ID = T2.ForeignID
AND T2.N = 1;
It uses a Common Table Expression and a windowing function.

Here is another variation that makes sure that table2 in joined to table1 on the proper key.
SELECT t1.Value,
t2.Value
FROM Table1 t1
INNER JOIN Table2 t2
ON t2.ForeignID = (SELECT MIN(sub.ForeignID)
FROM Table2 sub WHERE sub.ForeignID = t1.ID)

Edit, actually, scratch that. Tamila's answer looks better to me.
If you can add a boolean is_first flag to your table2 then things become a lot easier and this would do the job for you:
SELECT table1."value", table2."value"
FROM table1
INNER JOIN table2 ON table1.id = table2.foreign_id
WHERE table2.is_first = true
I don't know if Daniel's, cgatian's or Leons' answers will work for your database. I'm on Postgres and it doesn't work for me.

Related

Update row in a table based on multiple rows in another table

I have two tables: table1 and table2:
table1 has columns id and integer
table2 has columns id and boolean
table2 can have multiple rows with the same id
I want to update the integer column of table1 by looking at all rows with the same id in table2 and seeing if any of the boolean values are true. If so I want table1.integer to be 1, else I want it to be 0.
I have tried something like this:
UPDATE table1,
(
SELECT table2.id, Sum(table2.boolean) > 0
) AS 'condition'
from table2
WHERE 1
GROUP BY table2.id) table3
SET table1.integer =IF(table3.condition, 1, 0) where table1.id = table3.id
And it seems to work, but I wanted to ask if there is a nicer/cleaner/more succinct way of updating the rows of table1 according to multiple rows of table2.
I would recommend EXISTS:
UPDATE table1 t1
SET t1.integer = (EXISTS (SELECT 1
FROM table2 t2
WHERE t2.id = t.id AND
t2.boolean
)
);
This can take advantage of an index on table2(id, boolean). With such an index, it should be faster than an approach that uses JOIN and AGGREGATION.
The syntax of your query is MySql like, so you can do a join like this:
UPDATE table1 t1 INNER JOIN (
SELECT id, MAX(boolean) maxboolean
FROM table2
GROUP BY id
) t2 ON t2.id = t1.id
SET t1.integer = t2.maxboolean
If there are ids in table1 without a corresponding id in table2 and you want the integer column for them to be updated to 0 then use a LEFT join:
UPDATE table1 t1 LEFT JOIN (
SELECT id, MAX(boolean) maxboolean
FROM table2
GROUP BY id
) t2 ON t2.id = t1.id
SET t1.integer = COALESCE(t2.maxboolean, 0)

Inner Join with similar IDs

I have two tables, say:
Table 1
id, name, place
1, a, b
2, c, d
3, e, f
Table 2
id,text
1, hello
1, bye
1, what
2, tired
Desired Output
id, name, place, text
1, a, b, hello or bye or what (any one of the three)
2, c, d, tired
I have seen a lot of posts but I couldn't find anything similar. I am new to SQL/Postgresql. I am doing it in PostgreSQL.
You could use DISTINCT ON here and arbitrarily just take the alphabetically lowest text from the second table:
SELECT DISTINCT ON (t1.id) t1.id, t1.name, t1.place, t2.text
FROM Table1 t1
INNER JOIN Table2 t2
ON t1.id = t2.id
ORDER BY
t1.id,
t2.text;
Demo
You can aggregate multiple values in an array and pick a random value (postgres syntax):
select t1.id, (array_agg(t2.text))[floor(random() * count(t2.text) + 1)::int]
from table1 t1
inner join table2 t2 on t1.id = t2.id
group by t1.id
Or a bit faster, but will pick first value only:
select t1.id, (array_agg(t2.text))[1]
from table1 t1
inner join table2 t2 on t1.id = t2.id
group by t1.id
You would write something along the lines of
select t1.id, name, place, text from table 1 as t1
join table 2 as t2 on t2.id = t1.id
group by t1.id

not in operator in SQL

there is 2 tables
table1
ID
1
2
4
6
7
TABLE2
2
4
6
i want those number from table1 which is not in table2 how i do this ?
i try this
select id from table1 t1
inner join table2 t2 on t1.id=t2.id
where t1.id not in (select id from table2)
but this is not working
SELECT t1.id
FROM table1 t1
LEFT JOIN table2 t2 ON t2.id = t1.id
WHERE t2.id IS NULL
Conceptually, we select all rows from table1 and for each row we attempt to find a row in table2 with the same value for the id column. If there is no such row, we just leave the table2 portion of our result empty for that row. Then we constrain our selection by picking only those rows in the result where the matching row does not exist. Finally, We ignore all fields from our result except for the id column (the one we are sure that exists, from table1).
try this:
select id from table1 t1 where t1.id not in (select t2.id from table2 t2)
You don't need to join the two tables in this case. You could just do
select id from table1 A where A.id not in (select B.id from table2 B);
You could also just simply use the sql set difference EXCEPT operator to achieve this
(select id from table1) except (select id from table2);
Use NOT IN or NOT EXISTS
select id from table1 t1
where t1.id not in (select id from table2)
select id from table1 t1
where not exists (select id from table2 where id = t1.id)

Applying joins conditionally in SQL Server

I have some set of records, but now i have to select only those records from this set which have theeir Id in either of the two tables.
Suppose I have table1 which contains
Id Name
----------
1 Name1
2 Name2
Now I need to select only those records from table one
which have either their id in table2 or in table3
I was trying to apply or operator witin inner join like:
select *
from table1
inner join table2 on table2.id = table1.id or
inner join table3 on table3.id = table1.id.
Is it possible? What is the best method to approach this? Actually I am also not able to use
if exist(select 1 from table2 where id=table1.id) then select from table1
Could someone help me to get over this?
Use left join and then check if at least one of the joins has found a relation
select t1.*
from table1 t1
left join table2 t2 on t2.id = t1.id
left join table3 t3 on t3.id = t1.id
where t2.id is not null
or t3.is is not null
I would be inclined to use exists:
select t1.*
from table1 t1
where exists (select 1 from table2 t2 where t2.id = t1.id) or
exists (select 1 from table3 t3 where t3.id = t1.id) ;
The advantage to using exists (or in) over a join involves duplicate rows. If table2 or table3 have multiple rows for a given id, then a version using join will produce multiple rows in the result set.
I think the most efficient way is to use UNION on table2 and table3 and join to it :
SELECT t1.*
FROM table1 t1
INNER JOIN(SELECT id FROM Table2
UNION
SELECT id FROM Table3) s
ON(t.id = s.id)
Alternatively, you can use below SQL as well:
SELECT *
FROM dbo.Table1
WHERE id Table1.IN ( SELECT table2.id
FROM dbo.table2 )
OR Table1.id IN ( SELECT table3.id
FROM Table3 )

How to check if two selects return the same ids

Suppose, we have query like this:
SELECT
1
FROM DUAL WHERE
(SELECT id FROM table_1 t1 WHERE /*conditions*/)
IN
(SELECT id FROM table_1 t2 WHERE /*conditions*/)
I want to check if first query
SELECT id FROM table_1 t1 WHERE /*conditions*/
returns the same ids like the second query.
Of course this query (IN statement) doesn't work.
Try:
SELECT id FROM table_1 t1 WHERE /*conditions1*/ and id not in (SELECT id FROM table_1 t2 WHERE /*conditions2*/)
union
SELECT id FROM table_1 t1 WHERE /*conditions2*/ and id not in (SELECT id FROM table_1 t2 WHERE /*conditions1*/)
If both queries gives you the same id's the result should be empty.
This will return nothing if sets are equal:
SELECT id FROM table_1 t1 WHERE /*conditions*/
EXCEPT
SELECT id FROM table_1 t2 WHERE /*conditions*/
You can use EXCEPT.
EXCEPT returns distinct rows from the left input query that aren’t
output by the right input query.
EXCEPT sample in your case:
SELECT id
FROM table_1 AS t1
WHERE /*conditions*/
EXCEPT
SELECT id
FROM table_1 AS t2
WHERE /*conditions*/
Just as an alternative method that used Full Join in tsql:
SELECT CASE WHEN isnull(Count(*), 0) > 1 then 1 else 0 end as result
FROM (SELECT t1.id as t1_id, t2.id as t2_id FROM
(SELECT id FROM table1 WHERE /*conditions*/) As t1
Full Outer Join
(SELECT id FROM table2 WHERE /*conditions*/) As t2
On t1.id = t2.id
) As ft
WHERE ft.t1_id is null or ft.t2_id is null
And I think this can marked as a stupid way.