SQL ordering (MySQL) [duplicate] - mysql

This question already has answers here:
MySQL order by two values
(6 answers)
Closed 4 years ago.
This seemingly simple order requirement does not seem to have simple solution.
I would like a table be ordered by the item with the highest max value followed by the rest of the same item in descending order. Then the item with the next highest max value followed by the rest of the same item in descending order.. etc
For example I need to order a table similar like this:
item value
AAA 2
AAA 4
AAA 2
CCC 8
BBB 1
BBB 2
BBB 6
CCC 4
To be ordered like this..
item value
CCC 8
CCC 4
BBB 6
BBB 2
BBB 1
AAA 4
AAA 2
AAA 2
Does anyone know how to do this?

This is what you seem to be looking for:
select item, value
from tablename
order by item desc, value desc
Here is a working example: http://sqlfiddle.com/#!9/e56d7/1

select item, value from yourtable order by item desc, value desc;
MySQL order by two values

Here is a way: SQL Fiddle
MySQL 5.6 Schema Setup:
CREATE TABLE Table1
(`item` varchar(3), `value` int)
;
INSERT INTO Table1
(`item`, `value`)
VALUES
('AAA', 2),
('AAA', 4),
('AAA', 2),
('CCC', 8),
('BBB', 1),
('BBB', 2),
('BBB', 6),
('CCC', 4)
;
Query 1:
select
t1.*
from (
select item, max(value) mval from table1 group by item
) m1
inner join table1 t1 on m1.item = t1.item
order by
m1.mval DESC
, t1.item
, t1.value DESC
Results:
| item | value |
|------|-------|
| CCC | 8 |
| CCC | 4 |
| BBB | 6 |
| BBB | 2 |
| BBB | 1 |
| AAA | 4 |
| AAA | 2 |
| AAA | 2 |
In response to worthwhile comment below:
Here's another option that this approach permits (nb: case 'DDD' has been added)
SQL Fiddle
MySQL 5.6 Schema Setup:
CREATE TABLE Table1
(`item` varchar(3), `value` int)
;
INSERT INTO Table1
(`item`, `value`)
VALUES
('DDD', 8),
('DDD', 8),
('AAA', 2),
('AAA', 4),
('AAA', 2),
('CCC', 8),
('BBB', 1),
('BBB', 2),
('BBB', 6),
('CCC', 4)
;
Query 1:
select
t1.*
from (
select item, max(value) mval , sum(value) sval
from table1 group by item
) m1
inner join table1 t1 on m1.item = t1.item
order by
m1.mval DESC
, m1.sval DESC
, t1.item
, t1.value DESC
Results:
| item | value |
|------|-------|
| DDD | 8 |
| DDD | 8 |
| CCC | 8 |
| CCC | 4 |
| BBB | 6 |
| BBB | 2 |
| BBB | 1 |
| AAA | 4 |
| AAA | 2 |
| AAA | 2 |

Related

SELECT WHERE in varchar with wildcard

