✉️ Saņem šito visu e-pastā. Tā vietā, lai palaistu garām kaut ko no tā, ko es rakstu savā blogā, tagad vari pierakstīties un saņemt e-pastā visu, ko es te rakstu. Tas nav bieži.

← Uz sākumu

MySQL NULL

2007. gada 14. aprīlī, 23 komentāri

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 avar 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).

Tu atbildi augstāk redzamajam komentāram. Atcelt

Gravatar 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?

Gravatar 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 :)

Gravatar 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.

Gravatar 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? :)

Gravatar 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>

Gravatar 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

Gravatar laacz Autors

2007. gada 14. aprīlī, plkst. 15:29

e-remit: '' nav NULL.

Gravatar japets

2007. gada 14. aprīlī, plkst. 15:33

ja ar peedinju ir domaati divi apostrofi, tad ieksh oracle ir gan ;)

Gravatar 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

Gravatar 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)

Gravatar 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...

Gravatar japets

2007. gada 14. aprīlī, plkst. 15:55

ja 11. ir taisniiba, tad tas gan ir augstaakaas raudzes sviests

Gravatar 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 :)))

Gravatar 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

Gravatar 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...

Gravatar 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.

Gravatar 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

Gravatar cc

2007. gada 14. aprīlī, plkst. 21:09

17: ir arī tāds hints /*+ index_ffs(index) */

Gravatar 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).

Gravatar 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? :))

Gravatar 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...

Gravatar 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

Gravatar 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.