Autor Zpráva
Joker
Profil
Povedlo se mi najít způsob, jak udělat něco, o čem jsem si dodnes myslel, že to udělat nejde :-)

Takže- řekněme, že v tabulce máte třeba soubory a tabulka je ID (primary, auto_increment) - název - popisek. Řekněme, že název souboru nějak generuje systém z popisku a samozřejmě musí být unikátní (přičemž popisek unikátní být nemusí, navíc po zpracování, aby byl vhodný jako název).
Řešení, které se přímo nabízí, je vložit do názvu souboru ID řádku- to je unikátní, takže výsledkem bude vždy unikátní název.
Potíž je, že ve chvíli vložení řádku nevíte, jaké ID ten řádek bude mít. Zbývají dvě možnosti- jedna je uložit jen částečný název bez ID a při hledání či výběrech ho pokaždé sestavovat- čímž ale v databázi držíme defacto nesmyslný údaj a při každém vyhledávání výpočtem sestavujeme pořád tutéž hodnotu. Druhá možnost je udělat INSERT, zjistit si jeho ID (mysql_last_insert_id()), sestavit název souboru a udělat UPDATE.
Hodnotu počítadla auto_increment samozřejmě lze zjistit: SHOW TABLE STATUS LIKE '*název tabulky*', ve sloupci auto_increment je hodnota, kterou dostane příští vložený řádek. Potíž je, že nejde v INSERTu udělat poddotaz SHOW TABLE STATUS a výsledek pak použít.

Nicméně SHOW TABLE STATUS je jen jakýsi pohled na data vytahaná ze systémových tabulek MySQL a krom něj jde procházet i přímo ty tabulky, které jsou v databázi INFORMATION_SCHEMA. Konkrétně je zajímavá tabulka: INFORMATION_SCHEMA.TABLES, kde sloupec AUTO_INCREMENT je hodnota počítadla a sloupec TABLE_NAME je název tabulky.
No a navíc existuje konstrukce INSERT - SELECT, která umožňuje vložit do tabulky výsledek SELECT dotazu.

Tadááá, můžeme vcelku krkolomnou konstrukcí získat ID právě vkládaného řádku přímo uvnitř dotazu, kterým ho vkládáme :-)
INSERT INTO `moje_tabulka` (nazev,popis) SELECT CONCAT(`AUTO_INCREMENT`,'$nazev_souboru'), '$popis' 
FROM `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA`='moje_databaze' AND TABLE_NAME = 'moje_tabulka'


Co soudíte o tomhle způsobu? :-)
Je to lepší než dva dotazy, nebo raději dva dotazy, nebo byste raději měli nekompletní název a ID zvlášť a při každém SELECTu to skládali dohromady? Nebo byste to řešili úplně jinak?

edit: rozdělil jsem dotaz na více řádků, aby se vešel celý a byl lépe čitelný.
yFang
Profil
Joker
Co třeba mysql_insert_id? Není to jednodušší?
*edit: Aha, blbě jsem si to přečetl.
Alphard
Profil
yFang
to neřeší Jokerův problém, on to id potřebuje ještě v tom dotaze
Joker
pěkné řešení, nebude se hodit vždy, ale někdy uplatnění určitě najde
nevýhodu vidím v potřebě znát název databáze, někdy by mohl být trochu problém vytáhnout ho z configu, ale určitě ne neřešitelný
při velkém množství dotazů za krátký čas by hrozila kolize, nevím, do jaké míry je to pravděpodobné, ale tento přístup to IMHO nevylučuje
Mastodont
Profil
No nevím, ale bál bych se toho, jak často jsou ty údaje v INFORMATION_SCHEMA aktualizovány. Jinak řešení to není nové, viz třeba
http://blog.jamiedoris.com/geek/560/
(i s poznámkami o bezpečnosti tohoto řešení v diskusi)
Kajman_
Profil *
Co trigger before insert? Tam ještě id není?
Mastodont
Profil
Není.
Joker
Profil
Mastodont
No nevím, ale bál bych se toho, jak často jsou ty údaje v INFORMATION_SCHEMA aktualizovány. Jinak řešení to není nové
Na mém testovacím MySQL 5.0.67 to funguje celkem spolehlivě (zdá se).
Jinak samozřejmě to nemusí být nějaký převratný objev, nicméně já ještě na řešení toho problému nenarazil a vím, že i tady v diskusi se na to alespoň jednou nebo dvakrát někdo ptal a odpověď byla, že to nejde.

