MySQL GROUP BY rand()? - mysql

So, interesting problem I've run into. I'm sure there's an easy solution, but I'm not sure what it is. :)
Basically, imagine a very simple database, like so:
----------------
T1
----------------
r | nID
---------------
1 | A
2 | B
----------------
----------------
T2
----------------
nID | val
---------------
A | XXX
B | L
B | M
B | N
B | P
----------------
Basically, Table 2 references Table 1. Now, I'd like to select a random row from either A or B. However, I would like to first randomize the A and B, THEN choose an associated value.
In other words, flip a coin: Heads, XXX. Tails, L, M, N, or P.
My current query joins the two tables, orders by RAND(), and then LIMIT 1. However, this makes the odds of a B value being chosen much more likely than an A value being chosen. I'm using PHP, so I could easily just run two queries, but running one query would be much tidier, so I want to see what you guys recommend.
Any solutions? =)
EDIT:
Here's my current query, but it isn't working. Not sure why!
SELECT *
FROM t2
WHERE
nID =
(
SELECT nID
FROM t1
ORDER BY RAND()
LIMIT 1
)
ORDER BY RAND()
LIMIT 1
EDIT 2:
To demonstrate the issue I'm having, I created a test case. First, I created the following tables:
I want the odds of selecting XXX to be identical to selecting L, M, N, or P. The query I have should do it, right? So I tested it. The followings script runs the query 5000 times, and counts the results. They should be about 50-50, with XXX showing up approximately 2500 times, and everything else showing up about 2500 times as well.
$a = 0;
$b = 0;
$i = 0;
while ($i < 5000)
{
$query = mysql_query("
SELECT *
FROM t2
WHERE
nID =
(
SELECT nID
FROM t1
ORDER BY RAND()
LIMIT 1
)
ORDER BY RAND()
LIMIT 1
") or die(mysql_error());
$result = mysql_fetch_array($query);
if ($result['val'] == 'XXX')
{
$a++;
}
else
{
$b++;
}
$i++;
}
echo "XXX - $a<br />";
echo "Other - $b<br />";
Here are the results:
XXX - 937
Other - 4063
Let's run it again.
XXX - 968
Other - 4032
And let's run it one more time.
XXX - 932
Other - 4068
This is hardly the 50-50 split we'd expect to see given my query. What on earth's going on? Thanks for your help, guys!

You would expect that the subquery in your question would be run once per outer query, but it looks like that's not the case. I think the below might give you what you're after:
SET #randID = (SELECT nID
FROM T1
ORDER BY RAND()
LIMIT 1);
SELECT VAL
FROM T2
WHERE nID = #randID
ORDER BY RAND()
LIMIT 1;
(SQL Fiddle)

Your example inner query is evaluated multiple times, if you want it to choose A or B once, you need to rewrite it, for example as a JOIN;
SELECT q2.nID, q2.val
FROM ( SELECT nID FROM T1 ORDER BY RAND() LIMIT 1 ) q1
JOIN T2 q2 ON q1.nID = q2.nID
ORDER BY RAND()
LIMIT 1
If you're working with small tables, this query should be ok, but read here for example on why you shouldn't use ORDER BY RAND() for large tables.
Demo here.

Please try query given below
SELECT `table2`.* FROM `table2` WHERE table2.field1 = (Select table1.field2 from table1 order by RAND() limit 0,1) LIMIT 0,1
Here i assume column name field1 and field2 for both table so please use field name according your table structure.
thanks

SELECT
CASE rq.r WHEN '1' THEN t1q.r ELSE t2q.nID END AS Col1,
CASE rq.r WHEN '1' THEN t1q.nID ELSE t2q.val END AS Col2
FROM
(SELECT CASE WHEN RAND() < 0.5 THEN '1' ELSE '2' END AS r) AS rq
JOIN (SELECT * FROM T1 ORDER BY RAND() LIMIT 1) as t1q
JOIN (SELECT * FROM T2 ORDER BY RAND() LIMIT 1) as t2q
Observation: This query is inefficient because it requires selecting a random row from both tables even though only one is used. Perhaps a better way exists.

