- Funkcija ROW_NUMBER() priskiria unikalius, nuoseklius sveikuosius skaičius sutvarkytuose languose ir idealiai tinka deterministiniam puslapiavimui, reitingavimui ir dublikatų šalinimui PostgreSQL kalboje.
- Puslapiais pagrįstas ir žymekliu pagrįstas puslapiavimas naudingas naudojant ROW_NUMBER(), tačiau reikalauja stabilaus, unikalaus ORDER BY – dažnai derinant verslo stulpelius su pirminiu raktu.
- CTE, antrinės užklausos ir tinkamas PARTITION BY bei DISTINCT naudojimas yra pagrindiniai veiksniai, lemiantys, kurias eilutes ROW_NUMBER() išvardija ir kaip keičiasi našumas.
- Supratimas skirtumų tarp ROW_NUMBER(), RANK() ir DENSE_RANK(), taip pat konkrečiam varikliui skirtų optimizavimų padeda kurti nuspėjamas ir efektyvias puslapiavimo strategijas.
Didelių rezultatų rinkinių puslapiavimas „PostgreSQL“ kalboje iš pirmo žvilgsnio gali atrodyti paprastas, tačiau norint tai padaryti efektyviai ir teisingai – ypač kai jūsų eilės tvarkos stulpelyje yra lygiųjų – reikia šiek tiek daugiau nei vien tik įvesti LIMIT/OFFSET užklausoje. Lango funkcija ROW_NUMBER() yra vienas universaliausių įrankių, skirtų šiai problemai išspręsti ir kartu atverti daugybę papildomų analitinių naudojimo atvejų, tokių kaip reitingavimas, populiariausios N užklausos ar dublikatų aptikimas.
Šiame vadove išsamiai paaiškinama, kaip naudoti ROW_NUMBER() puslapiavimo funkcijos PostgreSQL kalboje – kaip ji veikia praktiškai, kuo skiriasi nuo kitų reitingavimo funkcijų, kokių našumo pokyčių galima tikėtis ir kaip kiti pagrindiniai duomenų bazių varikliai elgiasi panašiai. Taip pat apžvelgsime sudėtingus realaus pasaulio scenarijus, tokius kaip žymekliu pagrįstas puslapiavimas, kai rūšiavimo stulpelyje yra pasikartojančių elementų, ir kaip juos sujungti. ROW_NUMBER() su CTE, sujungimais ir antrinėmis užklausomis, kad būtų užtikrintas švarus, gamybai paruoštas SQL.
Ką iš tikrųjų atlieka PostgreSQL ROW_NUMBER() lango funkcija
Iš esmės ROW_NUMBER() yra lango funkcija, kuri kiekvienai rezultatų rinkinio eilutei priskiria unikalų, nuoseklų sveikąjį skaičių, pradedant nuo 1 ir didinant po 1 be tarpų. Ši numeracija gali būti taikoma visam rezultatui arba pradedama iš naujo kiekvienai eilučių grupei, priklausomai nuo to, kaip apibrėžiate langą.