Díky za ten odkaz, ale nakolik jsem ho procházel, autor řešil trochu jiný problém a tak v té mojí úvaze došel jen k předposlední fázi- použití dotazu SHOW TABLE STATUS.
S tím souvisí i to riziko, že v mezičase mezi zjištěním ID a vložením záznamu do databáze vložil jiný záznam.

Já ve své úvaze udělal ještě jeden krok, a sice že místo SHOW TABLE STATUS používám přímo SELECT do systémové databáze. No a právě to, že použiju příkaz SELECT, mi umožní použít konstrukci INSERT - SELECT a provést to všechno v jednom dotazu.

Zároveň to podle mě eliminuje možnost, že by mezi zjištěním stavu počítadla a zápisem řádku do databáze zapsal někdo jiný. Teda pokud není možné, aby MySQL mezi provedením poddotazu a hlavního dotazu provedlo ještě nějaký jiný dotaz.
Alphard
Profil
Zároveň to podle mě eliminuje možnost, že by mezi zjištěním stavu počítadla a zápisem řádku do databáze zapsal někdo jiný. Teda pokud není možné, aby MySQL mezi provedením poddotazu a hlavního dotazu provedlo ještě nějaký jiný dotaz.
Já jsem v předchozím příspěvku v podstatě napsal, že to možné je, aniž bych o tom nějak více přemýšlel, teď ale ztrácím jistotu.
Jestli něco najdu v manuálu, tak se ozvu.
Mastodont
Profil
mezi zjištěním stavu počítadla a zápisem řádku do databáze zapsal někdo jiný
Měl jsem na mysli spíš aktualizaci schématu po vložení nového záznamu ...
Joker
Profil
Alphard
Já jsem v předchozím příspěvku v podstatě napsal, že to možné je, aniž bych o tom nějak více přemýšlel, teď ale ztrácím jistotu.
Taky nevím, ale snad bych to pokládal i za chybu databázového stroje, kdyby výsledek poddotazu ve chvíli provádění hlavního dotazu už neodpovídal aktuálnímu stavu databáze.

Mastodont
Měl jsem na mysli spíš aktualizaci schématu po vložení nového záznamu ...
Tak jsem udělal pokus:
tabulka test: id (smallint, primary, auto_increment) | test (smallint) | txt (varchar(50))
SQL:
INSERT INTO `test` (`test`,`txt`) SELECT `AUTO_INCREMENT`, 'x' FROM `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA`='db_test' AND TABLE_NAME = 'test';

...v cyklu 5000x za sebou, následně: SELECT COUNT(*) FROM `test` WHERE id = `test`
->5000

Takže to vypadá spolehlivě.
Mastodont
Profil
Joker
Zkus to ještě celé zavřít do transakce, díky.
Joker
Profil
Mastodont
Takhle?
TRANSACTION START
...cyklus...
COMMIT

Taky proběhlo v pořádku. Tabulku jsem zkonvertoval na InnoDB, aby ty transakce měly vůbec nějaký efekt :)
Mastodont
Profil
Díky, v tom případě to vypadá bezpečně.
Kajman_
Profil *
Joker
Spíš by jsi měl spouštět více takových testů naráz. A zkusil bych tam semtam insert, který skončí chybou. Ale myslím, že s tím problém nebude, spíš jen v tom, že starší verze INFORMATION_SCHEMA nemají. Nebo jestli nemůžou být nakonfigurovány systémy tak, že nemá uživatel práva tuto tabulku číst.
Joker
Profil
Kajman_
Večer zkusím udělat ještě test na paralelní spuštění toho skriptu.
S právy by snad problém být neměl:
Each MySQL user has the right to access these tables, but can see only the rows in the tables that correspond to objects for which the user has the proper access privileges.
Tj. práva k metadatům by měla být navázaná na práva k tabulce

Ale koukám, že INFORMATION_SCHEMA je až od verze 5.0, doufal jsem, že to existuje už déle...
Kajman_
Profil *
Možná bych si vkládal pro jistotu i samotné id, pak by nekonzistence nemůže nastat... v nejhorším se nepodaří insert, že je duplikátní klíč.
INSERT INTO `test` (`id`,`test`,`txt`) SELECT `AUTO_INCREMENT`, `AUTO_INCREMENT`, 'x' ...

Vaše odpověď


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm: