MySQL: nākamā ID vērtība (next_id, AUTO_INCREMENT)
Problēma veca kā pasaule – kā MySQL datubāzē iegūt tabulas nākamo ID vērtību, pie nosacījuma, ka ID vērtība veidojas kā AUTO_INCREMENT.
Problēma veca kā pasaule – kā MySQL datubāzē iegūt tabulas nākamo ID vērtību, pie nosacījuma, ka ID vērtība veidojas kā AUTO_INCREMENT.
Šāda vajadzība varētu būt vajadzīga divos gadījumos:
a) Veicot jaunu ierakstu (INSERT) vēlāk varētu darboties ar jaunā ieraksta id vērtību;
b) Veikt darbības (formēt kodu, vai SSID vērtību, kas piesaistīta ID) vēl pirms reāls ieraksts ir veikts.
Ja ID vērtība vajadzīga pēc ieraksta izdarīšanas:
1. Praktiski jebkurā valodā ir PHP funkcijas mysql_insert_id analogs. Šī funkcija atgriež ID vērtību pēdējam izdarītam ierakstam. Tiesa gan, ja tādi bijuši vairāki, tad vienalga – tikai pēdējam.
mysql_query("INSERT INTO mytable (product) values ('kossu')"); printf("Last inserted record has id %d\n", mysql_insert_id());
2. Iekš MySQL trigera šādu vērtību var iegūt kā NEW.id pie nosacījuma, ka trigers tiek izpildīts pēc operācija (after). Tiesa gan, pēc operācijas nevar vairs izmainīt ierakstāmās vērtības. Bet šāds scenārijs der, ja jāveic operācija ar citu tabulu.
3. Ja datu rakstīšanas intensitāte nav neprātīgi liela, gan MySQL trigeros, gan PHP var izpildīt vaicājumu: SELECT max(ID) AS curr_id FROM my_table.
Ja ID vērtība vajadzīga pirms ieraksta izdarīšanas.
1. Pavisam nedrošs, bet teorētiski izmantojams scenārijs ir izpildot vaicājumu:
SELECT max(ID) + 1 AS next_id FROM my_table
Nedrošs tamdēļ, ka pēdējais ieraksts var tik izdzēsts, līdz ar ko nākošā lielākā ID vērtība vairs nebūs nākošā AUTO_INCREMENT vērtība.
2. Iekš PHP var nolasīt tabulas statusu:
function get_menu_id($menu) { $result = mysql_query("SHOW TABLE STATUS LIKE 'menu'"); $rows = mysql_fetch_assoc($result); return $rows['auto_increment']; }
šādu principu varētu izmantot arī tabulas trigerī, bet man nav zināms mehānisms kā ar MySQL rīkiem var nolasīt tabulas statusa konkrēto (AUTO_INCREMENT) lauku.
3. Iekš MySQL izveidot funkciju, kura no MySQL informācijas shēmas nolasīs norādītās tabulas AUTO_INCREMENT vērtību. Izskatās šādi:
CREATE FUNCTION next_id(_TABLE varchar(255)) RETURNS int(11) BEGIN DECLARE _NEXT_ID INT(11) DEFAULT 0; SELECT AUTO_INCREMENT FROM information_schema.`tables` WHERE TABLE_SCHEMA = „my_db” AND TABLE_NAME = _TABLE INTO _NEXT_ID; RETURN _NEXT_ID; END;
Tā kā informācijas shēma (information schema) glabā datus par visām datubāzēm un visām tabulām, tad vaicājumā jānorāda gan datubāze, gan tabula. Datubāze my_db tiek cieti ierakstīta vaicājumā, savukārt tabula ir funkcijas arguments - _TABLE.
Izpildot vaicājumu: SELECT next_id('my_table') rezultātā funkcija atgriež norādītās tabulas AUTO_INCREMENT vērtību. Šādu vaicājumu var izpildīt gan no PHP gan MySQL trigera (procedūras, funkcijas).
Tikai vēlams atcerēties, ka visas iepriekš iegūšanas tehnikas, gan max + 1, gan lasīšana no informācijas shēmas strādā tikai viena lietotāja variantā. Tiklīdz ar datubāzi darbojas vairāk kā viens lietotājs, tā pastāv varbūtība, ka kamēr esmu nolasījis un tagad sāku kaut ko bāzt iekšā, kāds konkurents jau to vērtību ir piesavinājies un ielicis pats ierakstu ar to.
Dažas lietas pārdomām:
- web aplikācijas pārsvarā būs viena lietotāja aplikācijas;
- vienas transakcijas ietvaros (arī triggera) visticamāk notiks tabulas vai ieraksta lock;
- Vēl ir DB transakcijas.
- Tabulas manuālais lock un release;
- Ja viss tas neder - nevajag veidot vērtības, kas balstītas uz vēl neesošām (auto_increment) vērtībām :)
Katra ziņā bija doma parādīt, ka ir arī kas labāks par MAX(*) + 1 (good)
Patiesībā ir pretēji -- webs pārsvarā nebūs viena lietotāja aplikācija.
Un jebkurā gadījumā vislabākais veids ir vispirms izveidot rowu db, dabūt ID un tad apdeitot. Ja jau vienalga jāveic vairāki pieprasījumi uz DB, tad kāpēc gan to nedarīt drošā (ar drošu domāju ne-max+1 variants) veidā? Tad vairs nav nozīmes, cik lietotāji, jo savu ID tu esi jau norezervējis.
Es arī balsoju par to, ka web aplikācijas ir tomēr parasti daudzlietotāju aplikācijas ;)
Autoincrement protams ir jāizmanto kā identifikatorus ģenerējoša vērtība un max(col) + 1 ir atstājams tikai un vienīgi tādiem gadījumiem, kad kaut kādu biznesa specifisku vajadzību dēļ savādāk nevar (a la likumā ierakstīts). BET to vajadzētu izmantot tā kā tas paredzēts, t.i., ielikt rindiņu dabūt id un tad ar to ko pasākt, nevis iepriekš kaut ko censties uzminēt. Vēl ir tāds sīkums, ka visi koda gabaliņi, kas ir rakstīti kaut cik sakarīgam projektam, ar laiku attīstās un šodienas viena lietotāja vietā ir rītdienas 10. Ja nu akurāti gribam rezervēt, tad ņemam Oracle un sekvences ;)
Par web aplikācijām vai drīzāk web lapām.
Droši vien, domāts vairāku sesiju (arī konkurējošo) nevis lietotāju pieslēgumi. Parasti jau tiek izveidots savienojums ar vienu lietotāju un vu-a-la :)