How to order by a "calculated" (not MySQL function) value? - mysql

there are students and their marks:
Student:
ID NAME
1 a
2 b
3 c
4 d
Mark:
ID STUDENT_ID MARK
1 1 4
1 1 2
1 3 4
I would like to get an ordered list about their average marks, to get:
a, (4+2)/2
b, -,
c, 4/1
d, -
I must not calculate it while SELECT-ing, not even store it in a column in Students table. The interesting part is when I would want to order and limit (paging) the result. To have a correct ordering, I must know all averages - so I wont use SELECT query without LIMIT. But how to? Ideally, ordering by ID or NAME is easy.

You can order by an expression, including a subquery:
order by (select avg(mark) from marks m where m.student_id = s.id)
I'm not sure I would want to do this as part of paging, unless the underlying data is pretty small.

You can get users with their average marks with this query:
SELECT student.*, AVG(mark.mark) as average_mark
FROM users
LEFT OUTER JOIN mark m on m.student_id= users.id
GROUP BY users.id
ORDER BY average_mark DESC

Related

SQL JOIN - comments with reactions (2 tables)

I need to make one SQL command.
From table with comments i'll get comment id, then
with this ID I need to get count of reactions with the same comment ID and user's names.
So for example I have this 2 tables:
Comments:
ID
Comm_text
1
Example text
2
Another example
and Reactions:
ID
comm_id
usr
etc..
1
1
Peter
another
2
1
John
collon
3
1
Dog
cuz
4
2
Cat
why not
I need to get this:
ID
Comm_text
Reactions_Count
Users
1
Example text
3
Peter, John, Dog
2
Another example
1
Cat
I tried this:
SELECT k.id, k.comm, COUNT(r.id) as reactions, r.usr
FROM `comms` k
INNER JOIN `reactions` r ON r.id=k.id
It's just one row with one comment and count of all rows in reaction table.
Thanks.
Try this query that makes the same output:
select comments.id as ID , comments.Comm_text as Comm_text ,
(select count(id) from Reactions where comm_id = comments.id) as Reactions_Count ,
(select coalesce(GROUP_CONCAT(usr ORDER BY usr DESC) , '') from Reactions WHERE comm_id = comments.id) as Users
from comments group by comments.id
You should use group by to group the comments and have just one row then use query to count and gather the data, based on each row of the group.
The GROUP_CONCAT attach the output with , and the coalesce set the output to a given string if the output was empty.
Read more about:
GROUP BY
GROUP_CONCAT
COALESCE
subquery
According to the names that u set in the example, this will work. Just fix the table names for your database structure.
SELECT `Comments`.`ID`, `Comments`.`Comm_text`, count(`Reactions`.`comm_id`) as react, `Reactions`.`usr`
FROM `Comments`
INNER JOIN `Reactions`
ON `Comments`.`ID`=`Reactions`.`comm_id`
GROUP BY `Reactions`.`comm_id`

Select ID of a row with max value

How can I select the ID of a row with the max value of another column in a query that joins multiple tables?
For example, say I have three tables. tblAccount which stores a grouping of users, like a family. tblUser which stores the users, each tied to a record from tblAccount. And each user can be part of a plan, stored in tblPlans. Each plan has a Rank column that determines it's sorting when comparing the levels of plans. For example, Lite is lower than Premium. So the idea is that each user can have a separate plan, like Premium, Basic, Lite etc..., but the parent account does not have a plan.
How can I determine the highest plan in the account with a single query?
tblAccount
PKID
Name
1
Adams Family
2
Cool Family
tblUsers
PKID
Name
AccountID
PlanID
1
Bob
1
3
2
Phil
2
2
3
Suzie
2
1
tblPlans
PKID
Name
Rank
1
Premium
3
2
Basic
2
3
Elite
4
4
Lite
1
Here's the result I'm hoping to produce:
AccountID
Name
HighestPlanID
PlanName
2
Adams Family
1
Premium
I've tried:
SELECT U.AccountID, A.Name, MAX(P.Rank) AS Rank, P.PKID as HighestPlanID, P.Name as PlanName
FROM tblPlans P
INNER JOIN tblUsers U ON U.PlanID = P.PKID
INNER JOIN tblAccounts A ON U.AccountID = A.PKID
WHERE U.AccountID = 2
and the query will not always work, selecting the MAX of Rank does not select entire row's values from tblPlans.
I am looking for a solution that is compatible with mysql-5.6.10
You can join the tables and use ROW_NUMBER() to identify the row you want. Then filtering is ieasy.
For example:
select *
from (
select a.*, p.*,
row_number() over(partition by a.pkid order by p.rank desc) as rn
from tblaccount a
join tblusers u on u.accountid = a.pkid
join tblplans p on p.pkid = u.planid
) x
where rn = 1
Inside the subquery you can add where u.accountid = 2 to retrieve a single account of interest, instead of all of them.
With the help of #the-impaler, I massaged their answer a bit and came out with something very similar:
select *
from (
select a.*, p.*
from tblaccount a
join tblusers u on u.accountid = a.pkid
join tblplans p on p.pkid = u.planid
where u.accountid = 2
order by p.rank desc
) x limit 1
The subquery sorts each user by plan rank from top to bottom, and then the top level query selects the top most row with limit 1. It seems to work!

Join two MySQL Tables and get result from categories

