Select from related tables - mysql

So in my data base i have 2 tables related:
The fields are id, name, price and a int so i know if they a all sold or not
fruits
|IDfruit| name | price | sold |
| 1 |orange | 5 | 0
| 2 |apple | 10 | 0
| 3 |grape | 15 | 1
| 4 |lemon | 7 | 1
primary key is IDfruit
images
|IDimage| url | idfruit_image
| 1 | image1.png | 1
| 2 | image2.png | 1
| 3 | image3.png | 2
| 4 | image4.png | 3
| 5 | image5.png | 4
| 6 | image6.png | 4
| 7 | image7.png | 4
IDimage is primary key and idfruit_image is a foreign key that references IDfruit
The result i want is all fruits and the FIRST image of each fruit.
So what i've done is
select fruits.*, url , idfruit_image
from fruits,images
where IDfruit = idfruit_image;
This return all fruits and all images of each fruit, but i would like just one image of each fruit, how can i achieve this?
And what if i want everything from all sold fruits and just the first image of each one

Use GROUP BY to get one row per fruit, and an aggregation function to select one of the images.
SELECT f.*, MAX(url) AS url
FROM fruits AS f
LEFT JOIN images AS i ON f.idfruit = i.idfruit_image
GROUP BY f.idfruit

I understand that you want the first image per fruit, first being defined as: the image with the smallest idimage.
If you just want the url of the image, a correlated subquery should be an acceptable solution:
select
f.*,
(
select i.url
from images i
where i.idfruit_image = f.idfruit
order by i.idimage
limit 1
) url
from fruits f
If you want the whole image record, one option is to join, then filter with a subquery:
select f.*, i.*
from fruits f
inner join images i on i.idimage = (
select min(i1.idimage) from images i1 where i1.idfruit_image = f.idfruit
)
Finally: in MySQL 8.0, you can use row_number() for this:
select *
from (
select
f.*,
i.*,
row_number() over(partition by i.idfruit_image order by i.idimage) as rn
from fruits f
inner join images i on i.idfruit_image = f.idfruit
) t
where rn = 1

Related

MySql : how to join this two tables with multiple pivot

I want to get the last activity of my client but i dont know how to that that with two tables that have more than one pivot. Please look at to the example below :
table product
--------------------------------------------------------------------------------------------------------------------------------------
id | name | check_mo (Activity1) | check_mo_account_id | check_pa (Activity2) | check_pa_account_id
--------------------------------------------------------------------------------------------------------------------------------------------
1 | product1 | 01/02/2020 | 63 | 05/02/2020 | 100
2 | product2 | 01/03/2020 | 23 | 10/03/2020 | 63
-----------------------------------------------------------------------------------------------------------------------------------
Table account
--------------------------------
id | name
--------------------------------
23 | name1
63 | name2
100 | name3
--------------------------------
I want this result (last activity is the lastest date of (check_mo and check_pa). and relationship between tables is (account.id => product.check_mo_account_id and product.check_pa_account_id))
------------------------------------------------
id | name | last activity
-------------------------------------------------
23 | name1 | 01/03/2020
63 | name2 | 10/03/2020
100 | name3 | 05/02/2020
-------------------------------------------------
Unpivot the columns. In MySQL, you can use union all. Use join to bring in the names and then a window function to get the most recent date:
select pn.*
from (select pn.*, max(dte) over (partition by name) as max_dte
from ((select n.name, p.check_mo as dte, p.check_mo_account_id as account_id
from product p join
name n
on p.check_mo_account_id = n.id
) union all
(select n.name, p.check_pa, p.check_pa_account_id as account_id, p.check_pa
from product p join
name n
on p.check_mo_account_id = n.id
)
) pn
) pn
where dte = max_dte;
If I understand correctly, you have two check IDs and two check dates in one row, but want to treat them equally, just as if you had just one table with one check ID and one check date per row. Use UNION ALL to get this table. Then find the maximum date per ID and join this to the account table.
select id, account.name, aggregated.last_activity
from account
join
(
select id, max(check) as last_activity
from
(
select check_mo_account_id as id, check_mo as check from product
union all
select check_pa_account_id as id, check_pa as check from product
) unioned
group by id
) aggregated using (id)
order by id;

