Join two tables containing two foreign keys - sql

I'm pretty amateur when it comes to writing SQL, so maybe this is an easy one for someone?
I have two tables as follows:
Table1 - DeviceName, DeviceID, AlternateID
MyPhone, 333, AAA
HerPhone, 444, CCC
Table2 - PhoneID, ProgramName
333, AngryBirds
CCC, Evernote
As you can see, Table2 uses two different PhoneID types from Table1 (DeviceID and AlternateID). I'm looking a sql statement that will result in output like:
MyPhone, AngryBirds
HerPhone, Evernote
Appreciate any assistance.
Cheers,
Mark

SELECT deviceName, programName
FROM table2 t2
JOIN table1 t1
ON(t1.DeviceID=t2.PhoneID OR t1.AlternateID=t2.PhoneID)
or (less readable but shorter)
SELECT deviceName, programName
FROM table2 t2
JOIN table1 t1
ON(t2.PhoneID IN (t1.DeviceID, t1.AlternateID))
Still, if DeviceIDs and AlternateIDs are from the same set, you should consider refactoring the database: What if a device could have multiple valid IDs, not just two?

What if both 333 and AAA are defined in Table 2? I'm going to assume you only want want one row returned per DeviceName, which means you need to join to Table 2 twice using outer joins:
select DeviceName,
t2a.ProgramName,
t2b.ProgramName as AltProgramName
from table1 t1
left outer join table2 t2a
on t1.DeviceID = t2a.PhoneID
left outer join table2 t2b
on t1.AlternateID = t2b.PhoneID
If you want to list only one ProgramName, and can establish a priority as to which to use in case both are present, you could do the following: (assuming DeviceID trumps AlternateID)
select DeviceName,
coalesce(t2a.ProgramName, t2b.ProgramName) as AltProgramName
from table1 t1
left outer join table2 t2a
on t1.DeviceID = t2a.PhoneID
left outer join table2 t2b
on t1.AlternateID = t2b.PhoneID
If you want the 1st program column to always contain a value, and only list a value in the 2nd column if both are present, then
select DeviceName,
coalesce(t2a.ProgramName, t2b.ProgramName) as ProgramName1,
case when t2a.ProgramName is not null then t2b.ProgramName end as ProgramName2
from table1 t1
left outer join table2 t2a
on t1.DeviceID = t2a.PhoneID
left outer join table2 t2b
on t1.AlternateID = t2b.PhoneID
EDIT
I assumed the query was being used for reporting purposes, in which case you most likely only want one row per device.
I just realized why the OP might want multiple rows returned per device. If the query is used to define a view for looking up devices by either name, then multiple rows are desired.
Jan's answer works perfectly for the lookup view. However, it may or may not perform well depending on the database engine's query optimizer and the size of the tables. If the tables are very large, then you are going to want an indexed lookup. The join with an OR condition may preclude an indexed lookup on some systems. An equivalent query using UNION ALL may support an indexed lookup on more systems.
select DeviceName,
ProgramName
from table1 t1
join table2 t2
on t1.DeviceID = t2.PhoneID
union all
select DeviceName,
ProgramName
from table1 t1
join table2 t2
on t1.AlternateID = t2.PhoneID

Related

Conditional Join if Exists

I need to join two tables together based on a three-column key stack. The problem is sometimes one of the key columns is translated and mapped differently in another table. I will attempt to example my issue using code:
select t1.TQ
from table1 t1
left join table2 t2 on t1.comp_cd = t2.comp_cd and t1.plcy_frm = t2.plcy_frm
and t1.val_cd = t2.val_cd
The columns "comp_cd" and "plcy_frm" are fine, however the problem is with val_cd. Sometimes the val_cd in table2 does not map correctly to table1 and must go through a third table, table3. Table3 structure is below:
Val_Cd Mapped_Val_Cd
A123 A564
So -> I need to join on Mapped_Val_Cd value when it exists in Table3, but join on Val_Cd from Table2 when Val_Cd does not exist in Table3.
I hope this makes sense - I have tried Case when exists syntax but cannot get that to work.
Assuming there are no duplicates in table3, you can left join it in and then choose the value that you want in the on clause:
select t1.TQ
from table1 t1 left join
table3 t3
on t1.val_cd = t3.val_cd
table2 t2
on t1.comp_cd = t2.comp_cd and
t1.plcy_frm = t2.plcy_frm and
t1.val_cd = coalesce(t3.Mapped_Val_Cd, t2.val_cd);