Related

SQL: SUM selected Rows [duplicate]

How can you select the top n max values from a table?
For a table like this:
column1 column2
1 foo
2 foo
3 foo
4 foo
5 bar
6 bar
7 bar
8 bar
For n=2, the result needs to be:
3
4
7
8
The approach below selects only the max value for each group.
SELECT max(column1) FROM table GROUP BY column2
Returns:
4
8
For n=2 you could
SELECT max(column1) m
FROM table t
GROUP BY column2
UNION
SELECT max(column1) m
FROM table t
WHERE column1 NOT IN (SELECT max(column1)
WHERE column2 = t.column2)
for any n you could use approaches described here to simulate rank over partition.
EDIT:
Actually this article will give you exactly what you need.
Basically it is something like this
SELECT t.*
FROM
(SELECT grouper,
(SELECT val
FROM table li
WHERE li.grouper = dlo.grouper
ORDER BY
li.grouper, li.val DESC
LIMIT 2,1) AS mid
FROM
(
SELECT DISTINCT grouper
FROM table
) dlo
) lo, table t
WHERE t.grouper = lo.grouper
AND t.val > lo.mid
Replace grouper with the name of the column you want to group by and val with the name of the column that hold the values.
To work out how exactly it functions go step-by-step from the most inner query and run them.
Also, there is a slight simplification - the subquery that finds the mid can return NULL if certain category does not have enough values so there should be COALESCE of that to some constant that would make sense in the comparison (in your case it would be MIN of domain of the val, in article it is MAX).
EDIT2:
I forgot to mention that it is the LIMIT 2,1 that determines the n (LIMIT n,1).
If you are using mySQl, why don't you use the LIMIT functionality?
Sort the records in descending order and limit the top n i.e. :
SELECT yourColumnName FROM yourTableName
ORDER BY Id desc
LIMIT 0,3
Starting from MySQL 8.0/MariaDB support window functions which are designed for this kind of operations:
SELECT *
FROM (SELECT *,ROW_NUMBER() OVER(PARTITION BY column2 ORDER BY column1 DESC) AS r
FROM tab) s
WHERE r <= 2
ORDER BY column2 DESC, r DESC;
DB-Fiddle.com Demo
This is how I'm getting the N max rows per group in MySQL
SELECT co.id, co.person, co.country
FROM person co
WHERE (
SELECT COUNT(*)
FROM person ci
WHERE co.country = ci.country AND co.id < ci.id
) < 1
;
how it works:
self join to the table
groups are done by co.country = ci.country
N elements per group are controlled by ) < 1 so for 3 elements - ) < 3
to get max or min depends on: co.id < ci.id
co.id < ci.id - max
co.id > ci.id - min
Full example here:
mysql select n max values per group/
mysql select max and return multiple values
Note: Have in mind that additional constraints like gender = 0 should be done in both places. So if you want to get males only, then you should apply constraint on the inner and the outer select

mysql select records where value occurs

I hope you can help me with this one. I've been looking for ways to set up a MySQL query that selects rows based on the number of times a certain value occurs, but have had no luck so far. I'm pretty sure i need to use count(*) somewhere, but i can only found how to count all values or all distinct values, instead of counting all occurences.
I have a table as such:
info setid
-- --
A 1
B 1
C 2
D 1
E 2
F 3
G 1
H 3
What i need is a query that will select all the lines where a setid occurs a certain number (x) of times.
So using x=2 should give me
C 2
E 2
F 3
H 3
because both setIds 2 and 3 each occur two times. Using x=1 or x = 3 should not give any results, and choosing x=4 should give me
A 1
B 1
D 1
G 1
Because only setid 1 occurs 4 times.
I hope you guys can help me. At this point i've been looking for the answer for so long that i'm not even sure this can be done in MySQL anymore. :)
select * from mytable
where setid in (
select setid from mytable
group by setid
having count(*) = 2
)
you can specify the # of times a setid needs to occur in the table in the having count(*) part of the subquery
Consider the following statement that uses an uncorrelated subquery:
SELECT ... FROM t1 WHERE t1.a IN (SELECT b FROM t2);
The optimizer rewrites the statement to a correlated subquery:
SELECT ... FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE t2.b = t1.a);
If the inner and outer queries return M and N rows, respectively, the execution time becomes on the order of O(M×N), rather than O(M+N) as it would be for an uncorrelated subquery.
But this time the subquery in Fuzzy Tree's solution is complety superfluous:
SELECT
set_id,
GROUP_CONCAT(info ORDER BY info) infos
COUNT(*) total
FROM
tablename
GROUP_BY set_id
HAVING COUNT(*) = 2

