Most efficient way to get lists? - sql

What's the best way to get a sublist of things?
I have two tables:
create table A (
id int primary key
)
create table B (
id int primary key,
aid int foreign key references A( id ),
sort_key int
)
I want to get a list of objects A, with subobjects B, but only the top five of B.
Let's say A is people, and B is type of food, with sort_key being how much a person likes that food. How do I get everybody (or some people) and their top 5 food?

On the previous comment if it's an INT you can't put non numerics in there.
Assuming the following data:
a
--
id
1
2
3
b
------------------------
id aid sort_key
1 1 1
2 1 2
3 2 1
4 3 1
5 1 3
6 1 4
7 1 5
8 1 6
9 2 2
10 2 3
The following query in MySQL would give you this:
SELECT a.*,
(SELECT GROUP_CONCAT(id) AS ids FROM b AS bi WHERE bi.aid = a.id ORDER BY sort_key LIMIT 5) AS ids
FROM a
Result:
id ids
1 1,2,5,6,7,8
2 3,9,10
3 4

This query assumes the sort key is one based, rather than zero:
SELECT a.name
b.food
FROM A a
JOIN B b ON b.aid = a.id
WHERE b.sortkey <= 5
ORDER BY a.name, b.sortkey

Related

Convert one-to-many relationship to one-to-one in SQL

I have two tables, A and B
Table A:
Id int
Id_B int (FK with B)
Table B:
Id
Current state: An Id in table A can have multiple Id_B relationships, and two Id from table A can have the same Id_B
New state: I wish to make it so that two Id from table A cannot have the same Id_b. If I find such a relationship then create a new Id in table B and link it to the Id in table A to enforce the aforementioned constraint
I am lost on where to begin with this in SQL.
Before
Table A
Id
Id_B
1
1
1
4
2
1
3
2
4
3
Table B
Id
1
2
3
4
After (since there were two occurrences of 1 in the Id_B column, I created a new row in table B (with id 5), and assigned the Id of this to the Id 2 in table A to enforce uniqueness)
Table A
Id
Id_B
1
1
1
4
2
5
3
2
4
3
Table B
Id
1
2
3
4
5
Find all duplicate A.B_ID rows, add new B rows for the duplicates and update the corresponding A.B_ID.
Start with a query like:
with q as
(
select *, row_number() over (partition by B_ID, order by ID) rn
from A
)
select *
from q
where rn > 1
Add a unique constraint on A.B_ID

Query to get index of member in multiple classes?

Given the following table Attendance, where AttID is the primary key and table is sorted. I'm attempting to search in the index of MemberID in each ClassID, or return total members in a class if the MemberID does not exist in the class (this condition is less important to me).
AttID
ClassID
MemberID
1
1
1
2
1
2
3
1
3
4
2
30
5
2
40
5
2
1
6
2
50
For example:
Given the target MemberID is 1, I will get the following
ClassID
Index
1
1
2
3
Given the target MemberID is 2, I will get the following
ClassID
Index
1
2
2
4
I'm using these results to determine whether a member that attended a class is within the classes' capacity.
"5" is repeated for Attid so it is not a primary key. I will assume this is a typo.
You have basically described the row_number() function:
select a.*
from (select a.*,
row_number() over (partition by classid order by attid) as seqnum
from attendance a
) a
where memberid = ?

How to compare each group of one table with a column in another table?

I have two tables:
TypeTable
TypeId PersonClassificationId
----------------------
1 1
1 2
1 3
2 1
2 2
PersonClassificationTable
PersonClassificationId Capacity
----------------------
1 2
2 2
3 2
I need to select such TypeId that in the entire TypeTable table do not have at least one PersonClassificationID specified in PersonTable.
So, if PersonTable has 1, 2, 3, then TypeId = 2 should be selected, because there is no record in TypeTable:
TypeId PersonClassificationId
----------------------
2 3
How can I do that?
It is undesirable to use cursors : )
I think that you can do what you want by generating all possible combinations of types and classifications, and then filter on those that do not exist in the mapping table:
select t.TypeId, pc.PersonClassificationId
from (select distinct TypeId from TypeTable) t
cross join PersonClassificationTable p
where not exists (
select 1
from TypeTable t1
where t1.TypeId = t.TypeId and t1.PersonClassificationId = p.PersonClassificationId
)

Update table column that is used for ordering according to alphabetical order on a second table

So I have two tables (A and B) that have a relation of n-n.
So there is a third table (C) that is used to connect both tables.
Table A and B both have an Id and a name.
Table C has IDA, IDB and an Order, the number that is used to sort and that is user given.
My issue is that I need to migrate table C since I just added that order column and so I need to give every line an ordering number, according to the B name.
So if table A has:
Id Name
1 A
2 B
3 C
And Table B has:
Id Name
1 J
2 L
3 M
And table C has:
IdA IdB Order
1 2 0
1 1 0
1 3 0
2 1 0
2 3 0
I need a query that updates table C to be more like:
IdA IdB Order
1 2 2
1 1 1
1 3 3
2 1 1
2 3 2
I have a query that can basically do what i want but it leaves me with "gaps"
reading my results above i get:
IdA IdB Order
1 2 2
1 1 1
1 3 3
2 1 1
2 3 3
I think this should work for what you need:
With ToUpdate As
(
Select C.*,
Row_Number() Over (Partition By C.IdA Order By B.Name) As NewOrder
From C
Join B On B.Id = C.IdB
)
Update C
Set "Order" = U.NewOrder
From ToUpdate U
Where U.IdA = C.IdA
And U.IdB = C.IdB
(In full disclosure, I'm not terribly familiar with postgres, but I think this should be valid).

Select unique subsets

I have a table like in example below.
SQL> select * from test;
ID PARENT_ID NAME
1 1 A
2 1 B
3 2 A
4 2 B
5 3 A
6 3 B
7 3 C
8 4 A
What I need is to get all unique subsets of names ((A,B), (A,B,C), (A)) or exclude duplicate subsets. You can see that (A,B) is twice there, one for PARENT_ID=1 and one for 2.
I want to exclude such duplicates:
ID PARENT_ID NAME
1 1 A
2 1 B
5 3 A
6 3 B
7 3 C
8 4 A
You can use DISTINCT to only return different values.
e.g.
SELECT DISTINCT GROUP_CONCAT(NAME SEPARATOR ',') as subsets
FROM TABLE_1
GROUP BY PARENT_ID;
SQL Fiddle
I have used 'group_concat' assuming you are using 'Mysql'. The equivalent function in Oracle is 'listagg()'. you can see it in action here in SQL fiddle
Here is the solution:-
Select a.* from
test a
inner join
(
Select nm, min(parent_id) as p_id
from
(
Select Parent_id, group_concat(NAME) as nm
from test
group by Parent_ID
) a
group by nm
)b
on a.Parent_id=b.p_id
order by parent_id, name