Jump to content
php.lv forumi

Nākamias neizmantotais numurs


gguntars

Recommended Posts

Man te tāds jautājumiņš...

 

Lietas būtība ir šāda:

Ir izveidota tabula iekš MySQL, kur tiek reģistrēti sacensību dalībnieki.

Lauks 'id' ir AUTO_INCREMENT un PRIMARY KEY.

 

Lauks 'numurs' ir unikāls INT, kas pēc būtības ir dalībnieka starta numurs.

Dalībniekiem starta numurs tiek piešķirts automātiski, bet tai pat laikā ir iespējams pašam izvēlēties savu īpašo numuru.

Līdz ar ko šajā laukā var būt savadīti dažādi numuri, piemēram, 1,2,3,50,88

 

Man ir vajadzīgs dabūt nākamo neizmantoto numuru, kas tiktu piešķirts nākamajam dalībniekam, kurš neizvēlas sev īpašu numuru

Šajā gadījumā numuram vajadzētu būt 4.

 

Vai tas ir iespējams? Un galvenais KĀ? ;)

Link to comment
Share on other sites

Auto incrament peec savaas buutiibas ir parasts skaitiitaajs , liidz ar to nakamais numurs buus 89, bet vari vienkarshi parbaudiit vai numurs jau nav izmantots ...

un ja nav tad drosi vari vinju insertot ... tb parbaudi vai numurs eksistee id ==4 , ja nee tad inserto ...

savaadak nekaa...

Edited by Grey_Wolf
Link to comment
Share on other sites

Ja pareizi sapratu, tad ja lietotājs(potenciālais dalībnieks) ievada numuru, tad viņš tiek reģistrēts ar šādu numuru(ja tāds jua neeksistē protams), bet ja neievada - tad automātiski viņam tiek piešķirts nākošais lielākais skaitlis sākot no mazākā(tb, 1,2,3,5 piešķirs 4).

Nepārbaudīts, bet varētu izskatīties apmērmam šādi:

<?
$sql = mysql_query("SELECT MAX(numurs) FROM dalibnieki");
$results = mysql_fetch_row($sql);
$last = $results[0];//dabujam pedejo lielako dalibnieka numuru

for($i=1;$i<=$last;$i++){
$qwerty = mysql_query("SELECT id FROM dalibnieki WHERE numurs = ".$i);
$count = mysql_num_rows($qwerty);
if($count==1){
//ir šāds numurs, turpnam meklēt
}
else{
//piemeram 4 nav DB, tad mēs varam ievietot jaunu dalibnieku ar šo 4 jeb $i numuru
$mysql_query("INSERT INTO dalibnieki(vards, numurs) VALUES ('".$_POST['name']."', '".$i."')");
$i = $last;//lai cikls beigtos
}
}
?>

Link to comment
Share on other sites

Tik vajag atcerēties, ka visu šo select max/insert phpjāņa kodā vajag darīt vienā transakcijā - noloko sākumā tabulu un begās to atloko. Citādi sanāks vēl, ka vienlaicīgi kāds darīs to pašu un piešķirsies viens un tas pats numurs.

Link to comment
Share on other sites

Man sanāca ar 1 kveriju.

Tabulā a tiek atrasts pirmais brīvais id;

SELECT g FROM (SELECT id,@i:=@i+1 as g FROM (select @i:=0) as t,a) as t WHERE g<>id LIMIT 1

 

 

EDIT:

šis varētu būt ātrāks:

SELECT @i as g,id FROM (select @i:=0) as t,a having (@i:=@i+1)<>id LIMIT 1  ;

atbilde izvada divas kollonas, jāņem pirmā.

 

EDIT:

P.S. id protams ir primary key, ja nav, tad derētu takā vēl order pēc id taisīt:

SELECT @i as g,id FROM (select @i:=0) as t,a having (@i:=@i+1)<>id order by id LIMIT 1 ;

