How to implement mysql order by in following situation - mysql

There are 2 tables - Person(personName, areaId, birthDate) and Area(areaId, areaCode). Now I want a list of persons with their areaCode in a manner such that the all the persons from an areaCode are listed together and the areaCode with the oldest person should be first areaCode to appear. How do I achieve this through mysql query?
I have tried using ORDER BY with multiple columns in vain.
SELECT P.personName, A.areaCode, P.birthDate
FROM Person P
JOIN Area A ON P.areaId = A.areaId
ORDER BY P.birthDate, A.areaCode
Suppose there are Area1 and Area2. Area2 has the oldest and newest born person. Therefore, Area2 records should appear first.
Update: I managed to solve this, but is there any way I can shorten this code.
SELECT *
FROM (SELECT DISTINCT
A.areaCode,
MIN(P.birthDate)
FROM Area A
JOIN Person P on P.areaId = A.areaId
GROUP BY A.areaCode
ORDER BY P.birthDate) T1
JOIN (SELECT DISTINCT
P.personName,
A.areaCode,
P.birthDate
FROM Area A
JOIN Person P on P.areaId = A.areaId
ORDER BY P.createdTimestamp) T2 ON T1.barcode = T2.binId;

You start with a subquery to get the earliest birthdate in each area code.
SELECT A.areaId, MIN(birthDate) earliestBirthDate
FROM Area A
JOIN Person P ON A.areaId = p.areaId
GROUP BY A.areaId
You can try this subquery to convince yourself it yields exactly one row per areaId, showing the needed earliest birth date.
Then you JOIN the subquery to your detail query
SELECT P.personName, A.areaCode, P.birthDate
FROM Person P
JOIN Area A ON P.areaId = A.areaId
JOIN (
SELECT A.areaId, MIN(birthDate) earliestBirthDate
FROM Area A
JOIN Person P ON A.areaId = p.areaId
GROUP BY A.areaId
) EARLY ON P.areaId = EARLY.areaID
ORDER BY EARLY.earliestBirthDate, P.birthDate, A.areaCode
The trick is to use a subquery to make a virtual table so you can order on a column from that table.
Pro tip: If you find yourself using SELECT DISTINCT to retrieve detail data from tables, it's a caution flag. You may be doing something wrong.

Related

Join 2 different counts from 2 different tables into one subtable in sql

I'm having a problem in where i want to count how many medals in total a country has won from both the individual and team competitions does not give me the disered outcome. i have managed so far tocome up with this.
select distinct C.Cname as Country, count(i.medal) as Medals_Won
from individual_results as i, Country as C, participant as p
where (i.Olympian = p.OlympicID and C.Cname = p.country)
union
select distinct C.Cname, count(r.medal) as medals_Won
from team_results as r, Country as C, participant as p, team as t
where (r.team = t.TeamID and t.Member1 = p.OlympicID and C.Cname = p.Country)
group by C.Cname
order by medals_won desc
enter image description here
but i get this result.
even tho if i run the two separate pieces of code i ge the wanted restuls that is enter image description here
You say you can run your query and it gives you a result. This is bad. It indicates that you are MySQL's notorious cheat mode that lets you run invalid queries.
You have something like this:
select ...
union
select ...
group by ...
order by ...
There are two queries the results of which you glue together, namely
select ...
and
select ...
group by ...
So, your first query becomes:
select distinct C.Cname as Country, count(i.medal) as Medals_Won
from individual_results as i, Country as C, participant as p
where (i.Olympian = p.OlympicID and C.Cname = p.country)
You COUNT medals, i.e. you aggregate your data. And there is no GROUP BY clause. So you get one result row from all your data. You say you want to count all rows for which i.medal is not null. But you also want to select the country. The country? Which??? Is there just one country in the tables? And even then your query would be invalid, because still you'd have to tell the DBMS from which row to pick the country. You can pick the maximum country (MAX(C.Cname)) for instance or the minimum country (MIN(C.Cname)), but not the country.
The DBMS should raise an error on this invalid query, but you switched that off.
Make sure in MySQL to always
SET sql_mode = 'ONLY_FULL_GROUP_BY';
It is the default in more recent versions, so either you are working with old software or you switched from good mode to bad mode voluntarily.
And talking of old software: Even at the first moment MySQL was published, comma joins had long been deprecated. They were made redudant in 1992. Please don't ever use commas in your FROM clause. Use explicit joins ([INNER] JOIN, LEFT [OUTER] JOIN, etc.) instead.
As to the task, here is a straight-forward solution with joins:
select
c.cname as country,
coalesce(i.medals, 0) as medals_individual,
coalesce(t.medals, 0) as medals_team,
coalesce(i.medals, 0) + coalesce(t.medals, 0) as medals_total
from country c
left outer join
(
select p.country, count(ir.medal) as medals
from participant p
join individual_results ir on ir.olympian = p.olympicid
group by p.country
) i on on i.country = c.name
left outer join
(
select p.country, count(ir.medal) as medals
from participant p
join team t on t.member1 = p.olympicid
join team_results tr on tr.team = t.teamid
group by p.country
) t on on t.country = c.name
order by medals_total desc;
You should sum the union result for each of the subquery grouped by cname
select t.Cname , sum( t.Medals_Won)
from (
select C.Cname as Country, count(i.medal) Medals_Won
from individual_results i
inner join participant p ON i.Olympian = p.OlympicID
inner join Country C ON C.Cname = p.country
group by C.Cname
union
select distinct C.Cname, count(r.medal)
from team_results as r
inner join team as t ON r.team = t.TeamID
inner join participant as p ON t.Member1 = p.OlympicID
inner join Country as C ON C.Cname = p.Country
group by C.Cname
) t
group by t.Cname
order by sum( t.Medals_Won) desc

