MySQL NULL
Un tagad pastāstīšu jums par to, kāpēc vajag izvairīties izmantot NULL
kolonnas. Es par to uzzināju šodien. Pilnīgi netīšām. Mūžu dzīvo, mūžu mācies. Iesākumam pavērsimies uz šo izrakstu.
Tad, nu tā. Kā redzam, COUNT(*)
un COUNT(b)
atgriež vienu un to pašu. Kā arī šie selekti faktiski netiek izpildīti, bet gan atgriež nokešotu tabulas rindu skaitu no MySQL iekšām. Bet pavisam cita opera sākas tad, ja mēs veicam COUNT(a)
, kur kolonna a
var saturēt NULL
vērtības. Kā izrādās:
Aggregate (summary) functions such as COUNT(), MIN(), and SUM() ignore NULL values. The exception to this is COUNT(*), which counts rows and not individual column values.
Attiecīgi, visi, kas uzskata, ka COUNT(*)
ir slikts, domā nepareizi. Šī metode atgriež jau nokešotu tabulas rindiņu skaitu. Ja tomēr tiek izmantots COUNT(kolonna)
, bet kolonna pēc definīcijas nevar saturēt NULL
vērtības, tad MySQL izpilda tieši to pašu operāciju, ko COUNT(*)
gadījumā.
Ar InnoDB tabulām viss ir tieši tas pats, ja neskaita to, ka šīm tabulām MySQL neglabā rindu skaitu, līdz ar ko jebkurā gadījumā vēršās pie indeksa. Viss iepriekšminētais (tas, ka COUNT(kolonna)
neskaita NULL
vērtības) attiecās, iespējams, uz visām SQL implementācijām.
Starp citu, iz digga: 10 tips for optimizing mysql queries un 10 Tips for Optimizing MySQL Queries (That don’t suck).
Vilx-
2007. gada 14. aprīlī, plkst. 14:23
...un? Es šo faktu pat esmu izmantojis - COUNT(...) var padot ne tikai kolonnas nosaukumu, bet arī kādu sarežģītāku izteiksmi, kura atgriež NULL vai ne-NULL. Un es tad saskaitu ne-NULL vērtības. Bet tas, ka COUNT() atgriež rindiņu skaitu tabulā(vai grupā, vai selektā), tas arī šķiet tikai loģiski. Cits jautājums - kas ir efektīvāk - COUNT() vai COUNT(kolonna), ja tā nav NULL. Un kas notiek, ja ir kādi WHERE nosacījumi?
laacz Autors
2007. gada 14. aprīlī, plkst. 14:40
Vilx: nav starpības, vai tas ir count(*), vai count(not-null-column). Rezultāts ir viens un tas pats. Skaits tiek nolasīts vai nu no indeksa vai no keša. Ja nav indeksu, tad tiek performēts pilns table scan. Un, ja ir WHERE nosacījumi, tad viss ir atkarīgs no taviem indeksiem :)
e-remit
2007. gada 14. aprīlī, plkst. 14:43
ar to NULL MySQL vispār iesako - uztaisi kolonnu <i>char not null</i>, un mierīgi bakā iekšā NULL vērtības (daži \m/ \m/ MySqlisti to vēl par vērtīgu fīču sauc). Bet vispār dažādās DBVS ir dažāda pieeja, ja salīdzina COUNT(), COUNT(1), COUNT(id). Ir dzirdēts, ka dažas DBVS pie COUNT(1) izmanto mazāk atmiņas, kā pie COUNT(), kā arī COUNT(id) mēdz izmantot indeksu, kas to pasākumu paātrina. Tiesa, jaunākajās versijās parasti pofig, ko lieto - viss strādā vienādi.
laacz Autors
2007. gada 14. aprīlī, plkst. 14:58
E-Remit, ko Tu stāsti? Tu maz esi mēģinājis ieinsertot NULL vērtību iekš <code>char not null</code> lauka? :)
e-remit
2007. gada 14. aprīlī, plkst. 15:13
laacz, esmu - izdevās... tas gan bija pirms pāris gadiem. tagad pārliecinājos vēlreiz: create table tmp_test (id integer not null, laacz char not null); insert into tmp_test values (1, ''); select * from tmp_test; ieinsertoja, kā pa sviestu - pats paprovē... un nestāsti, ka <i>'' != NULL</i>
Analgiins
2007. gada 14. aprīlī, plkst. 15:24
e-remit: lai ievietotu null vērtību, jāraksta insert into tmp_test values (1, null); un '' toč nav NULL
laacz Autors
2007. gada 14. aprīlī, plkst. 15:29
e-remit: '' nav NULL.
japets
2007. gada 14. aprīlī, plkst. 15:33
ja ar peedinju ir domaati divi apostrofi, tad ieksh oracle ir gan ;)
Vilx-
2007. gada 14. aprīlī, plkst. 15:37
Oracle šajā ziņā izceļas - un tā ir vēl viena lieta, kas man tajā nepatīk. :P
elfz
2007. gada 14. aprīlī, plkst. 15:38
Pieņemu, ka eremīta skatījums nāk no Oracle pasaules, kuram tukšs varchars bija (ir?) tas pats, kas NULL, un lai iestāstītu, ka tabulā tik tiešām jāliek tukšs teksts, nevis NULL, jālieto visādas izvirtības, piemēram, jāliek viena tukšumzīme ' '. Vai arī jāpārdomā, vai tiešām nepieciešama semantiskā atšķirība starp null un '', bet tas jau vairāk ir atkarīgs no reliģijas. (mans viedoklis ir, ka uzskatīt NULL un '' par vienu un to pašu, ir tāda pati ķecerība, kā mēģināt C pasludināt, ka (const) char* a = ""; un char* b = 0; ir viens un tas pats)
e-remit
2007. gada 14. aprīlī, plkst. 15:51
OK, es vecs orāklists... oracle tiešām divus apostrofus interpretē, kā NULL, kas nav pēc ANSI SQL92... pie iepriekšējā piemēra, izpildīt: insert into tmp_test (id) values (2) tā, kā otrs lauks nav norādīts, tad tajā tiek iestumts NULL, kaut gan vajadzētu atgriezt kļūdu...
japets
2007. gada 14. aprīlī, plkst. 15:55
ja 11. ir taisniiba, tad tas gan ir augstaakaas raudzes sviests
noisex
2007. gada 14. aprīlī, plkst. 16:09
1a liela DB:
mysql> select count() from logs; +-----------+ | count() | +-----------+ | 454132960 | +-----------+ 1 row in set (0.00 sec)
mysql> select count(id) from logs; +-----------+ | count(id) | +-----------+ | 454132960 | +-----------+ 1 row in set (0.00 sec)
Ka redzam skjirba 0e :)))
elfz
2007. gada 14. aprīlī, plkst. 16:13
Nja.
mysql 4.1.13: insert into test (a) values (2) Rows: 0, affected: 1
mysql 5.1.12: insert into test (a) values (2) ERROR 1364 (HY000): Field 'b' doesn't have a default value
e-remit
2007. gada 14. aprīlī, plkst. 16:16
par 11. sry, laikam tomēr pēc noklusējuma tiek iemests tukšs strings, nevis NULL...
elfz
2007. gada 14. aprīlī, plkst. 16:16
13, noisex, runa bija par performances kritumu laukam, kas var saturēt NULL vērtības, jo tam nepieciešams table-scans. Domāju, ka tev logu tabulai id lauks ir primary key not null, līdz ar ko datu bāzei ierakstu skaitu noteikt nav problēmu.
SpiegS
2007. gada 14. aprīlī, plkst. 20:35
e-remit @ 2007.04.14. 14:43, teica: ... kā arī COUNT(id) mēdz izmantot indeksu, kas to pasākumu paātrina.
ja selektā nav WHERE ierobežojuma tad raksti ko gribi starp iekavām - indeksu tāpat neizmantos, jo šinī gadījumā full scan nostrādās atrāk nekā index full scan :P
cc
2007. gada 14. aprīlī, plkst. 21:09
17: ir arī tāds hints /*+ index_ffs(index) */
e-remit
2007. gada 14. aprīlī, plkst. 22:39
SpiegS: pats notestēji? uz MySQL - 4.1.21 <code> CREATE TABLE
tmp_count_test
(id
INT NOT NULL ,sometext
VARCHAR( 10 ) NULL , PRIMARY KEY (id
) , INDEX (sometext
) ) ENGINE = innodb CHARACTER SET utf8 COLLATE utf8_bin;INSERT INTO
tmp_count_test
(id
,sometext
) VALUES ( '1', 'viens' ), ( '2', 'divi' );EXPLAIN EXTENDED SELECT count( id ) FROM
tmp_count_test
; </code> nez kāpēc man uzrāda "Using index"... (nav man MySQL konsole uz sitienu pieejama - lietoju phpMyAdmin).BigUgga
2007. gada 16. aprīlī, plkst. 02:34
17: nu par to neesu gan pārliecināts. piemēram, ja index ir uz
id
(pofig vai primary), tad pēc manas saprašanas tas index gan tiks izmantots.19: PRIMARY KEY (
id
) pēc Tavām domām nav index? :))e-remit
2007. gada 16. aprīlī, plkst. 11:14
20: protams, ka primary key ir indekss.. ;) tā bija atbilde uz 17, ka indeksu nekad pie COUNT neizmantos... Šo pārbaudīju arī uz Oracle - pirms statistikas savākšanas bija Full Scan, pēc - UNIQUE INDEX FULL SCAN...
sd
2007. gada 17. aprīlī, plkst. 13:49
NULL lielākā problēma ir tāda, ka tas prasa papildus uzmanību SQL kodā, līdz ar to iespējas iešaut pasam sev kājā pieaug.
-- piem atrast to kuriem kolonnas veertiiba nav 'janis'
WHERE col1 'janis' or col1 is NULL
Tas, ka count() skaita null, bet count(xxx) nee, bija zinaams fakts, bet parasti to aizmirst, tiesa ieradums vienmeer rakstiit count() pasargaa no negaidiitiem rezultaatiem, kad dazhas rindinjas deelj null nesaskaita
Shadowbird
2007. gada 18. aprīlī, plkst. 09:09
sd: Man sanāk, ka atrast to[s ierakstus], kuriem kolonnas veertiiba nav 'janis' būtu <code>WHERE
col1
NOT LIKE 'janis'</code> Manuprāt darbosies vienādi neatkarīgi no NULL.