Simple SQL Select from 2 Tables (What is a Join?) - sql

I'm new to SQL. I have a simple problem with getting the results from two different tables.
I have two tables in a database. The first table has a column with an id reference, which corresponds to rows in the second table. What SELECT do I need to perform to get a result such that the ids are repalced by all of the values in the second table. To visualize the tables I am discussing:
TABLE_USERS
===========
id username group
-- -------- -----
1 jim A
2 alice A
3 brandon B
TABLE_GROUPS
============
id groupname members
-- --------- -------
A designer 134
B photographer 39
DESIRED_SELECTION
=================
id username group
-- -------- -----
1 jim designer
2 alice designer
3 brandon photographer
Thanks!

You do, in fact, want to JOIN the two tables:
SELECT * FROM
TABLE_USERS LEFT JOIN TABLE_GROUPS
ON TABLE_USERS.group = TABLE_GROUPS.id
The trick of joining tables is to find the values that must match in the two tables, and use the on to tell SQL to match them. This table has a ID column to let you do that = you will join the table, ON, and then list the values that need to be equal.
If you do not want all of the columns in both tables, you can simply list only the columns you need in your final query. This means that instead of Select *, you list the columns you want. As shown below, if a column appears with the same name in both tables, you need to prepend the table name, so that SQL know which value you want.
SELECT TABLE_USERS.ID, Username, Groupname
FROM TABLE_USERS
LEFT JOIN TABLE_GROUPS
ON TABLE_USERS.group = TABLE_GROUPS.id

You want a JOIN:
SELECT
u.id,
username,
groupname
FROM
TABLE_USERS AS u
LEFT JOIN TABLE_GROUPS AS g
ON u.group = g.id

Related

compare user supplied list against table, with nulls for non-matching rows

Given a list of user-supplied numbers, which I'll refer to as myList, I want to find out which ones have a match against table MasterList, and which ones are null (no match)
so, given db contents
MasterList
----------
ID Number
1 3333333
2 4444444
3 5555555
If myList is ['1111111','2222222','3333333','4444444']
I want the following output:
1111111, null
2222222, null
3333333, 1
4444444, 2
Ideas I've tried:
This, of course, yields only the ones that match.
select Number, ID
from MasterList
where Number in('1111111','2222222','3333333','4444444')
My next idea is no more helpful:
select temp.Number, master.Number
from MasterList master
left join MasterList temp
on master.id=temp.id
and temp.Number in('1111111','2222222','3333333','4444444')
If the list were itself a table temp, it would be trivial to get the desired output:
select temp.number, master.id
from temp -- (ie, the list, where temp.number is the given list)
left join master on master.number=temp.number
-- optional where
where temp.number in('1111111','2222222','3333333','4444444')
This idea never materialized:
select temp.number, master.id
from (select '1111111','2222222','3333333','4444444') temp
left join master on master.number on....
Can this be done without a temporary table?
If not, how do you make a temporary table in DB2? (IBM documentation is helpful if you already know how to do it...)
You want an outer join, here a left outer join (we want all the rows from the left and any rows on the right that match the join condition), as you rightly say in the question. Here I'm using a Common Table Expression (CTE) to basically create a temp table on the fly.
WITH inlist (num) as ( VALUES
(111111),
(222222),
(333333),
(444444) )
SELECT num, id
FROM inlist LEFT OUTER JOIN masterlist
ON masterlist.number = inlist.num
ORDER BY num;
That yields:
NUM ID
----------- -----------
111111 -
222222 -
333333 1
444444 2
4 record(s) selected.
I'm not super-familiar with DB2 (haven't written SQL for that in at least 15 years, and not much back then), so I don't know how much you'll need to edit this to make it work, but I think this will do what you want (Edited SQL to use VALUES clause):
SELECT
my.Number1,
CASE WHEN ml.Number1 IS NULL
THEN NULL
ELSE ROW_NUMBER() OVER (ORDER BY my.Number1) END AS Indicator
FROM
(VALUES ('1111111'),('2222222'),('3333333'),('4444444'))
AS my(Number1)
LEFT JOIN
MasterList AS ml
ON
my.Number1 = ml.Number1;

Querying SQL table with different values in same column with same ID

I have an SQL Server 2012 table with ID, First Name and Last name. The ID is unique per person but due to an error in the historical feed, different people were assigned the same id.
------------------------------
ID FirstName LastName
------------------------------
1 ABC M
1 ABC M
1 ABC M
1 ABC N
2 BCD S
3 CDE T
4 DEF T
4 DEG T
In this case, the people with ID’s 1 are different (their last name is clearly different) but they have the same ID. How do I query and get the result? The table in this case has millions of rows. If it was a smaller table, I would probably have queried all ID’s with a count > 1 and filtered them in an excel.
What I am trying to do is, get a list of all such ID's which have been assigned to two different users.
Any ideas or help would be very appreciated.
Edit: I dont think I framed the question very well.
There are two ID's which are present multiple time. 1 and 4. The rows with id 4 are identical. I dont want this in my result. The rows with ID 1, although the first name is same, the last name is different for 1 row. I want only those ID's whose ID is same but one of the first or last names is different.
I tried loading ID's which have multiple occurrences into a temp table and tried to compare it against the parent table albeit unsuccessfully. Any other ideas that I can try and implement?
SELECT
ID
FROM
<<Table>>
GROUP BY
ID
HAVING
COUNT(*) > 1;
SELECT *
FROM myTable
WHERE ID IN (
SELECT ID
FROM myTable
GROUP BY ID
HAVING MAX(LastName) <> MIN(LastName) OR MAX(FirstName) <> MIN(FirstName)
)
ORDER BY ID, LASTNAME

