MySQL Sorting Results takes a long time - mysql

Lately I've been getting MySQL to hang on specific queries. I have a table with 500,000+ records. Here is the query being run:
SELECT * FROM items WHERE (itemlist_id = 115626) ORDER BY tableOrder DESC LIMIT 1
Here is the explain:
| 1 | SIMPLE | items | ALL | NULL | NULL | NULL | NULL | 587113 | Using where; Using filesort |
And here is the process_list entry:
| 252996 | root | localhost | itemdb | Query | 0 | Sorting result | SELECT * FROM items WHERE (itemlist_id = 115642) ORDER BY tableOrder DESC LIMIT 1 |
Any idea what could be causing this query to take 10 minutes to process? When I run it manually it's done quickly. (1 row in set (0.86 sec))
Thanks

You need to create an index on items (itemList_id, TableOrder) and rewrite the query a little:
SELECT *
FROM items
WHERE itemlist_id = 115626
ORDER BY
itemlist_id DESC, tableOrder DESC
LIMIT 1
The first condition in ORDER BY may seem to be redundant, but it helps MySQL to choose the correct plan (which does not sort).

Related

Why does this simple SELECT MySQL query hangs when the conditions are put together using the OR operator?

I have this simple MySQL query that mysteriously hangs forever.
Query #1 The following query works (using the first license only), and returning empty set as expected.
SELECT c_desc, c_timestamp FROM t_usage WHERE c_license_sign='license001' ORDER BY c_timestamp DESC limit 1;
Query #2 The following query works (using the second license only), and returning 1 row as expected.
SELECT c_desc, c_timestamp FROM t_usage WHERE c_license_sign='license002' ORDER BY c_timestamp DESC limit 1;
Query #3 The following query hangs forever (using the "first license OR second license"), and I have to use Ctrl+C to get out. Although there doesn't seem to have an increase in CPU nor memory usage while hanging.
SELECT c_desc, c_timestamp FROM t_usage WHERE c_license_sign='license001' OR
c_license_sign='license002' ORDER BY c_timestamp DESC limit 1;
This is the EXPLAIN for query#3
+------+-------------+---------+-------+---------------+---------------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+---------+-------+---------------+---------------+---------+------+------+-------------+
| 1 | SIMPLE | t_usage | index | licenses | idx_timestamp | 6 | NULL | 706 | Using where |
+------+-------------+---------+-------+---------------+---------------+---------+------+------+-------------+
Query #4 HOWEVER, this query works (using OR but removed the c_desc), and returning 1 row as expected.
SELECT c_timestamp FROM t_usage WHERE c_license_sign='license001' OR
c_license_sign='license002' ORDER BY c_timestamp DESC limit 1;
Keeping c_desc and removing the c_timestamp still hangs.
So, why does having OR and c_desc together makes it hangs?
This is the table schema (I removed some unnecessary columns). No primary key (intended). It has more than 1.5 million records.
+----------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------------+--------------+------+-----+---------+-------+
| c_custid | int(11) | YES | | NULL | |
| c_license_sign | varchar(255) | YES | MUL | NULL | |
| c_desc | longtext | YES | | NULL | |
| c_timestamp | datetime | YES | MUL | NULL | |
+----------------+--------------+------+-----+---------+-------+
It has 2 indices:
idx_timestamp index on c_timestamp only
licenses index is composite on (c_license_sign, c_timestamp)
I am using mysql Ver 15.1 Distrib 10.3.31-MariaDB, for Linux (x86_64) using readline 5.1
If you are always going to take latest record, better add DESC in c_timestamp column in licences index.
INDEX licences (c_license_sign, c_timestamp DESC)
I'd like to answer my own question after I found a solution for it.
The solution: use explicit index.
The query which will work is
SELECT c_desc, c_timestamp FROM t_usage USE INDEX(licenses)
WHERE c_license_sign='license001' OR c_license_sign='license002'
ORDER BY c_timestamp DESC limit 1;
Explicitly using the other index (idx_timestamp) will hang the same way as if without using an explicit index.
Unfortunately, I cannot explain why. The thing that maybe can be used as the hint is the EXPLAIN result. It says that the possible_keys is only licenses, but the actual key being used is idx_timestamp (how come?).
At least, if anyone stumble on similar issue like this, you can try to use an explicit index.