Does increasing the number of fields in JOIN statement increase/decrease the speed of execution?

I have two tables with 3.5 million rows of data. I am creating a left join between the two to create a new view.
Code 1:
SELECT t1.c1,t1.c2,t2.c3,t2.c4
from table1 as t1
left join table2 as t2
on t1.Location=t2.Location and t1.OrderNumber=t2.OrderNumber and t1.Customer=t2.Customer
Code 2:
SELECT t1.c1,t1.c2,t2.c3,t2.c4
from table1 as t1
left join table2 as t2
on t1.OrderNumber=t2.OrderNumber
Both snippets of code give the same desired result as the Order number field in table 2 has only unique values.
Is it better to give more fields to JOIN compared to only one?
SELECT t1.c1,t1.c2,t2.c3,t2.c4
from table1 as t1
left join table2 as t2
on t1.Location = t2.Location
and t1.OrderNumber = t2.OrderNumber
and t1.Customer = t2.Customer
If OrderNumber is the PK of either table then adding additional fields will not change the results and it will not improve performance unless an index as not present on the other side.
If Order number field in table 2 has only unique values it would not change the query. If it is a PK or has a unique constraint/index then addition fields would not help unless what Table2.OrderNumber was joined to was not indexed.

SQL join to return a table with multiple columns from other tables replacing its own

I am trying to write an SQL query that will return Table1, which has 10 columns. This table consists of a primary key id, 4 foreign key Id columns, and 5 other columns that I want to return but not change. The goal is to do a join to replace the foreign key Ids with their descriptions that are held in other tables.
Here is one attempt with the first FK Id:
Select * from Table1 t1
left join Table2 t2
on t1.BranchId = t2.BranchId;
This left join returns the description from table2, but does not replace it.
Here is another with the first FK Id:
Select t2.BranchName from Table1 t1
left join Table2 t2
on t1.BranchId = t2.BranchId;
This returns the name I want, but does not return table1 fully.
For the sake of an example you could pretend that OtherName3, OtherName4, OtherName5 are in tables Table3, Table4, Table5, respectively.
This may seem trivial for experienced SQL devs, but I am having a hard time figuring out the syntax.
Thanks!
I'm not sure what you mean by replace it.
I think you just need to list out all the columns you want:
Select t1.col1, t1.col2, t1.col3, . . .,
t2.name
from Table1 t1 left join
Table2 t2
on t1.BranchId = t2.BranchId;
I don't know what you mean by 'replace' but you just need to qualify what columns from which table you want. That goes for all tables you are joined to, especially if they have the same column name in multiple tables. I put junk columns in since I don't know your tables but you should get the general idea.
Select t2.BranchName, t1.BranchId, t1.Name, t1.Amount, t2.BranchLocation from Table1 t1
left join Table2 t2
on t1.BranchId = t2.BranchId;
I think this is what you are looking for:
select t1.*, t2.BranchName from Table1 t1
left join Table2 t2
on t1.BranchId = t2.BranchId;
Return Table1 fully (all columns) and only the description (BranchName) from Table2.
If using SQL Server, see all syntax options for the SELECT clause here:
https://msdn.microsoft.com/en-us/library/ms176104.aspx

Join SQL Tables with Unique Data (Not same number of columns!)