Lets say i have a table like this
CREATE TABLE Parts (ID int, part_number varchar(100), isActive TINYINT);
and these sample records
|ID | part_number | isActive|
===============================
1 | 1N3.805.327 | 1
2 | 1N3.805.327.B | 1
3 | 1N3.804.108.B | 1
4 | 1N3.804.108.C | 1
5 | 1N3.804.107.B | 1
6 | 1N3.804.107.C | 1
7 | 1N3.804.106.A | 1
8 | 1N3.804.105.A | 1
Problem
I would like to combine a where in clause with the wildcard % operator
In my dbfiddle sample i tried the string function find_in_set and the comparison operator in(). Both do not work:
-- without wildcard the query works
SELECT * FROM Parts WHERE part_number in ('1N3.804.108.B', '1N3.804.106.A'); -- 2
-- with wildcard no records are returned
SELECT * FROM Parts WHERE part_number in ('1N3.804.108%', '1N3.804.106%'); -- 0
SELECT * FROM Parts WHERE FIND_IN_SET(part_number, '1N3.804.108%,1N3.804.106%'); -- 0
Questions
I assume i could use WHERE LEFT(part_number, 11) in ('1N3.804.108', '1N3.804.106') But i do not know if this has any disadvantages.
Is there a way to use a wildcard operator with in()?
Sample records
INSERT INTO
Parts(ID, part_number, isActive)
VALUES
(1, '1N3.805.327',1),
(2, '1N3.805.327.B',1),
(3, '1N3.804.108.B',1),
(4, '1N3.804.108.C',1),
(5, '1N3.804.107.B',1),
(6, '1N3.804.107.C',1),
(7, '1N3.804.106.A',1),
(8, '1N3.804.105.A',1);
Use REGEXP for that, when you want to use OR
CREATE TABLE Parts (ID int, part_number varchar(100), isActive TINYINT);
INSERT INTO
Parts(ID, part_number, isActive)
VALUES
(1, '1N3.805.327',1),
(2, '1N3.805.327.B',1),
(3, '1N3.804.108.B',1),
(4, '1N3.804.108.C',1),
(5, '1N3.804.107.B',1),
(6, '1N3.804.107.C',1),
(7, '1N3.804.106.A',1),
(8, '1N3.804.105.A',1);
✓
✓
SELECT * FROm Parts WHeRE part_number REGEXP '^(1N3.804.108|1N3.804.106)'
ID | part_number | isActive
-: | :------------ | -------:
3 | 1N3.804.108.B | 1
4 | 1N3.804.108.C | 1
7 | 1N3.804.106.A | 1
MySQL Can only UNION a certain number of tables. i think it is about 53.
With an index on partnumber, this will be the fastest.
SELECT * FROm Parts WHeRE part_number REGEXP '^1N3.804.108'
UNION all
SELECT * FROm Parts WHeRE part_number REGEXP '^1N3.804.106'
ID | part_number | isActive
-: | :------------ | -------:
3 | 1N3.804.108.B | 1
4 | 1N3.804.108.C | 1
7 | 1N3.804.106.A | 1
SELECT * FROm Parts WHeRE part_number LIKE '1N3.804.108%'
UNION all
SELECT * FROm Parts WHeRE part_number LIKE '1N3.804.106%'
ID | part_number | isActive
-: | :------------ | -------:
3 | 1N3.804.108.B | 1
4 | 1N3.804.108.C | 1
7 | 1N3.804.106.A | 1
db<>fiddle here
you can also try to do
select *,
REGEXP_LIKE(part_number,[pattern]) as pattern_test from Parts
where pattern_test is TRUE
here first you will create the pattern that you are interested in and apply it on the column you wish to apply it to. The pattern_test will return true if it matches with your pattern and then you can filter on that [where clause]

MySQL get rows starting with specific id after sort / order by

