SQL column values based on values from another table - sql

I'm new to SQL and can't figure this simple thing out.
I have two tables:
+----------+----------+
| Person_1 | Person_2 |
+----------+----------+
| 1 | 2 |
| 1 | 4 |
| 3 | 2 |
+----------+----------+
and
+----+------+
| ID | City |
+----+------+
| 1 | A |
| 2 | B |
| 3 | A |
| 4 | A |
+----+------+
The values in columns Person_A and Person_B refer to the ID in the second table. I want to combine these two tables to have a resulting table like this:
+----------+----------+---------------+---------------+
| Person_1 | Person_2 | Person_1_City | Person_2_City |
+----------+----------+---------------+---------------+
| 1 | 2 | A | B |
| 1 | 4 | A | A |
| 3 | 2 | A | B |
+----------+----------+---------------+---------------+
How can I do this?

Use JOIN with city twice:
select p.persion1, p.persion2,
c1.city as persion1city,
c2.city as persion2city
from person p
left join city c1 on p.Person_1 = c1.id
left join city c2 on p.Person_2 = c2.id

Related

SQL Query - Add column data from another table adding nulls

I have 2 tables, tableStock and tableParts:
tableStock
+----+----------+-------------+
| ID | Num_Part | Description |
+----+----------+-------------+
| 1 | sr37 | plate |
+----+----------+-------------+
| 2 | sr56 | punch |
+----+----------+-------------+
| 3 | sl30 | crimper |
+----+----------+-------------+
| 4 | mp11 | holder |
+----+----------+-------------+
tableParts
+----+----------+-------+
| ID | Location | Stock |
+----+----------+-------+
| 1 | A | 2 |
+----+----------+-------+
| 3 | B | 5 |
+----+----------+-------+
| 5 | C | 2 |
+----+----------+-------+
| 7 | A | 1 |
+----+----------+-------+
And I just want to do this:
+----+----------+-------------+----------+-------+
| ID | Num_Part | Description | Location | Stock |
+----+----------+-------------+----------+-------+
| 1 | sr37 | plate | A | 2 |
+----+----------+-------------+----------+-------+
| 2 | sr56 | punch | NULL | NULL |
+----+----------+-------------+----------+-------+
| 3 | sl30 | crimper | B | 5 |
+----+----------+-------------+----------+-------+
| 4 | mp11 | holder | NULL | NULL |
+----+----------+-------------+----------+-------+
List ALL the rows of the first table and if the second table has the info, in this case 'location' and 'stock', add to the column, if not, just null.
I have been using inner and left join but some rows of the first table disappear because the lack of data in the second one:
select tableStock.ID, tableStock.Num_Part, tableStock.Description, tableParts.Location, tableParts.Stock from tableStock inner join tableParts on tableStock.ID = tableParts.ID;
What can I do?
You can use left join. Here is the demo.
select
s.ID,
Num_Part,
Description,
Location,
Stock
from Stock s
left join Parts p
on s.ID = p.ID
order by
s.ID
output:
| id | num_part | description | location | stock |
| --- | -------- | ----------- | -------- | ----- |
| 1 | sr37 | plate | A | 2 |
| 2 | sr56 | punch | NULL | NULL |
| 3 | sl30 | crimper | B | 5 |
| 4 | mp11 | holder | NULL | NULL |

Merge columns on two left joins

I have 3 tables as shown:
Video
+----+--------+-----------+
| id | name | videoSize |
+----+--------+-----------+
| 1 | video1 | 1MB |
| 2 | video2 | 2MB |
| 3 | video3 | 3MB |
+----+--------+-----------+
Survey
+----+---------+-----------+
| id | name | questions |
+----+---------+-----------+
| 1 | survey1 | 1 |
| 2 | survey2 | 2 |
| 3 | survey3 | 3 |
+----+---------+-----------+
Sequence
+----+---------+-----------+----------+
| id | videoId | surveyId | sequence |
+----+---------+-----------+----------+
| 1 | null | 1 | 1 |
| 2 | 2 | null | 2 |
| 3 | null | 3 | 3 |
+----+---------+-----------+----------+
I would like to query Sequence and join on both of video and survey tables and merge common columns without specifying the column names (in this case name) like this:
Query Result:
+----+---------+-----------+----------+---------+-----------+-----------+
| id | videoId | surveyId | sequence | name | videoSize | questions |
+----+---------+-----------+----------+---------+-----------+-----------+
| 1 | null | 1 | 1 | survey1 | null | 1 |
| 2 | 2 | null | 2 | video2 | 2MB | null |
| 3 | null | 3 | 3 | survey3 | null | 3 |
+----+---------+-----------+----------+---------+-----------+-----------+
Is this possible?
BTW the below sql doesn't work as it doesn't merge on the name field:
SELECT * FROM "Sequence"
LEFT JOIN "Survey" ON "Survey"."id" = "Sequence"."surveyId"
LEFT JOIN "Video" ON "Video"."id" = "Sequence"."videoId"
This query will show what you want:
select
s.*,
coalesce(y.name, v.name) as name, -- picks the right column
v.videoSize,
y.questions
from sequence s
left join survey y on y.id = s.surveyId
left join video v on v.id = s.videoId
However, the SQL standard requires you to name the columns you want. The only exception being * as shown above.

SQL get interection of values across multiple rows grouped by primary key

