The following query is done after disabling ONLY_FULL_GROUP_BY in MySQL. Now I want the same result without disabling ONLY_FULL_GROUP_BY mode like GROUP BY r.user_id, u.fullname, r.time_taken
SELECT u.fullname, ROUND(AVG(r.correct), 2) avg_correct, date_format(r.time_taken,'%d-%m-%Y') time_taken
FROM (SELECT user_id, concat( first_name, ' ', last_name) fullname from user) u
LEFT JOIN test_result r ON u.user_id = r.user_id
GROUP BY r.user_id
ORDER BY r.time_taken DESC
Screenshot:
Can anyone help me?
You must add u.fullname to the group by clause and this does not affect the results as the user's id and fullname are uniquely grouped together, but in the case of time_taken you must use any_value():
SELECT
u.fullname,
ROUND(AVG(r.correct), 2) avg_correct,
date_format(any_value(r.time_taken),'%d-%m-%Y') time_taken
FROM (SELECT user_id, concat( first_name, ' ', last_name) fullname from user) u
LEFT JOIN test_result r ON u.user_id = r.user_id
GROUP BY r.user_id, u.fullname
ORDER BY time_taken DESC
You can find more here: MySQL Handling of GROUP BY
I would recommend writing this query as:
SELECT CONCAT(u.first_name, ' ', u.last_name) as fullname,
ROUND(AVG(r.correct), 2) as avg_correct,
DATE_FORMAT(MAX(r.time_taken), '%d-%m-%Y') as time_taken
FROM user u LEFT JOIN
test_result r
ON u.user_id = r.user_id
GROUP BY u.user_id, fullname
ORDER BY MAX(r.time_taken) DESC;
Notes:
The subquery in the FROM clause does not help the query. It might impede the optimizer.
Don't GROUP BY columns from the second table in a LEFT JOIN (unless you really know what you are doing). The value would be NULL for non-matches.
MySQL and MariaDB allow column aliases in the GROUP BY clause.
For the ORDER BY to work as you intend, it needs to be on the value before formatting, not after formatting. The format %d-%m-%Y does not order by time.
Related
I have this SQL query and it is rather advanced for me. I have a table add_design with two columns status and home, so I want to add a part WHERE status=1 and home='Yes' in the query. I just don't understand where to put it. Can someone breakdown the query for me please?
SELECT mytable.*, CONCAT(fname, ' ', lname) AS user_name, user.image AS user_image,
user.fname, user.lname, add_design_doc.image as product_image
FROM
(
SELECT
add_design.id, add_design.user_id, title, category, COUNT(add_design.user_id) AS total_likes
FROM
add_design INNER JOIN
like_unlike ON (add_design.id = like_unlike.product_id AND
like_unlike.type = '1')
GROUP BY
add_design.id
ORDER BY
total_likes DESC ) AS mytable INNER JOIN
user ON mytable.user_id = user.id LEFT OUTER JOIN
add_design_doc ON mytable.id = add_design_id
GROUP BY
user_id LIMIT 4;
I have a query with multiple select and a single aggregated value, coming from a joined table, resulting an extensive and ugly GROUP BY (because of the one-to-many relation with the joined table).
It's something like this:
SELECT user.id, user.name, user.type, etc.
GROUP_CONCAT(car.id SEPARATOR ', ') AS cars
FROM user
INNER JOIN car ON user.id = car.userid
GROUP BY user.id, etc.
ORDER BY user.name, user.type, cars
I would like to eliminate the long GROUP BY, but how could I get the aggregated value without the JOIN? Is there a way with something like a subquery to join the values together like with the GROUP_CONCAT?
You can aggregate in car and then join to user:
SELECT u.id, u.name, u.type, etc.,
c.cars
FROM user u
INNER JOIN (
SELECT userid, GROUP_CONCAT(id SEPARATOR ', ') AS cars
FROM car
GROUP BY userid
) c ON u.id = c.userid
ORDER BY u.name, u.type, c.cars;
Or with a correlated subquery, which is equivalent to a LEFT join but may perform better:
SELECT u.id, u.name, u.type, etc.,
(SELECT GROUP_CONCAT(c.id SEPARATOR ', ') FROM car c WHERE u.id = c.userid) AS cars
FROM user u
ORDER BY u.name, u.type, cars;
You can group this way
SELECT user.id, user.name, user.type,
uc.cars
FROM (
SELECT userid, GROUP_CONCAT(id SEPARATOR ', ') AS cars
FROM car
GROUP BY userid) uc
INNER JOIN user ON user.id = uc.userid
I have two tables
users: id, email, firstName, lastName
subscriptions: id, userId, currentPeriodStart, currentPeriodEnd
Below just shows you how the two tables are related. I want to return subscriptions that expire after 1565827199, but it needs to check against each user's most recent subscription.
select
u.id
from users u
join subscriptions s on u.id s.userId
where s.currentPeriodEnd > 1565827199
ORDER BY u.lastName ASC
A user may have multiple subscriptions in the subscriptions table. What I need to do is modify the query above, so it checks against that user's most recent subscription and not the first one it finds.
select * from subscriptions ORDER BY currentPeriodEnd DESC LIMIT 1
I've tried a few different things (alias table, sub query) I found elsewhere on stackoverflow without any luck.
You can filter with a correlated subquery, like so:
select u.*, s.*
from users u
inner join subscriptions s on u.id = s.userId
where s.currentPeriodEnd = (
select max(s1.currentPeriodEnd)
from subscriptions s1
where s1.userId = u.id and s1.currentPeriodEnd > 1565827199
)
order by u.lastName
For performance, consider an index on subscriptions(userId, currentPeriodEnd).
Alternatively, if you are running MySQL 8.0, you can use row_number():
select *
from (
select
u.*,
s.*,
row_number() over(partition by u.id order by s.currentPeriodEnd desc)
from users u
inner join subscriptions s on u.id = s.userId
where s.currentPeriodEnd > 1565827199
) t
where rn = 1
order by lastName
Join with a subquery that gets the latest time for each user, and filters it down to just the ones after your specified timestamp.
select u.id
from users u
join (
select userid
FROM subscriptions
GROUP BY userid
HAVING MAX(currentPeriodEnd) > 1565827199
) s ON s.userid = u.id
ORDER BY u.lastName ASC
I'm facing a little problem with mysql where clause.
This is the query:
SELECT u.id user
, p.id product_purchased
, p.name product_name
, pl.store_id store
, COUNT(*) occurrences
, total_spent
, total_product_purchased
, pl.registration
FROM purchases_log pl
JOIN user u
ON pl.user_id = u.id
JOIN product p
ON pl.product_id = p.id
JOIN
( SELECT user_id
, SUM(price) total_spent
, COUNT(product_id) total_product_purchased
FROM purchases_log pl
GROUP
BY user_id
) t1
ON u.id = t1.user_id
WHERE pl.store_id IN (1,2,3)
AND occurrences > 1
GROUP
BY user
, product_name
ORDER
BY u.id ASC
, pl.registration ASC;
This is the output error:
Error Code: 1054. Unknown column 'occurrences' in 'where clause' 0.067 sec
I have already tried assign AS to occurrences or using pl.
So, can someone explain me how to correctly define the result of a count function in where clause?
You need to use HAVING instead of COUNT as group by is applied after WHERE clause and hence, it won't know about any group/aggregate columns, e.g/:
SELECT u.id user,p.id product_purchased, p.name product_name, pl.store_id store, COUNT(*) AS occurrences, total_spent, total_product_purchased, pl.registration
FROM purchases_log pl
JOIN user u ON pl.user_id=u.id
JOIN product p ON pl.product_id=p.id
JOIN (SELECT user_id, SUM(price) AS total_spent,COUNT(product_id) AS total_product_purchased FROM purchases_log pl GROUP BY user_id) t1 ON u.id=t1.user_id
WHERE pl.store_id IN (1,2,3)
GROUP BY user, product_name
HAVING COUNT(*) > 1
ORDER BY u.id ASC, pl.registration ASC;
Update
If a user has more than one product associated then it's good to add all the non aggregate columns in GROUP BY to get all the combinations of user and product. The current query will not return all the combinations.
For further optimization, as #strawberry has suggest, you can run EXPLAIN and see which indices are used and whether there is any need to create any new index.
I've looked around for an answer to this but I'm not finding it. I have a site with articles stored in an article table and writers in the user table. I wanted to get a list of authors ordered by how recently they'd written an article.
This gives me the User ids:
SELECT distinct a.user_id
FROM `article` as a
ORDER BY a.id desc
The problem is that as soon as I try to bring the names in by joining the order changes so that it's by user id. I've tried this:
SELECT distinct a.user_id, u.name
FROM `article` as a
LEFT JOIN user as u on u.id = a.user_id
ORDER BY a.id desc, u.id desc
and
SELECT distinct a.user_id, u.name
FROM `article` as a
LEFT JOIN user as u on u.id = a.user_id
ORDER BY u.id desc, a.id desc
but both alter the order of the names. I'm obviously doing something stupid, but what?
The fact that DISTINCT happens to work with ORDER BY in your first example is a fluke, and not standard SQL. You need something like this:
SELECT a.user_id, u.name
FROM article a
LEFT JOIN user u ON u.id = a.user_id
GROUP BY a.user_id
ORDER BY MAX(a.id) desc
try this
SELECT a.user_id, u.name
FROM `article` a
LEFT JOIN user u on u.id = a.user_id
GROUP BY a.user_id
ORDER BY a.id desc
Your ORDER BY sorts the rows by either the User ID and/or Article ID (depending on the example).
If you want reliable sorting by name, then include the u.name field in the ORDER BY fields.
this is postgres syntax, but you can easily translate it into mysql
select u.*, q.last_article_id
from user u
inner join (
select a.user_id, max(a.id) as last_article_id
from article a
group by a.user_id
) q
on u.id = a.q.id
order by q.last_article_id desc
Something along the lines of the following should work:
select users.id, articles.id, max(articles.created_at) as recent_date
from users
left join articles on users.id = articles.user_id
group by articles.id
order by recent_date
Not sure what exactly your fields are named and all, but this should point you in the right direction.
You got a lot of answers to choose from... I believe the key to what you're asking is use of the aggregate function MAX() http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_max