mysql Rand() function cause unexpected multirow results

When I try to get random row from table by id using RAND() function I get unexpected unstable results. The following query (where id column is primary key) returns 1, 2 or even more rows:
I tried next variant as well which produces same result:
SELECT id, word FROM words WHERE id = FLOOR(RAND() * 1000)
I found another solution for my task:
SELECT id, word FROM words ORDER BY RAND() LIMIT 1
But I want to know why MySQL behavior is so unexpected with using so elementary functionality. It scares me.
I experimented in different IDE with the same results.
The behavior is not unexpected. The RAND() function is evaluated per-row:
SELECT RAND() FROM sometable LIMIT 10
+----------------------+
| RAND() |
+----------------------+
| 0.7383128467372738 |
| 0.6141578719151746 |
| 0.8558508500976961 |
| 0.4367806654766022 |
| 0.6163508078235674 |
| 0.7714120734216757 |
| 0.0080079743713214 |
| 0.7258036823252251 |
| 0.6049945192458057 |
| 0.8475615799869984 |
+----------------------+
Keeping this in mind, this query:
SELECT * FROM words WHERE id = FLOOR(RAND() * 1000)
means that every row with id between 0 and 999 has 1/1000 probability of being SELECTed!

Retrieving the previous 10 results from a table that are nearest to a certain date and maintaining ascending sorting order

I have a table containing calendar items; in my web application, I have two views:
View 1: primary view that shows the next 10 items, starting from now
View 2: view that shows the previous/next 10 items based on the timestamp of the first/last item in view 1. This is the troublemaker.
On the bottom of the page, previous/next links are shown that lead to view 2.
The problem:
How do I retrieve the previous set of 10 items without knowing what date they are?
At first, this seemed quite simple to me, but apparently, it is not.
Database table:
+-------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+------------------+------+-----+---------+----------------+
| id | int(11) unsigned | NO | PRI | NULL | auto_increment |
| name | varchar(255) | YES | MUL | NULL | |
| start_time | datetime | YES | | NULL | |
| end_time | datetime | YES | | NULL | |
| created | datetime | NO | | NULL | |
| updated | datetime | YES | | NULL | |
| deleted | tinyint(1) | NO | | 0 | |
+-------------+------------------+------+-----+---------+----------------+
SQL query for showing next 10 items starting from now (no problems here):
SELECT ci.*
FROM `calendar_item` AS `ci`
WHERE ci.end_time >= NOW()
GROUP BY `ci`.`id`
ORDER BY `ci`.`end_time` ASC
LIMIT 10
SQL query for showing previous 10 items, based on timestamp of the 1st item in the primary view:
SELECT ci.*
FROM `calendar_item` AS `ci`
WHERE (ci.id IN (
SELECT id FROM calendar_item
WHERE (end_time < FROM_UNIXTIME(1334667600))
ORDER BY end_time DESC
))
GROUP BY `ci`.`id`
ORDER BY `ci`.`end_time` ASC
LIMIT 10
The timestamp is passed via the URL; in view 1 the subquery is not shown at all. The problem lies in the fact that items should be sorted ascending; this would result in the earliest items in the database being returned, instead of the nearest to timestamp. To counter this problem, I created a subquery that sorts descendingly. This subquery works fine when I run it as a normal query, but when contained by the above query, it simply displays the same results as would the following:
SELECT ci.*
FROM `calendar_item` AS `ci`
WHERE ci.end_time <= 1334667600
GROUP BY `ci`.`id`
ORDER BY `ci`.`end_time` ASC
LIMIT 10
I am most likely overlooking something, so I could use your help. Thanks in advance.
This is a simple one, LIMIT the subquery
SELECT ci.*
FROM `calendar_item` AS `ci`
WHERE (ci.id IN (
SELECT id FROM calendar_item
WHERE (end_time < FROM_UNIXTIME(1334667600))
ORDER BY end_time DESC
LIMIT 10
))
GROUP BY `ci`.`id`
ORDER BY `ci`.`end_time` ASC
LIMIT 10
Without the limit in the subquery you are selecting ALL rows with a timestamp < FROM_UNIXTIMESTAMP. You are then reordering ASC and selecting the first 10, i.e. the earliest 10.
If you limit the subquery you get the 10 highest which satisfy your FROM_UNIXTIME, and the outer can then select them.
An alternative (and my preferred) would be the following, where the subquery gets the data, and the outer query simply reorders it before spitting it back out.
SELECT i.*
FROM (
SELECT ci.*
FROM calendar_item AS ci
WHERE ci.end_time < FROM_UNIXTIME(1334667600)
ORDER BY ci.end_time DESC
LIMIT 10
) AS i
ORDER BY i.`end_time` ASC

