order by and group by both at time not working - mysql

My query is below
SELECT *
FROM
(
SELECT
usr.*,
messages.message_text, messages.message_id
FROM
`user` as usr
LEFT JOIN
(
SELECT
message.*
FROM
message AS message
ORDER BY
message.updated_at
DESC
) AS messages
ON
`usr`.`user_id` = `messages`.`sender_id` OR `usr`.`user_id` = `messages`.`receiver_id`
WHERE
`usr`.`is_delete` = 0 AND `usr`.`is_active` = 1
ORDER BY
messages.updated_at
DESC
) AS result
GROUP BY
result.user_id
if i remove the group by then it work well , but i want the result with group by Please Help.
I have one message table in that i have saved the sender id and receiver id, both id have foreign key with user table and i want the recent message send by user and user details
So for latest message i am using order by desc and after getting all the messages with descending order i am using group by but it is not working.

It was very hard making the table schemas and the data insert in them which would act like your scenario. So next time, please provide a fiddle link to your schema/situation.
Here were the issues in your query:
You dont need a subquery after LEFT JOIN
ORDER BY clause is always after GROUP BY
You must mention all the columns that you are SELECTing in the GROUP BY clause.
Tweaked your query a bit:
SELECT *
FROM
(
SELECT
usr.*,
messages.message_text, messages.message_id, messages.updated_at
FROM
user as usr
LEFT JOIN message messages
--(
-- SELECT
-- message.*
-- FROM
-- message AS message
-- ORDER BY
-- message.updated_at
-- DESC
--) AS messages
ON
usr.user_id = messages.sender_id OR usr.user_id = messages.receiver_id
WHERE
usr.is_delete = 0 AND usr.is_active = 1
--ORDER BY
--messages.updated_at
--DESC
) AS result
GROUP BY
result.user_id,result.name,result.is_delete,result.is_active,result.message_text,result.message_id,updated_at
ORDER BY
result.updated_at
DESC
Hope it helps :)

Related

Getting Newest Record from a table using mysql

This has to be a no brainer, but I am stumped. I'm used to using aggregate 'FIRST' in MsAccess, but MySql has no such thing.
Here is a simple table. I want to return the most recent record based on the date,
for each unique 'group ID'. I need the three records in yellow.
I was asked to add my full query. I tried one of the suggestions using the JOIN feature replacing 't' with the temp table name, but it failed to work. "Can't reopen table 't'"
The code is below. I know it's ugly, but it does return the correct data set.
I cleaned up the code a bit and added the JOIN code. Error: "Can't reopen table 't'"
enter code here
DROP TABLE IF EXISTS `tmpMaxLookupResults`;
create temporary table tmpMaxLookupResults
as
SELECT
REPORTS.dtmReportCompleted,
RESULTS.lngMainReport_ID, RESULTS.lngLocationGroupSub_ID
FROM
(tbl_010_040_ProcedureVsTest_Sub as ProcVsSub
INNER JOIN tbl_010_050_CheckLog_RESULTS as RESULTS
ON (ProcVsSub.lngLocationGroupSub_ID = RESULTS.lngLocationGroupSub_ID)
AND (ProcVsSub.lngProcedure_ID = RESULTS.lngProcedure_ID)
AND (ProcVsSub.lngItemizedTestList_ID = RESULTS.lngItemizedTestList_ID)
AND (ProcVsSub.strPasscodeAdmin = RESULTS.strPasscodeAdmin)
AND (ProcVsSub.strCFICode = RESULTS.strCFICode))
INNER JOIN
tbl_000_010_MAIN_REPORT_INFO as REPORTS ON (RESULTS.lngPCC_ID =
REPORTS.lngPCC_ID)
AND (RESULTS.lngProcedure_ID = REPORTS.lngProcedure_ID)
AND (RESULTS.lngMainReport_ID = REPORTS.idMainReport_ID)
AND (RESULTS.strPasscodeAdmin = REPORTS.strPasscodeAdmin)
AND (RESULTS.strCFICode = REPORTS.strCFICode)
WHERE
(((RESULTS.lngProcedure_ID) = 143)
AND ((RESULTS.dtmExpireDate) IS NOT NULL)
AND ((RESULTS.strCFICode) = 'ems'))
GROUP BY RESULTS.lngMainReport_ID, RESULTS.lngLocationGroupSub_ID
ORDER BY (REPORTS.dtmReportCompleted) DESC;
SELECT t.*
FROM tmpMaxLookupResults AS t
JOIN (
SELECT lngLocationGroupSub_ID,
MAX(dtmReportCompleted) AS max_date_completed
FROM tmpMaxLookupResults
GROUP BY lngLocationGroupSub_ID ) AS dt
ON dt.lngLocationGroupSub_ID = t.lngLocationGroupSub_ID AND
dt.max_date_completed = t.dtmReportCompleted
enter code here
Try this
SELECT
tn.*
FROM
tableName tn
RIGHT OUTER JOIN
(
SELECT
groupId, MAX(date_completed) as max_date_completed
FROM
tableName
GROUP BY
groupId
) AS gt
ON
(gt.max_date_completed = nt.date_completed AND gt.groupId = nt.groupId)
You can use the following SQL.
select * from table1 order by date_completed desc Limit 1;
Use Order By
SELECT *
FROM table_name
ORDER BY your_date_column_name
DESC
LIMIT 1
In a Derived Table, get the maximum date_completed value for every group_id.
Join this result-set back to the main table, in order to get the complete row corresponding to maximum date_completed value for every group_id
Try the following query:
SELECT t.*
FROM your_table_name AS t
JOIN (
SELECT group_id,
MAX(date_completed) AS max_date_completed
FROM your_table_name
GROUP BY group_id
) AS dt
ON dt.group_id = t.group_id AND
dt.max_date_completed = t.date_completed

