INSERT INTO ... ON DUPLICATE UPDATE, ale co když nechceme, aby se nový záznam vytvořil, ale jen upravil existující?
Určitě často využíváte funkce ON DUPLICATE UPDATE, já tedy ano. Ušetří to hodně času a dovolí to podle unikátního identifikátoru upravit sadu řádků jedním dotazem.
Například máte sadu záznamů s párovými klíči, v praxi to může být seznam identifikátorů produktů a k tomu ceny v databázi dbf, csv, xml souboru nebo jiným způsob uložené mimo databázi.
Například máme databázi uživatelů a potřebujeme upravit jejich kredit, pomocí tohoto dotazu:
- INSERT INTO uzivatele (id_uzivatel, kredit) VALUES (1, 100), (2, 150), (10, 300) ON DUPLICATE KEY UPDATE kredit=VALUES(kredit);
Ovšem může se stát, že nechcete, aby při neexistenci unikátního identifikátoru (id_uzivatel) byl nový uživatel vložen.
Já to řeším dočasnou tabulkou a sadou dotazů, takto:
- $q = "CREATE TEMPORARY TABLE `temp_uzivatele` (
- `id_uzivatel` INT(11) NOT NULL,
- `kredit` INT(11) NOT NULL,
- PRIMARY KEY (`id_uzivatel`)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_czech_ci;";
- if (!$sql->query($q)) throw new Exception("Nepodařilo se vytvořit dočasnou tabulku.");
- $ins=array("(1,100)","(2,150)","(10,300)"); // pochopitelně toto pole je vytvořeno v nějakém cyklu, který prochází csv, dbf, xml nebo jiný typ uložených dat
- $q="INSERT INTO temp_uzivatele (id_uzivatel, kredit) VALUES ".implode(",", $ins).";";
- if (!$sql->query($q)) throw new Exception("Nepodařilo se naplnit dočasnou tabulku.");
- $q="UPDATE uzivatele u JOIN temp_uzivatele tu ON (u.id_uzivatel=tu.id_uzivatel) SET u.kredit=tu.kredit;";
- if (!$sql->query($q)) throw new Exception("Nepodařilo se upravit data z dočasné tabulky.");
Co vy nato? Nějaký lepší způsob nebo přístup?
podobné články
| 24.01.2009 | Jeden dotaz do databáze nebo tisíce? ... je to stejné? | (88%) |
| 24.02.2009 | MSSQL UPSERT, INSERT INTO ... ON DUPLICATE KEY UPDATE | (13%) |
komentáře
RSS Komentáře
Nechci-li vložit záznam, pouze upravit, používá se odjakživa UPDATE SQL příkaz. K tomu slouží, sloužil a sloužit bude. Po vykonání příkazu UPDATE lze přečíst počet změněných řádků a zjistit, zda vůbec se našel záznam, který bylo možné změnit.
Stačí nehledat exotická řešení a použít naprosté databázové základy.
Miloslav Ponkrác
a co myslíte, že bude méně zatěžující pro server a rychleji provedené? mne přijde vaše řešení nereálné v mnoha situacích a hlavně složitější, opravdu existují projekty, kde na tom záleží a pokud si máte vybrat mezi zpracováním za 15minut nebo za 30 sekund volba je jasná, teď si jen říkám, že ta kniha o mysql, kde autor je jmenovec to je shoda jmen, že?
Bude to úplně stejně zatěžující, pokud to dobře napíšete, protože to vnitřně dělá naprosto to samé, co ten Váš INSERT INTO…
Nicméně nechápu, proč se bojíte více UPDATE příkazů. To, jestli pošlete jeden SQL příkaz, nebo padesát naprosto nic neříká o rychlosti, s jakou to databáze provede.
Jinak živím se databázemi už dvacet let, a bývával jsem krizovým člověkem – člověkem, který řešil problémy, když už databáze padala výkonově na ústa, a bylo potřeba z ní ještě vymáčknout něco, případně jakékoli jiné problémy, se kterým si běžní profesionální databázisté nevědí rady.
MySQL jsem mimochodem administroval na starém dnes již zaniklém DC hubu CZECHANNEL PRO, kde jsem musel uchodit naráz kolem 10 tisíc uživatelů nad ní. MySQL pracovala opravdu na hraně toho co dokázala.
Proč to říkám? Možná proto, abych Vám řekl, zbavte se mýtů ohledně databází a SQL dotazů. A první mýtus je, že jeden SQL dotaz musí být nutně rychlejší, než série SQL dotazů. Nemusí. Parsování SQL dotazu je naprosto zanedbatelně náročné pro databáze oproti jeho zpracování.
A ohýbat dotaz typu INSERT pro akce, které má ve skutečnosti dělat UPDATE a pak hlídat, aby to náhodou neudělalo to co má INSERT dělat – to je neskutečná volovina. Řekl bych to ostřeji, ale mám úctu k lidem jako jste Vy, kteří nad věcmi přemýšlejí, a snaží se dělat věci tak, aby to optimálně fungovalo.
rozšířil jsem tuto úvahu o další článek a uvedl nějaký test http://www.webfaq.cz/…je-to-stejne



Nevím, co používáš k databázím, ale pokud PDO, tak tam se dá nastavit, aby používalo výjimky a není nutné dělat šílenosti typu if (!$sql->query($q)) throw new …