How to sample rows in MySQL using RAND(seed)?

I need to fetch a repeatable random set of rows from a table using MySQL. I implemented this using the MySQL RAND function using the bigint primary key of the row as the seed. Interestingly this produces numbers that don't look random at all. Can anyone tell me whats going on here and how to get it to work properly?
select id from foo where rand(id) < 0.05 order by id desc limit 100
In one example out of 600 rows not a single one was returned. I change the select to include "id, rand(id)" and get rid of the rand clause in the where this is what I got:
| 163345 | 0.315191733944408 |
| 163343 | 0.814825518815616 |
| 163337 | 0.313726862253367 |
| 163334 | 0.563177533972242 |
| 163333 | 0.312994424545201 |
| 163329 | 0.312261986837035 |
| 163327 | 0.811895771708242 |
| 163322 | 0.560980224573035 |
| 163321 | 0.310797115145994 |
| 163319 | 0.810430896291911 |
| 163318 | 0.560247786864869 |
| 163317 | 0.310064677437828 |
Look how many 0.31xxx lines there are. Not at all random.
PS: I know this is slow but in my app the where clause limits the number of rows to a few 1000.
Use the same seed for all the rows to do that, like:
select id from foo where rand(42) < 0.05 order by id desc limit 100
See the rand() docs for why it works that way. Change the seed if you want another set of values.
Multiply the decimal number returned by id:
select id from foo where rand() * id < 5 order by id desc limit 100

Sum a generated list in MySQL, in a single query

I need to sum a result that I'm getting from an existing query. And the it has to extend the current query and remain a single query
(by this I mean NOT - DO 1; DO 2; DO3;)
My current query is:
SELECT SUM((count)/(SELECT COUNT(*) FROM mobile_site_statistics WHERE campaign_id='1201' AND start_time BETWEEN CURDATE()-1 AND CURDATE())*100) AS percentage FROM mobile_site_statistics WHERE device NOT LIKE '%Pingdom%' AND campaign_id='1201' AND start_time BETWEEN (CURDATE()-1) AND CURDATE() GROUP BY device ORDER BY 1 DESC LIMIT 10;
This returns:
+------------+
| percentage |
+------------+
| 47.3813 |
| 19.7940 |
| 5.6672 |
| 5.0801 |
| 3.9603 |
| 3.8500 |
| 3.1294 |
| 2.9924 |
| 2.9398 |
| 2.7136 |
+------------+
What I need is the total of that table (total percent used by the top 10 devices)(that's all) but it has to be a single query (Has to include the initial query)(Has to be a single query due to another program that's using the query)
Is this possible? every way I have tried so far has failed. We tried temporary tables, but that turned into multiple queries.
Just do a
SELECT SUM(percentage) AS total FROM (<YOUR_QUERY>) a
and replace the sub-query <YOUR_QUERY> with your initial query