Group results from different tables by rank

I'm sure I'm not the first to need to do this, but I couldn't find a similar question that accounted for the nuance.
I have 3 tables (fav_food, fav_color, and fav_place) that all follow a similar pattern:
userid | label | rank |
=================================
1 | red | 1
1 | green | 2
1 | orange | 3
2 | blue | 1
2 | red | 2
...
Each table will have at most 3 items per userid, but some users might have fewer than 3, and other users might have none for a given table. i.e., it's possible user 10 has 2 favorite colors, 1 favorite food, and 0 favorite places.
I'm looking for a query that can output my data like so:
userid | fav_food | fav_place | fav_color | rank
===========================================================
1 | pizza | New York | red | 1
1 | burgers | NULL | green | 2
1 | NULL | NULL | orange | 3
2 | tacos | Chicago | blue | 1
2 | burgers | Orlando | red | 2
...
Basically, all ranked 1 items together, ranked 2 items together, and ranked 3 items together (NULLs were no item of that rank exists).
I was able to get it working using 3 separate queries (one for each table) + post processing at the application layer, but for the sake of my personal knowledge base, I was wondering if anyone knew how to do it in a single query.
Many thanks!
#Isick,
You can do this with a LEFT OUTER JOIN on each table to a table containing just the userid and rank. DEMO
select user_rank.userid, user_rank.rank, f.food, p.place, c.color from
(
select userid, rank from fav_food
union
select userid, rank from fav_place
union
select userid, rank from fav_color
) user_rank
left outer join
( select userid, rank, label as food from fav_food) f
on user_rank.userid = f.userid and user_rank.rank = f.rank
left outer join
( select userid, rank, label as place from fav_place) p
on user_rank.userid = p.userid and user_rank.rank = p.rank
left outer join
( select userid, rank, label as color from fav_color) c
on user_rank.userid = c.userid and user_rank.rank = c.rank
order by userid, rank

How to get 2 videos (that belongs to a program and program belogns to a category) for every category?

There 3 tables category, program, video. Every video belongs to a program, and any program belongs to a category.
Category table
id | title
1 | cartoons
2 | documental
Program table
id | programcode | title | category_id
1 | WUCU | Program Name | 1
2 | ELKI | Program Name | 2
Video table
id | videocode | title | program_id
1 | ELKI00001 | Name | 2
2 | ELKI00002 | Name | 2
3 | ELKI00003 | Name | 2
4 | WUCU00001 | Name | 1
5 | WUCU00002 | Name | 1
6 | WUCU00003 | Name | 1
I need to get last 2 videos for every category
The problem: MySQL doesn't support getting the top most N values in a group. So we have to do it ourselves. This means we need a way to group the sets together (Category.Title and a way to know which videos to return belonging to each category. We'd like to use LIMIT here to limit the results by 2 but we can't limit by 2 for each category. We'd also like to use MAX to get the highest video ID for each category but that doesn't get us the 2nd one. So we have to build those in ourselves.
This is built using logic found:
http://www.xaprb.com/blog/2006/12/07/how-to-select-the-firstleastmax-row-per-group-in-sql/
but altered to fit your data set
This is working but I don't know why I'm having to do multiple subselects for #RNUM and #VCAT. Not sure why they have to be separated at this point. fiddle
set #Rnum :=0, #VCat :='';
SELECT * FROM (
Select SequencedSet.*, #Rnum := if(mvcat = CTitle, #Rnum + 1, 1) RowNumber from (
SELECT CTitle, VCode, VTitle, VID, #vcat mvcat,
#VCat := CTitle as VCAT
FROM (
SELECT C.Title CTitle, V.Code VCode, V.Title VTitle, V.ID VID
FROM Video V
INNER JOIN Program P
on P.ID = V.Program_Id
INNER JOIN Category C
on C.ID = P.Category_ID
ORDER BY C.Title, V.ID DESC) orderedset) sequencedSet) X
where X.ROWNUMBER<=2