Bendroji PostgreSQL sintaksė atrodo taip:
ROW_NUMBER() OVER ( [PARTITION BY partition_expression] ORDER BY order_expression )
Dvi dalys viduje OVER sąlyga kontroliuoja, kaip priskiriami eilučių numeriai: PARTITION BY (neprivaloma) padalija rezultatų rinkinį į nepriklausomas grupes ir ORDER BY (privalomas) apibrėžia kiekvieno skaidinio tvarką, kuri lemia, kuri eilutė gauna kurį numerį.
Jei praleisite PARTITION BY, funkcija visą rezultatų rinkinį traktuoja kaip vieną skaidinį ir tiesiog sunumeruoja visas eilutes pagal nurodytą tvarką. Kai pridėsite PARTITION BY, eilučių numeravimas kiekviename skaidinyje prasideda nuo 1, o tai labai naudinga reitinguojant pagal kategorijas, užklausoms pagal N geriausių grupių skaičių ir grupuotai deduplikacijai.
Skirtingai nuo reitingavimo funkcijų, tokių kaip RANK() or DENSE_RANK(), ROW_NUMBER() visiškai ignoruoja ryšius ir niekada nekartoja skaičiaus, net kai eilutėse yra identiškos reikšmės rūšiavimo stulpeliuose. Dėl to jis idealiai tinka deterministiniam puslapiavimui ir tiksliam eilučių pjaustymui.
Pagrindiniai ROW_NUMBER() pavyzdžiai intuicijai lavinti
Prieš naudodamiesi ROW_NUMBER() Puslapiavimo atveju naudinga pamatyti jį veikiant paprastuose pavyzdžiuose, kur tikslas yra tik kontroliuojamai sunumeruoti eilutes. Įsivaizduokite stalą employees su stulpeliais id, name, department bei salary.
Norėdami priskirti visuotinį eilutės numerį, surūšiuotą pagal atlyginimą mažėjimo tvarka, galite parašyti:
SELECT
id,
name,
department,
salary,
ROW_NUMBER() OVER (ORDER BY salary DESC) AS row_num
FROM employees;
Ši užklausa grąžina visus darbuotojus, surūšiuotus pagal atlyginimą nuo didžiausio iki mažiausio, su row_num = 1 geriausiai apmokamam darbuotojui, 2 antrajam ir taip toliau, be tarpų ir pasikartojančių reikšmių. Atlyginimų ryšiai nutraukiami savavališkai, nebent pratęsiate ORDER BY su papildomais stulpeliais.
Jei vietoj to reikia, kad eilučių numeracija kiekviename skyriuje prasidėtų iš naujo, galite sujungti PARTITION BY su ORDER BY:
SELECT
id,
name,
department,
salary,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS row_num
FROM employees;
Čia kiekvienas skyrius gauna savo seką 1, 2, 3, ..., todėl vėliau filtruodami galite lengvai rasti „daugiausiai uždirbantį darbuotoją kiekviename skyriuje“. row_num = 1 išorinėje užklausoje arba CTE. Šis modelis yra daugelio užklausų, kurių skaičius yra N grupėje, pagrindas.
Norint aiškiai atskirti numeravimo logiką nuo filtravimo logikos, įprasta lango funkciją apvynioti CTE arba antrinėje užklausoje, o tada filtruoti sugeneruotus eilučių numerius išorinėje SELECT. Tai ypač svarbu, nes lango funkcijų negalima naudoti tiesiogiai WHERE to paties SELECT sakinio, kuris juos apibrėžia, dalis.
Klasikiniam puslapiavimu pagrįstam puslapiavimui naudojamas ROW_NUMBER()
Paprasčiausias būdas atlikti puslapiavimą su ROW_NUMBER() „PostgreSQL“ kalboje reikia apskaičiuoti kiekvienos eilutės indeksą ir tada paprašyti skaitinio diapazono, atitinkančio norimą puslapį. Kartais tai naudojama kaip alternatyva OFFSET/LIMIT, ir taip pat puikiai veikia perkeliant puslapiavimo kodą iš SQL Server arba Oracle.
Tarkime, kad norite rezultatų puslapio, kurio dydis @PageSize ir puslapio numeris @PageNumber (0 pagrįstas indeksas). Įprasta T-SQL užklausa atrodo taip:
SELECT PK_ID, Truco, Descripcion
FROM (
SELECT
PK_ID,
Truco,
Descripcion,
ROW_NUMBER() OVER (ORDER BY Truco, PK_ID) AS RowNumber
FROM TrucosInformaticos
) AS Trucos
WHERE RowNumber BETWEEN (@PageSize * @PageNumber) + 1
AND @PageSize * (@PageNumber + 1);
Ta pati logika tiesiogiai taikoma ir PostgreSQL – jums tereikia pritaikyti parametro sintaksę ir, jei norite, įvilkti ją į funkciją, o ne į saugomą procedūrą. Esmė yra tokia: apskaičiuoti ROW_NUMBER() vieną kartą, tada suskaidykite eilutes skaitiniu intervalu, atitinkančiu jūsų puslapio ribas.
Pavyzdžiui, PostgreSQL sistemoje fiksuotam puslapiui galite parašyti:
WITH ranked_posts AS (
SELECT
id,
title,
ROW_NUMBER() OVER (ORDER BY title, id) AS row_num
FROM posts
)
SELECT id, title
FROM ranked_posts
WHERE row_num BETWEEN 11 AND 20;
Grąžina 11–20 eilutes tokia tvarka, kurią apibrėžia ORDER BY title, id, faktiškai pateikdamas jums antrą puslapį, kurio dydis yra 10. Didelis pranašumas, palyginti su paprastu OFFSET, yra tas, kad eilučių numeriai yra aiškūs ir gali būti derinami su papildoma logika, pavyzdžiui, sujungiant atgal, filtruojant arba atliekant tolesnę analizę.
Žymeklio stiliaus puslapiavimas, kai rūšiavimo stulpelyje yra pasikartojančių elementų
Poslinkiu pagrįstą puslapiavimą lengva pagrįsti, tačiau jis gali sukelti našumo problemų didelėse lentelėse ir tampa nestabilus, kai tarp užklausų keičiasi pagrindiniai duomenys. Žymekliu pagrįstas puslapiavimas (dar vadinamas klavišų rinkinio puslapiavimu) siekia tai išspręsti naudojant paskutinį matytą elementą kaip inkarą, o ne absoliutų poslinkį.
Padėtis tampa sudėtinga, kai stulpelyje, pagal kurį rūšiuojate, yra pasikartojančių reikšmių. Apsvarstykite schemą su posts bei comments:
CREATE TABLE posts(
id uuid PRIMARY KEY,
title varchar(255) NOT NULL
);
CREATE TABLE comments(
id uuid PRIMARY KEY,
post_id uuid NOT NULL REFERENCES posts
);
Įsivaizduokite, kad pirmiausia sukuriate užklausą, kuri įrašus rikiuoja pagal komentarų skaičių mažėjančia tvarka:
SELECT p.*, COUNT(c.id) AS comments_count
FROM posts AS p
LEFT JOIN comments AS c ON p.id = c.post_id
GROUP BY p.id
ORDER BY comments_count DESC;
Žymekliu pagrįsto puslapiavimo atveju gali kilti pagunda pasirinkti iki tam tikros ribos comments_count slenkstį ir tada taikyti LIMIT:
WITH cte AS (
SELECT p.*, COUNT(c.id) AS comments_count
FROM posts AS p
LEFT JOIN comments AS c ON p.id = c.post_id
GROUP BY p.id
ORDER BY comments_count DESC
)
SELECT *
FROM cte
WHERE comments_count <= (
SELECT comments_count FROM cte WHERE id = '00000000-0000-0000-0000-000000000003'
)
LIMIT 3;
Problema iškyla, kai keli įrašai dalijasi tuo pačiu comments_count. Jei dviejų įrašų skaičius yra 2 ir jūsų žymeklis rodo vieną iš jų, naudojant <= įtraukia abi susietas eilutes antrame puslapyje, naudojant < praleidžia visas eilutes su tuo pačiu skaičiumi ir peršoka per toli, praleisdamas kai kuriuos įrašus, kuriuos tikėjotės pamatyti.
Tai klasikinis žymeklio puslapiavime naudojamo neunikalus rakto simptomas – duomenų bazė negali deterministiškai suskirstyti duomenų rinkinio „per vidurį“ ryšių grupės, jei žymeklis koduoja tik neunikalią reikšmę. Norint saugiai apibrėžti žymeklį, reikia unikalios ir stabilios tvarkos.
Vienas iš problemos sprendimo būdų – sukurti sintetinį, unikalų rūšiavimo raktą, sujungiant komentarų skaičių su pirminiu raktu, pavyzdžiui, sujungiant:
WITH cte AS (
SELECT
p.,
COUNT(c.id) AS comments_count,
CONCAT(COUNT(c.id), ':', p.id) AS comments_count_id
FROM posts AS p
LEFT JOIN comments AS c ON p.id = c.post_id
GROUP BY p.id
ORDER BY comments_count_id DESC
)
SELECT *
FROM cte
WHERE comments_count_id < (
SELECT comments_count_id
FROM cte
WHERE id = '00000000-0000-0000-0000-000000000003'
)
LIMIT 3;
Sukūrus sudėtinį raktą, pvz. '2:00000000-...-0003', jūs padarote tvarką visiškai unikalią ir galite drąsiai sakyti „duokite man eilutes su comments_count_id mažiau nei inkaras“ be dviprasmybių. Tai ta pati idėja, kaip visada įtraukiant id į jūsų ORDER BY kaip lemiamas veiksnys.
Praktiškai nereikia jungti į eilutę; galite tiesiog naudoti kelis stulpelius ORDER BY ir užkoduokite juos žymeklio objekte programos sluoksnyje. Svarbi duomenų bazės dalis yra ta, kad bendra tvarka yra unikali ir atkartojama tarp iškvietimų.
Puslapiavimas naudojant ROW_NUMBER() ir LIMIT bei OFFSET
PostgreSQL palaiko klasikinį LIMIT bei OFFSET sintaksė iš karto naudojama, ir daugeliui mažų ir vidutinių rezultatų rinkinių ji puikiai tinka naudoti. Jums tereikia nurodyti, kiek eilučių praleisti ir kiek grąžinti.
Tačiau OFFSET pagrindu veikiantis puslapiavimas turi du didelius trūkumus: našumą ir stabilumą. As OFFSET augant, „PostgreSQL“ vis tiek turi nuskaityti ir atmesti visas ankstesnes eilutes, kol gali pradėti pateikti rezultatus, o tai brangiai kainuoja dirbant su dideliais duomenų rinkiniais. Be to, jei duomenys pasikeičia tarp užklausų, puslapiai gali „pasislinkti“ ir rodyti pasikartojančias eilutes arba praleisti eilutes.
Naudojant ROW_NUMBER() puslapiavimui suteikia galimybę vieną kartą materializuoti eilutės indeksą, o tada jį tvarkingai supjaustyti:
WITH numbered_products AS (
SELECT
product_id,
product_name,
price,
ROW_NUMBER() OVER (ORDER BY product_name) AS row_number
FROM products
)
SELECT product_id, product_name, price
FROM numbered_products
WHERE row_number BETWEEN 11 AND 20
ORDER BY product_name;
Šis šablonas skaitomas intuityviai: pirmiausia kiekvienam produktui priskiriama jo pozicija rūšiuotame sąraše, tada išorinė užklausa nuskaito 11–20 eilutes. Kol pagrindiniai duomenys nesikeičia tarp vykdymo ir puslapio suvartojimo, gaunate stabilią loginės sekos dalį.
Tai sakė ROW_NUMBER()puslapiavimas taip pat nėra stebuklinga našumo gerinimo priemonė. Duomenų bazė vis tiek turi įvertinti lango funkciją visose tinkamose eilutėse, kad priskirtų numerius, todėl itin didelėms lentelėms tai gali būti taip pat brangu, kaip ir didelis OFFSET. Tai ypač naudinga, kai derinama su papildomu filtravimu arba kai norima deterministinės, eilučių numeriais pagrįstos logikos, o ne vien puslapiavimo.
Kaip langų funkcijos puslapiavimas veikia skirtinguose duomenų bazių varikliuose
Langų funkcijos, pvz. ROW_NUMBER() yra standartizuotos SQL funkcijos, tačiau kiekvienas duomenų bazės variklis jas optimizuoja skirtingai, kad atitiktų puslapiavimo tipo šablonus. Kai kurie produktai gali atpažinti „top-N su lango funkcija“ užklausas ir anksčiau laiko nutraukti nuskaitymą naudojant indekso prieigą; kiti kaskart tyliai apdoros visą rinkinį.
Apsvarstykite šią tipinę „top-N“ / puslapiavimo stiliaus užklausą, naudodami ROW_NUMBER per sutvarkytą indeksą sales lentelėje:
SELECT *
FROM (
SELECT
sales.*,
ROW_NUMBER() OVER (ORDER BY sale_date DESC, sale_id DESC) AS rn
FROM sales
) AS tmp
WHERE rn BETWEEN 11 AND 20
ORDER BY sale_date DESC, sale_id DESC;
Čia ROW_NUMBER skaičiuoja eilutes pagal nustatytą tvarką OVER sąlyga ir išorinė WHERE apriboja rezultatą iki konkretaus puslapio (11–20 eilutės). Tai logiškai atitinka „top-N“ užklausą kartu su poslinkiu.
Pavyzdžiui, „Oracle“ gali atpažinti sustabdymo sąlygą ir naudoti indeksą sale_date bei sale_id įdiegti „top-N in konvejeris“ elgseną. Jo vykdymo planas gali parodyti WINDOW NOSORT STOPKEY, nurodant, kad varikliui nereikia papildomo rūšiavimo ir jis sustos, kai tik pasieks viršutinę prašomo lango ribą.
Šio tipo optimizavimo palaikymas nėra universalus. Kai kurios „PostgreSQL“ ir kitų variklių, tokių kaip „MySQL“, „MariaDB“ ir „Db2“, versijos šiuose lango funkcijomis pagrįstuose šablonuose nesustabdo indekso nuskaitymo anksti, o tai reiškia, kad jos vis tiek apdoroja daugiau eilučių, nei būtina norint pateikti prašomą puslapį.
Naujausiose „PostgreSQL“ versijose (15+ ir naujesnėse) patobulintas langų funkcijų našumas ir optimizavimas, tačiau veikimas vis tiek gali skirtis tarp pagrindinių versijų. Visada patikrinkite vykdymo planus su EXPLAIN (ANALYZE) , kad pamatytumėte, ar jūsų duomenų bazė gali išnaudoti indeksus ir anksti sustoti, ar ji nuskaito ir rūšiuoja visą rezultatų rinkinį.
ROW_NUMBER() derinimas su DISTINCT, CTE ir antrinėmis užklausomis
Dažnas gedimas naudojant ROW_NUMBER() šalia DISTINCT yra tai, kad lango funkcija įvertinama prieš dublikatų pašalinimo žingsnį. Dėl to gali būti gauti painūs rezultatai, kai, matyt, pasikartojančios reikšmės vis tiek gauna skirtingus eilučių numerius.
Pavyzdžiui, jei bandysite išvardyti skirtingas kainas iš products lentelė su viena užklausa, pvz.:
SELECT DISTINCT price,
ROW_NUMBER() OVER (ORDER BY price) AS rn
FROM products;
Galite nustebti pamatę kelias eilutes su tuo pačiu price bet kitoks rn reikšmės, nes lango funkcija veikė visose eilutėse prieš tai DISTINCT pašalino dublikatus iš galutinės projekcijos.
Patikimas būdas tai išspręsti – pirmiausia materializuoti skirtingas reikšmes (naudojant CTE arba papildomą užklausą), o tada taikyti ROW_NUMBER() be to:
WITH prices AS (
SELECT DISTINCT price
FROM products
)
SELECT price,
ROW_NUMBER() OVER (ORDER BY price) AS rn
FROM prices;
Arba galite naudoti antrinę užklausą tiesiogiai FROM sąlyga, pasiekianti tą patį efektą. Svarbiausia idėja yra aiškiai nuspręsti, kuris rezultatų rinkinys yra „langas“, kuriame ROW_NUMBER() turėtų veikti, o jei reikia unikalumo, pirmiausia sukurkite tą rinkinį.
Šis šablonas yra itin patogus puslapiavimo užduotims, tokioms kaip „gauti produktą su trečia pagal dydį kaina“ arba „išvardinti skirtingas kainas su eilučių numeriais ir tada pasirinkti konkrečią“. Pirmiausia galite gauti unikalias surūšiuotas kainas su ROW_NUMBER() ir tada prisijunkite arba filtruokite pagal konkretų jums rūpimą rangą.
ROW_NUMBER() reitingavimui, N geriausiųjų ir dublikatų šalinimui
Nors daugiausia dėmesio skiriame puslapiavimui, būtų nesąmonė nepaminėti to ROW_NUMBER() yra puiki priemonė duomenims reitinguoti ir dublikatams šalinti. Daugelis tų pačių šablonų, kuriuos naudojate puslapiavimui, taip pat atlieka ir reitingavimo logikos funkciją.
Norėdami gauti N populiariausių įrašų pagal kategoriją, galite suskirstyti pagal kategorijas ir rikiuoti eilutes pagal metriką, pvz., kainą mažėjimo tvarka:
WITH ranked_products AS (
SELECT
product_id,
product_name,
category_id,
price,
ROW_NUMBER() OVER (
PARTITION BY category_id
ORDER BY price DESC
) AS rank
FROM products
)
SELECT product_id, product_name, category_id, price
FROM ranked_products
WHERE rank <= 2;
Tai grąžina du brangiausius kiekvienos kategorijos produktus. Tada galite prisijungti prie categories lentelė naudojant USING (category_id) arba aiški jungtis, kad būtų rodomi žmonėms skaitomi pavadinimai.
Dėl dublikatų pašalinimo, ROW_NUMBER() dažnai naudojamas kartu su PARTITION BY , kad pažymėtumėte visus, išskyrus pirmąjį, kiekvienos grupės atvejus kaip dublikatus. Apsvarstykite paprastą lentelę:
CREATE TABLE items (
id INT,
name VARCHAR
);
Tarkime, kad įterpiate kelis pasikartojančius vardus ir norite pašalinti papildomas kopijas, išlaikydami mažiausią kiekvieno vardo ID. Pirmiausia galite nustatyti dublikatus:
SELECT
id,
name,
ROW_NUMBER() OVER (PARTITION BY name ORDER BY id) AS row_number
FROM items
ORDER BY id;
Bet kuri eilutė su row_number > 1 yra dublikatas. Tada galite naudoti CTE ir DELETE pareiškimas juos pašalinti:
DELETE FROM items
WHERE id IN (
WITH ranked_items AS (
SELECT
id,
name,
ROW_NUMBER() OVER (PARTITION BY name ORDER BY id) AS row_number
FROM items
)
SELECT id
FROM ranked_items
WHERE row_number > 1
);
Po to, kai tai paleisite, pasirinksite iš items bus rodomi tik skirtingi pavadinimai, o kiekvienai reikšmei bus palikta po vieną reprezentatyvią eilutę. Tai švarus, deklaratyvus būdas pašalinti dublikatus, tuo pačiu tiksliai kontroliuojant, kurią eilutę išsaugoti.
ROW_NUMBER(), RANK() ir DENSE_RANK() puslapiavimo scenarijuose
„PostgreSQL“ siūlo keletą reitingavimo lango funkcijų: ROW_NUMBER(), RANK()ir DENSE_RANK(). Nors visi jie priskiria surūšiuotus numerius, jie elgiasi skirtingai, kai surūšiuotuose stulpeliuose yra lygiųjų.
Svarbiausi skirtumai yra šie:
ROW_NUMBER()visada priskiria unikalų sveikąjį skaičių kiekvienai eilutei, net ir esant lygiosioms; skaičiai yra griežtai nuoseklūs (1, 2, 3, 4 ir kt.).RANK()suteikia tą patį rangą identiškoms reikšmėms, bet praleidžia skaičius po lygiųjų (pavyzdžiui, 1, 2, 2, 4 – trūksta 3 rango).DENSE_RANK()taip pat suteikia tą patį rangą lygiosioms, bet nepraleidžia skaičių (1, 2, 2, 3).
Puslapiavimui ROW_NUMBER() paprastai yra saugiausias pasirinkimas, nes garantuoja lygiai vieną eilutę vienam numeriui, kuris natūraliai susieja su puslapių diapazonais, pvz., 1–10, 11–20 ir kt. Jei naudojote RANK() or DENSE_RANK(), dėl sąsajų galite gauti puslapius, kuriuose bus mažiau arba daugiau eilučių nei tikėtasi.
Kita vertus, ataskaitų teikimo atvejais, pavyzdžiui, varžybų rezultatams, kai sutampančios vertės turi būti toje pačioje pozicijoje, RANK() or DENSE_RANK() geriau atspindi ketinimus nei ROW_NUMBER(). Vis dar galite puslapiuoti šiuos rezultatus, bet turite žinoti, kad „pozicija“ nebeatitinka fizinio eilutės numerio.
Praktiniai patarimai, spąstai ir našumo aspektai
Naudojant ROW_NUMBER() Kalbant apie puslapiavimą ir analizę, keletas gerų praktikų padės išvengti subtilių klaidų ir nereikalingų našumo problemų. Dauguma jų yra aiškūs ir deterministiniai.
Visada aiškiai apibrėžkite ORDER BY viduje OVER() sąlyga. Be jo „PostgreSQL“ gali grąžinti eilutes bet kokia tvarka lango funkcijos tikslais, o eilučių numeriai gali keistis tarp vykdymų, net jei pagrindiniai duomenys yra identiški.
Kai tik įmanoma, pabaigoje įtraukite unikalų stulpelį (dažnai pirminį raktą). ORDER BY sąrašas. Tai paverčia tvarką visiška tvarka ir išvengia dviprasmybių dėl ryšių, o tai labai svarbu žymekliu pagrįstam puslapiavimui ir nuspėjamiems viršutinių N rezultatų rezultatams.
Nesitikėkite naudoti lango funkcijų tiesiogiai WHERE to paties SELECT sakinio dalis. Verčiau apvyniokite juos CTE arba antrinėje užklausoje ir filtruokite pagal išvestinį stulpelį išorinėje užklausoje. Šis šablonas yra paprastas, daugkartinio naudojimo ir užtikrina, kad jūsų SQL būtų skaitomas.
Puslapiuojant, kai tik įmanoma, pirmenybė teikiama rūšiavimui pagal indeksuotus stulpelius. Abu ORDER BY bei ROW_NUMBER() pasikliauti rūšiavimu arba indeksų nuskaitymu; tinkamas indeksavimas gali lemti skirtumą tarp milisekundžių ir sekundžių didelėse lentelėse.
Būkite atsargūs derindami PARTITION BY su puslapiavimu kai kuriuose varikliuose. Tam tikruose duomenų bazių produktuose ir versijose, naudojant skaidinius langų funkcijas rodiniuose arba antrinėse užklausose, gali būti išjungtos kitaip prieinamos „stopkey“ optimizacijos, todėl modulis apdoros daugiau eilučių nei būtina. Būtina testuoti su realiais duomenimis ir skaityti užklausų planus.
Labai didelių duomenų rinkinių ir labai dinamiškų duomenų atveju apsvarstykite galimybę sujungti ROW_NUMBER() Puslapiavimas „administratoriaus stiliaus“ rodiniams su žymekliu pagrįstu klavišų rinkinio puslapiavimu naudotojui skirtuose galiniuose taškuose. Tokiu būdu gausite ir deterministines įrankių užklausas, ir efektyvią, stabilią navigaciją savo API arba vartotojo sąsajoje.
Žvelgiant į visumą, ROW_NUMBER() nėra tik puslapiavimo triukas: tai galingas analitinis elementas, leidžiantis sunumeruoti, reitinguoti, skaidyti ir išvalyti rezultatų rinkinius „PostgreSQL“ ir kituose pagrindiniuose SQL varikliuose, naudojant tą pačią pagrindinę logiką. Įvaldyti tai – kartu su tvirtu supratimu apie OVER(), PARTITION BY, ir skirtumai nuo RANK() bei DENSE_RANK() – suteikia labai lankstų įrankių rinkinį efektyviam puslapiavimui, „top-N“ užklausoms ir patikimam dvyliktų duomenų deduplikavimui realiose programose.