select 2nd row of every ID in mysql

I have a table :
ID | time
1 | 300
1 | 100
1 | 200
2 | 200
2 | 500
I want to get 2nd row for every ID
I know that I can get 1st row as
select ID,time from T group by ID;
But I don't know about how to get 2nd row for every ID.
I know about limit and offset clause in mysql, but can't figure out how to use them here.
How can I do it ?
EDIT : Actually, time is not ordered. I forgot to specify that. I have made an edit in the table.
i have just an idee how to make it but i couldnt fix it , maybe you can fix it. any suggest is appreciated to correct my query
first this to select the first row of each id.
SELECT min(id) id
FROM TableName t2
group by id
then select the min(id) which are not in the first query to select to min(id) (which is second row)
like that
SELECT min(id) id ,time
FROM TableName
WHERE id NOT IN (
SELECT min(id) id
FROM TableName
GROUP BY id
)
GROUP BY id
** as i said its just suggest . it returns me 0 values.if u fix it let me edit my post to be helpful
here a demo
SELECT ID, MAX(time) time
FROM
(
select ID, Time
from TableName a
where
(
select count(*)
from TableName as f
where f.ID = a.ID and f.time <= a.time
) <= 2
) s
GROUP BY ID
SQLFiddle Demo
SELECT x.*
FROM test x
JOIN test y
ON y.id = x.id
AND y.time >= x.time
GROUP
BY id,time
HAVING COUNT(*) = n;
Note that any entries with less than n results will be omitted
You cannot do this with the tables that you have. You could make a valiant attempt with:
select id, time
from (select id, time
from t
group by t
) t
where not exists (select 1 from t t2 where t2.id = t.id and t2.time = t.time)
group by id
That is, attempt to filter out the first row.
The reason this is not possible is because tables are inherently unordered, so there is not real definition of "second" in your tables. This gives the SQL engine the opportunity to rearrange the rows as it sees fit during processing -- which can result in great performance gains.
Even the construct that you are using:
select id, time
from t
group by id
is not guaranteed to return time from the first row. This is a (mis)feature of MySQL called Hidden Columns. It is really only intended for the case where all the values are the same. I will admit that in practice it seems to get the value from the first row, but you cannot guarantee that.
Probably your best solution is to select the data into a new table that has an auto-incrementing column:
create table newtable (
autoid int auto_increment,
id int,
time int
);
insert into newtable(id, time)
select id, time from t;
In practice, this will probably keep the same order as the original table, and you can then use the autoid to get the second row. I want to emphasize, though, the "in practice". There is no guarantee that the values are in the correct order, but they probably will be.

How to SELECT a record per category in mysql query?