Show only 2 softwares for each category

I have a table software (id_software, software_name, category)
What SQL query to show only 2 software for each category ?
For example I want to get :
|id_software | software_name | category|
-+------------+---------------+---------+-
| 1 | Photoshop | 5 |
| 2 | illustrator | 5 |
| 3 | Firefox | 1 |
| 4 | I.E | 1 |
-+--------------------------------------+-
select * from
(select *
from table1 n
where
( select count(*)
from table1 m
where n.categorie = m.categorie
and n.id_software <= m.id_software) <= 2
order by n.id_software, n.id_software desc) as tn
Source: http://mindbuffer.wordpress.com/2013/07/09/mysql-get-the-top-2-rows-for-each-category-with-certain-condition/
You could use the row_number function for this.
SELECT *
FROM
(SELECT
Category,
id_software,
software_name,
[Nth_Software] = ROW_NUMBER() OVER (Partition by Category ORDER BY Id_software)
FROM
table
) T
WHERE
T.Nth_Software <=2
This gives you the first two software entries based on softwareId for every category.

Order table by presences in a third table

I have a movie database with a table for actors and another one for movies, I created a third table to add an actor partecipation in a movie. I added a field "star" to distinque leading actors from not leading actors.
I wish create a list order by the actors importance and so by the the total number of "stars".
SELECT a.id, a.name, COUNT( p.star ) AS star
FROM actors a
JOIN playing p, movies m
WHERE p.id_actor = a.id
AND p.id_movie = m.id
AND p.star =1
GROUP BY p.star
ORDER BY p.star DESC;
ACTORS
+----+---------+
| id | name |
+----+---------+
| 1 | actor01 |
| 2 | actor02 |
| 3 | actor03 |
+----+---------+
MOVIES
+----+----------+
| id | title |
+----+----------+
| 1 | movie01 |
| 2 | movie02 |
| 3 | movie03 |
+----+----------+
PLAYING
+----------+----------+-------+------+
| id_movie | id_actor | char | star |
+----------+----------+-------+------+
| 1 | 1 | char1 | 0 |
| 1 | 2 | char2 | 1 |
| 2 | 3 | char3 | 1 |
+----------+----------+-------+------+
I Need output Like:
+----------+--------------+
| actor | protagonist |
+----------+--------------+
| actor01 | 2 times |
| actor02 | 3 times |
+----------+--------------+
You need to fix the group by clause to group by the actor not the star column. You need to fix the order by to group by the aggregated column, not the original column:
SELECT a.id, a.name, sum( p.star = 1) AS stars
FROM actors a join playing p
on p.id_actor = a.id join
movies m
on p.id_movie = m.id
GROUP BY a.id, a.name
ORDER BY stars DESC;
Along the way, I fixed the from so it uses proper join syntax (with an on clause). And changed the query so it returns all actors, even those who have never been the star.
1.If you want to count all stars for an actor, you should group by actor but not stars.(Unless you want to count how many times an actor gets 1 star in a movie, you may not want to group by star)
2.You may want to use ON with JOIN
3.You may want to ORDER BY star but not ORDER BY p.star since you want to order by the result.
4.You may want to use SUM instead of COUNT to get the star counts.(SUM calculates the value but COUNT calculates the number. With SUM, you can set star value to whatever you want without change your sql. You can have star=2 which shows the actor is important to the movie or have star=-1, which means the actor stinks.)
You may have a look at the sql below:
SELECT a.id, a.name, SUM( p.star ) AS sum
FROM actors a
LEFT JOIN playing p ON p.id_actor = a.id
LEFT JOIN movies m ON p.id_movie = m.id
GROUP BY a.id
ORDER BY sum DESC;