SELECT art.*,arg. FROM rd_articles AS art
LEFT JOIN rd_argument AS arg ON art.cat=arg.id WHERE art.enabled=1 ORDER BY art.id DESC
LIMIT 10
This is simple join query
Article table structure is
ID cat Description Date
1 1 Abc 08-01-2014
2 1 Aaa 10-01-2014
3 2 Abcv 11-01-2014
4 3 Aaa 12-01-2014
5 3 Aaa 14-01-2014
Arguments table is
ID Name
1 A
2 B
3 C
I want pick last updated(Date) one item from each cat.
How ?
This assumes that the enabled column is in rd_articles:
SELECT art.*, arg.*
FROM (
SELECT * FROM rd_articles
INNER JOIN (
SELECT cat, MAX(date) AS maxdate
FROM rd_articles
WHERE enabled = 1
GROUP BY cat
) md ON rd_articles.cat = md.cat AND rd_articles.date = md.maxdate
) art
LEFT JOIN rd_argument AS arg ON art.cat = arg.id
The innermost query gets the maximum date for each category, then joins it to the rd_articles table to get only those rd_articles rows that have the latest date for each article. That becomes the cat alias, which is then left-joined to the arguments table just like in your original query. You can add the LIMIT 10 at the end if needed; I wasn't sure what to do with that.
Note that if there's a tie for a category's latest date, you'll get more than one row for each category. If a tie could happen you'll need to break the tie somehow, for example by using the description or the ID. Let me know if that's the case and I'll update my answer.
SELECT ART.*, ARG.*
FROM ARTICLE AS ART
INNER JOIN RD_AGRUEMENT AS ARG
ON ARG.ID = ART.ID
WHERE (ID, DATE) IN
(SELECT ID, MAX(DATE) FROM ARTICLE GROUP BY ID)

How to get the list of repeated entries in a table with its count

How to get the list of categories with number of vehicles from the below table (in descending order of no. of vehicles).
Vehicle | category
ab A
cd A
ef A
gh B
ij B
kl C
Expected output:
A 3
B 2
C 1
You can use an aggregate function count() with a GROUP BY:
select category, count(*) Total
from yourtable
group by category
order by total desc;
See Demo
Simply GROUP BY, and retrieve the count. That is,
SELECT COUNT(*) AS repetitions FROM Vehicles GROUP BY category

MySQL How do I count the number of occurences of each ID?

It may be difficult to explain what I am after, apologies if the question is vague.
I have a table which associates products with keywords using IDs
So I may have product IDs, 2,3,4,5 associated with Keyword id 14
and product IDs 3,6,9 associated with Keyword id 15
My question is How do I count and store the total for those IDs associated with Keyword 14 and for those IDs associated with Keyword 15 and so on (New Keywords added all the time)?
MY SQL so far:
select products_keyword_categories.key_cat_name
from products_keyword_to_product
inner join products_keyword_categories
on products_keyword_categories.key_cat_id = products_keyword_to_product.key_cat_id
group by products_keyword_categories.key_cat_name
Many thanks in advance for any advice. Also, if there is any terminology that will aid me in further research via a Google search that would also be most welcome.
Edit to add: In the example above the table containing the associations is products_keyword_to_product - I inner join the other table to return the Keyword name.
Edit to add (2): Sorry I was afraid my question would be vague.
If I wanted to just count all the products using keyword ID 14 I would use COUNT() AS - As mentioned in the answers but I also need to count the number of products using Keyword ID 15 and Keyword ID 16 etc. - Hope that makes more sense.
select key_cat_name ,count(*)
from products_keyword_categories pkc
inner join products_keyword_to_product ptk on pkc.id=ptk.key_id
group by id;
select cat.key_cat_name, count(*) from
products_keyword_categories cat inner join products_keyword_to_product prod
on prod.key_cat_id=cat.key_cat_id
group by cat.key_cat_name
Edit:
select cat.key_cat_name, prod_assoc.product_id, count(*) from
products_keyword_categories cat inner join products_keyword_to_product prod_assoc
on prod_assoc.key_cat_id=cat.key_cat_id
group by cat.key_cat_name,prod_assoc.product_id
Assuming your tables structure is like this:
products_keyword_categories
key_cat_id key_cat_name
1 Electronics
2 Toys
3 Software
products_keyword_to_product
key_cat_id product_id
1 1
2 1
3 2
1 2
products
product_id name
1 Product A
2 Robot
Edit 2:
Try this
SELECT key_cat_name, product_id, COUNT(*)
FROM
(select cat.key_cat_name, prod_assoc.product_id from
products_keyword_categories cat inner join products_keyword_to_product prod_assoc
on prod_assoc.key_cat_id=cat.key_cat_id) as tbl
GROUP BY key_cat_name, product_id
Edit 3:
The query above is made of 2 parts:
The inner part:
(select cat.key_cat_name, prod_assoc.product_id from
products_keyword_categories cat inner join products_keyword_to_product prod_assoc
on prod_assoc.key_cat_id=cat.key_cat_id)
Which gives 1 row per combination of product_id and key_cat_name.
The outer part:
SELECT key_cat_name, product_id, COUNT(*)
FROM (...) as tbl
GROUP BY key_cat_name, product_id
Which operates on the results of the inner part (as tbl), counting how many times a combination of key_cat_name and product_id appears on the inner part.
Check this: Subqueries in MySQL, Part 1
You are almost there, you just need to add the following:
select count(products_keyword_to_product.id), products_keyword_categories.key_cat_name
...
the rest is correct
Updated Answer:
SELECT COUNT(*), reference_field FROM table WHERE...
HAVING field=value
GROUP BY field
For aggregate conditions you must use HAVING