MySQL: Conditional Order By - mysql

I have a column called first_name in my 'employee' table.
I would like to select & order those names by DESC if COUNT(first_name) is an odd number or select & order those names by ASC if COUNT(first_name) is an even number.
Something like that is written below
SELECT first_name
FROM employee
ORDER BY (first_name DESC if COUNT (first_name) % 2 != 0 ELSE first_name ASC);

You have to force mysql to choose one option
like
SELECT first_name
FROM employee
ORDER BY (CASE WHEN CHAR_LENGTH(first_name) % 2 <> 0 THEN first_name END) ASC,
(CASE WHEN CHAR_LENGTH(first_name) % 2 = 0 THEN first_name END) DESC
It can only be one or the other.

Related

Is it possible to do this task more efficiently? MySQL

Currently, I have a task: for the presented table, to change students' order by replacing odd and even sequence (example below). There is only one condition, if the number of students is odd, that last number should not be changed. I currently wrote a code like this, however, for me, it seems pretty clumsy. How different and more efficient should this code be written?
CREATE TABLE student (id int, name varchar(128));
INSERT INTO student (id,name) VALUES
(1,'Aurimas'),
(2,'Darius'),
(3,'Eligijus'),
(4,'Giedrius'),
(5,'Justinas');
SELECT CASE
WHEN mod((SELECT id FROM student ORDER BY id DESC LIMIT 1),2) = 0 THEN
CASE
WHEN mod(id, 2) = 0 THEN id-1
ELSE id=id+1
END
ELSE
CASE
WHEN mod(id, 2) = 0 AND id <> (SELECT id FROM student ORDER BY id DESC LIMIT 1) THEN id-1
WHEN mod(id, 2) = 1 AND id <> (SELECT id FROM student ORDER BY id DESC LIMIT 1) THEN id+1
ELSE id
END
END AS new_id, name
FROM student
ORDER BY new_id ASC;
I have this:
id name
1 Aurimas
2 Darius
3 Eligijus
4 Giedrius
5 Justinas
And it should look like this:
id name
1 Darius
2 Aurimas
3 Giedrius
4 Eligijus
5 Justinas
You could group your ids by the value of (id+1) DIV 2. Then all you have to do is swap the minimum id with the maximum id in each pair of rows and make sure you handle the case when there are odd number of rows properly.
Something like this:
set #maxId = (select max(id) from student);
select
if (
#maxId = id and mod(id, 2) != 0,
id,
if (
mod((id+1), 2) = 0,
id+1,
id-1
)
) as id,
name
from student
order by id;
db<>fiddle
I'm not sure how you're defining 'efficient', but how about...
select ROW_NUMBER() OVER (
ORDER BY ceiling(id/2), id desc
) new_id,name from student
The ROW_NUMBER function has been supported in MySQL since version 8.0.
EDIT: I suppose something like will satisfy the quibblers...
WITH cte (id,n) AS (SELECT id, ROW_NUMBER() OVER (ORDER BY id) n FROM student)
SELECT x.name
, ROW_NUMBER() OVER (ORDER BY CEILING(cte.n/2), x.id desc) new_id
FROM student x
JOIN cte
ON cte.id = x.id;
I would recommend to use the below query because
it has many advantages here I am calculating only one max id select but in your case for every case, one select will execute this will reduce that time and only one max id will be used for the odd case
your odd and even operation is the same but in odd you are skipping last record so I added that condition
SET #MAXID = (SELECT MAX(ID) FROM STUDENT);
SELECT
CASE
WHEN MOD(#MAXID,2) != 0 AND ID=#MAXID THEN ID
ELSE CASE WHEN MOD(ID, 2) = 0 THEN ID-1 ELSE ID+1 END
END AS NEW_ID, NAME
FROM STACK_USER.STUDENT
ORDER BY NEW_ID ASC;
I think this would be useful in your case I have checked every condition in my local it will work properly
Screen Shot For Better Understanding:

Get latest two distinct location from table

Below is my table,
SELECT DISTINCT(availability_location) as location FROM table_name WHERE user_id = '8' ORDER BY availability_date DESC LIMIT 2
I'm getting following result
I want following result :
2016-05-27 pune
2016-05-20 Burbank
i.e. Unique availability_location as well as latest two entries.
You have to use GROUP BY for this:
SELECT availability_location as location,
MAX(availability_date) AS max_date
FROM table_name
WHERE user_id = '8'
GROUP BY location
ORDER BY max_date DESC LIMIT 2
You can use GROUP BY and order by the max date :
SELECT t.availability_location
FROM table_name t
WHERE user_id = '8'
GROUP BY t.availability_location
ORDER BY max(s.availability_date) DESC LIMIT 2
Output :
availability_location
---------------------
pune
Burbank
EDIT: next time, you should mention that you want it to be case sensitive. You can try doing it like this:
SELECT t.availability_location
FROM table_name t
INNER JOIN(SELECT s.availability_location , max(s.availability_date) as max_d
FROM table_name s
WHERE s.user_id = '8'
GROUP BY s.availability_location) t2
ON(t2.availability_location = t.availability_location AND
t2.max_d = t.availability_date)
ORDER BY t.availability_date DESC LIMIT 2

SQL Query containing 2 result sets

select name from persons where gender = 'male' order by name limit 10
select name from persons where gender = 'female' order by name limit 10
How can I get the first 10 males and the first 10 females in a single result set (20 entries) with a single SQL query call? Is this possible?
The correct syntax uses parentheses and union all:
(select name
from persons
where gender = 'male'
order by name
limit 10
) union all
(select name
from persons
where gender = 'female'
order by name
limit 10
)
It is possible and it's called UNION
You can use UNION Operator.
UNION is used to combine the result from multiple SELECT statements
into a single result set.
select name from persons where gender = 'male' order by name limit 10
UNION
select name from persons where gender = 'female' order by name limit 10
SELECT name FROM persons WHERE gender = 'male' ORDER BY name LIMIT 0,10 ;
UNION
SELECT name FROM persons WHERE gender = 'female' ORDER BY name LIMIT 0,10 ;
with ROW_NUMBER()
SELECT * FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY gender ORDER BY gender) row_no,* FROM Person
) A WHERE A.row_no <= 10

