SQL GROUP BY multiple columns? - mysql

I have one table and I need to group the entries by two different columns
here is my code
SELECT *
FROM (
SELECT max(user_msg.id) AS mid, max(user_msg.timestamp) AS tsp, user_msg.text,
usr1.id AS u1_id, usr1.nickname AS u1_nickname, usr1.avatar AS u1_avatar, usr1.avatar_art AS u1_avatar_art,
usr2.id AS u2_id, usr2.nickname AS u2_nickname, usr2.avatar AS u2_avatar, usr2.avatar_art AS u2_avatar_art,
COUNT(user_msg.id) AS cnt
FROM user_msg
join user using (client_id)
LEFT JOIN user AS usr1 ON user_msg.from_id=usr1.id
LEFT JOIN user AS usr2 ON user_msg.to_id=usr2.id
WHERE user_msg.to_id = '".$user_id."' AND to_delete='0' OR user_msg.from_id = '".$user_id."' AND to_delete='0'
group by u1_id, u2_id ORDER by tsp DESC
) c
it should be something like group by u1_id AND u2_id

You need to use the original column names, not the aliases. They aren't processed yet (well, in SQL Server they aren't: not sure of MYSQL).
...
WHERE user_msg.to_id = '".$user_id."' AND to_delete='0' OR user_msg.from_id = '".$user_id."' AND to_delete='0'
group by usr1.id, usr2.id
ORDER by tsp DESC
...
Edit: MySQL allows aliases in the GROUP BY
So, I suspect the GROUP BY is wrong and ambiguous and need to be like the standard SQL underneath. With or without aliases
Like this question: SQL Query not showing expected result
[end edit]
Note, to make this standard SQL (or run on any other RDBMS), you need to use all the columns in the GROUP BY that are not in an aggregate:
SELECT
max(user_msg.id) AS mid,
max(user_msg.timestamp) AS tsp,
user_msg.text,
usr1.id AS u1_id, usr1.nickname AS u1_nickname, usr1.avatar AS u1_avatar, usr1.avatar_art AS u1_avatar_art,
usr2.id AS u2_id, usr2.nickname AS u2_nickname, usr2.avatar AS u2_avatar, usr2.avatar_art AS u2_avatar_art,
COUNT(user_msg.id) AS cnt
FROM user_msg
join user using (client_id)
LEFT JOIN user AS usr1 ON user_msg.from_id=usr1.id
LEFT JOIN user AS usr2 ON user_msg.to_id=usr2.id
WHERE user_msg.to_id = '".$user_id."' AND to_delete='0' OR user_msg.from_id = '".$user_id."' AND to_delete='0'
GROUP BY
user_msg.text, usr1.id, usr1.nickname, usr1.avatar, usr2.avatar_art,
usr2.id, usr2.nickname, usr2.avatar, usr1.avatar_art
ORDER by tsp DESC

Related

Optimize Query Mysql to count data in each district

i have this query for calculate success total in each district. this query works but its take until 2min to output data, i have 15k rows in orders.
SELECT
nsf.id,
nsf.province,
nsf.city,
nsf.district,
nsf.shipping_fee,
IFNULL((SELECT COUNT(orders.id) FROM orders
JOIN users ON orders.customer_id = users.id
JOIN addresses ON addresses.user_id = users.id
JOIN subdistricts ON subdistricts.id = addresses.subdistrict_id
WHERE orders.status_tracking IN ("Completed","Successful Delivery")
AND subdistricts.ninja_fee_id = nsf.id
AND orders.transfer_to = "cod"),0) as success_total
from ninja_shipping_fees nsf
GROUP BY nsf.id
ORDER BY nsf.province;
the output should be like this
can you help me to improve the peformance? Thanks
Try performing the grouping/calculation in a joined "derived table" instead of a "correlated subquery"
SELECT
nsf.id
, nsf.province
, nsf.city
, nsf.district
, nsf.shipping_fee
, COALESCE( g.order_count, 0 ) AS success_total
FROM ninja_shipping_fees nsf
LEFT JOIN (
SELECT
subdistricts.ninja_fee_id
, COUNT( orders.id ) AS order_count
FROM orders
JOIN users ON orders.customer_id = users.id
JOIN addresses ON addresses.user_id = users.id
JOIN subdistricts ON subdistricts.id = addresses.subdistrict_id
WHERE orders.status_tracking IN ('Completed', 'Successful Delivery')
AND orders.transfer_to = 'cod'
GROUP BY subdistricts.ninja_fee_id
) AS g ON g.ninja_fee_id = nsf.id
ORDER BY nsf.province;
"Correlated subqueries" are often a source of poor performance.
Other notes, I prefer to use COALESCE() because it is ANSI standard and available in most SQL implementations now. Single quotes are more typically used to denote strings literals.

