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

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.

Related

SQL return only rows where value exists multiple times and other value is present

I have a table like this in MS SQL SERVER
+------+------+
| ID | Cust |
+------+------+
| 1 | A |
| 1 | A |
| 1 | B |
| 1 | B |
| 2 | A |
| 2 | A |
| 2 | A |
| 2 | B |
| 3 | A |
| 3 | B |
| 3 | B |
| 3 | C |
| 3 | C |
+------+------+
I don't know the values in column "Cust" and I want to return all rows where the value of "Cust" appears multiple times and where at least one of the "ID" values is "1".
Like this:
+------+------+
| ID | Cust |
+------+------+
| 1 | A |
| 1 | A |
| 1 | B |
| 1 | B |
| 2 | A |
| 2 | A |
| 2 | A |
| 2 | B |
| 3 | A |
| 3 | B |
| 3 | B |
+------+------+
Any ideas? I can't find it.
You may use COUNT window function as the following:
SELECT ID, Cust
FROM
(
SELECT ID, Cust,
COUNT(*) OVER (PARTITION BY Cust) cn,
COUNT(CASE WHEN ID=1 THEN 1 END) OVER (PARTITION BY Cust) cn2
FROM table_name
) T
WHERE cn>1 AND cn2>0
ORDER BY ID, Cust
COUNT(*) OVER (PARTITION BY Cust) to check if the value of "Cust" appears multiple times.
COUNT(CASE WHEN ID=1 THEN 1 END) OVER (PARTITION BY Cust) to check that at least one of the "ID" values is "1".
See a demo.

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.

Transform data in rows to columns

I'm trying to transform data of the following form:
| ID | X | Y |
--------------
| 1 | a | m |
| 1 | b | n |
| 1 | c | o |
| 2 | d | p |
| 2 | e | q |
| 3 | f | r |
| 3 | g | s |
| 3 | h | |
To this form:
| ID | X1 | X2 | X3 | Y1 | Y2 | Y3 |
------------------------------------
| 1 | a | b | c | m | n | o |
| 2 | d | e | | p | q | |
| 3 | f | g | h | r | s | |
What is the best way to accomplish this in SQL Server 2017? Is there a better way to do transformations like this using another tool?
I don't think you can solve this problem on the DB side. You should do some backend programming. You would be able to use Pivot function, if you wanted to reverse your row values as column but you want to group them based on duplicate ids. I would solve this problem by checking duplicates by using the query below. At the results of that query, you'll be able to get max count for duplicated id. For example 1 duplicated 3 times, so you need to create a data table with 3x2+1=7 columns in your backend code. 1 stands for id column. After that you can just fill that table by checking data for each id.
WITH Temp (id, count)
AS
(
Select id, count(*)
from MyTable
group by id
having count(*)>1
)
select max(count) from Temp

Limit a sorted number of rows joined

I have two tables, A and B, and a join table M. I want to, for each A.id, get the top 2 B.id's sorting on the value in table M, producing the results below. This is running on an Azure SQL database
Table A Table M Table B
+-----+ +-----+-----+-------+ +-----+
| Id | | AId | BId | Value | | Id |
+-----+ +-----+-----+-------+ +-----+
| 1 | | 1 | 3 | 4 | | 1 |
| 2 | | 1 | 2 | 3 | | 2 |
| 3 | | 3 | 2 | 3 | | 3 |
| 4 | | 3 | 5 | 6 | | 4 |
+-----+ | 3 | 3 | 4 | | 5 |
| 4 | 1 | 2 | +-----+
| 4 | 2 | 1 |
| 4 | 4 | 3 |
+-----+-----+-------+
Result
+-----+-----+-------+
| AId | BId | Value |
+-----+-----+-------+
| 1 | 3 | 4 |
| 1 | 2 | 3 |
| 3 | 5 | 6 |
| 3 | 3 | 4 |
| 4 | 1 | 2 |
| 4 | 4 | 3 |
+-----+-----+-------+
I know that I can select all the M.AId rows where they equal 1, sort it, and limit by 2, but I need to do this for every row in Table A. I've made an attempt to use group by, but I wasn't sure how to sort and limit it. I've also tried to search for resources associated with this issue but I couldn't find any resources.
(I also wasn't sure how to word the title for this issue)
You can just use ROW_NUMBER:
SELECT
AId, BId, Value
FROM (
SELECT *,
Rn = ROW_NUMBER() OVER(PARTITION BY AId ORDER BY Value DESC)
FROM M
) t
WHERE Rn <= 2

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.