In a table of articles
title varchar(255),
category int(11),
processed enum('yes', 'no'),
... other columns
, I want to process rows (SELECT a row and then UPDATE). However, I need to do this diversely for all categories. Not processing randomly, e.g. all records for a category, but nothing for another.
Basic Case: process x rows for each category.
Advanced Case: define a daily limit for each category (in its table). This will be similar to crawlers, as we define how many pages should be crawled for a domain in a given period of time.
Example:
SELECT * from articles WHERE process='no' LIMIT 1
edit the columns in PHP
UPDATE articles .... WHERE id=xx (id comes from SELECT).
Table:
id title category process
1 title1 3 no
2 title2 3 no
3 title3 3 no
4 title4 3 no
5 title5 5 no
6 title6 5 no
7 title7 5 no
If I run the query regularly by cron, it will process all articles in category 3 then category 5. I want a query to process one from category 3, then one from category 5, and so forth. I want to process from all categories gradually.
SELECT *
FROM Table
WHERE category =
(SELECT category
FROM Table
WHERE process = 'no'
GROUP BY category
ORDER BY COUNT(category) DESC
LIMIT 1)
ORDER BY id
LIMIT 1
..will give you one row with the smallest id for the category with the most rows that haven't been processed. The subbquery returns the category with the most process='no' rows.
If you have lots more 5s than 3s, this will keep giving you 5s until there are more 3s than 5s then it will start alternating with each query (as long as you are marking the row as process = 'yes' each time).
To select $n from each category:
SET #last := NULL;
SELECT * FROM (
SELECT *,
#fetch:=IF(category=#last, #fetch-1, $n) x,
#last :=category
FROM articles
WHERE process='no'
ORDER BY category
) t WHERE t.x > 0;
To select for each category the associated number from the numbers table:
SET #last := NULL;
SELECT * FROM (
SELECT *,
#fetch:=IF(category=#last, #fetch-1, numbers.number) x,
#last :=category
FROM articles JOIN numbers USING (category)
WHERE process='no'
ORDER BY category
) t WHERE t.x > 0;
See them on sqlfiddle.
I suppose the processing that is to be done in PHP is somethign that invloves either user editing or some complex procedure (crawling) that cannot be done by SQL. In that case, you can use this query to get the columns you need from articles table.
One article per category:
SELECT
a.*
FROM
category AS c
JOIN
articles AS a
ON a.id =
( SELECT id
FROM articles AS aa
WHERE category = c.id
AND process = 'no'
ORDER BY whatever
LIMIT 1
) ;
and then update:
UPDATE
articles
SET
process = 'yes'
, other_column = ...
WHERE
id = ? --- one of the ids you have previously
--- selected and processed.
Update query of eggyal : set variable for #n
`
SET #n := 3;
SET #last := NULL;
SELECT * FROM (
SELECT *,
#fetch:=IF(category=#last, #fetch-1, #n) x,
#last :=category
FROM articles
WHERE process='no'
ORDER BY category
) t WHERE t.x > 0;
//query run
`

How can we find gaps in sequential numbering in MySQL?

