MySQL order by IN order - mysql

i have simple query:
SELECT data FROM table WHERE id IN (5, 2, 8, 1, 10)
Question is, how can i select my data and order it like in my IN.
Order must be 5, 2, 8, 1, 10.
Problem is that i have no key for order. IN data is from other query (1), but i need to safe order.
Any solutions?
(1)
SELECT login
FROM posts
LEFT JOIN users ON posts.post_id=users.id
WHERE posts.post_n IN (
2280219,2372244, 2345146, 2374106, 2375952, 2375320, 2371611, 2360673, 2339976, 2331440, 2279494, 2329266, 2271919, 1672114, 2301856
)
Thanx for helping, solutions works but very slow, maybe find something better later, thanx anyway

The only way I can think to order by an arbitrary list would be to ORDER BY comparisons to each item in that list. It's ugly, but it will work. You may be better off sorting in whatever code you are doing the selection.
SELECT data FROM t1 WHERE id IN (5, 2, 8, 1, 10)
ORDER BY id = 10, id = 1, id = 8, id = 2, id = 5
The order is reversed because otherwise you would have to add DESC to each condition.

You can use a CASE statement
SELECT data
FROM table WHERE id IN (5, 2, 8, 1, 10)
ORDER BY CASE WHEN id = 5 THEN 1 WHEN id = 2 THEN 2 WHEN id = 8 THEN 3 WHEN id = 1 THEN 4 WHEN id = 10 THEN 5 END

SELECT data FROM table
WHERE id IN (5, 2, 8, 1, 10)
ORDER BY FIELD (id, 5, 2, 8, 1, 10)
http://dev.mysql.com/doc/refman/5.5/en/string-functions.html#function_field
Might be easier to auto-generate (because it basically just needs inserting the wanted IDs comma-separated in the same order a second time) than the other solutions suggested using CASE or a number of ID=x, ID=y ...

http://sqlfiddle.com/#!2/40b299/6
I think that's what you're looking for :D Adapt it to your own situation.

To do this dynamically, and within MySql, I would suggest to do the following:
Create a temp table or table variable (not sure if MySql has these), with two columns:
OrderID mediumint not null auto_increment
InValue mediumint -(or whatever type it is)
Insert the values of the IN clause in order, which will generate ID's in order of insertion
Add a JOIN to your query on this temp table
Change your Order By to be
order by TempTable.OrderID
Drop temp table (again, in SQL inside a stored proc, this is automatic, not sure about MySql so mentioning here for full disclosure)
This effectively circumvents the issue of you not having a key to order by in your table ... you create one. Should work.

Related

Mysql query performance multiple and or conditions