Left join sql query

I want to get all the data from the users table & the last record associated with him from my connection_history table , it's working only when i don't add at the end of my query
ORDER BY contributions DESC
( When i add it , i have only the record wich come from users and not the last connection_history record)
My question is : how i can get the entires data ordered by contributions DESC
SELECT * FROM users LEFT JOIN connections_history ch ON users.id = ch.guid
AND EXISTS (SELECT 1
FROM connections_history ch1
WHERE ch.guid = ch1.guid
HAVING Max(ch1.date) = ch.date)
The order by should not affect the results that are returned. It only changes the ordering. You are probably getting what you want, just in an unexpected order. For instance, your query interface might be returning a fixed number of rows. Changing the order of the rows could make it look like the result set is different.
I will say that I find = to be more intuitive than EXISTS for this purpose:
SELECT *
FROM users u LEFT JOIN
connections_history ch
ON u.id = ch.guid AND
ch.date = (SELECT Max(ch1.date)
FROM connections_history ch1
WHERE ch.guid = ch1.guid
)
ORDER BY contributions DESC;
The reason is that the = is directly in the ON clause, so it is clear what the relationship between the tables is.
For your casual consideration, a different formatting of the original code. Note in particular the indented AND suggests the clause is part of the LEFT JOIN, which it is.
SELECT * FROM users
LEFT JOIN connections_history ch ON
users.id = ch.guid
AND EXISTS (SELECT 1
FROM connections_history ch1
WHERE ch.guid = ch1.guid
HAVING Max(ch1.date) = ch.date
)
We can use nested queries to first check for max_date for a given user and pass the list of guid to the nested query assuming all the users has at least one record in the connection history table otherwise you could use Left Join instead.
select B.*,X.* from users B JOIN (
select A.* from connection_history A
where A.guid = B.guid and A.date = (
select max(date) from connection_history where guid = B.guid) )X on
X.guid = B.guid
order by B.contributions DESC;

Mysqli - #1052 - Column 'id' in field list is ambiguous

SELECT id,firstName,lastName, SUM(qty*price)
FROM orders,menu,customers
WHERE menu.id = orders.id_menu
AND orders.id_customer = customers.id
GROUP BY (id,firstName,lastName)
ORDER BY (SUM(qty*price)) LIMIT 1;
1052 - Column 'id' in field list is ambiguous
i need to get the best customer
Some simple rules when writing SQL:
Never use commas in the FROM clause.
Always use explicit, proper JOIN syntax.
Always alias your tables with abbreviations.
Always qualify your column references.
Your resulting queries are more likely to work the first time:
SELECT c.id, c.firstName, c.lastName, SUM(o.qty * m.price)
FROM orders o JOIN
menu m
ON m.id = o.id_menu JOIN
customers c
ON c.id = o.id_customer
GROUP BY c.id, c.firstName, c.lastName
ORDER BY SUM(o.qty * m.price)
LIMIT 1;
Change the query to this
SELECT customers.id,firstName,lastName, SUM(qty*price) FROM orders,menu,customers WHERE menu.id = orders.id_menu AND orders.id_customer = customers.id GROUP BY (customers.id,firstName,lastName) ORDER BY (SUM(qty*price)) LIMIT 1;
I believe all these tables have column called 'id' and you need to specify which one you need otherwise it will give you this message.
You have many columns as name 'id' in different tables so mysql can not know where is it from (menu.id vs customer.id).
change to:
SELECT id,firstName,lastName, SUM(qty*price)
FROM orders,menu,customers
WHERE menu.id = orders.id_menu
AND orders.id_customer = customers.id
GROUP BY (orders.id,firstName,lastName)
ORDER BY (SUM(qty*price)) LIMIT 1;
you want orders.id as those will be associated with qty and price