Joining tables on multiple conditions

I have a little problem - since im not very experienced in SQL - about joining the same table on multiple values. Imagine there is table 1 (called Strings):
id value
1 value1
2 value2
and then there is table 2 (called Maps):
id name description
1 1 2
so name is reference into the Strings table, as is description. Without the second field referencing the Strings table it would be no problem, id just do an inner join on Strings.id = Maps.name. But now id like to obtain the actual string also for description. What would be the best approach for a SELECT that returns me both? Right now it looks like this:
SELECT Maps.id, Strings.value AS mapName FROM Maps INNER JOIN Strings ON Strings.id = Maps.name;
But that obviously only covers one of the localized names. Thank you in advance.
You can do this with two joins to the same table:
SELECT m.id, sname.value AS mapName, sdesc.value as description
FROM Maps m INNER JOIN
Strings sname
ON sname.id = m.name INNER JOIN
Strings desc
ON sdesc.id = m.description;
Note the use of table aliases to distinguish between the two tables.
As long as you want to get a single value from another table, you can use subqueries to do these lookups:
SELECT id,
(SELECT value FROM Strings WHERE id = Maps.name) AS name,
(SELECT value FROM Strings WHERE id = Maps.description) AS description
FROM Maps

What kind of SQL join do I need to compress a One to Many relationship into the same view row?

Edit: this isn't to be a dynamic output, the output view structure is fixed.
I am trying to create a SQL Server view that shows a single fixed column row for each user, and flattens out an associated one to many table into that row.
Although the associated table has a one to many relationship, the output table structure is limited to 4 elememts form that table.
My table structure is like so:
User (Id, FirstName, LastName)
Assessment (Id, Date, Location, User_Id)
Topics (Id, Topic, Assessment_Id)
Where the Assessment is joined to the User by the User_Id (One 2 One), and the Topics are joined to the Assessment by the Assessment_Id.
So, if I have three topics for an assessment, I'd want the view to look something like:
User_Id | FirstName | LastName | Date | Location | Topic1 | Topic2 | Topic3 | Topic4 |
1 | dave | toby | 2/2/11 | In situ | apples | pears | lemons | NULL |
My current SQL looks like this:
SELECT User.Id, User.FirstName, User.LastName, Assessment.Date, Assessment.Location, Topic.Topic
FROM User LEFT OUTER JOIN
Assessment INNER JOIN
Topic ON Assessment.Id = Topic.Assessment_Id ON
User.Id = Assessment.User_Id
But this returns a row for each concern - it doesn't compress them to one line. I've played with a few different joins, but haven't been able to get the behaviour I want.
Is it possible to do this in a view?
What do I need to do to make it happen??
Thanks!
There is no such JOIN. SQL has a fixed column output: so you can't add arbritrary numbers of columns. It doesn't matter if it's a view, direct or in a stored procedure.
There are 2 main options
concatenate the many rows into one column which is a popular questions here on SO. One random solution using XML PATH
use dynamic SQL to add a column per row in a stored procedure.
Note: PIVOT is fixed column output too
Edit: for a maximum of 4 child rows
SELECT
P.col1, P.col2,
C1.col1 AS Topic1,
C2.col1 AS Topic2,
C3.col1 AS Topic2,
C4.col1 AS Topic4
FROM
Parent P
LEFT JOIN
Child C1 ON P.Key = C1.FKey AND C1.ID = 1
LEFT JOIN
Child C2 ON P.Key = C2.FKey AND C2.ID = 2
LEFT JOIN
Child C3 ON P.Key = C3.FKey AND C3.ID = 3
LEFT JOIN
Child C4 ON P.Key = C4.FKey AND C4.ID = 4
You can use PIVOT too but I prefer the simpler self joins.
Take a look at PIVOT table functionality - e.g. http://www.help-sql.info/27/9/610208.html and http://blog.sqlauthority.com/2008/05/22/sql-server-pivot-table-example/
Although you will need to know the AssessmentId's before you can write the PIVOT

SQL how to map a row to column?

Database structure with two 1-n connections.
User table
==========
user_id
Attribute table
===============
attribute_id
user_id
attribute_name
Attribute_Value table
=====================
attribute_value_id
attribute_id
attribute_value
Is there a way that I can receive the data in the following row style:
user_id | firstname | lastname
---------------------------
1 | Simon | Smith
2 | John | Doe
Where name is the first attribute_name entry from the Attribute table and lastname the second.
Since you know the names of the values that you are looking for, a join will do the trick.
select
u.user_id,
a1.attribute_value as firstname,
a2.attribute_value as lastname
from User u
inner join Attribute a1
on u.user_id = a1.user_id
and a1.attribute_name = 'name'
inner join Attribute_Value v1
on a1.attribute_id = v1.attribute_id
inner join Attribute a2
on u.user_id = a2.user_id
and a2.attribute_name = 'lastname'
inner join Attribute_Value v2
on a2.attribute_id = v2.attribute_id
or so (did not run this). Use left joins if not everybody has a first or last name.
The only way you can do it with a dynamic number of columns is to use dynamic sql- use one sql statement to generate another by string concatenation.
Edit: I am not sure this is possible in mysql since there's not pivot command. Let me know if you want to see an example query from MS Sql I wrote. It is a similar data model, but there's just a lot more fields / joins.
Also I want to point out that you have a flaw in your design if attribute names wind up not being unique.