mysql group and sort a UNION - mysql

In my guestbook I have 2 tables: messages and replies.
Now, I want to get all messages grouped by id (means that message and corresponding replies will be grouped/together) and sorted by date DESC (the newest messages will be first; if a message is the oldest one, but the corresponding reply is the newest of all messages, this group will be on the top of the table), while replies will be sorted by date ASC (oldest reply on the top).
Here my mysql query that works good except it doesnt sort replies by date ASC
SELECT msg.id as id, msg.comment, msg.date_added as date_added, 0 as is_reply
FROM messages AS msg
UNION
SELECT reply.msg_id as id, reply.comment, reply.date_added as date_added, 1 as is_reply
FROM pg_reply as reply
GROUP BY id
ORDER BY date_added DESC, is_reply ASC
is_reply ASC doesnt do the job as I supposed
reply.msg_id specifies id of reply's parent (messages.id)
What the result should look like>
- message A
- oldest reply B
- old reply C
- new reply Z // this is the newest message in the guestbook
- newer message E // is newer than A but older than the newest message in the guestbook, which is Z
- reply F // (this reply is newer than all messages but message Z)

For this answer I'm going to assume that reply.msg_id is a linkfield to the original message.
SELECT id, comment, date_added, is_reply FROM (
SELECT
msg.id as id
, msg.comment
, msg.date_added as date_added
, 0 as is_reply
FROM messages AS msg
UNION
SELECT
reply.msg_id as id
, reply.comment
, reply.date_added as date_added
, 1 as is_reply
FROM pg_reply as reply ) AS allmsg
ORDER BY id DESC, is_reply, date_added DESC
This works assuming that msg_id is an autoincrement field and that newer id's also have a newer date_added timestamp.
Remarks on the original code
In your original code you have
GROUP BY id
ORDER BY date_added DESC, is_reply ASC
The GROUP BY implicitly orders on id ASC; the following ORDER BY overrides that and orders by date_added first and is_reply second.
However if date_added is a datetime then the chance of two post having the same time are remote (esp. for replies, it takes time to write them),
so the 2nd order clause hardly ever gets used.
GROUP BY should only be used if you have an aggregate function in your select, such as SUM or COUNT
If you want to remove duplicates from your select do not use group by, use distinct as in select distinct a,b,c from table1 where ...

similar solution:
SELECT sort, project, reviewdate, reviewby, subject, venue, filename, remarks1, remarks2, url
FROM (
SELECT '1' AS sort, project, last_updated AS reviewdate, reviewby, concat( project, '(',
TYPE , '): ', subject ) AS subject, venue, filename, remarks1, remarks2, concat( project, '/',
TYPE , '/', filename ) AS url
FROM `upload_cmg`
WHERE (
(
subject LIKE '%prt%'
OR project LIKE '%prt%'
OR TYPE LIKE '%prt%'
)
AND (
subject LIKE '%mouda%'
OR project LIKE '%mouda%'
OR TYPE LIKE '%mouda%'
)
)
UNION SELECT '2' AS sort, project, last_updated AS reviewdate, reviewby, concat( project, '(',
TYPE , '): ', subject ) AS subject, venue, filename, remarks1, remarks2, concat( project, '/',
TYPE , '/', filename ) AS url
FROM `upload_cmg`
WHERE subject LIKE '%mouda%'
GROUP BY url
) AS vin
ORDER BY sort, reviewdate DESC

I would recommend adding a message parent field in both and sorting on that as your primary sort, with date then being the sort after that.. otherwise you will have replies showing up mixed up with other messages that were posted between replies. You can have the message parent of a non-reply message be itself.

Try this:
SELECT id, comment, date_added, is_reply
FROM (
SELECT msg.id as id, msg.comment, msg.date_added as date_added, 0 as is_reply
FROM messages AS msg
INNER JOIN pg_reply as reply
ON reply.msg_id = msg.id
GROUP BY msg.id, msg.comment, msg.date_added, 0 as is_reply
ORDER BY CASE WHEN MAX(reply.date_added) > msg.date_added THEN MAX(reply.date_added) ELSE msg.date_added END DESC
UNION
SELECT reply.msg_id as id, reply.comment, reply.date_added as date_added, 1 as is_reply
FROM pg_reply as reply
ORDER BY date_added ASC ) a
ORDER BY id

