MySQL: Order by specific items first then by time - mysql

Lets say we have a table
names
-------------------
id name created_at
1 alpha 2020-10-23 17:30:35
2 beta 2020-10-24 17:30:35
3 gamma 2020-10-25 17:30:35
4 kilo 2020-10-26 17:30:35
5 charlie 2020-10-27 17:30:35
6 hector 2020-10-28 17:30:35
I want to order the first few rows by a fixed array let's say 6,3,2 and the rest by created_at in desc order.
So the order that I'm expecting would be 6,3,2,5,4,1.
How can I achieve this using Mysql ?
I tried using field() but can't get it to work with the other column.

FIELD() is tricky for this, because it returns 0 if there are no matches. You can construct an expression that does what you want:
order by coalesce(nullif(field(id, 6, 3, 2), 0), 999999),
created_at desc
If you know that the ids are always descending for the fixed values, then you can use:
order by (case when id in (6, 3, 2) then id end) desc,
created_at desc

SELECT * FROM names ORDER BY (
CASE
WHEN id = 6 THEN 1
WHEN id = 3 THEN 2
WHEN id = 2 THEN 3
ELSE 4
END, created_at DESC
)
The CASE statement ensures the first 3 items listed are 6, 3 and 2, and the rest are listed in created_at DESC order.

One option would be to write a case expression as follows
select *
from names
order by case when id in (6,3,2) then 0
else 1
end asc,created_at desc

Related

Select less nearest date from table [group by and order by]

I'm trying to find the nearest date for each group Type, Subtype, s_stype, category_id. If there is no date found take a with default value:
Sample data :
Type
subtype
s_stype
category_Id
date
1
1
1
211
20000000
1
1
1
211
30000000
1
1
2
211
20000000
1
1
2
211
20000000
1
1
3
211
null
1
1
2
311
50000000
1
1
2
311
40000000
1
1
2
311
null
Query:
Select *
from Table
where date <= input_date or date is null
group by Type, Subtype, s_stype, category_id
order by date desc
The query should take less nearest date for each type, subtype, s_stype, category.
For example, given input_date = 25000000:
Type
subtype
s_stype
category_Id
date
1
1
1
211
20000000
1
1
2
211
20000000
1
1
3
211
null
1
1
2
311
null
the query should give above result instead it gives incorrect row that takes a first row which satisfy where condition of given group criteria
As i have used mysql 5.7 so i need solution without window functions solution like the above
Getting the best row per group means there does not exist a better row for the group. And "closer" means that the absolute value of the difference of the two dates is smaller. This is how my query below works.
As your dates in your example are integers, I am showing integer math here. If you are working with real dates, you must use DATEDIFF instead of subtraction, because MySQL has a flaw concerning this allowing subtracting one date from another but returning some number that doesn't seem to have a meaning instead of returning an interval or a value of a predefined unit such as dates.
select *
from mytable
where not exists
(
select null
from mytable better
where better.type = mytable.type
and better.subtype = mytable.subtype
and better.s_stype = mytable.s_stype
and better.category_id = mytable.category_id
and
(
abs(better.date - 25000000) < abs(mytable.date - 25000000)
or
(better.date is not null and mytable.date is null)
)
)
order by type, subtype, s_stype, category_id, date;

How to sort mysql from pre-defined data

I have a table:
id
product_id
price
1
1
1
2
2
2
3
3
3
4
4
4
5
5
5
6
6
6
and I have a data set :
[{id:2:weight:8},{id:5,weight:6},{id:4,weight:3}]
I want to get the products which price < 5 and sort them first by weight asc from the dataset above and then sort them by price desc, so the result should be:
[{
id:4,product_id:4,id:4
},{
id:2,product_id:2,id:2
},{
id:3,product_id:3,id:3
},{
id:1:product_id:1,id:1
}]
You can use CASE syntax to get the desired results.
SELECT * FROM (
SELECT t.id, t.product_id, t.price,
CASE
WHEN t.id=2 THEN 8
WHEN t.id=5 THEN 6
WHEN t.id=4 THEN 3
ELSE 0
END AS weight
FROM mytable t
WHERE t.price < 5
) res ORDER BY res.weight ASC, res.price DESC;
you can specify the way to short for each column declared in the sorted list. here is an example:
select *
from myDataset
where price < 5
order by price desc, weight asc

MYSQL Count entries and group by two criteria with output in to a 2d table

