Tælle rækker, også med LIMIT

November 11, 2008 · Posted in Udvikling · Comment 

I mange tilfælde har man en webside der trækker en liste over ting fra en database. Det kan være en liste over brugere, annoncer, artikler m.m. Sådanne liste har det med at blive lange, og man vil inddele den i flere sider af fx 25 rækker.

I MySQL er det rimelig nemt at begrænse sit data. Det gøres bare ved at bruge LIMIT. Fx:

1
SELECT * FROM foo LIMIT 25;

Eller med et offset:

1
SELECT * FROM foo LIMIT 50, 25;

Hvor MySQL returnerer 25 rækker startende fra række 50.

Men så kommer problemet. Man skal jo også vise hvor mange sider ens data er inddelt i. Bruger man PHP’s mysql_num_rows() vil den kun returnerer 25 selvom man måske har flere tusinde rækker.

Jeg har desværre set en del der bare dropper LIMIT, og så beskærer dataene via PHP. Dette er selvfølgelig tåbeligt, da man arbejder med alle rækkerne hver gang.

Nogle suplerer deres SELECT med en tilsvarende query bare med SELECT COUNT(*) i stedet. Dette er bedre, men hvis ens query er meget kompliceret kan det give noget grim kode.

Den smarte løsning er at tilføje SQL_CALC_FOUND_ROWS til sin SELECT. Bruger man den kan man umiddelbart efter ens query har kørt hente antallet af rækker MySQL ville ha’ returneret, hvis den ikke var begrænset af en LIMIT. Dette gøres ved at kalde FOUND_ROWS().

Jeg har lavet et lille eksempel. Først opretter jeg en test-tabel:

1
2
3
4
CREATE TABLE letters (
    letter char(1) NOT NULL,
    PRIMARY KEY (letter)
);

Så indsætter jeg noget data (26 bogstaver):

1
2
3
4
INSERT INTO letters (letter) VALUES ('a'), ('b'), ('c'),
('d'), ('e'), ('f'), ('g'), ('h'), ('i'), ('j'), ('k'),
('l'), ('m'), ('n'), ('o'), ('p'), ('q'), ('r'), ('s'),
('t'), ('u'), ('v'), ('w'), ('x'), ('y'), ('z');

Nu vil jeg hente tre rækker, men for eksemplets skyld vil jeg springe de to første over:

1
2
SELECT SQL_CALC_FOUND_ROWS * FROM letters
ORDER BY letter ASC LIMIT 2, 3;

Hvilket returnerer c, d og e. Umiddelbart bagefter min SELECT skal jeg kalde FOUND_ROWS() for at få det totale antal rækker:

1
SELECT FOUND_ROWS() AS total_cnt;

Den returnerer en total_cnt på 26. Hvilket passer fint med det antal jeg indsatte.

Det er vigtigt at man kalder FOUND_ROWS() lige efter ens SELECT.


  • Om websmed.dk

    Jeg hedder Morten, og jeg har udviklet webapplikationer siden slutningen af 90'erne.

    Jeg vil her dele ud af min erfaring, og med jævne mellemrum poste nogle tips og tricks, samt løsningsforslag på generelle dagligdags problemstillinger.

    Jeg har en forkærlighed for PHP og MySQL, hvorfor I nok vil se flest indlæg der vedrører den gren af webudvikling.

    Jeg har en anden blog af mere personlig karakter på mbn.dk, hvor I kan finde kontaktmuligheder m.m.

    Mine indlæg vil tit være baseret på problemstillinger i mit daglige virke, eller i mit eget firma MRLYTICS, hvor jeg sælger kundeundersøgelser samt kundedatabaser.

    Denne side er hostet hos slicehost.

  • Skrevet på Twitter