Related

order by with union in SQL is not working

Is it possible to order when the data comes from many select and union it together? Such as
In this statement, the vouchers data is not showing in the same sequence as I saved on the database, I also tried it with "ORDER BY v_payments.payment_id ASC" but won't be worked
( SELECT order_id as id, order_date as date, ... , time FROM orders WHERE client_code = '$searchId' AND order_status = 1 AND order_date BETWEEN '$start_date' AND '$end_date' ORDER BY time)
UNION
( SELECT vouchers.voucher_id as id, vouchers.payment_date as date, v_payments.account_name as name, ac_balance as oldBalance, v_payments.debit as debitAmount, v_payments.description as descriptions,
vouchers.v_no as v_no, vouchers.v_type as v_type, v_payments.credit as creditAmount, time, zero as tax, zero as freightAmount FROM vouchers INNER JOIN v_payments
ON vouchers.voucher_id = v_payments.voucher_id WHERE v_payments.client_code = '$searchId' AND voucher_status = 1 AND vouchers.payment_date BETWEEN '$start_date' AND '$end_date' ORDER BY v_payments.payment_id ASC , time )
UNION
( SELECT return_id as id, return_date as date, ... , time FROM w_return WHERE client_code = '$searchId' AND w_return_status = 1 AND return_date BETWEEN '$start_date' AND '$end_date' ORDER BY time)
Wrap the sub-select queries in the union within a SELECT
SELECT id, name
FROM
(
SELECT id, name FROM fruits
UNION
SELECT id, name FROM vegetables
)
foods
ORDER BY name
If you want the order to only apply to one of the sub-selects, use parentheses as you are doing.
Note that depending on your DB, the syntax may differ here. And if that's the case, you may get better help by specifying what DB server (MySQL, SQL Server, etc.) you are using and any error messages that result.
You need to put the ORDER BY at the end of the statement i.e. you are ordering the final resultset after union-ing the 3 intermediate resultsets
To use an ORDER BY or LIMIT clause to sort or limit the entire UNION result, parenthesize the individual SELECT statements and place the ORDER BY or LIMIT after the last one. See link below:
ORDER BY and LIMIT in Unions
(SELECT a FROM t1 WHERE a=10 AND B=1)
UNION
(SELECT a FROM t2 WHERE a=11 AND B=2)
ORDER BY a LIMIT 10;

SQL GROUP BY does not keep correct order by

I have a table that I am running this query on:
SELECT datetime, from_email, from_name
FROM emails
WHERE user_seq = '1'
GROUP BY from_email
ORDER BY datetime DESC
the query executes and returns data however it is not showing the data in the order of datetime DESC
When I remove the GROUP BY, the data shows in the correct order.
Looks like you are using mysql as it allows you to exclude fields from the group by clause that aren't included in an aggregate, in this case, your datetime field. Considering you want to sort the datetime descending, perhaps you really just need to use max:
SELECT max(datetime),
from_email,
from_name
FROM emails
WHERE user_seq = '1'
GROUP BY from_email, from_name
ORDER BY 1 desc
I think ,you want to group the results using from_email.. Try with the below script.. and the script is for SQL server..
;WITH cte_1
as( SELECT datetime, from_email, from_name
, ROW_NUMBER ()OVER(PARTITION BY from_email ORDER BY datetime desc) RNo
FROM emails
WHERE user_seq = '1')
SELECT datetime, from_email, from_name
FROM cte_1
WHERE RNO=1
Following code seems to work in MySql..
SELECT datetime, from_email, from_name
FROM emails e
JOIN (SELECT from_email,MAX(datetime) MaxDate
FROM emails
GROUP BY from_email)t on e.from_email=t.from_email AND e.datetime=t.MaxDate

looking for tricky sql query solution