I have a table that looks something like this;
(actual table is larger with several million rows)
Test_table
ID Day Value
=============
1 1 4
2 1 -1
3 1 27
4 1 3
5 1 -2
6 1 -5
7 1 3
8 1 1
9 1 1
10 1 Null
11 2 1
12 2 1
13 2 2
14 2 -1
15 2 -3
I want to produce a table of these two columns with the count of the number of times each entry appears, a 2d table with the day down the rows, and the values across the top with each cell containing the count of entries in that criteria like the below;
Desired output
Day Null -5 -3 -2 -1 1 2 3 4 27
==================================================================================
1 1 1 1 1 2 2 1 1
2 1 1 2 1
A query like;
select day, value, count(*) as count
from test_Table
group by day, value
Order by day asc, value desc
;
produces the data as many rows and only 3 columns... How can I get the desired output?
You can do this with conditional aggregation:
select day,
sum(value is NULL) as "NULL",
sum(value = -5) as "-5",
sum(value = -3) as "-3",
sum(value = -2) as "-2",
sum(value = -1) as "-1",
sum(value = 1) as "1",
sum(value = 2) as "2",
sum(value = 3) as "3",
sum(value = 4) as "4",
sum(value = 27) as "27"
from test_Table
group by day
Order by day asc;
Note two things. First, the column values are fixed. If you want dynamic column names, then you need to use dynamic SQL. Second, instead of blanks this will have 0 for the days with no count of a particular value.
The short answer is that it can't be done in MySQL.
The reason is that a SELECT statement has to specify the number of columns to be returned, a name and datatype for each column. And MySQL cannot dynamically generate columns to be returned for you.
The longer answer is that you would need a query of the form:
SELECT t.Day
, SUM(IF(t.value IS NULL,1,0)) AS `Null`
, SUM(IF(t.value = -5 ,1,0) AS `-5`
, SUM(IF(t.value = -3 ,1,0) AS `-3`
, ...
FROM mytable t
GROUP BY t.Day
with each column specified in the SELECT list.
One trick you can use is to use another, separate query to help write that query you need. This has to be a separate step, a separate query. To get the list of values you want returned as column headers would be of the form:
SELECT IFNULL(v.value,'Null') AS val
FROM mytable v
GROUP BY v.value
ORDER BY IF(v.value IS NULL,0,1), v.value
If you are doing this just in MySQL (and not an application), you can have MySQL help generate the required SQL text for you (using SQL to generate SQL)
SELECT CONCAT(' , SUM(IF(t.value',
IFNULL(v.value,' IS NULL',CONCAT(' = ',v.value)),
',1,0)) AS `',v.value,'`'
) AS expr
FROM mytable v
GROUP BY v.value
ORDER BY IF(v.value IS NULL,0,1), v.value
Then copy the string values returned from the expr column, paste those into an editor, and finish creating the SQL statement, like the example shown above.
The answer from Gordon shows the expression IF(col=12,1,0) can be abbreviated to col=12.
I always find myself typing that out the IF(conditional,valtrue,valfalse), but that's just the way my brain works. It's just easier for me to read.
Similarly the expression in the ORDER BY in my example...
ORDER BY IF(v.value IS NULL,0,1)
could be rewritten...
ORDER BY v.value IS NOT NULL

order by clause on status in my sql

I am doing a sorting on a table to show the records with status having 3 first then status having 4 and then 1.
in current table this is the output
id status
1 3
2 4
3 4
4 3
5 1
now when i apply the query
select * from table order by model.status desc
the output is:
id status
2 4
3 4
1 3
4 3
5 1
what i want actually the below output. first with status 3 then with status 4 and then with status 1. How to achieve the below output
id status
1 3
4 3
2 4
3 4
5 1
instead of a magic 9999999 number, you can use ~0, which represents the maximum possible value for big int... should be enough ;)
order by (case when status = 1 then ~0 else status end)
other solution, without any magic number
order by status = 1 , status
which will sort by a "boolean" (0 or 1 in DB) first, then by status
You can use FIELD to do that easily on MySQL;
SELECT *
FROM model
ORDER BY FIELD(status, 3, 4, 1);
SQLfiddle here.
If FIELD is not working for you (perhaps due to older version of MySql) then try this. It will work
SELECT * FROM model
ORDER BY CASE status WHEN 3 THEN 1 WHEN 4 THEN 2 WHEN 1 THEN 3
END, id
Check it working on this link
Following should work
ORDER BY CASE status WHEN 3 THEN 1
WHEN 4 THEN 2
WHEN 1 THEN 3
END, id asc
Use SQL FIELD
SELECT * FROM `table` ORDER BY FIELD (status, 3, 4, 1);
$invoiceList = InvoicesModel::where('hotel_id', $hotel_id)
->where('status', 'PENDING')
->orWhere('status', 'COOKING_PROCESS')
->orderBy(
DB::raw('(CASE WHEN status = "COOKING_PROCESS"
THEN 1 WHEN status = "PENDING"
THEN 2 END)')
)->get();
Use "FIELD()" in order by clause if status values are 1,3 and 4 only.
select * from table order by FIELD( model.status 3,4,1)

mysql random with condition

I have a table in mysql, say table1.
I am running this on it:
SELECT FLOOR( MAX(id) * RAND()) FROM `table1`
This works well, but I am now trying to add a condition of "AND tom".
Where tom is a integer field.
For example:
id tom
1 0
2 3
3 2
4 0
5 0
6 3
7 1
8 1
9 3
etc.
So, my question is,
How can I pick a random value from id, which also satisfies tom='0' say?
SELECT id FROM `table1` WHERE tom = 0 ORDER BY RAND() LIMIT 1
This will first get all rows in which tom = 0,then order those results randomly. MySQL will then limit those results to just one, returning the single value you want to retrieve.
I hope I understood correctly:
SELECT id FROM `table1` WHERE tom = 0 order by rand() limit 1
select * from (
select * from table where tom = 0 ) as t order by rand() limit 1