Sorting issue with limit and offset MYSQL - mysql

I am facing sorting issue in mysql
See the output of below query:
select astrologers.id,astrologers.name,chat_online,online,experience from `astrologers`
where `astrologers`.`status` = '1'
order by experience asc limit 10;
id
name
chat_online
online
experience
15
Astro Anoop
0
0
3
20
Test Astro2
0
0
3
3
Test anoop
0
0
5
4
Anoop Kumar trivedi
0
0
5
7
Test
0
0
5
58
Neeraj yadav
1
0
5
45
Satish Kumar Gupta
1
1
10
56
AP Sharma
1
0
15
40
VG Astrologer App
1
0
55
In above result id 58 (Neeraj yadav) is at 6th position but when I run the same query with limit 3, same id 58 (Neeraj yadav) is at 3rd position:
select astrologers.id,astrologers.name,chat_online,online,experience
from `astrologers`
where `astrologers`.`status` = '1'
order by experience asc limit 3;
id
name
chat_online
online
experience
20
Test Astro2
0
0
3
15
Astro Anoop
0
0
3
58
Neeraj yadav
1
0
5
The 3rd row in above result should be id 3 (Test anoop) but it gives id 58 (Neeraj yadav)
Is this bug in mysql?

Is this a bug in MySQL?
No. The problem is that your sort is not deterministic, and gives ties in the third position:
| 3 | Test anoop | 0 | 0 | 5 |
| 4 | Anoop Kumar trivedi | 0 | 0 | 5 |
| 7 | Test | 0 | 0 | 5 |
| 58 | Neeraj yadav | 1 | 0 | 5 |
All 4 users have the same experience, hence leaving the database to figure out how they should be sorted.
When asked to return to top 3 rows, the database picks the first two, and then one of the 4 ties. The result that you get might not be consistent over consequent executions of the same query, as you are starting to see.
Bottom line: know you data; if you want a deterministic result, then use a deterministic sort. We could, for example, use id to break the ties, hence making the result predictable:
order by experience, id limit 3

Related

How to update the same row with condition in MySql?

Here's data
KeyID | Queue | Pay
65 1 0
60 2 0
58 3 1
57 4 1
55 5 0
54 6 0
53 7 1
50 8 1
if the data like this , I need a single MySql query to update it to be like below data table which update only Queue column.
KeyID | Queue | Pay
65 0 0
60 0 0
58 1 1
57 2 1
55 0 0
54 0 0
53 3 1
50 4 1
I have try this
update tabl1
set Queue = case when Pay = 0 then Queue=Queue-1 else Queue
But Queue number not look like this.
Please suggest.
Thank you in advance
We can try joining to a subquery which computes the sequence:
UPDATE yourTable k1
INNER JOIN
(
SELECT KeyID, rn
FROM
(
SELECT t1.KeyID,
(SELECT SUM(t2.Pay = 1) FROM yourTable t2
WHERE t2.KeyID >= t1.KeyID) rn
FROM yourTable t1
) t
) k2
ON k1.KeyID = k2.KeyID
SET
Queue = CASE WHEN Pay = 0 THEN 0 ELSE k2.rn END;
To see how the above logic is working, here is what the intermediate join table looks like:
KeyID | Queue | Pay | rn
65 | 1 | 0 | 0
60 | 2 | 0 | 0
58 | 3 | 1 | 1
57 | 4 | 1 | 2
55 | 5 | 0 | 2
54 | 6 | 0 | 2
53 | 7 | 1 | 3
50 | 8 | 1 | 4
That is, the innermost correlated subquery generates the queue sequence by counting the number of times Pay is 1.
Note that if you are using MySQL 8+, then there is a much simpler query using analytic functions:
UPDATE yourTable
SET Queue = CASE WHEN Pay = 0
THEN 0
ELSE SUM(Pay = 1) OVER (ORDER BY KeyID DESC) END;

use month names instead of numbers, in union select mysql

First, I just used union, and I don't really understand it.
basically, to change the name of the month I usually use
select MONTHNAME (date)
But in this case I dont know Union more deeply so it is very confusing to change the number of months to the name of the month. Can you help me? please don't just give an answer but please also include an explanation.
this my query, and fyi this query was helped by the 'sticky bit' in my previous topic.
SELECT m.month month_table,
coalesce(s.count, 0) cstart,
coalesce(e.count, 0) cend
FROM (SELECT 1 month
UNION ALL
SELECT 2 month
UNION ALL
SELECT 3 month
UNION ALL
SELECT 4 month
UNION ALL
SELECT 5 month
UNION ALL
SELECT 6 month
UNION ALL
SELECT 7 month
UNION ALL
SELECT 8 month
UNION ALL
SELECT 9 month
UNION ALL
SELECT 10 month
UNION ALL
SELECT 11 month
UNION ALL
SELECT 12 month) m
LEFT JOIN (SELECT month(n.start_date) month,
count(*) count
FROM newdata n
GROUP BY month(n.start_date)) s
ON s.month = m.month
LEFT JOIN (SELECT month(n.end_date) month,
count(*) count
FROM newdata n
GROUP BY month(n.end_date)) e
ON e.month = m.month
ORDER BY m.month;
I have tried to replace all 'month' to 'monthname' but the results is 0.
Before i change month to monthname
month_table|cstart|cend
1 | 1 | 0
2 | 0 | 1
3 | 0 | 0
4 | 0 | 0
5 | 0 | 0
6 | 0 | 0
7 | 0 | 0
8 | 1 | 0
9 | 0 | 0
10 | 1 | 2
11 | 0 | 0
12 | 0 | 0
and when i change month to monthname the results be
month_table|cstart|cend
1 | 0 | 0
2 | 0 | 0
3 | 0 | 0
4 | 0 | 0
5 | 0 | 0
6 | 0 | 0
7 | 0 | 0
8 | 0 | 0
9 | 0 | 0
10 | 0 | 0
11 | 0 | 0
12 | 0 | 0
You can just use CONCAT to convert your month number into a date string that you can then pass to MONTHNAME i.e. change the first line of your query to:
SELECT MONTHNAME(CONCAT('2018-', m.month, '-01')) month_table,
Output (for your sample data from your previous question):
month_table cstart cend
January 1 0
February 0 1
March 0 0
April 0 0
May 0 0
June 0 0
July 0 0
August 1 0
September 0 0
October 1 2
November 0 0
December 0 0
SQLFiddle demo
Most people just create look up table for this kind of thing, populate it once then use it. Not only does it perform vastly better, but it’s easily understood and reused.
Another common tick is to create a “calendar” table that contains all the dates within a decade or so, with day names, public holiday indicators, etc, which makes writing queries that output data for each day, even if there is no data for that day, super easy. And it’s completely portable.
Forget the SQL Kung Fu and apply KISS.