i'm having the following database structure (for messaging):
id from_userid to_userid time_stamp message
let's say i'm user with id 1 and i want to get a list of ALL user_ids i've been interacting with, sorted by timestamp - any idea how to do it?
thanks
Something like this, perhaps?
SELECT *
FROM (
SELECT from_id AS id, time_stamp
FROM <table>
WHERE to_id=<user id>
UNION
SELECT to_id AS id, time_stamp
FROM <table>
WHERE from_id=<user id>
) AS t
ORDER BY time_stamp
SELECT *
FROM your_table
WHERE from_userid = 1 OR to_userid = 1
ORDER by time_stamp
I would do it like this:
select all values + timestamps where "me" is from_userid
select all values + timestamps where "me" is to_userid
in both selects assign the same name to the "other" user id
join the result sets using UNION ALL
then order the result by the timestamp column
group by user id and min(timestamp)
In sql it would be something like this:
select rel_user, min(time_stamp) as first_contact from
(
select time_stamp, to_userid as rel_user where from_userid=my_ID
union all
select time_stamp, from_userid as rel_user where to_userid=my_ID
)
group by rel_user
order by min(time_stamp)
Since all of the other ways use more than one SELECT here's one using CASE
SELECT CASE
WHEN to_userid=1 THEN from_userid
ELSE to_userid
FROM table WHERE to_userid=1 OR from_userid=1 ORDER BY time_stamp

order by unselected column with union

Hey i have a query ordered by date, and i want in case the date is identical to order by time added to maintain stable results.
I have the following query :
(
SELECT 'album' AS RowType, id, DATE, title, time_added
FROM albums
WHERE is_temp =0
AND subject_id = '3'
)
UNION ALL (
SELECT 'video' AS RowType, id, DATE, title, time_added
FROM videos
WHERE is_temp =0
AND subject_id = '3'
)
UNION ALL (
SELECT 'story' AS RowType, id, DATE, title, time_added
FROM stories
WHERE is_temp =0
AND subject_id = '3'
)
ORDER BY DATE, time_added
Which works perfectly but i don't want to select the time_added column as i don't need it in my result set.
Can i somehow achive this? if i remove the time_added the query won't run.
I have found the following :
The ORDER BY clause causes the output rows to be sorted.
The argument to ORDER BY is a list of expressions that are used as the
key for the sort. The expressions do not have to be part of the result
for a simple SELECT, but in a compound SELECT each sort expression
must exactly match one of the result columns. Each sort expression may
be optionally followed by a COLLATE keyword and the name of a
collating function used for ordering text and/or keywords ASC or DESC
to specify the sort order.
Which i can't decide if means it's impossible or i should try harder.
try this
SELECT RowType
, id
, DATE
, title
FROM
(
SELECT 'album' AS RowType , id , DATE , title , time_added
FROM albums
WHERE is_temp = 0 AND subject_id = '3'
UNION ALL
SELECT 'video' AS RowType , id , DATE , title , time_added
FROM videos
WHERE is_temp = 0 AND subject_id = '3'
UNION ALL
SELECT 'story' AS RowType , id , DATE , title , time_added
FROM stories
WHERE is_temp = 0 AND subject_id = '3'
) s
ORDER BY DATE , time_added

Balancing out MYSQL select statements

I inserted 'vanity_name' and 'name' into the first and second SELECT statements respectively.
I get a mismatched number of columns error, which I'm confused about because I added a column to both select statements to maintain a balance.
SQL Statement:
SELECT id,
vanity_name,
Date_format(DATE, '%M %e, %Y') AS DATE,
TYPE
FROM (SELECT resume_id AS id,
date_mod AS DATE,
'resume' AS TYPE
FROM resumes
WHERE user_id = '1'
UNION ALL
SELECT profile_id,
name,
date_mod AS DATE,
'profile'
FROM profiles
WHERE user_id = '1'
ORDER BY DATE DESC
LIMIT
5) AS d1
ORDER BY DATE DESC
Erm, you have four columns in your outer select, three in the inner select.
id, vanity_name, date, type
vs.
id, date, TYPE
Based on the parenthesis, you're trying to union:
(SELECT resume_id AS id, date_mod AS date, 'resume' AS TYPE FROM resumes WHERE user_id = '1'
with
SELECT profile_id,name,date_mod AS date, 'profile' FROM profiles ... LIMIT 5)
and they obviously don't match. Reposition your parens.