UNION ALL and ORDER BY at the same time - mysql

I have this query that does not work and I do not understand why.
Each SELECT statements should return descending results, but they're ordered ascendingly.
Why ?
(SELECT * FROM table WHERE deleted_at = 0 ORDER BY id DESC)
UNION ALL
(SELECT * FROM table WHERE deleted_at <> 0 ORDER BY id DESC)
LIMIT 0,30
I have to say, this query does not generates any error and the results are what I expect.
They are just not well ordered.

There is no guarantee of ordering when using subqueries. If you want the results ordered by id descending, then use:
(SELECT * FROM table WHERE deleted_at = 0)
UNION ALL
(SELECT * FROM table WHERE deleted_at <> 0)
order by id desc
LIMIT 0,30;
However, I think the query you really want is:
select *
from table
order by deleted_at = 0 desc, id desc
limit 0, 30;
This puts the deleted_at = 0 rows first and then fills out the data with the rest of the data.
Note: if deleted_at can be NULL and you want to filter them out too, then add a where clause for this filtering.

from the manual:
To apply ORDER BY or LIMIT to an individual SELECT, place the clause inside the parentheses that enclose the SELECT:
(SELECT a FROM t1 WHERE a=10 AND B=1 ORDER BY a LIMIT 10)
UNION
(SELECT a FROM t2 WHERE a=11 AND B=2 ORDER BY a LIMIT 10);
However, use of ORDER BY for individual SELECT statements implies nothing about the order in which the rows appear in the final result because UNION by default produces an unordered set of rows. Therefore, the use of ORDER BY in this context is typically in conjunction with LIMIT, so that it is used to determine the subset of the selected rows to retrieve for the SELECT, even though it does not necessarily affect the order of those rows in the final UNION result. If ORDER BY appears without LIMIT in a SELECT, it is optimized away because it will have no effect anyway.
If you need it sorted in total:
SELECT * FROM table WHERE deleted_at = 0
UNION ALL
SELECT * FROM table WHERE deleted_at <> 0
ORDER BY deleted_at = 0 DESC, id DESC
LIMIT 0,30
But this is of source the same as:
SELECT * FROM table
ORDER BY deleted_at = 0 DESC, id DESC
LIMIT 0,30

Because you apply the ORDER BY statement before the UNION ALL happens so on just one part of your data, you want to apply it on the whole result and it should be something like this :
SELECT * FROM table WHERE deleted_at = 0
UNION ALL
SELECT * FROM table WHERE deleted_at <> 0
ORDER BY id DESC
LIMIT 0,30

Related

How to display a specific row at the top while selecting multiple rows in mysql

I have a table as activities with column id,title,content,posted_date and I want to fetch record like the row would be displayed at the top of which I have specified the id and there after all the rows should be displayed. I have written the following query but It's throwing an error.
SELECT * FROM `activities` limit 0,4 put top WHERE id = 4
You can order by id=4 ...
Add , posted_date asc to order the rest or ommit if you don't care the order of the rest..
select
*
from
activities
order by
id=4 desc,
posted_date asc
limit 4
You can use conditional order by clause
SELECT *
FROM `activities`
ORDER BY id = 4 DESC
LIMIT 0,4
Or
SELECT *
FROM `activities`
ORDER BY CASE WHEN id = 4 THEN 1 ELSE 0 END DESC
LIMIT 0,4

Will DISTINCT always return a same order of values for a same SQL?