Select specified items from rows at first priority mySql

I have a table users
id | Name |
1 John
2 Dwayne
3 Daniel
4 Ronaldo
5 Messi
6 Gareth
7 Leonardo
8 Brad
If i have id's in order of (7,5,1,3) and i want to select limit 6 then the expected output should be as following :-
id Name
7 | Leonardo
5 | Messi
1 | John
3 | Daniel
2 | Dwayne
4 | Ronaldo
What i did previously was this but did not get the expected output . Help!!
"select * from users ORDER BY FIELD(user_id,7,5,1,3) LIMIT 6"
Your query is close. It just doesn't take into account all the other user ids. Try this:
ORDER BY FIELD(user_id,7,5,1,3) > 0 desc,
FIELD(user_id,7,5,1,3)
LIMIT 6;
The problem with your version is that field() returns 0 when there is no match. And the 0s will be ordered first.

MySQL - Select rows whose timestamp is within a set time of another row

I have a table of events with timestamps, and types (1 or 0). Im looking to select all the type 0 rows where a type 1 row has a timestamp within 10 (or whatever) seconds.
event_id | type | timestamp
1 | 0 | 2012-1-1 00:00:00
2 | 0 | 2012-1-1 00:00:01
3 | 1 | 2012-1-1 00:00:09
4 | 1 | 2012-1-1 00:00:10
5 | 0 | 2012-1-1 00:00:14
6 | 0 | 2012-1-1 00:00:20
7 | 1 | 2012-1-1 00:00:25
8 | 0 | 2012-1-1 00:00:40
9 | 0 | 2012-1-1 00:00:50
10 | 1 | 2012-1-1 00:01:00
So in this example it would grab rows 1,2, and 6
I know how to do it if I run a new query for each type 0 event, but obviously that can be incredibly slow once the table becomes thousands of rows.
As you suggested, doing this query for each row would be inefficient. However, a JOIN seems to fit your need nicely:
SELECT ones.*
FROM my_table ones
JOIN my_table zeroes
ON zeroes.type = 0 AND
TIME_TO_SEC(TIMEDIFF(ones.timestamp, zeroes.timestamp)) <= 10
WHERE ones.type = 1
SQLFiddle demo
select distinct t1.* from t as t1
JOIN t as t2 on (t2.type=1) and
(t2.timestamp between t1.timestamp
AND t1.timestamp + INTERVAL 10 SECOND
)
where t1.type=0

Select according to rows increments

I have a MySQL database like this one :
ID | Twin | Tloss
0 | 300 | 250 #first entry
1 | 301 | 250 #win; score 1 - 0
2 | 302 | 250 #win; score 2 - 0
3 | 302 | 251 #lose: score 2 - 1
4 | 303 | 251 #win; score 3 - 1
5 | 304 | 251 #end of match1 : Win 4 - 1
6 | 304 | 252 #lose; score 0 - 1
7 | 304 | 253 #lose; score 0 - 2
8 | 304 | 254 #lose; score 0 - 3
9 | 304 | 255 #end of match2 : Lose 0 - 4
10 | 304 | 256 #lose; score 0 - 1
11 | 305 | 253 #win; score 1 - 1
12 | 306 | 254 #win; score 2 - 1
13 | 306 | 255 #lose; score 2 - 2
14 | 307 | 255 #win; score 3 - 2
15 | 307 | 256 #end of match3 : Draw 3 - 3
....
I want to select all the ID corresponding to the match number "n",
considering a match is ended as soon as he wins 4 times or loses 4 times, draw is possible as the maximum number of round per match is 6.
I use SQL a lot since 1 months but I'm really lost on this one.
Could someone help me ?
Thanking you in advance,
I am assuming that some of the data above is broken (records 11-15, field tloss; record 5 should be Win 4 - 1). I also don't know what are the numbers 300 and 250 and how they change in the table. With these assumptions, this untested SQL might be want you want:
(revised after feedback from GordonLinoff)
SELECT #matchno := #matchno + 1 AS matchno
FROM (SELECT #matchno := 0) mn,
(SELECT ID, Twin, Tloss,
IF((Twin - #twin) = 4
OR (Tloss - #tloss) = 4
OR ((Twin - #twin) = 3 AND (Tloss - #tloss) = 3),
#twin := Twin AND #tloss := Tloss,
0)
FROM matches, (SELECT #twin := 300, #tloss := 250) AS base
WHERE (Twin - #twin) = 4
OR (Tloss - #tloss) = 4
OR ((Twin - #twin) = 3 AND (Tloss - #tloss) = 3)
ORDER BY ID
) endmatches