Return the company with most film in a genre

I am working on this project at my university, where I need to create a query to the database. I want the query to return the company with most movies in the given genre. At the moment I have this query, but this only return one company, but there can probably be more than one.
SELECT CompanyID, CategoryID, COUNT(*) as NumberOfMovies
FROM Movie
NATURAL JOIN CategoryFilm
NATURAL JOIN Category
NATUAL JOIN Comapny
GROUP BY CategoryID, CompanyID
Order by NumberOfMovies DESC LIMIT 1
I beleave I will need a "having" in here.
pls try this, it may because you added limit 1, which only show 1st retrieved record
SELECT CompanyID, CategoryID, COUNT(*) as NumberOfMovies
FROM Movie
NATURAL JOIN CategoryFilm
NATURAL JOIN Category
NATURAL JOIN Comapny
GROUP BY CategoryID, CompanyID
Order by NumberOfMovies DESC
I assume by "category" you mean "genre" -- or that they are the same thing.
Do not use NATURAL JOIN. It does not even use properly declared foreign key relationships, instead relying merely on name similarity between tables. It is dangerous because the columns used are not specified and can introduce hard-to-debug errors. I often refer to it as an "abomination" because it does not take table declarations into account.
If you have a given category, then I would expect a WHERE clause:
SELECT CompanyID, COUNT(*) as NumberOfMovies
FROM Movie m JOIN
CategoryFilm cf
ON cf.movie_id = m.movie_id JOIN
Company c
ON c.company_id = m.company_id
WHERE cf.category_id = ?
GROUP BY CategoryID
ORDER BY NumberOfMovies DESC
LIMIT 1;
If you want to allow ties, you can use window function rank():
select *
from (
select
co.companyID,
ca.categoryID,
count(*) NumberOfMovies,
rank() over(partition by c.categoryID order by count(*) desc) rn
from movie m
inner join categoryFilm cf on cf.movieID = m.movieID
inner join category ca on ca.categoryID = cf.categoryID
inner join company co on co.companyID = m.companyID
group by co.companyID, ca.categoryID
) t
where rn = 1
order by ca.categoryID
This gives you the top company for each and every category, ties included. If you want to filter on a given category, you can just add a where clause to the inner query.
Side note: do not use natural joins: they are error-prone. I rewrote the query to use inner joins instead (I made a few assumptions on the relations).

How to bring ALL data from a query in MySQL?