I got this table:
id | score
1 | 1
2 | 4
3 | 4
4 | 3
5 | 2
6 | 2
7 | 1
8 | 4
9 | 2
10 | 3
I need to order it by score desc:
id | score
2 | 4
3 | 4
8 | 4
4 | 3
10 | 3
5 | 2
6 | 2
9 | 2
1 | 1
7 | 1
and get first 3 rows which starts with id 6
So the result should be:
6 | 2
9 | 2
1 | 1
Is this possible? Thanks in advance
I would approach this with a cumulative sum() (available in MySQL 8.0):
select
id,
score
from mytable
order by
sum(id = 6) over(order by score desc, id) desc,
score desc,
id
limit 3
The sum() orders record in the required direction; as soon as the record that has id = 6 is met, the sum takes value 1. It allows to put these records on top. The rest is just adding the additional sorting criteria and limiting the number of results.
Demo on DB Fiddle:
| id | score |
| --- | ----- |
| 6 | 2 |
| 9 | 2 |
| 1 | 1 |
In earlier versions of mysql, you can emulate the window sum with a user variable, as follows:
select
id,
score
from
(select #sm := 0) s
cross join (select id, score from mytable order by score desc, id) t
order by
case when id = 6 then #sm := #sm + 1 end desc,
score desc,
id
limit 3
Demo on DB Fiddle: same results
With this:
select t.*
from tablename t cross join (select * from tablename where id = 6) c
where t.score < c.score or (t.score = c.score and t.id >= c.id)
order by t.score desc, t.id
limit 3
See the demo.
Results:
| id | score |
| --- | ----- |
| 6 | 2 |
| 9 | 2 |
| 1 | 1 |
With this table
CREATE TABLE table3
(`id` int, `score` int)
;
INSERT INTO table3
(`id`, `score`)
VALUES
(1, 1),
(2, 4),
(3, 4),
(4, 3),
(5, 2),
(6, 2),
(7, 1),
(8, 4),
(9, 2),
(10, 3)
;
And this select
SELECT `id`, `score`
FROM (SELECT `id`,`score`,if (id = 8,#scoreid:= #scoreid +1,#scoreid) scoreid
From table3, (SELECT #scoreid :=0) s Order by score desc) t1
Where scoreid > 0 LIMIT 3;
you get
id score
8 4
4 3
10 3
DBFiddle example https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=95e2051d560c2ac27fdcc8f9d04acf5d

Relation Between 2 columns

In MYSQL there are 2 columns in table column A and column B and if in column A continuously comes one 10th time and in column B 11th time comes true(B can be 1 or 0 between these 10 times ) so I want that column id of B.
+----+---+---+
| id | A | B |
+----+---+---+
| 1 | 1 | 0 |
| 2 | 0 | 1 |
| 3 | 1 | 0 |
| 4 | 1 | 0 |
| 5 | 1 | 0 |
| 6 | 1 | 1 |
| 7 | 1 | 0 |
| 8 | 1 | 1 |
| 9 | 1 | 1 |
| 10 | 1 | 0 |
| 11 | 1 | 1 |
| 12 | 1 | 0 |
| 13 | 0 | 1 |
+----+---+---+
I need this (column B id) (Where Column A continuously come 1 (10 times) and Column B (11th id after contenious 10 time 1 in column A )
You could use a running total in a sub query to help you with this on versions prior to mysql 8.0
drop table if exists t;
create table t
(id int,A int,B int);
insert into t values
(1, 1 ,0),
(2, 0 ,1),
(3, 1 ,0),
(4, 1 ,0),
(5, 1 ,0),
(6, 1 ,1),
(7, 1 ,0),
(8, 1 ,1),
(9, 1 ,1),
(10, 1 ,0),
(11, 1 ,1),
(12, 1 ,0),
(13, 1 ,1),
(14, 1 ,1),
(15, 1 ,1),
(16, 0 ,1);
select t1.id,t1.a,t1.b
from
(
select t.*,
if(t.a = 1, #rt:=#rt+1,#rt:=0) rt
from t
cross join (select #rt:=0) r
order by t.id
) t1
where t1.rt >= 10;
+------+------+------+
| id | a | b |
+------+------+------+
| 12 | 1 | 0 |
| 13 | 1 | 1 |
| 14 | 1 | 1 |
| 15 | 1 | 1 |
+------+------+------+
4 rows in set (0.00 sec)
This is a complicated one. Using window functions available in MySQL 8.0, I would proceed in 3 steps:
first compute row numbers in the overall group and in groups of A values
then do a cumulative sum of A values within groups of consecutive A values (using the difference between the 2 above groups), while using LEAD() to recover the id and B value of the next record
finally, filter on the record whose cumulative SUM is 10 and whose next B value is 1; the id of the next record is what you are looking for
Query:
SELECT leadID id
FROM (
SELECT
id,
SUM(A) OVER(PARTITION BY rn1 - rn2 ORDER BY id) sm,
LEAD(id) OVER(ORDER BY id) leadID,
LEAD(B) OVER(ORDER BY id) leadB
FROM (
SELECT
id,
A,
B,
ROW_NUMBER() OVER(ORDER BY id) rn1,
ROW_NUMBER() OVER(PARTITION BY A ORDER BY id) rn2
FROM mytable
) x
) x
WHERE sm = 10 AND leadB = 1
This demo on DB Fiddle with your sample data yields:
| id |
| --- |
| 13 |

Get last mysql record only from a column

This is my existing table
id name version
| 1 | a | 1.1 |
| 2 | b | 2.1 |
| 3 | c | 3.1 |
| 4 | d | 1.2 |
| 5 | e | 4.1 |
how can I write a query to generate results where i will return all records but only the last record in the column version is selected like below?
id name version
| 4 | d | 1.2 |
| 2 | b | 2.1 |
| 3 | c | 3.1 |
| 5 | e | 4.1 |
If you prefer a slightly less laborious solution...
SELECT x.*
FROM t x
JOIN
( SELECT MAX(grade) grade
FROM t
GROUP
BY FLOOR(grade)
) y
ON y.grade = x.grade
http://sqlfiddle.com/#!9/f17db1/16
This is a bit laborious but it can be done
SELECT
SUBSTRING_INDEX(GROUP_CONCAT(id ORDER BY REPLACE(grade,'.','')*1 DESC),',',1) as id,
SUBSTRING_INDEX(GROUP_CONCAT(letter ORDER BY REPLACE(grade,'.','')*1 DESC),',',1) as letter,
MAX(grade) as grade
FROM
t
GROUP BY SUBSTRING_INDEX(grade,'.',1)
ORDER BY REPLACE(grade,'.','')*1
Assuming the last column is float you can use ORDER BY lastcol directly
FIDDLE
CREATE TABLE t
(`id` int, `letter` varchar(7), `grade` varchar(55))
;
INSERT INTO t
VALUES
(1, 'a', '1.1'),
(2, 'b', '2.1'),
(3, 'c', '3.1'),
(4, 'd', '1.2'),
(5, 'e', '4.1')

select random rows in mysql

I have one table named Mydata as follows
id name type
--------------------------------------------
1 vinu 1
2 rinu 2
3 dilu 1
4 raju 2
5 manu 3
6 saju 3
7 ragu 3
8 sonu 1
9 sam 1
10 rag 1
--------------------------------------------
I want to print records with alternating type, for example:
First row with type =1
Second row with type =2
Third row with type =3
4th row type=1
5th row type=2 and so on
Required result as follows
id name type
-----------------------------------------
1 vinu 1
2 rinu 2
5 manu 3
3 dilu 1
4 raju 2
6 saju 3
8 sonu 1
7 ragu 3
9 sam 1
10 rag 1
----------------------------------------------
Sample data:
CREATE TABLE t
(`id` int, `name` varchar(4), `type` int)
;
INSERT INTO t
(`id`, `name`, `type`)
VALUES
(1, 'vinu', 1),
(2, 'rinu', 2),
(3, 'dilu', 1),
(4, 'raju', 2),
(5, 'manu', 3),
(6, 'saju', 3),
(7, 'ragu', 3),
(8, 'sonu', 1),
(9, 'sam', 1),
(10, 'rag', 1)
;
Query:
SELECT id, name, type FROM (
SELECT
t.*,
#rn := IF(#prev_type = type, #rn + 1, 1) AS rownumber,
#prev_type := type
FROM
t
, (SELECT #rn := 0, #prev_type := NULL) var_init_subquery
ORDER BY type
) sq
ORDER BY rownumber, type
Result:
| id | name | type |
|----|------|------|
| 1 | vinu | 1 |
| 4 | raju | 2 |
| 5 | manu | 3 |
| 9 | sam | 1 |
| 2 | rinu | 2 |
| 7 | ragu | 3 |
| 8 | sonu | 1 |
| 6 | saju | 3 |
| 10 | rag | 1 |
| 3 | dilu | 1 |
see it working live in an sqlfiddle
Caveat:
Don't expect this to be performant when you have lots of data. It's doing a full table scan.
Here's a manual entry to read when you're interested about how this variables work.
This cannot be done via a raw SQL query. Extract the rows you need to display, and then sort them via your application.
Alternatively... you could write a stored procedure, but I don't recommend this. You will need a temporary table and a cursor (that transparently creates another temporary table). Too much for a query that should be executed often.