Order by clause is not working with group by in sql query

MYSQL query is getting data exactly as I want.But ORDER BY clause is not working.
But when I removed the GROUP BY from query then ORDER BY clause is work.
SELECT MESS. * , US. * , LF. *
FROM messages MESS
LEFT JOIN users AS US ON US.id = MESS.to_id
LEFT JOIN label_infos LF ON LF.user_id = MESS.to_id
WHERE MESS.frm_id =27
OR MESS.to_id =27
GROUP BY US.id
ORDER BY MESS.id DESC

MAX() Function not working as expected

I've created sqlfiddle to try and get my head around this http://sqlfiddle.com/#!2/21e72/1
In the query, I have put a max() on the compiled_date column but the recommendation column is still coming through incorrect - I'm assuming that a select statement will need to be inserted on line 3 somehow?
I've tried the examples provided by the commenters below but I think I just need to understand this from a basic query to begin with.
As others have pointed out, the issue is that some of the select columns are neither aggregated nor used in the group by clause. Most DBMSs won't allow this at all, but MySQL is a little relaxed on some of the standards...
So, you need to first find the max(compiled_date) for each case, then find the recommendation that goes with it.
select r.case_number, r.compiled_date, r.recommendation
from reporting r
join (
SELECT case_number, max(compiled_date) as lastDate
from reporting
group by case_number
) s on r.case_number=s.case_number
and r.compiled_date=s.lastDate
Thank you for providing sqlFiddle. But only reporting data is given. we highly appreciate if you give us sample data of whole tables.
Anyway, Could you try this?
SELECT
`case`.number,
staff.staff_name AS ``case` owner`,
client.client_name,
`case`.address,
x.mx_date,
report.recommendation
FROM
`case` INNER JOIN (
SELECT case_number, MAX(compiled_date) as mx_date
FROM report
GROUP BY case_number
) x ON x.case_number = `case`.number
INNER JOIN report ON x.case_number = report.case_number AND report.compiled_date = x.mx_date
INNER JOIN client ON `case`.client_number = client.client_number
INNER JOIN staff ON `case`.staff_number = staff.staff_number
WHERE
`case`.active = 1
AND staff.staff_name = 'bob'
ORDER BY
`case`.number ASC;
Check below query:
SELECT c.number, s.staff_name AS `case owner`, cl.client_name,
c.address, MAX(r.compiled_date), r.recommendation
FROM case c
INNER JOIN (SELECT r.case_number, r.compiled_date, r.recommendation
FROM report r ORDER BY r.case_number, r.compiled_date DESC
) r ON r.case_number = c.number
INNER JOIN client cl ON c.client_number = cl.client_number
INNER JOIN staff s ON c.staff_number = s.staff_number
WHERE c.active = 1 AND s.staff_name = 'bob'
GROUP BY c.number
ORDER BY c.number ASC
SELECT
case.number,
staff.staff_name AS `case owner`,
client.client_name,
case.address,
(select MAX(compiled_date)from report where case_number=case.number),
report.recommendation
FROM
case
INNER JOIN report ON report.case_number = case.number
INNER JOIN client ON case.client_number = client.client_number
INNER JOIN staff ON case.staff_number = staff.staff_number
WHERE
case.active = 1 AND
staff.staff_name = 'bob'
GROUP BY
case.number
ORDER BY
case.number ASC
try this