Query returning false PHP

I am having trouble with a MySQL query. The query is as follows:
SET #catlocation = (SELECT id FROM categories WHERE url_name='hexcode');
SELECT
subs.display_name AS display,
subs.url_name AS url,
(
SELECT title
FROM threads
WHERE location = subs.id
ORDER BY time_submitted DESC
LIMIT 1
) AS title,
(
SELECT username
FROM users
WHERE uid = (
SELECT uid
FROM threads
WHERE location = subs.id
ORDER BY time_submitted DESC
LIMIT 1
)
LIMIT 1
) AS author,
(
SELECT COUNT(*)
FROM threads
WHERE location = subs.id
ORDER BY time_submitted DESC
LIMIT 1
) AS thread_count
FROM (
SELECT *
FROM categories
WHERE parent_id = #catlocation
) AS subs
When I try to run this through PHP I get a false result and an error of:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SELECT subs.display_name AS display, subs.url_name AS url, ( SELECT threads.' at line 7
I have no idea what the syntax error could be, if someone could point it out to me that would be wonderful.
EDIT: Could this be caused by having two select statements (The one that sets #catlocation and the main query?)
You can refactor your request with joins to increase performance.
SELECT s.display_name display, s.url_name url,
t1.title, u.username author,
COUNT(t2.title) total
FROM categories s
LEFT JOIN threads t1 ON t1.id = (SELECT id FROM threads
WHERE location = s.id
ORDER BY time_submitted DESC
LIMIT 1)
LEFT JOIN users u ON u.uid = t1.uid
LEFT JOIN threads t2 ON t2.location = s.id
WHERE s.parent_id = #catlocation
GROUP BY s.display_name, s.url_name, t1.title, u.username
In a ansi SQL you need to declare a tag for each table or omit it if there is only one. Try taking out the "threads." everywhere, it is not needed
It appears the first SELECT statement which set #catlocation was causing the problem. I moved it into a subquery and the query executed successfully
The new query is as follows:
SELECT categories.display_name display,
categories.url_name url,
threads.title title,
users.username author,
( SELECT COUNT(title)
FROM threads
WHERE location = categories.id
) total
FROM categories
LEFT JOIN threads
ON threads.tid = ( SELECT tid
FROM `threads`
WHERE location = categories.id
ORDER BY time_submitted DESC
LIMIT 1 )
LEFT JOIN users ON users.uid = threads.uid
WHERE categories.parent_id = ( SELECT id
FROM `categories`
WHERE url_name='hexcode'
LIMIT 1 );
I will continue to refactor the query by using JOINs (once I learn how to use them). Thanks to all that suggested fixes, I didn't understand the JOIN answer and still couldn't get it to run without error.

MySQL sort grouped data

I have tried to program a inbox that display messages in the order they were received and then by if they have been read or not, it seemed to work for a while, but not it doesn't. It may have only worked under certain circumstances maybe..
Anyway here is my query;
SELECT `id`, `from_userid`, `read`, max(sent) AS sent
FROM (`who_messages`)
WHERE `to_userid` = '41'
GROUP BY `from_userid`
ORDER BY `read` ASC, `sent` DESC
I believe the problem is that the messages are being grouped in the wrong order.. as the inbox is always showing as read, when new messages exist. I get the right time of the new messages, but I am guessing this because I selected max(sent).
Is my logic wrong? or can I sort and then group as all my efforts have resulted in 'Every derived table must have its own alias'
Setup an SQL Fiddle - here's the best I came up with. Basically I do the ordering first in a sub-query then group them afterwards. That seemed to work with the (limited) test data I entered.
SELECT *
FROM (SELECT id, from_userid, is_read, sent
FROM who_messages
WHERE to_userid = 41
ORDER BY from_userid ASC, is_read ASC) m
GROUP BY m.from_userid
ORDER BY m.is_read ASC, m.sent DESC
See the fiddle to play around: http://sqlfiddle.com/#!2/4f63d/8
You are selecting non-grouping fields in a grouped query. It is not guaranteed which record of the group will be returned, and ORDER BY is processed after GROUP BY.
Try this:
SELECT m.*
FROM (
SELECT DISTINCT from_userid
FROM who_messages
WHERE to_userid = 41
) md
JOIN who_messages m
ON m.id =
(
SELECT mi.id
FROM who_message mi
WHERE (mi.to_userid, mi.from_userid) = (41, md.from_userid)
ORDER BY
mi.sent DESC, mi.id DESC
LIMIT 1
)
Create an index on who_message (to_userid, from_userid, sent, id) for this to work fast.
Update
The above query will return the record for the last message from any given user (including its read status). If you want to check that you have any unread messages from the user, use this:
SELECT m.*, md.all_read
FROM (
SELECT from_userid, MIN(read) AS all_read
FROM who_messages
WHERE to_userid = 41
GROUP BY
from_userid
) md
JOIN who_messages m
ON m.id =
(
SELECT mi.id
FROM who_message mi
WHERE (mi.to_userid, mi.from_userid) = (41, md.from_userid)
ORDER BY
mi.sent DESC, mi.id DESC
LIMIT 1
)
For this to work fast, create an index on who_message (to_userid, from_userid, read) (in addition to the previous index).
As Quassnoi said, you are using a GROUP BY query and ordering on 'read' which is not an aggregate function. Therefore you can't be certain of the value used by the MySQL engine (usually the last of the group but...)
I would suggest writing your query this way, as it doesn't involve any subquery and has some many other performance-friendly usage:
SELECT
from_userid,
COUNT(*) AS nb_messages,
SUM(NOT is_read) AS nb_unread_messages,
MAX(sent) AS last_sent
FROM who_messages
WHERE to_userid = 41
GROUP BY from_userid
ORDER BY nb_unread_messages DESC, last_sent DESC;
(I used Andy Jones' fiddle schema: http://sqlfiddle.com/#!2/4f63d/8.
By the way, many thanks Andy, this site is great !)
Hope this help !
"inbox that display messages in the order they were received and then by if they have been read or not ... however it is suppose to be the latest message" - assumes read is a nullable date/time column, and messages are stored in the order they are sent (newer have larger id than older - autoid)
SELECT wm.id, wm.from_userid, (wm.read IS NULL) as unread, wm.sent
FROM (SELECT MAX(id) AS id FROM who_messages WHERE to_userid = '41' GROUP BY from_userid) sub
INNER JOIN who_messages wm ON sub.id = wm.id
ORDER BY wm.sent DESC, wm.read

Group by subkey but if new read if not 1 show 0

Ok I know this is going to sound stupid. But I have tried everything.
Here is my code to start of with
SELECT toD.username AS ToUser,
fromD.username AS FromUser,
rvw.* FROM usermessages AS rvw
LEFT JOIN users AS toD
ON toD.id = rvw.touserid
LEFT JOIN users AS fromD ON fromD.id = rvw.fromuserid
WHERE touserid = '" . $this->userid . "'
AND deleted = '0'
GROUP BY subkey
ORDER BY rvw.read ASC, rvw.created DESC
while this does work, what I am finding is that if there is a new message, and the read is 0 it still shows up as 1. I know this is because I am grouping the rows together.
But am not sure of any other way to do this.
It doesn't work because mysql can return any row from the group no matter how you try to order your set. To find first row in the group using some custom order you have to split it into two tasks - first finding all distinct values for the column you group by and then finding first row in the subquery for every referenced value. So your query should look like:
SELECT toD.username AS ToUser, fromD.username as FromUser, msg.* FROM
( SELECT DISTINCT touserid, subkey
FROM usermessages
WHERE touserid = 'insert_your_id_here'
AND deleted=0 ) msgg
JOIN usermessages msg
ON msg.id = ( SELECT msgu.id
FROM usermessages msgu
WHERE msgu.touserid = msgg.touserid
AND msgu.subkey = msgg.subkey
AND deleted=0
ORDER BY msgu.read ASC, msgu.created DESC
LIMIT 1 )
JOIN users fromD ON msg.fromuserid = fromD.id
JOIN users toD ON msg.touserid = toD.id
Make sure you have an index on (touserid,subkey). Depending on how big your db is you may need more.

How do I get more than one column from a SELECT subquery?

Here is my problem :
I have 3 tables : account, account_event and account_subscription
account contains details like : company_name, email, phone, ...
account_event contains following events : incoming calls, outgoing calls, visit, mail
I use account_subscription in this query to retrieve the "prospects" accounts. If the account does not have a subscription, it is a prospect.
What I am using right now is the following query, which is working fine :
SELECT `account`.*,
(SELECT event_date
FROM clients.account_event cae
WHERE cae.account_id = account.id
AND cae.event_type = 'visit'
AND cae.event_done = 'Y'
ORDER BY event_date DESC
LIMIT 1) last_visit_date
FROM (`clients`.`account`)
WHERE (SELECT count(*)
FROM clients.account_subscription cas
WHERE cas.account_id = account.id) = 0
ORDER BY `last_visit_date` DESC
You can see that it returns the last_visit_date.
I would like to modify my query to return the last event details (last contact). I need the event_date AND the event_type.
So I tried the following query which is NOT working because apparently I can't get more than one column from my select subquery.
SELECT `account`.*,
(SELECT event_date last_contact_date, event_type last_contact_type
FROM clients.account_event cae
WHERE cae.account_id = account.id
AND cae.event_done = 'Y'
ORDER BY event_date DESC
LIMIT 1)
FROM (`clients`.`account`)
WHERE (SELECT count(*)
FROM clients.account_subscription cas
WHERE cas.account_id = account.id) = 0
ORDER BY `last_visit_date` DESC
I tried a lot of solutions around joins but my problem is that I need to get the last event for each account.
Any ideas?
Thank you in advance.
Jerome
Get a PRIMARY KEY in a subquery and join the actual table on it:
SELECT a.*, ae.*
FROM account a
JOIN account_event ae
ON ae.id =
(
SELECT id
FROM account_event aei
WHERE aei.account_id = a.id
AND aei.event_done = 'Y'
ORDER BY
event_date DESC
LIMIT 1
)
WHERE a.id NOT IN
(
SELECT account_id
FROM account_subscription
)
ORDER BY
last_visit_date DESC
Try moving the subquery to from part and alias it; it will look as just another table and you'll be able to extract more than one column from it.