Edited by codez
Link to comment
Share on other sites

Tā kā neesmu MySQL specs uz potenciāliem mysql mainīgo gļukiem u.c. nezināmām lietām, tad jūtieties brīvi kritizēt, bet man uz pāris piemēriem strādā:

mysql> create table numuri(a int);
Query OK, 0 rows affected (0.13 sec)

mysql> insert into numuri values (1);
Query OK, 1 row affected (0.05 sec)

mysql> insert into numuri values (2);
Query OK, 1 row affected (0.03 sec)

mysql> insert into numuri values (4);
Query OK, 1 row affected (0.03 sec)

mysql> insert into numuri values (6);
Query OK, 1 row affected (0.01 sec)

mysql> select * from numuri;
+------+
| a	|
+------+
|	1 |
|	2 |
|	4 |
|	6 |
+------+
4 rows in set (0.01 sec)

Kā redzams nākošās mazākās vērtības ir 3, pēc tam 5, pēc tam būtu jābūt 7:

Skatamies ko darīs šis te:

mysql> insert into numuri
-> select min(salidz) FROM (
->   SELECT IF(rownum - a = 0, null, rownum) salidz FROM (
->	 SELECT @rownum:=@rownum+1 rownum, a
->	 FROM (SELECT @rownum:=0) r, numuri
->	 ORDER BY a) q
->   UNION ALL select max(a)+1 FROM numuri) q1;
Query OK, 1 row affected (0.03 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> select * from numuri;
+------+
| a	|
+------+
|	1 |
|	2 |
|	4 |
|	6 |
|	3 |
+------+
5 rows in set (0.00 sec)

mysql> insert into numuri
-> select min(salidz) FROM (
->   SELECT IF(rownum - a = 0, null, rownum) salidz FROM (
->	 SELECT @rownum:=@rownum+1 rownum, a
->	 FROM (SELECT @rownum:=0) r, numuri
->	 ORDER BY a) q
->   UNION ALL select max(a)+1 FROM numuri) q1;
Query OK, 1 row affected (0.03 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> select * from numuri;
+------+
| a	|
+------+
|	1 |
|	2 |
|	4 |
|	6 |
|	3 |
|	5 |
+------+
6 rows in set (0.00 sec)

mysql> insert into numuri
-> select min(salidz) FROM (
->   SELECT IF(rownum - a = 0, null, rownum) salidz FROM (
->	 SELECT @rownum:=@rownum+1 rownum, a
->	 FROM (SELECT @rownum:=0) r, numuri
->	 ORDER BY a) q
->   UNION ALL select max(a)+1 FROM numuri) q1;
Query OK, 1 row affected (0.01 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> select * from numuri;
+------+
| a	|
+------+
|	1 |
|	2 |
|	4 |
|	6 |
|	3 |
|	5 |
|	7 |
+------+
7 rows in set (0.00 sec)

 

Man izskatās ka strādā.

Ideja par rownum ģenerēšanu no šejienes trešais komentārs no augšas.

Pārējais no manis :)

 

P.S. Nav ne jausmas kā tas strādā uz daudz datiem.

P.P.S. Protams, ka jāatceras, ka ja vienlaicīgi darbojas vairāki lietotāji, tad pastāv risks, ka 2 var ielikt vienu un to pašu, bet tur acīmredzot vienkārši ir jāuzliek UK ierobežojums, kas arī to aizliegs.

 

Gints Plivna

http://datubazes.wordpress.com

Link to comment
Share on other sites

nu, ok!

tagad ir arī pēdējais.

SELECT IF((@i=c) and (@i=id),@i+1,@i) as g,id,c.c FROM (select @i:=0) as t,(SELECT count(id) as c from a) as c,a having (@i:=@i+1)<>id OR (@i=c) order by id LIMIT 1

 

Doma sekojoša: selektojam tabulu a un pie katra rowa palielinam maniīgo @i par viens. Ja kādā brīdī @i<>id, tad selektojam, vai arī pašās beigās, ja nonākam līdz pēdējajam (@i=c) un tas izrādās arī vienāds ar id "and (@i=id)" , tad arī selektojam, bet tad selektojām @i+1 ar IF((@i=c) and (@i=id),@i+1,@i)

Link to comment
Share on other sites

tad jūtieties brīvi kritizēt

 

rownum - a = 0

ir

rownum = a

 

Sekojošais apakškverijs sākumā uzbūvē tabulu, no visiem rowiem. Ja būs daudz ierakstu, bet tukš id bieži atradīsies sākumā, tad tiks veikta daudz liekas darbības.

SELECT @rownum:=@rownum+1 rownum, a
FROM (SELECT @rownum:=0) r, numuri
ORDER BY a

 

Un otrs sekojošais apakškverijs vēlreiz būvē tabulu, no visiem rowiem.

SELECT IF(rownum - a = 0, null, rownum) salidz 
FROM ( SELECT @rownum:=@rownum+1 rownum, a
FROM (SELECT @rownum:=0) r, numuri
ORDER BY a) q

 

Kamēr manā variantā netiek būvēta tabula no atmetamajiem rezultātiem un pie tam selektošana apstājas, pie pirmā derīgā atrastā id.

Edited by codez
Link to comment
Share on other sites

SELECT IFNULL(MIN(id)+1, 1) FROM tabule AS t WHERE NOT EXISTS (SELECT id FROM tabule WHERE id = t.id+1)

 

Nezinu, kā mysql šādus optimizē, bet otrā selektošana varētu būt O(log2(N)) un tā ir jādara N reizes.

Kopā O(N log2(N)).

 

Kamēr manā variantā ir O(K), kur K ir pirmā neaizņemtā id vieta un K<=N+1

Edited by codez
Link to comment
Share on other sites

Doma sekojoša: selektojam tabulu a un pie katra rowa palielinam maniīgo @i par viens. Ja kādā brīdī @i<>id, tad selektojam, vai arī pašās beigās, ja nonākam līdz pēdējajam (@i=c) un tas izrādās arī vienāds ar id "and (@i=id)" , tad arī selektojam, bet tad selektojām @i+1 ar IF((@i=c) and (@i=id),@i+1,@i)

Izklausās forši :)

Man ar' diez ko tie apakšselekti nepatika, bet šāda ideja prātā neienāca, pie tam šī bija pirmā reize, kad rakstīju MySQL selektu ar mainīgajiem ;)

 

Gints Plivna

http://datubazes.wordpress.com

Link to comment
Share on other sites

Neesmu MySQL optimizācijas pratējs, bet explain komanda izvadīja:

+----+--------------------+--------+--------+---------------+---------+---------+------+------+--------------------------+
| id | select_type		| table  | type   | possible_keys | key	 | key_len | ref  | rows | Extra					|
+----+--------------------+--------+--------+---------------+---------+---------+------+------+--------------------------+
|  1 | PRIMARY			| t	  | index  | NULL		  | PRIMARY | 4	   | NULL |	4 | Using where; Using index |
|  2 | DEPENDENT SUBQUERY | tabule | eq_ref | PRIMARY	   | PRIMARY | 4	   | func |	1 | Using where; Using index |
+----+--------------------+--------+--------+---------------+---------+---------+------+------+--------------------------+

Cik saprotu eq_ref ir ļoti labs tips - O(1).

Un "index" sakārtotu bināro koku gadījumā ir O(logN).

Respektīvi O(logN) * O(1) = O(logN) < O(N).

 

Edit: šķiet, ka kļūdos. "index" tips laikam nozīmē pilno pārlasi - O(N). Tātad nekas īpaši labāks par tavējo O(N) nav sanācis.

Link to comment
Share on other sites

×
×
  • Create New...