We have a database with a table whose values were imported from another system. There is an auto-increment column, and there aren’t any duplicate values, but there are missing values. For example, running this query:
select count(id) from arrc_vouchers where id between 1 and 100
should return 100, but it returns 87 instead. Is there a query I can run that will return the values of the missing numbers? For example, the records may exist for id 1-70 and 83-100, but there aren’t any records with id's of 71-82. I want to return 71, 72, 73, etc.
Is this possible?
A better answer
JustPlainMJS provided a much better answer in terms of performance.
The (not as fast as possible) answer
Here's a version that works on a table of any size (not just on 100 rows):
SELECT (t1.id + 1) as gap_starts_at,
(SELECT MIN(t3.id) -1 FROM arrc_vouchers t3 WHERE t3.id > t1.id) as gap_ends_at
FROM arrc_vouchers t1
WHERE NOT EXISTS (SELECT t2.id FROM arrc_vouchers t2 WHERE t2.id = t1.id + 1)
HAVING gap_ends_at IS NOT NULL
gap_starts_at - first id in current gap
gap_ends_at - last id in current gap
This just worked for me to find the gaps in a table with more than 80k rows:
SELECT
CONCAT(z.expected, IF(z.got-1>z.expected, CONCAT(' thru ',z.got-1), '')) AS missing
FROM (
SELECT
#rownum:=#rownum+1 AS expected,
IF(#rownum=YourCol, 0, #rownum:=YourCol) AS got
FROM
(SELECT #rownum:=0) AS a
JOIN YourTable
ORDER BY YourCol
) AS z
WHERE z.got!=0;
Result:
+------------------+
| missing |
+------------------+
| 1 thru 99 |
| 666 thru 667 |
| 50000 |
| 66419 thru 66456 |
+------------------+
4 rows in set (0.06 sec)
Note that the order of columns expected and got is critical.
If you know that YourCol doesn't start at 1 and that doesn't matter, you can replace
(SELECT #rownum:=0) AS a
with
(SELECT #rownum:=(SELECT MIN(YourCol)-1 FROM YourTable)) AS a
New result:
+------------------+
| missing |
+------------------+
| 666 thru 667 |
| 50000 |
| 66419 thru 66456 |
+------------------+
3 rows in set (0.06 sec)
If you need to perform some kind of shell script task on the missing IDs, you can also use this variant in order to directly produce an expression you can iterate over in Bash.
SELECT GROUP_CONCAT(IF(z.got-1>z.expected, CONCAT('$(',z.expected,' ',z.got-1,')'), z.expected) SEPARATOR " ") AS missing
FROM ( SELECT #rownum:=#rownum+1 AS expected, IF(#rownum=height, 0, #rownum:=height) AS got FROM (SELECT #rownum:=0) AS a JOIN block ORDER BY height ) AS z WHERE z.got!=0;
This produces an output like so
$(seq 1 99) $(seq 666 667) 50000 $(seq 66419 66456)
You can then copy and paste it into a for loop in a bash terminal to execute a command for every ID
for ID in $(seq 1 99) $(seq 666 667) 50000 $(seq 66419 66456); do
echo $ID
# Fill the gaps
done
It's the same thing as above, only that it's both readable and executable. By changing the "CONCAT" command above, syntax can be generated for other programming languages. Or maybe even SQL.
A quick-and-dirty query that should do the trick:
SELECT a AS id, b AS next_id, (b - a) -1 AS missing_inbetween
FROM
(
SELECT a1.id AS a , MIN(a2.id) AS b
FROM arrc_vouchers AS a1
LEFT JOIN arrc_vouchers AS a2 ON a2.id > a1.id
WHERE a1.id <= 100
GROUP BY a1.id
) AS tab
WHERE
b > a + 1
This will give you a table showing the id that has ids missing above it, and next_id that exists, and how many are missing between... E.g.,
id next_id missing_inbetween
1 4 2
68 70 1
75 87 11
If you are using a MariaDB database, you have a faster (800%) option using the sequence storage engine:
SELECT * FROM seq_1_to_50000 WHERE SEQ NOT IN (SELECT COL FROM TABLE);
If there is a sequence having gap of maximum one between two numbers (like
1,3,5,6) then the query that can be used is:
select s.id+1 from source1 s where s.id+1 not in(select id from source1) and s.id+1<(select max(id) from source1);
table_name - source1
column_name - id
An alternative solution that requires a query + some code doing some processing would be:
select l.id lValue, c.id cValue, r.id rValue
from
arrc_vouchers l
right join arrc_vouchers c on l.id=IF(c.id > 0, c.id-1, null)
left join arrc_vouchers r on r.id=c.id+1
where 1=1
and c.id > 0
and (l.id is null or r.id is null)
order by c.id asc;
Note that the query does not contain any subselect that we know it's not handled performantly by MySQL's planner.
That will return one entry per centralValue (cValue) that does not have a smaller value (lValue) or a greater value (rValue), i.e.:
lValue |cValue|rValue
-------+------+-------
{null} | 2 | 3
8 | 9 | {null}
{null} | 22 | 23
23 | 24 | {null}
{null} | 29 | {null}
{null} | 33 | {null}
Without going into further details (we'll see them in next paragraphs) this output means that:
No values between 0 and 2
No values between 9 and 22
No values between 24 and 29
No values between 29 and 33
No values between 33 and MAX VALUE
So the basic idea is to do a RIGHT and LEFT joins with the same table seeing if we have adjacents values per value (i.e., if central value is '3' then we check for 3-1=2 at left and 3+1 at right), and when a ROW has a NULL value at RIGHT or LEFT then we know there is no adjacent value.
The complete raw output of my table is:
select * from arrc_vouchers order by id asc;
0
2
3
4
5
6
7
8
9
22
23
24
29
33
Some notes:
The SQL IF statement in the join condition is needed if you define the 'id' field as UNSIGNED, therefore it will not allow you to decrease it under zero. This is not strictly necessary if you keep the c.value > 0 as it's stated in the next note, but I'm including it just as doc.
I'm filtering the zero central value as we are not interested in any previous value and we can derive the post value from the next row.
I tried it in a different manner, and the best performance that I found was this simple query:
select a.id+1 gapIni
,(select x.id-1 from arrc_vouchers x where x.id>a.id+1 limit 1) gapEnd
from arrc_vouchers a
left join arrc_vouchers b on b.id=a.id+1
where b.id is null
order by 1
;
... one left join to check if the next id exists, only if next if is not found, then the subquery finds the next id that exists to find the end of gap. I did it because the query with equal (=) is better performance than the greater than (>) operator.
Using the sqlfiddle it does not show so a different performance compared to the other queries, but in a real database this query above results in 3 times faster than the others.
The schema:
CREATE TABLE arrc_vouchers (id int primary key)
;
INSERT INTO `arrc_vouchers` (`id`) VALUES (1),(4),(5),(7),(8),(9),(10),(11),(15),(16),(17),(18),(19),(20),(21),(22),(23),(24),(25),(26),(27),(28),(29)
;
Follow below all the queries that I made to compare the performance:
select a.id+1 gapIni
,(select x.id-1 from arrc_vouchers x where x.id>a.id+1 limit 1) gapEnd
from arrc_vouchers a
left join arrc_vouchers b on b.id=a.id+1
where b.id is null
order by 1
;
select *, (gapEnd-gapIni) qt
from (
select id+1 gapIni
,(select x.id from arrc_vouchers x where x.id>a.id limit 1) gapEnd
from arrc_vouchers a
order by id
) a where gapEnd <> gapIni
;
select id+1 gapIni
,(select x.id from arrc_vouchers x where x.id>a.id limit 1) gapEnd
#,coalesce((select id from arrc_vouchers x where x.id=a.id+1),(select x.id from arrc_vouchers x where x.id>a.id limit 1)) gapEnd
from arrc_vouchers a
where id+1 <> (select x.id from arrc_vouchers x where x.id>a.id limit 1)
order by id
;
select id+1 gapIni
,coalesce((select id from arrc_vouchers x where x.id=a.id+1),(select x.id from arrc_vouchers x where x.id>a.id limit 1)) gapEnd
from arrc_vouchers a
order by id
;
select id+1 gapIni
,coalesce((select id from arrc_vouchers x where x.id=a.id+1),concat('*** GAT *** ',(select x.id from arrc_vouchers x where x.id>a.id limit 1))) gapEnd
from arrc_vouchers a
order by id
;
You can see and test my query using this SQL Fiddle:
http://sqlfiddle.com/#!9/6bdca7/1
It is probably not relevant, but I was looking for something like this to list the gaps in a sequence of numbers and found this post that has multiple different solutions depending upon exactly what you are looking for. I was looking for the first available gap in the sequence (i.e., next available number), and this seems to work fine.
SELECT MIN(l.number_sequence + 1) as nextavabile
from patients as l
LEFT OUTER JOIN patients as r on l.number_sequence + 1 = r.number_sequence
WHERE r.number_sequence is NULL
Several other scenarios and solutions discussed there, from 2005!
How to Find Missing Values in a Sequence With SQL
Create a temporary table with 100 rows and a single column containing the values 1-100.
Outer Join this table to your arrc_vouchers table and select the single column values where the arrc_vouchers id is null.
This should work:
select tempid from temptable
left join arrc_vouchers on temptable.tempid = arrc_vouchers.id
where arrc_vouchers.id is null
Although these all seem to work, the result set returns in a very lengthy time when there are 50,000 records.
I used this, and it find the gap or the next available (last used + 1) with a much faster return from the query.
SELECT a.id as beforegap, a.id+1 as avail
FROM table_name a
where (select b.id from table_name b where b.id=a.id+1) is null
limit 1;
Based on the answer given by matt, this stored procedure allows you to specify the table and column names that you wish to test to find non-contiguous records - thus answering the original question and also demonstrating how one could use #var to represent tables &/or columns in a stored procedure.
create definer=`root`#`localhost` procedure `spfindnoncontiguous`(in `param_tbl` varchar(64), in `param_col` varchar(64))
language sql
not deterministic
contains sql
sql security definer
comment ''
begin
declare strsql varchar(1000);
declare tbl varchar(64);
declare col varchar(64);
set #tbl=cast(param_tbl as char character set utf8);
set #col=cast(param_col as char character set utf8);
set #strsql=concat("select
( t1.",#col," + 1 ) as starts_at,
( select min(t3.",#col,") -1 from ",#tbl," t3 where t3.",#col," > t1.",#col," ) as ends_at
from ",#tbl," t1
where not exists ( select t2.",#col," from ",#tbl," t2 where t2.",#col," = t1.",#col," + 1 )
having ends_at is not null");
prepare stmt from #strsql;
execute stmt;
deallocate prepare stmt;
end
A simple, yet effective, solution to find the missing auto-increment values:
SELECT `id`+1
FROM `table_name`
WHERE `id`+1 NOT IN (SELECT id FROM table_name)
Another simple answer that identifies the gaps. We do a query selecting just the odd numbers and we right join it to a query with all the even numbers. As long as you're not missing id 1; this should give you a comprehensive list of where the gaps start.
You'll still have to take a look at that place in the database to figure out how many numbers the gap is. I found this way easier than the solution proposed and much easier to customize to unique situations.
SELECT *
FROM (SELECT * FROM MyTABLE WHERE MYFIELD % 2 > 0) AS A
RIGHT JOIN FROM (SELECT * FROM MyTABLE WHERE MYFIELD % 2 = 0) AS B
ON A.MYFIELD=(B.MYFIELD+1)
WHERE a.id IS NULL;
This works for me:
SELECT distinct(l.membership_no + 1) as nextavabile
from Tablename as l
LEFT OUTER JOIN Tablename as r on l.membership_no + 1 = r.membership_no
WHERE r.membership_no is NULL and l.membership_no is not null order by nextavabile asc;
Starting from the comment posted by user933161,
select l.id + 1 as start from sequence as l inner join sequence as r on l.id + 1 = r.id where r.id is null;
is better in that it will not produce a false positive for the end of the list of records. (I'm not sure why so many are using left outer joins.)
Also,
insert into sequence (id) values (#);
where # is the start value for a gap will fill that start value. (If there are fields that cannot be null, you will have to add those with dummy values.)
You could alternate between querying for start values and filling in each start value until the query for start values returns an empty set.
Of course, this approach would only be helpful if you're working with a small enough data set that manually iterating like that is reasonable. I don't know enough about things like phpMyAdmin to come up with ways to automate it for larger sets with more and larger gaps.
CREATE TABLE arrc_vouchers (id int primary key);
INSERT INTO `arrc_vouchers` (`id`) VALUES (1),(4),(5),(7),(8),(9),(10),(11),(15),(16);
WITH RECURSIVE odd_num_cte (id) AS
(
SELECT (select min(id) from arrc_vouchers)
union all
SELECT id+1 from odd_num_cte where id <(SELECT max(id) from arrc_vouchers)
)
SELECT cte.id
from arrc_vouchers ar right outer join odd_num_cte cte on ar.id=cte.id
where ar.id is null;