How can I join three or four SQL tables that DO NOT have an equal amount of rows while ensuring that there are no duplicates of a primary/foreign key?
Structure:
Table1: id, first_name, last_name, email
Table2: id (independent of id in table 1), name, location, table1_id, table2_id
Table3: id, name, location
I want all of the data from table 1, then all of the data from table 2 corresponding with the table1_id without duplicates.
Kind of tricky for a new guy...
Not sure what do you want to do with Table3.
A LEFT JOIN returns all records from the LEFT table, and the matched records from the right table. If there is no match (from the right side), then the result is NULL.
So per example:
SELECT * FROM Table1 AS t
LEFT JOIN Table2 AS tt
ON t.id = tt.id
The LEFT table refers to the table statement before the LEFT JOIN, and the RIGHT table refers to the table statement after the LEFT JOIN. If you want to add in Table3 as well, use the same logic:
SELECT * FROM Table1 AS t
LEFT JOIN Table2 AS tt
ON t.id = tt.id
LEFT JOIN Table3 AS ttt
ON t.id = ttt.id
Note, that I use alias names for the tables (by using AS), so that I can more easily refer to a specific table. For example, t refers to Table1, tt refers to Table2, and ttt refers to Table3.
Joins are often used in SQL, therefore it is useful to look into: INNER JOIN, RIGHT JOIN, FULL JOIN, and SELF JOIN, as well.
Hope this helps.
Good luck with learning!
You will want to use an LEFT JOIN
SELECT * FROM table1 LEFT JOIN table2 ON Table1.ID = Table2.table1_id

How to get key of the maximal record in group?

I have two tables in my database, one holds the names of files, and other holds records of information described in them, inincluding sizes of sections. it can be descrived as:
Table1: id as integer, name as varchar
Table2: recid as integer primary key, file_id as integer, score as float
Between the tables there is an one-to-many link, from Table1.id to table2.file_id. What i need is for every file which name matches a certain pattern retrieve the id of the linked record with the maximum score and the score itself.
So far i have used:
SELECT name,MAX(score)
FROM Table1
LEFT OUTER JOIN Table2 ON Table2.file_id=Table1.id
WHERE name LIKE :pattern
GROUP BY name
but i cannot retrieve the id of the record in Table2 this way.
The dialect i am using is Sqlite.
What query should be used to retrieve data on the record that has maximum score for every file?
Update:
With this query, i am getting close to what i want:
SELECT name,score,recid
FROM Table1
LEFT OUTER JOIN Table2 ON file_id=id
WHERE name LIKE :pattern
GROUP BY name
HAVING score=MAX(score)
However, this leaves out the entries in the first table that have no corresponding entries in the second table out. How can i ensure they are in the end result anyway? Should i use UNION, and if so - how?
This can actually be achieved without a GROUP BY by using a brilliantly simple technique described by #billkarwin here:
SELECT name, t2.score
FROM Table1 t1
LEFT OUTER JOIN Table2 t2 ON t2.file_id = t1.id
LEFT OUTER JOIN Table2 t2copy ON t2copy.file_id = t2.file_id
AND t2.score < t2copy.score
WHERE name LIKE :pattern
AND t2copy.score IS NULL
See SQL Fiddle demo.
I think that you must use a subquery
SELECT name, recid, score
FROM Table1
LEFT OUTER JOIN Table2 ON Table2.file_id=Table1.id
WHERE name LIKE :pattern AND score = (SELECT MAX(score) FROM Table2.score)
I think the easiest way to do this is with a correlated subquery:
SELECT name, recid, score
FROM Table1 LEFT OUTER JOIN
Table2
ON Table2.file_id=Table1.id
WHERE name LIKE :pattern AND
score = (SELECT MAX(t2.score)
FROM Table1 t1 LEFT OUTER JOIN
Table2 t2
ON t2.file_id=t1.id
where t1.name = table1.name
);
Note that you need table aliases to distinguish the tables in the inner query from the outer query. I am guessing which tables the columns are actually coming from.