I have a sql statement is with pattern
SELECT id FROM table WHERE [conditions] ORDER BY [orders] LIMIT 10000 OFFSET 0
and the return value is like below:
id
-----
1
0
0
0
0
1
1
1
2
2
...
Then I want to get the distinct value as the order of their first appearance, since there's an ORDER BY in the sql, and DISTINCT or GROUP BY are both happened before ORDER BY in a sql, I tried below sql.
SELECT DISTINCT id FROM (SELECT id FROM table WHERE [conditions] ORDER BY [orders] LIMIT 10000 OFFSET 0) tmp;
And the result is like what I want:
id
----
1
0
2
...
My question is: can I ensure that in a same pattern SQL, DISTINCT will always return the distinct id as the order their first appearance?
Thanks.
---------------Notes------------------
Below can be ignored. I just noticed many peoples are recommended to try GROUP BY, so I tried below sql as well:
SELECT id FROM (SELECT id FROM table WHERE [conditions] ORDER BY [orders] LIMIT 10000 OFFSET 0) tmp GROUP BY id;
but the returning is reordered by the alpha-beta order (it's not integer order because the column is a CHAR column for the real id is a string), which is not what I want.
id
----
0
1
10
100
....
If you want results in a particular order, then you need to specify that in the ORDER BY for the outermost SELECT. That suggests something like this:
SELECT id
FROM tabl
WHERE [conditions]
ORDER BY [orders]
LIMIT 10000 OFFSET 0
Then, if you want to order by the first appearance, you need a column that specifies the first appearance. Let's call this CreatedAt (perhaps it is orders?). If so:
SELECT id
FROM table
WHERE conditions
GROUP BY id
ORDER BY min(CreatedAt)
LIMIT 10000;
Note: SQL tables represent unordered sets, so you need a column to specify the ordering of interest.

Select last N rows from MySQL

I want to select last 50 rows from MySQL database within column named id which is primary key. Goal is that the rows should be sorted by id in ASC order, that’s why this query isn’t working
SELECT
*
FROM
`table`
ORDER BY id DESC
LIMIT 50;
Also it’s remarkable that rows could be manipulated (deleted) and that’s why following query isn’t working either
SELECT
*
FROM
`table`
WHERE
id > ((SELECT
MAX(id)
FROM
chat) - 50)
ORDER BY id ASC;
Question: How is it possible to retrieve last N rows from MySQL database that can be manipulated and be in ASC order ?
You can do it with a sub-query:
SELECT * FROM
(
SELECT * FROM table ORDER BY id DESC LIMIT 50
) AS sub
ORDER BY id ASC;
This will select the last 50 rows from table, and then order them in ascending order.
SELECT * FROM table ORDER BY id DESC LIMIT 50
save resources make one query, there is no need to make nested queries
SELECT * FROM table ORDER BY id DESC, datechat DESC LIMIT 50
If you have a date field that is storing the date (and time) on which the chat was sent or any field that is filled with incrementally (order by DESC) or de-incrementally (order by ASC) data per row put it as second column on which the data should be ordered.
That's what worked for me!!!! Hope it will help!!!!
Use it to retrieve last n rows from mysql
Select * from tbl order by id desc limit 10;
use limit according to N value.
if anyone need this
you can change this into
SELECT
*
FROM
`table`
WHERE
id > ((SELECT
MAX(id)
FROM
chat) - 50)
ORDER BY id ASC;
into
SELECT
*
FROM
`table`
WHERE
id > (SELECT MAX(id)- 50 FROM chat)
ORDER BY id ASC;
select * from Table ORDER BY id LIMIT 30
Notes:
* id should be unique.
* You can control the numbers of rows returned by replacing the 30 in the query

mysql order by date get 10 result back then order by id again

In my current method, I use order by date in sql query, then set the back results as a new array, sort array by id via php.
SELECT * FROM table
WHERE
(
MATCH (title,content)
AGAINST ('+$search' IN BOOLEAN MODE)
)
order by date DESC
limit 0,10
I wanna to ask, Is there a sql method, first order by date get 10 result back then in these 10 results, order by id again, just in one sql query?
Wrap an outer query around your current query.
SELECT q.*
FROM (SELECT * FROM table
WHERE
(
MATCH (title,content)
AGAINST ('+$search' IN BOOLEAN MODE)
)
order by date DESC
limit 0,10) q
ORDER BY q.id
This might work:
SELECT * FROM (
SELECT * FROM table
WHERE ...
ORDER BY date DESC
LIMIT 10
)
ORDER BY id
Simply put a comma followed by another column to order by:
SELECT * FROM table
WHERE
(MATCH (title,content)
AGAINST ('+$search' IN BOOLEAN MODE))
order by date, id DESC
limit 0,10

MySQL Query with first 20 items ordered by one field and rest ordered by name ASC

I have a database table that has two fields , date and name.
I want to have my query pull the first 20 by newest date first, then the rest of the query to pull the other elements by name alphabetically.
So that way the top 20 newest products would show first, then the rest would be ordered by name.
It's a bit ugly, but you can do it in one query:
SELECT name,
`date`
FROM ( SELECT #rank := #rank + 1 AS rank,
name,
`date`
FROM (SELECT #rank := 0) dummy
JOIN products
ORDER BY `date` DESC, name) dateranked
ORDER BY IF(rank <= 20, rank, 21), name;
The innermost query, dummy, initializes our #rank variable. The next derived table, dateranked, ranks all rows by recency (breaking ties by name). The outermost query then simply re-orders the rows by our computed rank, treating ranks greater than 20 as rank #21, and then by name.
UPDATE: This query version is more compact, puts the conditional ranking logic in the outermost ORDER BY, uses IF() rather than CASE/END.
I'm afraid this has to be done by adding a special column to your table or creating a temporary table, TPup. If you let me know whether you are interested in those options, I'll tell you more.
The two queries option like the following might be a possibility, but my version of MySQL tells me LIMIT isn't available in sub-queries.
SELECT `date`, `name` from `table` ORDER BY `date` LIMIT 0, 20;
SELECT `date`, `name` from `table` WHERE `id` NOT IN (SELECT `id` from `table` ORDER BY `date` LIMIT 0, 20) ORDER BY `name`;
Use sql UNION operator to combine result of two SELECT queries.
According to MySQL docs:
use of ORDER BY for individual SELECT statements implies nothing
about the order in which the rows appear in the final result because
UNION by default produces an unordered set of rows.
...
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. The following example uses both clauses:
(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;
Edit:
I missed the part that explain OP needs to sort one set of the result on the date and the other set of the result alphabetically. I think you need to create a temporary field for the sorting purpose. And SQL query would be something similar to this.
(SELECT *, 'firstset' as set_id FROM t1 ORDER BY date LIMIT 0, 20)
UNION
(SELECT *, 'secondset' as set_id FROM t1 ORDER BY date LIMIT 20, 18446744073709551615)
ORDER BY
CASE
WHEN set_id='firstset' THEN date
WHEN set_id='secondset' THEN name
END DESC ;