I have table with data as follows
+----+------+
| id | code |
+----+------+
| 1 | M |
| 1 | Y |
| 2 | M |
| 2 | S |
| 3 | M |
| 3 | Q |
+----+------+
I would like to know if its possible to write a query that would return a list of codes that are unique to each ID? If there is no intersection the query should return no rows.
In the example above the only value common to all is M.
+----+------+
| id | code |
+----+------+
| 1 | M |
| 1 | S |
| 2 | M |
| 2 | S |
| 2 | H |
| 3 | M |
| 3 | S |
| 3 | Q |
+----+------+
The above would return M and S, common to all three ID's
Thanks
Try this:
SELECT code
FROM mytable
GROUP BY code
HAVING COUNT(*) = (SELECT COUNT(DISTINCT id) FROM mytable)
The above query assumes that code can appear only once per id.

SQL compare multiple rows or partitions to find matches

The database I'm working on is DB2 and I have a problem similar to the following scenario:
Table Structure
-------------------------------
| Teacher Seating Arrangement |
-------------------------------
| PK | seat_argmt_id |
| | teacher_id |
-------------------------------
-----------------------------
| Seating Arrangement |
-----------------------------
|PK FK | seat_argmt_id |
|PK | Row_num |
|PK | seat_num |
|PK | child_name |
-----------------------------
Table Data
------------------------------
| Teacher Seating Arrangement|
------------------------------
| seat_argmt_id | teacher_id |
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
| 5 | 2 |
------------------------------
---------------------------------------------------
| Seating Arrangement |
---------------------------------------------------
| seat_argmt_id | row_num | seat_num | child_name |
| 1 | 1 | 1 | Abe |
| 1 | 1 | 2 | Bob |
| 1 | 1 | 3 | Cat |
| | | | |
| 2 | 1 | 1 | Abe |
| 2 | 1 | 2 | Bob |
| 2 | 1 | 3 | Cat |
| | | | |
| 3 | 1 | 1 | Abe |
| 3 | 1 | 2 | Cat |
| 3 | 1 | 3 | Bob |
| | | | |
| 4 | 1 | 1 | Abe |
| 4 | 1 | 2 | Bob |
| 4 | 1 | 3 | Cat |
| 4 | 2 | 2 | Dan |
---------------------------------------------------
I want to see where there are duplicate seating arrangements for a teacher. And by duplicates I mean where the row_num, seat_num, and child_name are the same among different seat_argmt_id for one teacher_id. So with the data provided above, only seat id 1 and 2 are what I would want to pull back, as they are duplicates on everything but the seat id. If all the children on the 2nd table are exact (sans the primary & foreign key, which is seat_argmt_id in this case), I want to see that.
My initial thought was to do a count(*) group by row#, seat#, and child. Everything with a count of > 1 would mean it's a dupe and = 1 would mean it's unique. That logic only works if you are comparing single rows though. I need to compare multiple rows. I cannot figure out a way to do it via SQL. The solution I have involves going outside of SQL and works (probably). I'm just wondering if there is a way to do it in DB2.
Does this do what you want?
select d.teacher_id, sa.row_num, sa.seat_num, sa.child_name
from seatingarrangement sa join
data d
on sa.seat_argmt_id = d.seat_argmt_id
group by d.teacher_id, sa.row_num, sa.seat_num, sa.child_name
having count(*) > 1;
EDIT:
If you want to find two arrangements that are the same:
select sa1.seat_argmt_id, sa2.seat_argmt_id
from seatingarrangement sa1 join
seatingarrangement sa2
on sa1.seat_argmt_id < sa2.seat_argmt_id and
sa1.row_num = sa2.row_num and
sa1.seat_num = sa2.seat_num and
sa1.child_name = sa2.child_name
group by sa1.seat_argmt_id, sa2.seat_argmt_id
having count(*) = (select count(*) from seatingarrangement sa where sa.seat_argmt_id = sa1.seat_argmt_id) and
count(*) = (select count(*) from seatingarrangement sa where sa.seat_argmt_id = sa2.seat_argmt_id);
This finds the matches between two arrangements and then verifies that the counts are correct.

Finding number of types of accounts from each customer

I am having a lot of trouble with trying to construct a query that will give me the name of each customer and the number of different types of accounts each has. The three types are Checkings, Savings, and CD.
customers:
+--------+--------+
| cid | name |
+--------+--------+
| 1 | a |
| 2 | b |
| 3 | c |
+--------+--------+
accounts:
+-----------+-----------+
| aid | type |
+-----------+-----------+
| 1 | Checkings |
| 2 | Savings |
| 3 | Checkings |
| 4 | CD |
| 5 | CD |
| 6 | Checkings |
+-----------+-----------+
transactions:
+--------+--------+--------+
| tid | cid | aid |
+--------+--------+--------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 3 |
| 4 | 3 | 4 |
| 5 | 1 | 5 |
| 6 | 3 | 4 |
| 7 | 1 | 6 |
+--------+--------+--------+
The expected answer would be:
a, 3
b, 1
c, 1
Getting the names is simple enough, but how can I keep count of each individual's account as well as compare the accounts to make sure that it is not the same type?
just add DISTINCT inside the COUNT
SELECT a.cid, a.name, COUNT(DISTINCT c.type) totalCount
FROM customers a
INNER JOIN transactions b
ON a.cis = b.cid
INNER JOIN accounts c
ON b,aid = c.aid
GROUP BY a.cid, a.name
Query:
SQLFiddleExample
SELECT
a."name",
COUNT(DISTINCT c."type") totalCount
FROM customers a
INNER JOIN transactions b
ON a."cid" = b."cid"
INNER JOIN accounts c
ON b."aid" = c."aid"
GROUP BY a."cid", a."name"
ORDER BY totalCount DESC
Result:
| NAME | TOTALCOUNT |
---------------------
| a | 3 |
| b | 1 |
| c | 1 |