29. juni 2006 - 05:47Der er
15 kommentarer og 1 løsning
Find næste record
hejsa...
Jeg har en tabel der ser således ud:
fortløbende løbenummer varenummer f3 f4 f5 etc
Hvis jeg har fundet en vare baseret på varenummer vil jeg gerne kunne finde den næste, sorteret efter varenummer. Varenumrene er unikke, men ikke fortløbende
SELECT t1.varenummer, MIN(t2.varenummer) as NxtVarenummer FROM produkter t1, produkter t2 WHERE t1.varenummer = ? AND t2.varenummer > t1.varenummer GROUP BY t1.varenummer
Det skulle gerne give dig både det varenummer du søger og det næste i rækken...
Men den skal vel stadig kun tage den første linje ikke, og hvis ikke det er sorteret vælger den så den der er en højere, eller vælger den næste i forhold til PK?
Hvis der findes et index på feltet varenummer, er der sikkert ikke den store forskel på forslagene fra thrytter og nielle, da man må gå ud fra, at SQL server kan optimere søgningen, så der afbrydes når den første række efter den søgte er læst.
Hvis der ikke findes et index, vil thrytters metode sikkert kræve, at et sådant opbygges, da TOP instruktionen er baseret på en ordnet tabel. Vha. nielles metode, KAN det måske være en fordel at gennemløbe rækkerne sekventielt og på denne måde finde det mindste varenummer, som samtidig er større end det søgte. Det afhænger af tabellens størrelse, og den afvejning (om det kan betale sig at opbygge et midlertidigt index), vil SQL server skulle gøre sig, når den laver sin execution plan.
Koden 29/06-2006 08:19:32 kan finde næste *række* hvis den modificeres lidt:
SELECT TOP 1 * FROM produkter WHERE varenummer > @varenummer ORDER BY varenummer DESC
Den bruger imidlertid en del resourcer på at sortere resten af posterne - arbejde som på en eller anden måde er spildt da du jo kun ønsker næste række og derfor er ligeglad med den næste i rækkefølgen eller den næste igen.
Hvis du derfor kun er interesseret i at kende det næste *varenummer*, hvilket no er det som den originale form gør:
SELECT TOP 1 varenummer FROM produkter WHERE varenummer > @varenummer ORDER BY varenummer DESC
- ja, så kan den effektiviseres med Min()-funktionen (koden i 29/06-2006 08:24:46):
SELECT Min(varenummer) AS nextVarenummer FROM produkter WHERE varenummer > @varenummer
Hmm... Ikke for at putte malurt i dit bæger, nielle, men jeg er nu ikke sikker på, at dit ræsonnement er helt rigtigt. Hvis SQL server er "dum", vil dit forslag med brug af MIN funktionen faktisk være langsommere. Forestil dig følgende situation:
Der findes et index på varenummer feltet.
1. SQL bruger indexet til at placere cursoren på rækken efter det søgte varenummer. 2. Herefter looper den de resterende rækker igennem og kalder for hver række MIN funktionen, som til sidst returnerer den mindste værdi.
Modsat med TOP 1: 1. SQL bruger indexet til at placere cursoren på rækken efter det søgte varenummer. 2. TOP 1 siger, at der kun skal returneres 1 række. Altså returneres værdien straks.
Jeg er klar over, at det er usandsynligt, at SQL server er så primitiv, at den ikke identificerer, at feltet der bruges i MIN funktionen i forvejen er sorteret i ASC orden via indexet, og at den første værdi derfor må være den laveste. Men kan man være sikker på det? Og er det i så fald ikke nøjagtig den samme kode der bliver eksekveret i begge tilfælde? Er det overhovedet sikkert, at SQL server vil bruge indexet hvis det ikke er explicit angivet med en ORDER BY?
Bare rolig, jeg tager såmæn ikke den slags ilde op. Man har vel lov til at lære noget nyt hele tiden.
Jeg forstår dog ikke rigtigt hvorfor at du som udgangspunkt mener at eksistensen af et indeks ikke også skulle effektivisere beregningen af Min(), og at den kun skulle slå igennem for TOP. Der er vel ingen grund til at databasen skulle loope igennem alle rækkerne for at finde den midste - når den jo ved at den allerede har sorteret dem i stigende orden?
Well, i stedet for at diskutere her fra til dommedag ... hvorfor så ikke bare måle efter? Så jeg lavede et eksperiment:
1000 varer med unikke varerenumre i intervallet 0-9999
- og disse to SQL'er:
1) SELECT TOP 1 varenummer FROM e718197 WHERE varenummer > 5000 ORDER BY varenummer DESC 2) SELECT Min(varenummer) AS nextVarenummer FROM e718197 WHERE varenummer > 5000
For at kunne måle tiden, gentog jeg hver 10.000 gange. Disse målinger gentog jeg 5 gange. Databasen var en SQL Server 2005, den fulde version.
Denne gang vinder 1) altså over 2) i effektivitet ... og jeg skal da være den første til at indrømme at marginen er noget mere overbevisende denne gang.
Men, for lige at få det sidste ord ind, så ses det tydeligt at indekset også har hjulpet på effektiviteten for Min()-versionen. :^)
Tak for din indgangsvinkel. Jeg har desværre ikke haft mulighed for at teste mine teorier, så det er jo godt, at du har taget skraldet... ;-)
Jeg mener nu ikke på noget tidspunkt at have sagt, at eksistensen af et index ikke skulle effektivisere Min funktionen. Alene muligheden for at kunne placere cursoren på den søgte startrække som angivet i where sætningen, betyder jo, at Min funktionen kun behøver at behandle resten af rækkerne herfra, modsat situationen uden index, hvor den skal loope alle rækker igennem.
Faktisk kunne det dog være helt interessant, om ikke Min funktionen faktisk blev hurtigere uden index i en situation, hvor det søgte varenummer f.eks. blev sat til 1, således alt næsten alle rækker skulle behandles. I den situation ville alene det, at SQL skal bruge indexet under gennemløbet af de resterende rækker måske være en "bagdel". Under alle omstændigheder burde kløften mellem behandlingstiden for hhv. med og uden index være svundet en del ind i den situation.
Men konklusionen på dine tests må under alle omstændigheder være, at SQL server ikke er særlig "klog" mht. til at optimere en forespørgsel til at bruge indexet til andet end at placere cursoren. Den har jo helt tydeligt ikke indset, at da feltet Min funktionen bruges på er indexeret, er der ingen grund til at gennemløbe de resterende rækker, da den første værdi nødvendigvis må være den mindste.
Tror at jeg springer over points på denne her. Jeg har alligevel fået noget meget mere værdifuldt ud af spørgsmålet (viden). Men ellers tak for tilbudet. :^)
Ok - jamen i skal have mange tak for hjælpen ihvertfald....
God sommer
Synes godt om
Ny brugerNybegynder
Din løsning...
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.