I have this query in mysql with very poor performance.
select `notifiables`.`notification_id`
from `notifiables`
where `notifiables`.`notification_type` in (2, 3, 4)
and ( ( `notifiables`.`notifiable_type` = 16
and `notifiables`.`notifiable_id` = 53642)
or ( `notifiables`.`notifiable_type` = 17
and `notifiables`.`notifiable_id` = 26358)
or ( `notifiables`.`notifiable_type` = 18
and `notifiables`.`notifiable_id` = 2654))
order by `notifiables`.`id` desc limit 20
Is this query can be optimized in any way. Please help
This table has 2M rows. and taking upto 1-4 seconds in searching
Updated indexes and Explain select
Possible solutions:
Turning OR into UNION (see #hongnhat)
Row constructors (see #Akina)
Adding
AND notifiable_type IN (16, 17, 18)
Index hint. I dislike this because it often does more harm than good. However, the Optimizer is erroneously picking the PRIMARY KEY(id) (because of the ORDER BY instead of some filter which, according to the Cardinality should be very good.
INDEX(notification_type, notifiable_type, notifiable_id, id, notification_id) -- This is "covering", which can help because the index is probably 'smaller' than the dataset. When adding this index, DROP your current INDEX(notification_type) since it distracts the Optimizer.
VIEW is very unlikely to help.
More
Give this a try: Add this to the beginning of the WHERE
WHERE notifiable_id IN ( 53642, 26358, 2654 )
AND ... (all of what you have now)
And be sure to have an INDEX starting with notifiable_id. (I don't see one currently.)
Use the next syntax:
SELECT notification_id
FROM notifiables
WHERE notification_type IN (2, 3, 4)
AND (notifiable_type, notifiable_id) IN ( (16, 53642), (17, 26358), (18, 2654) )
ORDER BY id DESC LIMIT 20
Create index by (notification_type, notifiable_type, notifiable_id) or (notifiable_type, notifiable_id, notification_type) (depends on separate conditions selectivity).
Or create covering index ((notification_type, notifiable_type, notifiable_id, notification_id) or (notifiable_type, notifiable_id, notification_type, notification_id)).
You can make different kinds of "VIEW" from the data you want and then join them.

Subquery returns different result than when called on its own

Using GROUP BY in a MySQL subquery returns results not appearing when calling the GROUP BY query on its own.
This example is about as stripped down as I can make it.
I feel like I'm missing something elementary about using GROUP BY in a subquery, but haven't found an existing question or tutorial that addresses this particular issue.
First, build the table:
CREATE TABLE Person (
Id INT
Email VARCHAR(20)
);
INSERT INTO Person VALUES (1, "a#b.com");
INSERT INTO Person VALUES (2, "c#d.com");
INSERT INTO Person VALUES (3, "a#b.com");
INSERT INTO Person VALUES (5, "c#d.com");
INSERT INTO Person VALUES (7, "e#f.com");
INSERT INTO Person VALUES (11, "e#f.com");
INSERT INTO Person VALUES (13, "g#h.com");
Now compare these two pairs of queries:
Test 1: subquery using explicit IDs
SELECT Id FROM Person WHERE Id IN (1, 2, 7, 13)
returns Id (1, 2, 7, 13)
SELECT Id FROM Person WHERE Id IN (SELECT Id FROM Person WHERE Id IN (1, 2, 7, 13))
returns Id (1, 2, 7, 13)
Test 2: subquery using GROUP BY for Email uniqueness (gives first in each group)
SELECT Id FROM Person GROUP BY Email
returns Id (1, 2, 7, 13)
SELECT Id FROM Person WHERE Id IN (SELECT Id FROM Person GROUP BY Email)
returns Id (1, 2, 3, 5, 7, 11, 13)... not (1, 2, 7, 13), as expected.
I expected the output of the compound query on both of these tests to be
Id (1, 2, 7, 13)
since the subquery in each outputs
Id (1, 2, 7, 13)
as input to the top-level query.
This leads me to believe that the displayed results are not actually the full results (at least, when it comes to a GROUP BY). Any elucidation on this confusing situation would be greatly appreciated.
SELECT Id FROM Person GROUP BY Email
This query is invalid according to the SQL standard. You group by Email. So you get one result row per Email. Then you want to show the ID for the Email. But there is not the ID per Email, there can be many. For Email = 'a#b.com' for instance there are the IDs 1 and 3. The DBMS should raise an error. But MySQL silently substitutes this with
SELECT ANY_VALUE(Id) FROM Person GROUP BY Email
i.e. returns an arbitrarily chosen value. It's left to chance whether the query returns ID 1 or 3 for Email = 'a#b.com'.
This explains why you get different results. One time the DBMS chooses this value one time the other.
Group BY should be use with aggregation function.
(If you need distinct result use the DISTINCT clause and not use group by improperly)
In mysql versone < 5.7 the The use of group by without aggregation function produce unpredictable result ..
in mysql >= 5.7 is not allowed by default and this kind of use produce error.
If you want control the result based on group by you should use a proper aggregation function eg: min() or max()
SELECT Id
FROM Person
WHERE Id IN ( SELECT min(Id )
FROM Person
GROUP BY Email
)

MySQL counter for specific situations

I need to generate unique "ids", the catch is, it can be only between 1 - 99999.
The "good" thing is, that it has to be unique only in group with another column.
We have groups, each group has its own "group_id" and each group need something like unique('group_id', 'increment_id')
The 99999 records is enough for several years for each group right now, but it is not enough for all groups together, therefore I cant just create table with AUTO_INCREMENT and inserting there records and taking its auto increment.
For example, if I need 5 records for Group one and three records for Group two, I suppose to get something like this:
group_id, increment_id
1, 1,
1, 2,
1, 3,
1, 4,
1, 5,
2, 1
2, 2,
2, 3
Also, the conflict is not an option, therefore using something like "length" can be tricky, if done programatically (there can be i.e. 10 requests at once, each of them first select length for given group_id and then tries to create 10 rows with same increment_id)
However I am thinking - if I set it up as the value of subselect of count, than it will always be "ok"?
You can create a auxiliar table named counters to manage that:
table: counters
columns: group_id, current_counter
OR
Each time you insert a row increment_id = select max(increment_id)+1 from table_xxx where group_id = group_xxxx
You can use user variables to get the incrementing number within each group_id:
select
t.*,
#rn := if(#group_id = group_id,
#rn + 1,
if(#group_id := group_id, 1, 1)
) increment_id
from (
select group_id
from your_table t
/* some where clauses */
order by group_id
) t
cross join (
select #rn := 0,
#group_id := - 1
) t2

Selecting * from max id when grouping by parentid (MySQL)

I have a big problem that I've been trying to tackle. Reading older similar issues hasn't been fruitful.
My table has columns [id], [parentid], [data1], [data2] etc.
There are some cases where only one record has same [parentid] and others have 2-10.
I would like to group by [parentid] and print out all data of the record which has maximum [id]. So I would only get all the "latest" details for the records which share the same [parentid] number.
I hope this is comprehensible goal in any way :).
I've also tried to tackle this in Crystal Reports XI and Qlikview 11 without major success.
Thanks in advance for any help!
Can values in your ID column be reused? If no, then juergen's answer will work.
If they can be reused, you will need to use the same table twice in your query, once to get the max id for each parent id, and once to get the row for each max id/parent id.
Like so:
select
t1.*
from aTable t1,
(select parentid, max(id) as id
from aTable group by parentid) t2
where t1.id = t2.id
and t1.parentid = t2.parentid
SQLFIddle!
select * from your_table
where id in
(
select max(id)
from your_table
group by parentid
)
A solution with qlikview would be:
Script:
Table:
Load id,
parentid,
d1,
d2
inline
[
id, parentid, d1, d2
1, 0, Hep, 01-04-2010
2, 1, Hap, 09-04-2010
3, 1, Hup, 10-10-2012
4, 2, Bep, 01-12-2009
5, 4, Ceg, 02-10-2010
6, 4, Pen, 05-10-2009
7, 4, Heg, 01-10-2009
8, 4, Ran, 08-01-2010
];
Then I added the fields id and parentid to the dashboard.
To calulate the results use a table diagram where parentid is the dimension. Add a formula
=max(id)
and name it 'max(id)'
Then you get the following result:

MySQL - Check if array values with the same id exist

I've got table with two fields: id, numbers
I want to check if the same array values (etc 1,2,5,6) already exist in table, but they have to have the same 1nd row number (id).
I've tried with this sql query, but this one doesn't check if there is the same id:
SELECT id, numbers FROM `table` WHERE numbers IN (1,2,5,6) AND id = id
I know that "id = id" doesn't work, but I posted it so you'll know what I mean.
SELECT id, COUNT(*) num_count, GROUP_CONCAT(numbers ORDER BY numbers) all_numbers
FROM `table`
WHERE numbers IN (1, 2, 5, 6)
GROUP BY id
If you only want to see the ones that have all 4 numbers, add:
HAVING num_count = 4
If you want the IDs that have all and only those 4 numbers, use:
SELECT id, COUNT(*) all_count, SUM(numbers IN (1, 2, 5, 6)) in_count
FROM `table`
GROUP BY id
HAVING all_count = in_count