I want to write a query that can show the amount of purchases made in the month of June, grouped by city. So I wrote this query:
SELECT state, city, COUNT(*)
FROM address
JOIN person
JOIN purchase
WHERE purchase.person_FK = person.id
AND address.person_FK = person.id
AND MONTH(purchase.purchase_date) = 5
GROUP BY state, city
ORDER BY state, city;
But this query doesn't return the cities that have no purchases in that month, and I want to show them. Can you help me?
You need a city table with all the cities, then do a LEFT JOIN.
And put the JOIN condition on the ON section not the WHERE
SELECT Cities.state, Cities.city, COUNT(*)
FROM Cities
LEFT JOIN Purchase
ON Cities.city = Purchase.city
AND Cities.state = Cities.state
JOIN person
ON purchase.person_FK = person.id
AND MONTH(purchase.purchase_date) = 5
JOIN address
ON address.person_FK = person.id
GROUP BY Cities.state, Cities.city
ORDER BY Citiesstate, Cities.city;
Look at your joins, 'JOIN' is the same as 'INNER JOIN' which only shows results which is in both tables, you'll need to use a LEFT or FULL join to get what you need.
Theres a diagram here which explains them well
You will need to have a table that provides a listing of all cities you want to show (if you don't already have that). Then you join to the city table as well. Otherwise, your query has no idea which cities to show with a zero count. In addition, you will need to change your JOIN's to LEFT JOIN's
SELECT city.state, city.city, COUNT(*)
FROM address
LEFT JOIN person ON person.id = address.person_FK
LEFT JOIN purchase ON purchase.person_FK = person.id
LEFT JOIN city ON purchase.city = city.city
WHERE MONTH(purchase.purchase_date) = 5
GROUP BY address.state, address.city
ORDER BY address.state, address.city;

Link to another table to get name with inner join

The following code shows the max date for an item and all works well.
SELECT
pricing_id, pricing.field, pricing.region, price, max_date
FROM
pricing
INNER JOIN
(SELECT
field, MAX(end_date) AS 'max_date'
FROM
pricing, regions
GROUP BY field) AS tmptable ON tmptable.max_date = pricing.end_date
AND tmptable.field = pricing.field
ORDER BY region, pricing.field
I am trying to pull region name from regions.region_name to replace the pricing.region column which just shows the ID. I have tried the usual where clause to join tables and display the descriptive name but it breaks it.
Can anyone help?
Thanks,
John
You have to select region_name from the regions table
somthing like
SELECT
pricing_id, pricing.field, tmptable.region_name , price, max_date
FROM
pricing
INNER JOIN
(SELECT
region_name , field, MAX(end_date) AS 'max_date'
FROM
pricing, regions
GROUP BY field) AS tmptable ON tmptable.max_date = pricing.end_date
AND tmptable.field = pricing.field
ORDER BY region, pricing.field
should work.
Assuming your table is similar in setup this should work. I am making an assumption that you don't want to try to find it in the sub-select which seems like more work than required. Instead I added it as a LEFT JOIN and changed the rest of the query around to match.
SELECT
pricing_id, pricing.field, r.region_name, price, max_date
FROM
pricing
INNER JOIN
(SELECT
field, MAX(end_date) AS 'max_date'
FROM
pricing
GROUP BY field) AS tmptable ON tmptable.max_date = pricing.end_date
AND tmptable.field = pricing.field
LEFT JOIN regions r ON r.id = pricing.region
ORDER BY r.region_name, pricing.field

How can I use MySQL to COUNT with a LEFT JOIN?

How can I use MySQL to count with a LEFT JOIN?
I have two tables, sometimes the Ratings table does not have ratings for a photo so I thought LEFT JOIN is needed but I also have a COUNT statement..
Photos
id name src
1 car bmw.jpg
2 bike baracuda.jpg
Loves (picid is foreign key with photos id)
id picid ratersip
4 1 81.0.0.0
6 1 84.0.0.0
7 2 81.0.0.0
Here the user can only rate one image with their IP.
I want to combine the two tables in order of the highest rating. New table
Combined
id name src picid
1 car bmw.jpg 1
2 bike baracuda.jpg 2
(bmw is highest rated)
My MySQL code:
SELECT * FROM photos
LEFT JOIN ON photos.id=loves.picid
ORDER BY COUNT (picid);
My PHP Code: (UPDATED AND ADDED - Working Example...)
$sqlcount = "SELECT p . *
FROM `pics` p
LEFT JOIN (
SELECT `loves`.`picid`, count( 1 ) AS piccount
FROM `loves`
GROUP BY `loves`.`picid`
)l ON p.`id` = l.`picid`
ORDER BY coalesce( l.piccount, 0 ) DESC";
$pics = mysql_query($sqlcount);
MySQL allows you to group by just the id column:
select
p.*
from
photos p
left join loves l on
p.id = l.picid
group by
p.id
order by
count(l.picid)
That being said, I know MySQL is really bad at group by, so you can try putting the loves count in a subquery in your join to optimize it:
select
p.*
from
photos p
left join (select picid, count(1) as piccount from loves group by picid) l on
p.id = l.picid
order by
coalesce(l.piccount, 0)
I don't have a MySQL instance to test out which is faster, so test them both.
You need to use subqueries:
SELECT id, name, src FROM (
SELECT photos.id, photos.name, photos.src, count(*) as the_count
FROM photos
LEFT JOIN ON photos.id=loves.picid
GROUP BY photos.id
) t
ORDER BY the_count
select
p.ID,
p.name,
p.src,
PreSum.LoveCount
from
Photos p
left join ( select L.picid,
count(*) as LoveCount
from
Loves L
group by
L.PicID ) PreSum
on p.id = PreSum.PicID
order by
PreSum.LoveCount DESC
I believe you just need to join the data and do a count(*) in your select. Make sure you specify which table you want to use for ambigous columns. Also, don't forget to use a group by function when you do a count(*). Here is an example query that I run on MS SQL.
Select CmsAgentInfo.LOGID, LOGNAME, hCmsAgent.SOURCEID, count(*) as COUNT from hCmsAgent
LEFT JOIN CmsAgentInfo on hCmsAgent.logid=CmsAgentInfo.logid
where SPLIT = '990'
GROUP BY CmsAgentInfo.LOGID, LOGNAME, hCmsAgent.SOURCEID
The example results form this will be something like this.
77615 SMITH, JANE 1 36
29422 DOE, JOHN 1 648
Hope that helps. Good Luck.