Get next and previous row based on SQL ordered by two text columns

I have this sql statement:
SELECT * FROM members ORDER BY last_name ASC, first_name ASC LIMIT 0, 10
The above is used on the index page.
On the details page, I use
SELECT * FROM members WHERE id = :id
How can I get the previous and next row based on the current row id? (the prev and next one ordered by last_name ASC, first_name ASC)
I assume it would be something like this:
SELECT id FROM members WHERE last_name >= : last_name AND id != :id ORDER BY last_name ASC, first_name ASC
But that is not right. I also need to add first_name to the statement somehow, and just doing first_name >= : first_name is not correct.
You can use union all along with order by and limit. The following gets the previous name in the first subquery and the next name in the second:
(select m.*
from members m
where last_name < (select last_name from members where id = :id)
order by last_name desc
limit 1
) union all
(select m.*
from members m
where last_name > (select last_name from members where id = :id)
order by last_name asc
limit 1
);
Probably the easiest way to handle first and last names is to concatenate them together:
(select m.*
from members m
where concat_ws(' ', last_name, first_name) < (select concat_ws(' ', last_name, first_name) from members where id = :id)
order by last_name asc, first_name desc
limit 1
) union all
(select m.*
from members m
where concat_ws(' ', last_name, first_name) > (select concat_ws(' ', last_name, first_name) from members where id = :id)
order by last_name asc, first_name
limit 1
)
This could have slightly unexpected results in cases where the first and/or last name have spaces in them. I'm guessing that does not happen in the data, but other separators could be used in that case.

mySQL order by count subquery trouble

I have a table called items_status which has 3 fields, item_id, user_id, and status, which can be either 'have' or 'want'.
Field Type Null Key
user_id varchar(10) NO PRI
item_id varchar(10) NO PRI
status set('have','want') YES NULL
I have a page where I want to get a list of all the user ids in the table ordered by the number of records their user id is associated with in the table where status is 'have'. So far, this is the best I can come up with:
SELECT user_id
FROM items_status AS is
ORDER BY
//Subquery to get number of items had by user
(SELECT COUNT(i.item_id)
FROM items_status AS i
WHERE i.user_id = is.user_id AND i.status = 'have') DESC
GROUP BY user_id
However, this pulls up an error on the subquery. How can I get all of the user ids in the table ordered by the number of items they have?
you can do it like this:
SELECT user_id
FROM items_status
WHERE `status` = 'have'
GROUP BY userID
ORDER BY COUNT(user_id) DESC
SQLFiddle Demo
with slight difference of column name but the query is the same
SELECT user_id, SUM(CASE WHEN i.status = 'have' THEN 1 ELSE 0) AS s
FROM items_status AS is
GROUP BY user_id
ORDER BY SUM(CASE WHEN i.status = 'have' THEN 1 ELSE 0) DESC