SlideShare a Scribd company logo
1 of 79
Finjustering och optimering
      av SQL databaser




                 Krister Karlström
Administration och Design av Databassystem V2003

               Arcada Polytechnic
Finjustering och optimering av SQL databaser                                                                        02.04.2003
Krister Karlström
INNEHÅLLSFÖRTECKNING

1.   Introduktion till optimering.......................................................................................................... 5
  1.1.    Bakgrund .............................................................................................................................. 5
  1.2.    Finjustering och optimering ................................................................................................. 5
2. Enkla sökningar och operatorer ................................................................................................... 6
  2.1.    Begränsningar av kapitlet..................................................................................................... 6
  2.2.    Allmän optimering av villkor och sökningar ....................................................................... 6
  2.3.    Transformationer.................................................................................................................. 7
  2.4.    Datum................................................................................................................................... 8
  2.5.    Sammanslagning av flere konstanter ................................................................................... 9
  2.6.    Datatyper .............................................................................................................................. 9
  2.7.    AND..................................................................................................................................... 9
  2.8.    OR...................................................................................................................................... 10
  2.9.    AND plus OR..................................................................................................................... 10
  2.10.     NOT ............................................................................................................................... 11
  2.11.     IN ................................................................................................................................... 12
  2.12.     LIKE............................................................................................................................... 13
  2.13.     SIMILAR ....................................................................................................................... 14
  2.14.     UNION........................................................................................................................... 14
  2.15.     EXCEPT......................................................................................................................... 15
  2.16.     CASE ............................................................................................................................. 15
  2.17.     Syntax............................................................................................................................. 16
  2.18.     Datatypkonvertering (CAST)......................................................................................... 16
3. Sortering av data ........................................................................................................................ 17
  3.1.    Allmänt om sortering ......................................................................................................... 17
  3.2.    Sorteringseffektivitet.......................................................................................................... 17
  3.3.    Sorteringshastighet hos olika datatyper ............................................................................. 18
  3.4.    ORDER BY........................................................................................................................ 19
  3.5.    Att sortera eller att inte sortera........................................................................................... 19
  3.6.    Sorteringsnycklar ............................................................................................................... 20
  3.7.    Sortering med index........................................................................................................... 21
4. Gruppering av data..................................................................................................................... 22
  4.1.    GROUP BY ....................................................................................................................... 22
  4.2.    Optimal gruppering ............................................................................................................ 22
  4.3.    HAVING............................................................................................................................ 24
  4.4.    Alternativ till GROUP BY ................................................................................................. 24
  4.5.    Gruppering och sortering ................................................................................................... 25
  4.6.    Gruppering med index ....................................................................................................... 25
  4.7.    COUNT.............................................................................................................................. 26
  4.8.    SUM och AVG................................................................................................................... 26
  4.9.    MAX och MIN ................................................................................................................... 27
5. Joins (Kopplingar, Föreningar).................................................................................................. 28
  5.1.    Bakgrundsinformation om föreningar av tabeller .............................................................. 28
  5.2.    Nested-Loop Joins .............................................................................................................. 29
  5.3.    Sort-Merge Joins ................................................................................................................ 31
  5.4.    Hash Joins .......................................................................................................................... 32
  5.5.    Undvikning av Joins .......................................................................................................... 32
  5.6.    Gemensamma index för Joins ............................................................................................ 33
  5.7.    Sammansatta tabeller ......................................................................................................... 34
  5.8.    Kopplingar mellan tre eller flera tabeller ........................................................................... 35

                                                                                                                                     Sida 2 / 79
Finjustering och optimering av SQL databaser                                                                         02.04.2003
Krister Karlström
  5.9.    Outer Joins ......................................................................................................................... 35
6.   Underförfrågningar (subqueries)................................................................................................ 36
  6.1.    Bakgrund ............................................................................................................................ 36
  6.2.    Föreningar eller underförfrågningar - Joins eller Subqueries? .......................................... 37
  6.3.    IN och ANY....................................................................................................................... 38
  6.4.    Flattening ........................................................................................................................... 39
  6.5.    DISTINCT ......................................................................................................................... 40
  6.6.    EXISTS .............................................................................................................................. 40
  6.7.    TOP .................................................................................................................................... 40
7. Kolumner ................................................................................................................................... 41
  7.1.    Datatyper ............................................................................................................................ 41
  7.2.    Fixerad eller varierbar längd? ............................................................................................ 41
  7.3.    Ögonblicksinformation ...................................................................................................... 42
  7.4.    Heltal.................................................................................................................................. 42
  7.5.    Flyttal ................................................................................................................................. 43
  7.6.    Decimaltal.......................................................................................................................... 44
  7.7.    Serienummer ...................................................................................................................... 44
  7.8.    Bitar.................................................................................................................................... 45
  7.9.    Stora objekt (LOB)............................................................................................................. 45
  7.10.     NULL............................................................................................................................. 46
  7.11.     Kolumnernas ordningsföljd i en tabell........................................................................... 46
8. Tabeller ...................................................................................................................................... 47
  8.1.    Bakgrund ............................................................................................................................ 47
  8.2.    Lagringsarkitektur .............................................................................................................. 47
  8.3.    Datahögar (heaps) .............................................................................................................. 49
  8.4.    Migration............................................................................................................................ 49
  8.5.    Fragmentering .................................................................................................................... 51
  8.6.    Grupperade tabeller............................................................................................................ 51
  8.7.    Normalisering..................................................................................................................... 51
  8.8.    Vyer.................................................................................................................................... 52
9. Index........................................................................................................................................... 53
  9.1.    Bakgrund ............................................................................................................................ 53
  9.2.    Binära träd.......................................................................................................................... 54
  9.3.    Vanliga index..................................................................................................................... 55
  9.4.    Sammansatta index ............................................................................................................ 55
  9.5.    Heltäckande index.............................................................................................................. 57
  9.6.    Unika index........................................................................................................................ 57
  9.7.    Grupperade index............................................................................................................... 57
  9.8.    Bitmap index...................................................................................................................... 57
  9.9.    Övriga index....................................................................................................................... 57
  9.10.     Indexnycklar, trunkering och komprimering ................................................................. 57
10.     Begränsningar ........................................................................................................................ 57
  10.1.     Bakgrund ........................................................................................................................ 57
  10.2.     NOT NULL.................................................................................................................... 57
  10.3.     CHECK .......................................................................................................................... 57
  10.4.     FOREIGN KEY ............................................................................................................. 57
  10.5.     PRIMARY KEY ............................................................................................................ 57
  10.6.     UNIQUE ........................................................................................................................ 57
  10.7.     Triggningar..................................................................................................................... 57
  10.8.     Inaktivering av begränsningar........................................................................................ 57
  10.9.     Onödiga begränsningar i SELECT satser ...................................................................... 57

                                                                                                                                      Sida 3 / 79
Finjustering och optimering av SQL databaser                                                                          02.04.2003
Krister Karlström
11.     Lagrade Procedurer (Stored Procedures) ............................................................................... 57
  11.1.      Bakgrund ........................................................................................................................ 57
  11.2.      Fördelar med Lagrade Procedurer.................................................................................. 57
12.     Dataförändringar .................................................................................................................... 57
  12.1.      Bakgrund ........................................................................................................................ 57
  12.2.      Loggningar..................................................................................................................... 57
  12.3.      INSERT.......................................................................................................................... 57
  12.4.      UPDATE........................................................................................................................ 57
  12.5.      DELETE......................................................................................................................... 57
  12.6.      COMMIT och ROLLBACK.......................................................................................... 57
13.     Källor ..................................................................................................................................... 57




                                                                                                                                       Sida 4 / 79
Finjustering och optimering av SQL databaser                                           02.04.2003
Krister Karlström

1. Introduktion till optimering
   1.1. Bakgrund
        En dåligt designad databasprodukt kan orsaka fördröjningar för den enskilda användaren
        och även påverka andra applikationer som körs på samma dator eller i samma nätverk.

        Avsikten med denna föreläsning är att gå igenom de grundläggande detaljerna i
        optimering av SQL databaser samt att visa vilka fel och brister databasprogrammerare ofta
        gör och hur man kan förebygga dessa. Föreläsningen förutsätter grundläggande kunskap i
        SQL och kännedom om relationsdatabasmodellen. De exempel och eventuella
        rekommendationer som nämns i föreläsningen är främst SQL optimeringar och är inte
        specifika för någon viss databashanterare, om ej annat framgår.

   1.2. Finjustering och optimering
        Det är möjligt att skilja på begreppen finjustering och optimering i databassammanhang.
        Finjustering är vad som görs åt en databas (exempelvis justering av buffertstorlekar,
        uppdatering av index) medan optimering är vad som oftast görs i ett program (exempelvis
        justering av SQL förfrågningar, effektivering av tillgängliga resurser, omskrivning av
        programkod).

        Med ordet finjustering avses ofta i databassammanhang en hastighetsökning.
        Hastighet kan definieras på två olika sätt:

            − Svarstid: Den tid som löper sen dess att klienten skickat en förfrågan till
              databashanteraren tills klienten fått svaret.
            − Genomströmning: Antalet operationer och förfrågningar databashanteraren hinner
              utföra under en viss tidsenhet.

        I ett stort månganvändarsystem är det databashanterarens genomströmningshastighet av
        förfrågningar som utgör grunden för svarstiden till de enskilda klienterna. En god
        förhandsoptimering av alla SQL förfrågningar ökar genomströmningshastigheten i
        databashanteraren och minskar samtidigt svarstiden till klienterna.

        Oavsett vilken databashanterare som används så kan prestanda alltid höjas genom att alltid
        göra de rätta sakerna och utföra de korrekta förfrågningarna.

        ”In our experience (confirmed by many industry experts) 80% of the performance gains on SQL Server
        come from making improvements in SQL code, not from devising crafty configuration adjustments or
        tweaking the operating system.”
                                         - Kevin Kline et al., Transact-SQL Programming, O’Reilly & Associates

        “Experience shows that 80 to 90 per cent of all tuning is done at the application level, not the database
        level.”
                                                          - Thomas Kyte, Expert One on One: Oracle, Wrox Press




                                                                                                    Sida 5 / 79
Finjustering och optimering av SQL databaser                                 02.04.2003
Krister Karlström
        Optimering av prestanda är ett brett område. Det omfattar bland annat:

           − Att kunna bygga SQL satser utan att göra sånt som är vedertaget känt för att vara
             resurskrävande
           − Att förstå den fysiska strukturen och tillvägagångssättet i en typisk databas
           − Att kunna lösa de verkliga problemen istället för de imaginära problemen.


2. Enkla sökningar och operatorer
   2.1. Begränsningar av kapitlet
        I detta kapitel berättas hur man kan syntaxoptimera SQL satser. Många
        sökningsförfrågningar är det ingen idé att försöka optimera, eftersom det är endast vissa
        sökningsförfrågningar som har sådana parametrar som går att optimera. Fastän sökningar
        som består av flera tabeller med kopplingar (joins ) och underförfrågningar (subqueries) är
        de mest resurskrävande, tas de inte upp i det här kapitlet. För optimering av joins och
        subqueries finns det skilda kapitel, här tas endast enkla sökningar i en tabell upp.
        Dessutom behandlar detta kapitel endast sökvillkor med WHERE satsen. Satserna
        HAVING, IF och ON kommer senare.

        Kapitlet börjar med optimeringar som utförs på en mera generell nivå och i slutet av
        kapitlet finns specifika optimeringar för de vanligaste SQL kommandon som används.

        På grund av portabilitetsproblem så är en del av de lösningar och tips som presenteras i
        detta kapitel inte tillgängliga för alla databashanterare. Dessutom kan det finnas små
        variationer mellan olika plattformer.

   2.2. Allmän optimering av villkor och sökningar
        I denna del av kapitlet nämns sådana saker som man bör hålla i minnet när man skriver
        enkla sökningar.

        De bästa sökningarna är de som påverkar det minsta antalet rader i en tabell och med
        sökvillkor som har en låg kostnad. Med låg kostnad avses låg komplexitet, enkelt
        utförande och snabb exekvering.

        Tabellen nedan visar en typisk rangordning av sökvillkor (tabellen baserar sig på
        tillverkarnas egna manualer och är vedertagen känd i databassammanhang). Desto högr e
        poäng en operator eller en operand har, desto effektivare är de att använda i sökningar.

         Operator Poäng                     Operand                              Poäng
         =              10                  Ensam direkt given operand            10
         >              5                   Ensam kolumn                          5
         >=             5                   Ensam parameter                       5
         <              5                   Multioperand                          3
         <=             5                   Exakt numerisk datatyp                2
         LIKE           3                   Annan numerisk datatyp                1
         <>             0                   Temporär datatyp                      1
                                            Textbaserad datatyp                   0
                                            NULL                                  0

                                                                                         Sida 6 / 79
Finjustering och optimering av SQL databaser                                 02.04.2003
Krister Karlström
        Ur poängtabellen kan man läsa att den bästa och effektivaste sökningen skulle vara
        någonting i den här stilen:

             ... WHERE smallint_column = 12345

        Det här exemplet skulle enligt poängtabellen få hela 27 poäng!
        Poängen har beräknats enligt följande:

        −   5 poäng för att kolumnen (smallint_column) är ensam på vänster sida
        −   2 poäng för kolumnens datatyp (exakt numerisk datatyp)
        −   10 poäng för likhetsoperatorn (=)
        −   10 poäng för den direkta operanden (12345) på höger sida

        Här kommer ett annat exempel:

             ... WHERE char_column >= varchar_column || ‘x’

        Det här sökvillkoret ger enligt samma tabell endast 13 poäng.

        −   5 poäng för att kolumnen (char_column) är ensam på vänster sida
        −   0 poäng för den textbaserade datatypen (CHAR) på vänster sida
        −   5 poäng för operatorn större än eller lika med (>=)
        −   3 poäng för multioperand-uttrycket (varchar_column || ’x’) på höger sida
        −   0 poäng för den textbaserade datatypen (VARCHAR) på höger sida

        Den exakta poängen för alla operatorer och operander varierar något mellan olika
        tillverkare på databashanterare. Rangordningen är dock den samma för de flesta
        tillverkare.

        Med hjälp av dessa enkla kunskaper kan man bestämma sig för den mest optimala
        ordningen i uttrycken och om det lönar sig att byta ut en del av uttrycket mot något annat
        uttryck som gör samma sak. De flesta förfrågningar och sökningar kan skrivas på många
        olika sätt, men de får alla olika poäng. En del databashanterare kan själva optimera en
        dåligt skriven förfrågan innan den utförs, men om uttrycken är tämligen komplexa så kan
        det nog hända att databashanteraren inte klarar av att omforma uttrycket effektivare.

   2.3. Transformationer
        En SQL förfrågan kan alltså oftast skrivas om på många olika sätt utan att resultatet
        förändras. För detta finns det en formell regel som man bör följa:

        IF           (A <operator> B) IS TRUE AND (B <operator> C) IS TRUE
        THEN         (A <operator> C) IS TRUE AND NOT (A <operator> C) IS FALSE

        Som operator duger en av de följande jämförelseoperatorerna: =, >, >=, <, <=
        Dessa operatorer duger inte i transformationslagen: <> och LIKE

        Transformationslagen säger alltså att man kan byta ut B till C utan att påverka resultatet.
        Om en transformation innebär att en konstant flyttas kallas processen konstantöverföring.



                                                                                        Sida 7 / 79
Finjustering och optimering av SQL databaser                                   02.04.2003
Krister Karlström
        Följande två exempel ger samma resultat, men det senare exemplet ger en högre poäng
        eftersom en kolumn har bytts ut till en konstant.

        Uttryck #1
            ... WHERE column1 < column2
                 AND column2 = column3
                 AND column1 = 5

        Uttryck #2
            ... WHERE 5 < column2
                 AND column2 = column3
                 AND column1 = 5

        Uttryck ett och två är transformationer av varandra och ger således exakt samma resultat,
        men uttryck två utför en effektivare och snabbare sökning, i alla fall i några
        databashanterare. Som tidigare nämnts i detta kapitel, så kan de flesta databashanterare
        göra enkla transformationer som denna. Däremot så försöker inte de flesta
        databashanterare utföra transformationer om uttrycket innehåller en eller flera nivåer av
        parenteser samt nyckelorder NOT.

        Detta exempel kan vara ett långsamt uttryck:

             SELECT * FROM Table1
                  WHERE column1 = 5
                  AND NOT (column3 = 7 OR column1 = column2)

        Genom att tillämpa transformationsprinciperna uppstår detta uttryck:

             SELECT * FROM Table1
                  WHERE column1 = 5
                  AND column3 <> 7
                  AND column2 <> 5

        I de flesta databashanterare är det transformerade uttrycket betydligt snabbare. Det kan
        med andra ord ibland löna sig att själv tillämpa transformationslagen. I vissa fall kan det
        hända att konstantöverföringe n av ett flyttal inte medför någon ökning av prestandan i
        uttrycket, eftersom det på grund av avrundningar ibland är möjligt att en jämförelse,
        beroende på vad man jämför med, blir både större än och lika med samtidigt.

   2.4. Datum
        En SQL förfrågan som innehåller dagens datum kan göras snabbare genom att inte
        använda databashanterarens globala variabel för dagens datum. Jämför förfrågan ett och
        två nedan:

        Förfrågan #1
            SELECT * FROM Table1
                 WHERE date_column = CURRENT_DATE
                 AND amount * 5 > 100.00




                                                                                        Sida 8 / 79
Finjustering och optimering av SQL databaser                                  02.04.2003
Krister Karlström
        Förfrågan #2
            SELECT * FROM Table1
                 WHERE date_column = DATE ’2003-03-26’
                 AND amount * 5 > 100.00

        Denna optimering kräver att förfrågan skrivs om varje dag, och är således endast
        användbar om förfrågan genereras dynamiskt av ett applikationsgränssnitt.

   2.5. Sammanslagning av flere konstanter
        Vid sammanslagning av flere konstanter i samma uttryck kan konstantöverföring uppstå.
        Därför är sammanslagning av konstanter en lönsam operation. Det enkla uttrycket

             ... WHERE a – 3 = 5

        skall alltså hellre skrivas som

             ... WHERE a = 8                        /* a – 3 = 5         ->    a = 5 + 3 */

        för att uppnå en högre poäng.

   2.6. Datatyper
        I en 32-bitars processor fungerar aritmetiken snabbast om processorn kan jobba med 32-
        bitars ord. Därför är datatypen INTEGER (32-bitar brett ord med tecken) den snabbaste
        datatypen som kan användas av databashanterare vid jämföringar och aritmetiska uttryck.
        Datatyperna SMALLINT, DECIMAL och FLOAT är alltså i de flesta databashanterare
        långsammare eftersom databashanteraren inte kan arbeta med full ordlängd på 32-bitar.

   2.7. AND
        När SQL uttrycket endast innehåller lika med operatorer så kommer de flesta
        databashanterare att utvärdera villkoren i den ordning de ges i SQL satsen.
        Databashanteraren kommer med andra ord att ställa upp alla villkor i en lista som den
        därefter går igenom från vänster till höger. Noteras bör att det inte finns några regler som
        säger att så här måste en databashanterare göra – de gör bara på det viset (undantag är
        Oracle som utvärderar villkoren från höger till vänster om den kostnadsbaserade
        förfrågningsoptimeraren är aktiv). Detta beteende kan utnyttjas genom att först ge det
        villkor som har minst sannolikhet att uppfyllas. Om alla villkor har samma sannolikhet
        sätts det minst komplexa villkoret först. Om uttrycket som blivit satt längst till vänster är
        falskt behövs således inga flera testningar av de övriga villkoren eftersom
        databashanteraren vet redan i det här skedet att slutresultatet blir falskt – oavsett om de
        övriga villkoren är falska eller sanna.

        Exempeluttrycket

             ... WHERE column1 = ’A’ AND column2 = ’B’

        omformas till

             ... WHERE column2 = ‘B’ AND column1 = ‘A’

        om det är mindre sannolikt att column2 är lika med ’B’ än att column1 är lika med ’A’.

                                                                                          Sida 9 / 79
Finjustering och optimering av SQL databaser                                02.04.2003
Krister Karlström
   2.8. OR
        När man skriver SQL satser med OR sätts det villkor som har störst sannolikhet att
        uppfyllas f rst. Detta är den exakta motsatsen till de givna råden för AND operationen
                   ö
        eftersom OR operationen ger upphov till att flera tester utförs om det första villkoret är
        falskt, medan AND orsakar flera tester om första villkoret är sant.

        Exempeluttrycket

             ... WHERE column2 = ’B’ OR column1 = ’A’

        omformas till

             ... WHERE column1 = ‘A’ OR column2 = ‘B’

        om det är mera sannolikt att column1 är lika med ’A’ än att column2 är lika med ’B’.

        OR operationer är även snabbare om villkoren omfattar så få kolumner som möjligt
        eftersom det minskar på databashanterarens behov att slå upp i indextabeller. Därför bör
        villkor i en längre SQL sats bestående av flera OR operationer som berör samma kolumn
        komma efter varandra. Uttryck ett blir effektivare om det omformas till uttryck två i
        nedanstående exempel:

        Uttryck #1
            ... WHERE column1 = 1
                 OR column2 = 3
                 OR column1 = 2

        Uttryck #2
            ... WHERE column1 = 1
                 OR column1 = 2
                 OR column2 = 3

   2.9. AND plus OR
        Den distributiva lagen konstaterar följande:

        A AND (B OR C) = (A AND B) OR (A AND C)

        Antag att en SQL förfrågan behöver utföras som består av både AND och OR villkor.
        Förfrågningen är formulerad på sånt sätt att AND operationerna måste utföras före OR
        operationen kan utföras:

             SELECT * FROM Table1
                  WHERE (column1 = 1 AND column2 = ’A’)
                  OR (column1 = 1 AND column2 = ‘B’)




                                                                                      Sida 10 / 79
Finjustering och optimering av SQL databaser                                  02.04.2003
Krister Karlström
        SQL förfrågningen görs mot följande tabell:

             Rad #      column1        column2
               1            3             A
               2            2             B
               3            1             C

        När databashanteraren gör sökningar som slås upp i index i den ordningen de nämns i
        SQL förfrågningen, kan det hända att den behöver gå igenom alla följande steg:

        1.   Indexsökning: column1 = 1. Resultat = {rad #3}
        2.   Indexsökning: column2 = ’A’. Resultat = {rad #1}
        3.   AND operationen körs för att förena de bägge delresultaten. Resultat = { }
        4.   Indexsökning: column1 = 1. Resultat = {rad #3}
        5.   Indexsökning: column2 = ‘B’. Resultat = {rad # 2}
        6.   AND operationen körs för att förena de bägge delresultaten. Resultat = { }
        7.   OR operationen körs för att förena resultaten av bägge AND operationerna. Resultat =
             {}

        Genom att tillämpa den distributiva lagen baklänges erhålls följande SQL förfrågan:

              SELECT * FROM Table1
                   WHERE column1 = 1
                   AND (column2 = ’A’ OR column2 = ’B’)

        När databashanteraren gör indexbaserade sökningar enligt det senare uttrycket kan det
        hända att endast följand e steg behövs:

        1.   Indexsökning: column2 = ’A’. Resultat = {rad #1}
        2.   Indexsökning: column2 = ’B’. Resultat = {rad #2}
        3.   OR operationen körs för att förena de bägge delresultaten. Resultat = {rad #1, rad #2}
        4.   Indexsökning: column1 = 1. Resultat = {rad #3}
        5.   AND operationen körs för att förena delresultaten. Resultat = { }

        Två delmoment kan alltså inbesparas genom att tillämpa den distributiva lagen baklänges.
        En del av de vanligaste databashanterarna klarar av att göra det här själva, men det skadar
        aldrig att färdigt optimera förfrågningarna.

        Den här optimeringen gäller inte om det finns kopplingar med i förfrågningen.

   2.10. NOT
        Det är alltid bekvämt att skriva om NOT till något mera läsligare och lättförståeligare. Den
        mänskliga hjärnan har som tendens att bli råddig om uttrycket innehåller allt för mycket
        inverteringar av diverse delresultat. Ett enkelt uttryck kan förenklas genom att t.ex. byta
        riktning på jämförelseoperatorn:

              ... WHERE NOT (column1 > 5)

        transformeras enkelt till

              ... WHERE column1 <= 5

                                                                                        Sida 11 / 79
Finjustering och optimering av SQL databaser                                  02.04.2003
Krister Karlström
        Ett mera komplicerat uttryck kräver försiktighet vid transformeringen. DeMorgan’s
        teorem kan tillämpas för att omforma uttryck med NOT.

        DeMorgan’s teorem består av två regler:

        NOT (A AND B) = (NOT A) OR (NOT B)
        NOT (A OR B) = (NOT A) AND (NOT B)

        Om DeMorgan’s teorem tillämpas på följande exempel

              ... WHERE NOT (column1 > 5 OR column2 = 7)

        uppstår följande uttryck:

              ... WHERE column1 <= 5
                   AND column2 <> 7

        Den uppmärksamme kommer nu ihåg att inte lika med operatorn bör undvikas och i de
        flesta fall skulle en sådan här transformering ha en negativ effekt. På längre sikt, i vilken
        som helst godtycklig mängd av spridda värden och när det är fler än två rader inblandade,
        tar de förluster som uppstår av inte lika med operatorn ändå ut de vinster som erhålls a v
        lika med operatorerna. På grund av detta så använder inte vissa databashanterare
        indextabeller för att utföra jämförelser med inte lika med operatorn, men däremot
        använder de nog indextabeller för att utföra jämförelser med mindre än och större än
        operatorerna. Därför lönar sig följande transformation:

              ... WHERE NOT (bloodtype = ’O’)

        Transformeras till:

              ... WHERE bloodtype < ’O’
                   OR bloodtype > ’O’

   2.11. IN
        Många tror att det inte är någon skillnad på dessa två uttryck:

        Uttryck #1
            ... WHERE column1 = 5
                 OR column1 = 6

        Uttryck #2
            ... WHERE column1 IN (5, 6)

        Dessa personer har en aningen fel. Hos några databashanterare är IN snabbare än OR. Det
        lönar sig alltså alltid att transformera OR till IN om möjligt. De databashanterare där IN
        inte är snabbare än OR kommer ändå att transformera tillbaka uttrycket till OR, så inget
        går förlorat på att använda IN.




                                                                                        Sida 12 / 79
Finjustering och optimering av SQL databaser                                02.04.2003
Krister Karlström
        När en IN operator har en större mängd heltal (integers) som parametrar så är det klokare
        att fråga ”vad som är ute” än ”vad som är inne”. Därför bör uttrycket:

             ... WHERE column1 IN (1, 3, 4, 5)

        transformeras till det något effektivare

             ... WHERE column1 BETWEEN 1 AND 5
                  AND column1 <> 2

   2.12. LIKE
        De flesta databashanterare använder index för att leta efter ett LIKE mönster om mönstret
        börjar med ett konkret tecken, men kommer att undvika index om mönstret börjar med ett
        jokertecken (antingen % eller _ ). Till exempel, om förfrågningen har ett sådant här
        villkor:

             ... WHERE column1 LIKE ’C_F%’

        kommer databashanteraren att försöka leta efter matchande data genom att första leta efter
        alla indexnycklar som börjar med ett ‘C’. Därefter filtreras sådana nycklar bort som inte
        har ett ’F’ i position tre. Det finns med andra ord inga optimeringstips för en sådan här
        sökning.

        I vissa specialfall kan LIKE bytas ut mot en vanlig lika med operator. Exempelvis så är
        uttryck ett utbytbart mot uttryck två :

        Uttryck #1
            ... WHERE column1 LIKE ’ABC’

        Uttryck #2
            ... WHERE column1 = ’ABC’

        Det finns en liten fallgrop här, de är nämligen inte exakt samma uttryck. I standard SQL
        beaktar LIKE operatorn inledande tomrum medan lika med operatorn ignorerar inledande
        extra tomrum. Dessutom kan det hända att LIKE och lika med operatorn inte använder sig
        av samma teckentabell (collation).

        Om en kolumn endast är två eller tre tecken bred kan det vara frestande att använda
        SUBSTRING istället för LIKE. Hur som helst, så är SUBSTRING ett klassiskt exempel
        på en funktion som inte är effektiv om den får en kolumn som parameter. LIKE operatorn
        kommer alltid att klå multipla SUBSTRING funktioner. Uttryck ett skall med andra ord
        alltid omformas såsom uttryck två lyder:

        Uttryck #1
            ... WHERE SUBSTRING(column1 FROM 1 FOR 1) = ‘F’
                 OR SUBSTRING(column1 FROM 2 FOR 1) = ‘F’
                 OR SUBSTRING(column1 FROM 3 FOR 1) = ‘F’

        Uttryck #2
            ... WHERE column1 LIKE ‘%F%’


                                                                                      Sida 13 / 79
Finjustering och optimering av SQL databaser                                   02.04.2003
Krister Karlström
        Inom en mycket snar framtid så kommer vissa sökningar som idag görs med LIKE att bli
        onödiga, eftersom fulltextindex blir allt vanligare.

   2.13. SIMILAR
        Operatorn SIMILAR introducerades i SQL3 (SQL:1999). Operatorns syntax och agerande
        påminner en aning om grep kommandot i Unix. I vissa fall kan användningen av
        SIMILAR istället för LIKE ge en prestandaökning, SIMILAR är i alla fall inte
        långsammare än LIKE och därför rekommenderas det ofta att använda SIMILAR istället
        för LIKE. Exemplet nedan visar ett fall där SIMILAR avsevärt förbättrar förfrågningens
        prestanda.

        Uttryck #1
            .. WHERE column1 = ’A’
                 OR column1 = ’B’
                 OR column1 = ’K’

        Uttryck #2
            ... WHERE column1 SIMILAR TO ’[ABK]’

   2.14. UNION
        En UNION är i SQL ett sätt att förena två resultat till ett resultat. UNION operatorn tar
        hand om att inga dubbla rader uppstår i resultatet även om föreningen består av två
        sökningar från samma tabell. På grund av detta så är UNION en populär operator vid
        förening av data från flera olika sökningar. Men är operatorn verkligen det bästa sättet att
        göra detta på?
        Ta följande två exempel:

        Uttryck #1
            SELECT * FROM Table1
                 WHERE column1 = 5
            UNION
            SELECT * FROM Table1
                 WHERE column2 = 5

        Uttryck #2
            SELECT DISTINCT * FROM Table1
                 WHERE column1 = 5
                 OR column2 = 5

        Bägge uttrycken ger upphov till samma slutresultat. Uttryck två kommer i så gott som alla
        fall att galant slå uttryck ett. Orsaken till detta varierar något mellan olika
        databashanterare, men den ligger alltid i databashanterarnas automatiska
        förfrågningsoptimerare.

        Ena orsaken är att de flesta optimerare endast klarar av att optimera inom samma WHERE
        sats. Operatorn UNION i uttryck ett kommer därför att separera de två WHERE satserna
        från varandra, vilket leder till att databashanteraren först skannar column1 för värden som
        är lika med fem och därefter skannar column2 för värden som är lika med fem.
        Databashanteraren hamnar alltså att utföra dubbla skanningar! (Med skanning avses en
        sökning genom hela tabellen, rad för rad.) Därför borde uttryck ett ta dubbelt så lång tid att


                                                                                         Sida 14 / 79
Finjustering och optimering av SQL databaser                                 02.04.2003
Krister Karlström
        utföra än uttryck två, om kolumnerna inte är indexerade. Om kolumnerna är indexerade så
        klarar de flesta databashanterare av att gottgöra denna prestandaförlust.

        Andra orsaken, som delvis talar för UNION operatorns fördel, är att några optimerare
        totalt kommer att vägra använda index om WHERE satsen innehåller OR. Detta försämrar
        dock inte prestandan mera än vad som vinns på att inte använda UNION.

        Rådet lyder alltså: Använd hellre OR istället för UNION, speciellt när kolumnerna inte är
        indexerade.

   2.15. EXCEPT
        Vilket som helst uttryck i stilen A AND NOT B kan omformas till ett uttryck med
        EXCEPT, och tvärt om. I likhet med UNION så kommer EXCEPT att ge upphov till
        dubbla skanningar av en tabell eller flere. Dessutom är det väldigt få av de stora
        databashanterarna som överhuvudtaget stöder EXCEPT, eftersom operatorn har så dålig
        prestanda. Uttrycken nedan ger bägge samma resultat, uttryck ett rekommenderas.

        Uttryck #1
            SELECT * FROM Table1
                 WHERE column1 = 7
                 AND NOT column2 = 8

        Uttryck #2
            SELECT * FROM Table1
                 WHERE column1 = 7
            EXCEPT
            SELECT * FROM Table1
                 WHERE column2 = 8

   2.16. CASE
        Om en förfrågan innehåller mer än ett långsamt funktionsanrop så kan CASE användas för
        att undvika att funktionsanropet händer två eller flera gånger. Uttryck två nedan visar hur
        dubbla funktionsanrop kan undvikas med CASE:

        Uttryck #1
            ... WHERE slow_function(column1) = 3
                 OR slow_function(column1) = 5

        Uttryck #2
            ... WHERE 1 =
                 CASE slow_function(column1)
                      WHEN 3 THEN 1
                      WHEN 5 THEN 1
                 END




                                                                                       Sida 15 / 79
Finjustering och optimering av SQL databaser                                 02.04.2003
Krister Karlström

   2.17. Syntax
        Det är även viktigt att hålla en konsekvent stil när man skriver SQL förfrågningar. Ta
        följande fyra uttryck som exempel:

        SELECT column1*4 FROM Table1 WHERE COLUMN1 = COLUMN2 + 7
        SELECT Column1 * 4 FROM Table1 WHERE column1=(column2 + 7)

        Istället för att exekvera dessa två ovanstående uttryck bör man istället använda sig av
        dessa uttryck:

        SELECT column1 * 4 FROM Table1 WHERE column1 = column2 + 7
        SELECT column1 * 4 FROM Table1 WHERE column1 = column2 + 7

        Fastän alla dessa uttryck ger samma resultat, så kommer ibland det sista uttrycket att köra
        snabbare. Detta beror på att vissa databashanterare sparar vad som kan kallas
        förkompileringar av tidigare körda förfrågningar i en buffert, ibland även riktiga resultat
        om datan ännu inte har ändrats sen den senaste förfrågningen. En grundförutsättning för
        att denna buffert skall kunna utnyttjas är att förfrågningen ser exakt likadan ut som den
        tidigare, inklusive alla mellanslag och stora och små bokstäver.

        Förutom att förfrågningarna blir lättare att läsa och förstå, så kan alltså en konsekvent
        skrivstil även bidra till att höja förfrågningens prestanda. Här kommer några tips för en
        läsbar och entydig syntax:

           − Nyckelord med stora bokstäver men kolumner med små bokstäver
           − Tabellnamn har stor första bokstav
           − Enkla mellanslag mellan varje ord och runtom varje aritmetisk operator

   2.18. Datatypkonvertering (CAST)
        Ibland hamnar man ut för att behöva konvertera en datatyp till en annan. Microsoft påstår i
        sin online dokumentation över SQL Server 2000 att man aldrig skall använda CAST
        funktioner om de verkligen inte är nödvändiga. Dessutom avråder de från att skriva ett
        uttryck som har samma kolumn på bägge sidorna om operatorn om den ena sidan
        innehåller en datakonvertering med CAST och kolumnen ingår som parameter.

        Antag att vi har en kolumn innehållande priser, skapad som DECIMAL(7, 2) (7 positioner
        för hela tal och två decimaler), och ett svar sökes på frågan ”Vilka priser är jämna
        priser?”.

        Nedan finns tre olika lösningar till detta problem:

        Uttryck #1
            ... WHERE MOD(decimal_column, 1) = 0

        Uttryck #2
            ... WHERE CAST(decimal_column AS CHAR(7))
                 LIKE ’%.00%’




                                                                                       Sida 16 / 79
Finjustering och optimering av SQL databaser                                  02.04.2003
Krister Karlström
        Uttryck #3
            ... WHERE decimal_column =
                 CAST(decimal_column AS INTEGER)

        Så vilket uttryck är det bästa? Om den kunskap som tidigare presenterats i detta kapitel
        tillämpas kan följande konstateras:

        Uttryck ett är definitivt det sämsta alternativet. Fastän det inte finns någon CAST
        operation så innehåller uttrycket en underförstådd och oundviklig datakonvertering från
        DECIMAL till INTEGER, eftersom MOD operatorn enbart fungerar med INTEGER.
        Uttrycket innehåller även en underförstådd division, i det här fallet division med ett.

        Uttryck två är det näst bästa uttrycket. Vissa databashanterare lagrar datatypen DECIMAL
        fysiskt som CHAR, vilket gör datakonverteringen vä ldigt enkel. Nackdelen är att LIKE
        operatorn börjar med ett jokertecken, vilket gör den mindre effektiv.

        Uttryck tre är den klara vinnaren, även om Microsoft troligtvis skulle hävda något annat.
        Om man jämför alla tre uttrycken och beräknar deras poäng enligt tabellen i stycke 2.2 så
        kommer uttryck tre att få den högsta prestandapoängen. Den höga poängen uppnås genom
        att uttrycket använder sig av lika med operatorn och har de enklaste operanderna.


3. Sortering av data
   3.1. Allmänt om sortering
        Sortering av data är en av de vanligaste förekommande funktionerna som behövs i alla
        sammanhang. Trots att en mängd olika algoritmer, den ena bättre än den andra, har
        utvecklats är sortering än idag en resurs- och tidskrävande process. Man bör med andra
        ord verkligen vara på det säkra med att sortering verkligen behövs innan man slänger in en
        sorteringssats i SQL. Sortering av stora resultatmängder kräver enorma mängder
        minnesutrymme och cpu kraft, vilket leder till att även andra användare av databasservern
        känner av sorteringens bieffekter.

        Ibland inträffar sortering i databashanteraren utan att man kanske tänker på det eller över
        huvudtaget vet om det. SQL funktionerna GROUP BY, DISTINCT, CREATE [UNIQUE]
        INDEX och UNION kan kalla på samma sorteringsalgoritmer som används av ORDER
        BY. Mera sällsynt är de fall där databashanteraren väljer att sortera två listor när den
        behöver utföra en inner join eller outer join, men enstaka fall finns. I dessa fall är
        sorteringen endast en bieffekt, men det här kapitlet handlar främst om sorteringen som
        händer vid ORDER BY.

   3.2. Sorteringseffektivitet
        Sortering har varit ett populärt ämne för forskning i flera decennier. Det är inte svårt att
        hitta en lämplig sorteringsalgoritm för datan som behöver sorteras. Även
        databashanterarna besitter en större repertoar över tillgängliga sorteringsalgoritmer.

        Det som påverkar sorteringseffektiviteten i en databashanterare är ganska långt hur
        komplext sorteringsvillkoret är och hur bred den data är som skall sorteras. Sist och
        slutligen så påverkar den osorterade datans ordning ganska så lite hur snabbt sorteringen
        blir klar.

                                                                                        Sida 17 / 79
Finjustering och optimering av SQL databaser                                  02.04.2003
Krister Karlström
        Det är först och främst tre saker som påverkar sorteringens hastighet. Nedan är de nämnda
        i viktighetsordning.

        1. Antalet rader sorteringen omfattar
        2. Antalet kolumner som nämns i ORDER BY satsen
        3. Databredden på de kolumner som nämns i ORDER BY satsen

        Det lönar sig alltså att försöka omfatta så få rader som möjligt när man sorterar. Generellt
        kan man säga att om antalet rader i sorteringen ökar tiofalt så ökar sorteringstiden
        tjugofalt.

        En ökning av antalet kolumner i ORDER BY satsen ökar behovet av sorteringstid mera än
        vad motsvarande ökning i kolumnens databredd gör. Det lönar sig att slå ihop två
        kolumner före sorteringen, om det bara är möjligt, för att undvika två kolumner i ORDER
        BY satsen.

        Sorteringskolumnens databredd påverkar även sorteringstiden. Om det är möjligt kan man
        använda exempelvis SUBSTRING för att minska databredden på det data som skall
        sorteras. Det hjälper även om några av de första tecknen är unika sinsemellan eller om det
        redan finns någon form av ordning bland den osorterade datan.

        Genom att lägga till flera processorer och mera minne i databasservern kan man försnabba
        sorteringarna. De flesta sorteringsalgoritmer och databashanterare klarar nämligen av att
        utnyttja flera processorer och kan dela upp sorteringen i flera parallella trådar. Om
        dessutom centralminnet är tillräckligt stort så att all data ryms i minnet går det ännu
        snabbare.

   3.3. Sorteringshastighet hos olika datatyper
        VARCHAR

        Det är alltid den definierade databredd som bestämmer hur lång tid det tar att sortera. En
        kolumn med varierbar databredd, såsom VARCHAR, har alltid en definierad bredd och en
        aktuell bredd. Om en VARCHAR(30) kolumn innehåller strängen ’ABC’ så är dess
        definierade bredd 30 tecken medan den aktuella bredden är tre tecken. Vid sortering så är
        det den definierade bredden på 30 tecken som bestämmer sorteringstiden. Detta beror
        troligen på att de flesta databashanterare redan på förhand allokerar det minnesutrymme
        sorteringen behöver. Minnet allokeras enligt det största möjliga behovet, vilket i det här
        fallet kan vara 30 tecken per rad.

        SMALLINT

        I Windows miljö, där en INTEGER är 32-bitar och en SMALLINT är 16-bitar, kunde man
        kanske tro att sortering av 16-bitars ord skulle vara snabbare än sortering av 32-bitars ord.
        Eftersom processorns ordstorlek i de flesta fall är 32-bitar, och databashanteraren kan
        utnyttja fulla bredden, är det vanligen snabbare att jämföra integers.




                                                                                        Sida 18 / 79
Finjustering och optimering av SQL databaser                                            02.04.2003
Krister Karlström
        CHAR

        Datatypen CHAR är som bekant 8-bitar. Om man skapar en kolumn som CHAR(4) så blir
        den totala bredden 4 x 8 = 32-bitar. INTEGER är också 32-bitar. Går det då lika snabbt att
        sortera en CHAR(4) kolumn som en INTEGER kolumn? Tyvärr inte, åtminstone inte i de
        flesta fall och hos de flesta databashanterare. Orsaken torde ligga i att en teckensträng inte
        kan jämföras 4 byte i gången. Däremot går det bra att jämföra 4 byte i gången när man
        jämför med INTEGER.

        INTEGER

        Den stora vinnaren är alltså datatypen INTEGER. Den sorteras alltid snabbast och passar
        perfekt in i 32-bitars arkitektur. När dessutom databashanteraren har sina knep för att
        utnyttja full databredd så är datatypen oslagbar i prestanda. Däremot tar datatypen fysiskt
        upp mera plats än 16-bitars SMALLINT och 8-bitars CHAR och vill man optimera
        databasens fysiska storlek skall man givetvis alltid välja så liten datatyp som möjligt.

   3.4. ORDER BY
        Syntaxen för en ORDER BY sats är följande:

              SELECT <column list>
                   FROM <Table list>
                   ORDER BY <column expression> [ASC | DESC] [,...]

        I SQL-92 var ORDER BY lite mera begränsad än vad den är idag i SQL:1999. I SQL-92
        var man tvungen att ha ett kolumnnamn som dessutom måste ingå i SELECT listan. I
        SQL:1999 är det tillåtet att ha ett helt uttryck som operand till ORDER BY. För att
        exempelvis sortera nummer i fallande ordning finns det idag två olika alternativ att göra
        detta på:

        SQL-92
            SELECT numeric_column, numeric_column * -1 AS num
                 FROM Table1
                 ORDER BY num

        SQL:1999
            SELECT numeric_column
                 FROM Table1
                 ORDER BY numeric_column * -1

        Resultatet av ORDER BY numeric_column * -1 kan särskilja sig en aning från
        resultatet av ORDER BY numeric_column DESC. Skillnaden beror på om NULL
        värden existerar och hur databashanteraren behandlar dessa.

   3.5. Att sortera eller att inte sortera
        ”Do not use ORDER BY if the query has a DISTINCT or GROUP BY on the same set of terms, because
        they have the side effect of ordering rows.”
                                             - Kevin Kline et al., Transact-SQL Programming, O’Reilly & Associates

        Det är ingen idé att sortera data om den redan är sorterad. Det finns en mängd situationer
        där så är fallet, där sådana operationer används vilka har som bieffekt att de sorterar data.

                                                                                                    Sida 19 / 79
Finjustering och optimering av SQL databaser                                  02.04.2003
Krister Karlström
        Åtminstone följande satser returnerar data i sorterad ordning utan att innehålla en ORDER
        BY sats:

        − SELECT column1 FROM Table1
          Returnerar sorterad data om Table1 är grupperad (clustered) och column1 är
          grupperingsnyckeln (cluster key) eller om tabellen annars råkar vara färdigt sorterad.

        − SELECT column1 FROM Table1 WHERE column1 > -32768
          Returnerar sorterad data om column1 är indexerad i stigande ordning och
          databashanteraren använder index.

        − SELECT DISTINCT column1 FROM Table1
          Returnerar sorterad data om column1 inte är UNIQUE.

        Om ORDER BY fogas till någon utav dessa SQL förfrågningar blir resultatet endast en
        försämring i prestanda. Databashanterarna tar heller inte automatiskt bort onödiga
        ORDER BY satser.

   3.6. Sorteringsnycklar
        Utelämnande av ORDER BY satsen och påtvingande av sortering via teckentabeller och
        ordlistor är två olika metoder som kan användas för att snabba upp en sortering. Bägge
        alternativen är sådana lösningar som kanske inte stöds av alla databashanterare.

        Det finns tre saker man kan göra som försnabbar sortering utan att kräva speciellt stöd från
        databashanteraren: sorteringsnycklar, påtvingande av index och försortering.

        Om man behöver sortera data enligt exotiska teckenuppsättningar, eller annars bara
        behöver en djup sortering, sjunker sorteringshastigheten drastiskt. Bägge dessa problem är
        lösbara – addera en kolumn som innehåller sorteringsnycklar till tabellen.

        En sorteringsnyckel är en teckensträng bestående av en sekvens av en-bytes nummer.
        Sorteringsnyckeln representerar den relativa ordningsföljden mellan tecknen.

        Det finns en färdig funktion i MS Windows NT API för att konvertera vilken teckensträng
        som helst till en användbar sorteringsnyckel. Funktionen heter LCMapString. Funktionens
        indata kan vara vilka bokstäver som helst i någon av de teckenuppsättningar som stöds av
        Windows NT och funktionens utdata är en sorteringsnyckel automatiskt viktad enligt de
        lokaliserade inställningar som gjorts i systemet.




                                                                                        Sida 20 / 79
Finjustering och optimering av SQL databaser                                     02.04.2003
Krister Karlström
        Nedan finns ett skript för att skapa sorteringsnycklar för varje rad i en tabell.

             locale_id = an ID representing country/collation/etc.
             ...
             DECLARE Cursor1 CURSOR FOR
                  SELECT character_column FROM Table1;

             OPEN Cursor1;

             for(;;) {
                  FETCH Cursor1 INTO :character_string;
                  if(NO_DATA_FOUND)
                       break;
                  LCMapString(locale_id, character_string, sort_key);

             UPDATE Table1
                  SET sort_key_column = :sort_key
                  WHERE CURRENT OF Cursor1;
             }
             ...

        För att skriptet skall fungera bör en kolumn sort_key_column ha skapats på förhand
        med förhandsinställd binär sortering. Eftersom kolumnen använder sig utav binär sortering
        går det alltså lika snabbt att sortera teckensträngarna som med vilken binär sortering som
        helst, vilket är mycket snabbare än någon ordlistbaserad sortering. När tabellen innehåller
        en kolumn med sorteringsnycklar kan teckensträngarna hämtas i sorterad ordning med
        följande förfrågan:

             SELECT * FROM Table1
                  ORDER BY sort_key_column

   3.7. Sortering med index
        Sorteringen kan snabbas upp om man kan uppmuntra databashanteraren att utnyttja
        eventuella index. Nyckelordet WHERE orsakar i de flesta databashanterare ett försök att
        hitta posten i ett index. Uttryck två kan vara snabbare än uttryck ett, om coulumn1 är
        indexerad:

        Uttryck #1
            SELECT * FROM Table1
                 ORDER BY column1

        Uttryck #2
            SELECT * FROM Table1
                 WHERE column1 >= ’’
                 ORDER BY column1




                                                                                            Sida 21 / 79
Finjustering och optimering av SQL databaser                                   02.04.2003
Krister Karlström
        I kapitel 3.5 konstaterades att en sökning med WHERE returnerar resultatet i sorterad
        ordning om kolumnen är indexerad. Detta kan dock inte tas för en självklarhet och
        ORDER BY kan därför inte lämnas bort. Hur som helst så blir hela sorteringen snabbare
        eftersom ORDER BY utförs snabbare om raderna redan är något så när sorterade. Ett par
        saker är värt att noteras om detta trick:

        − ORDER BY satsen kan inte utelämnas eftersom det inte finns några garantier för att
          databashanterare faktiskt använder indexsökningar. WHERE satsen skall närmast ses
          som en uppmuntran till användning av index.
        − WHERE satsen orsakar att alla NULL värden filtreras bort vilket kanske inte är
          ändamålet alltid.


4. Gruppering av data
   4.1. GROUP BY
        Eftersom GROUP BY kan orsaka extra sorteringar så stämmer en stor del av de saker som
        nämndes for ORDER BY i föregående kapitel även för GROUP BY. Det lönar sig med
        andra ord att så långt som möjligt undvika GROUP BY och om de används bör de vara
        uppbyggda så enkla som möjligt.

        GROUP BY kan direkt användas i SQL förfrågningar:

             SELECT column1 FROM Table1
                  GROUP BY column1

        Gruppering inträffar ibland även underförstått utan att själva nyckelordet GROUP BY
        finns med i klartext. För exempel, HAVING och dataihopsamlingsfunktioner såsom AVG,
        COUNT, MAX, MIN, SUM och andra ger upphov till gruppering av data:

             SELECT COUNT(*) FROM Table1
                  HAVING COUNT(*) = 5

        Vissa databashanterare stöder extra parametrar och uttryck till GROUP BY och en del kan
        ha extra inbyggda funktioner för dataanalys, till exempel STDEV för standardavvikelse.

   4.2. Optimal gruppering
        Precis som för ORDER BY så blir GROUP BY långsammare för varje extra kolumn som
        läggs till i listan över grupperingskolumner. Grupperingslistan kan hållas så kort som
        möjligt genom att se till att inga överflödiga kolumner sätts till. Följande exempel visar ett
        fall där en överflödig kolumn finns i grupperingslistan:

        SELECT secondary_key_column, primary_key_column, COUNT(*)
            FROM Table1
            GROUP BY secondary_key_column, primary_key_column




                                                                                         Sida 22 / 79
Finjustering och optimering av SQL databaser                                  02.04.2003
Krister Karlström
        Eftersom kolumnen innehållande primärnyckeln är unik och inte kan innehålla NULL
        värden, är secondary_key_column i grupperingslistan överflödig och kunde
        uteslutas. Problemet är att om secondary_key_column tas bort ut grupperingslistan
        genereras ett felmeddelande. Alla databashanterare (förutom MySQL och Sybase)
        kommer att vägra ha secondary_key_column i selectlistan om den inte är med i
        grupperingslistan. En lösning, som är snabbare, kunde vara följande:

        SELECT MIN(secondary_key_column), primary_key_column,
            COUNT(*)
            FROM Table1
            GROUP BY primary_key_column

        GROUP BY har som tendens att resultera i ett färre antal rader och JOIN tenderar att
        utöka antalet rader i ett resultat. Den korrekta ordningen vore alltså att första utföra alla
        grupperingsfunktioner (GROUP BY) innan föreningsfunktionerna (JOIN) utförs.
        Databashanteraren är tvungen att första utvärdera resultaten från FROM och WHERE
        innan grupperingsfunktioner kan inledas, vilket gör det svårt att utnyttja detta tänkande.

        I vissa fall är det möjligt att påverka ordningsföljden och således optimera förfrågningen.
        Om en databashanterare med stöd för mängdfunktioner (UNION, EXCEPT,
        INTERSECT) används så kan en SQL förfrågan som består av både GROUP BY och
        JOIN skrivas om till två förfrågningar som enbart utför GROUP BY, förenade av
        INTERSECT.

        Uttryck #1
            SELECT SUM(Table1.column2), SUM(Table2.column2)
                 FROM Table1
                 INNER JOIN Table2
                      ON Table1.column1 = Table2.column1
                 GROUP BY Table1.column1

        Uttryck #2
            SELECT column1, SUM(column2), 0
                 FROM Table1
                 GROUP BY column1
            INTERSECT
            SELECT column1, 0, SUM(column2)
                 FROM Table2
                 GROUP BY column1

        Uttryck två utförs snabbare på grund av att ingen JOIN behöver utföras. Dessutom sparar
        operationen den mängd arbetsminne som behövs för att utföra förfrågningen. Här finns
        dock ett stort portabilitetsproblem. Många utav de stora tillverkarna för databashanterare
        stöder inte INTERSECT. Dessa är åtminstone Informix, Ingres, InterBase, Microsoft,
        MySQL och Sybase. För dessa databashanterare fungerar alltså inte den optimering som
        här presenterades.

        När man grupperar resultat som uppstått via en JOIN är det effektivast om
        grupperingskolumnen är från samma tabell som kolumnen på vilken en eventuell
        mängdfunktion tillämpas på. Det här tipset nämns i en del tillverkares manualer.


                                                                                        Sida 23 / 79
Finjustering och optimering av SQL databaser                                   02.04.2003
Krister Karlström
        Prestandan kan som bekant ökas genom att undvika JOIN. Eftersom GROUP BY inträffar
        vid användning av mängdfunktioner så kan uttryck ett skrivas om till uttryck två, om
        Table1.column1 är unik, för att undvika en JOIN.

        Uttryck #1
            SELECT COUNT(*) FROM Table1, Table2
                 WHERE Table1.column1 = Table2.column1

        Uttryck #2
            SELECT COUNT(*) FROM Table2
                 WHERE Table2.column1
                 IN (SELECT Table1.column1 FROM Table1)

   4.3. HAVING
        De flesta databashanterare sammanfogar inte WHERE och HAVING satser. Följande två
        uttryck ger exakt samma resultat, men uttryck två är snabbare.

        Uttryck #1
            SELECT column1 FROM Table1
                 WHERE column2 = 5
                 GROUP BY column1
                 HAVING column1 > 6

        Uttryck #2
            SELECT column1 FROM Table1
                 WHERE column2 = 5
                 AND column1 > 6
                 GROUP BY column1

        Endast sådana fall där jämförelsen av column1 är svår och tidskrävande kan dra nytta av
        att utföra jämföringen först i HAVING skedet.

   4.4. Alternativ till GROUP BY
        Om man skriver en förfrågan som inte involverar mängdfunktioner kan man ofta använda
        DISTINCT som ett alternativ till GROUP BY. DISTINCT har tre fördelar framom
        GROUP BY:

        1. Operatorn är mycket enkel
        2. Operatorn kan användas i mera komplicerade uttryck
        3. Databashanterare kör ofta DISTINCT snabbare än GROUP BY

        Det är effektivare att använda uttryck två istället för uttryck ett:

        Uttryck #1
            SELECT column1 FROM Table1
                 GROUP BY column1

        Uttryck #2
            SELECT DISTINCT column1 FROM Table1



                                                                                       Sida 24 / 79
Finjustering och optimering av SQL databaser                                  02.04.2003
Krister Karlström
   4.5. Gruppering och sortering
        När en GROUP BY utförs så kommer databashanteraren att behöva leta efter dubbletter av
        data. Därför utförs nästan alltid en sortering före grupperingen kan utföras. Tabellen nedan
        visar två listor, en osorterad och en sorterad.

           Osorterad lista           Sorterad lista
           Belgrad                   Belgrad
           Sofia                     Budapest
           Budapest                  Sofia

        Antag att en gruppering enligt ”Belgrad” skall utföras i den osorterade tabellen. Hur vet
        databashanteraren om det finns dubbletter av ”Belgrad”? Jo, databashanteraren är tvungen
        att gå igenom hela tabellen och jämför ”Belgrad” med både ”Sofia” och ”Budapest” innan
        den vet om det finns dubbletter. Den kan inte vara säker på att inga dubbletter finns förrän
        den har gått igenom hela tabellen, eftersom datan ligger i slumpmässig ordning.

        Om man däremot utför en gruppering enligt ”Belgrad” i den sorterade tabellen räcker det
        med att databashanteraren jämför ”Belgrad” med nästa rad, eftersom datan ligger i
        stigande alfabetisk ordning. Så fort som databashanteraren jämfört [n] med [n + 1] är den
        klar. Detta betyder alltså att en gruppering går snabbt om tabellen är sorterad.

        Ett annat effektivt sätt att utföra grupperingar är att använda en hash-lista för att snabbt
        kunna kontrollera om det finns likadana poster. Informix använder sig (enligt uppgifter)
        utav hash tekniken.

        Eftersom en GROUP BY föregås av en ORDER BY kan man alltså effektivera
        grupperingen genom att ge kolumnerna i samma ordningsföljd både i ORDER BY satsen
        och i GROUP BY satsen. Skriv inte en SQL förfrågan på det här viset:

              SELECT * FROM Table1
                   GROUP BY column1, column2
                   ORDER BY column1

        Skriv den istället på det här viset:

              SELECT * FROM Table1
                   GROUP BY column1, column2
                   ORDER BY column1, column2

        Den här optimeringen gäller för databashanterare som inte använder sig utav hash listor.
        Har man en sådan hanterare skall man använda sig utav den första varianten, alternativ två
        blir långsammare i ett hash-baserat system.

   4.6. Gruppering med index
        De flesta databashanterare använder index vid datagrupperingar. Som redan tidigare
        nämndes, så underlättas en gruppering om tabellen är sorterad. Att kunna utnyttja index är
        egentligen samma sak som att ha tabellen sorterad, inte riktigt samma sak, men i alla fall
        är beteendet i det här sammanhanget väldigt lika. Här är det även värt att tänka på att
        GROUP BY har en lägre prioritet än JOIN och WHERE satser. GROUP BY kommer med
        andra ord att utföras efter en eventuell JOIN eller WHERE sats.

                                                                                        Sida 25 / 79
Finjustering och optimering av SQL databaser                                 02.04.2003
Krister Karlström
        Följande exempel kan inte utnyttja ett index för att snabba upp grupperingen:

             SELECT column1 FROM Table1
                  WHERE column2 = 55
                  GROUP BY column1

        Orsaken till att index inte kan användas av GROUP BY torde vara ganska så klar.
        Databashanteraren utför WHERE satsen först och den jämförelsen görs via index över
        column2. Efter att detta är gjort fortsätter den med GROUP BY satsen, me n nu är det
        ingen idé längre att utnyttja index över column1, eftersom indexet är ett index över hela
        tabellen och inte över den data som WHERE satsen filtrerat fram.

        På grund av detta fenomen är index endast lämpliga för små och enkla grupperingar.
        Lyckligtvis är de flesta grupperingar som görs så pass enkla att ett index kan utnyttjas.
        Grupperingar där GROUP BY står ensamt och grupperingskolumnerna är i samma
        ordningsföljd som de indexerade kolumnerna i selectlistan kommer att utnyttja ett index.
        Följande exempel utför grupperingen snabbare, om column1 har ett index:

             SELECT column1 FROM Table1
                  GROUP BY coumn1

   4.7. COUNT
        Funktionen COUNT kommer att utnyttja ett index om endast en kolumn (eller *) finns i
        selectlistan och om inga flera satser följer FROM satsen. Följande förfrågan utförs
        snabbare av vissa databashanterare om tabellen har ett index:

             SELECT COUNT(*) FROM Table1

        COUNT har dessutom väldigt ofta databasspecifika specialoptimeringar, vilket kan göra
        det svårt att försöka tillföra ytterligare optimering.

   4.8. SUM och AVG
        Index

        Även funktionerna SUM och AVG kommer att utnyttja ett index om endast en kolumn
        finns med i selectlistan och den kolumnen är indexerad. Tyvärr utförs inte funktionerna
        snabbare med hjälp av indexet – tvärtom, det tar ofta längre tid att utföra funktionerna om
        de utförs på indexerade kolumner. På längre sikt kan man ändå anse att de förluster som
        uppstår är väldigt små i förhållande till de vinster som uppnås via index hos COUNT,
        MAX och MIN.

        Följande exempel utförs ofta långsammare om column1 är indexerad:

             SELECT SUM(column1) FROM Table1
                  WHERE coumn1 > 5




                                                                                        Sida 26 / 79
Finjustering och optimering av SQL databaser                                    02.04.2003
Krister Karlström
        Precision

        Precision kan även bli ett dilemma om man vill summera en större mängd flyttal av
        datatypen FLOAT. Speciellt om följande två fall förekommer kan det bli problem med
        precisionen: (a) talens exponenter varierar mycket sinsemellan och (b) när många
        subtraktionen inträffar på grund av negativa tal. Förståeliga förluster i precision kan även
        inträffa när man lagrar tal som avses vara exakta som FLOAT. Ta till exempel det
        ”exakta” talet 0.1 och lagra det i en FLOAT kolumn. Addition tio gånger av den kolumnen
        behöver nödvändigtvis inte ge slutresultatet 1.0, på grund av precisionsförluster.

        Högre flyttalsprecision kan uppnås genom att konvertera talen till en datatyp med högre
        precision innan summeringsfunktioner tillämpas, exempelvis till datatypen DECIMAL
        eller DOUBLE. Märk väl att det är viktigt att konvertera varje enskilt tal före additionen,
        inte själva additionsresultatet. Uttryck ett ger alltså ingen bättre precision, men uttryck två
        kan förbättra precisionen hos slutresultatet:

        Uttryck #1
            SELECT CAST(SUM(column1) AS DECIMAL(10)) FROM Table1

        Uttryck #2
            SELECT SUM(CAST(column1 AS DECIMAL(10))) FROM Table1

        Precisionen blir även en aning bättre om man använder SUM(x + y) istället för SUM(x) +
        SUM(y) eftersom det totala antalet additioner blir färre. Däremot blir precisionen bättre
        om man använder SUM(x) – SUM(y) istället för SUM(x - y) därför att det totala antalet
        subtraktioner blir färre.

        Med heltal kan precisionsförluster aldrig uppstå. Däremot kan man i långsökta fall få
        problem med begränsningar och spill (overflow) kan uppstå. Om man försöker addera två
        celler som bägge innehåller talet 2 miljarder så uppstår spill eftersom summan 4 miljarder
        kan inte representeras med 32-bitar. Problemet kan lösas med datatypkonvertering. För
        system som stöder datatypen BIGINT kan alternativ ett användas. Vill man hålla sig till
        standard SQL skall alternativ två användas.

        Alternativ #1
            SELECT SUM(CAST(column1 AS BIGINT)) FROM Table1

        Alternativ #2
            SELECT SUM(CAST(column1 AS DECIMAL(10))) FROM Table1

   4.9. MAX och MIN
        Funktionerna MAX och MIN kommer att utnyttja ett index om funktionen är ensam i
        selectlistan och inga andra satser följer efter FROM satsen. Om ett index existerar blir
        dessa två funktioner väldigt enkla och snabba att utföra. Funktionen MIN kan direkt läsa
        det minimala värdet från det första indexvärdet och funktionen MAX får direkt det
        maximala värdet genom att avläsa det sista indexet (eller tvärtom i ett fallande index, som
        i IBM).

        Databashanterarna löser följande problem väldigt lätt om tabellen har ett index:

             SELECT MIN(column1) FROM Table1

                                                                                          Sida 27 / 79
Finjustering och optimering av SQL databaser                                     02.04.2003
Krister Karlström
        Men följande exempel är en betydligt svårare nöt att knäcka och tar betydligt längre tid att
        utföra:

             SELECT MIN(column1), MAX(column1) FROM Table1

        Villkoret för indexanvändning var ju att funktionen skulle vara ensam i selectlistan, därför
        tar detta exempel väsentligt mera tid på sig att utföras. I sådana här situationer är det bättre
        att skriva förfrågningen som två skilda SELECT satser, eller förena dem med en UNION.
        Följande exempel rekommenderas:

             SELECT MIN(column1) FROM Table1
             SELECT MAX(column1) FROM Table1


5. Joins (Kopplingar, Föreningar)
   5.1. Bakgrundsinformation om föreningar av tabeller
        En gång i tiden fanns det en enkel regel för joins – indexera alla kolumner och föreningar
        av tabeller kommer att gå snabbt. Medan databashanterarna har utvecklats och blivit mera
        sofistikerade har flera olika strategier för olika typer av föreningar tagits fram. De flesta
        databashanterare innehar en ganska stor repertoar utav dessa och optimering av föreningar
        är idag mera raffinerande än förut.

        Det första man bör hålla i färskt minne när man planerar förfrågningar som förenar flera
        tabeller är att man inte vill utföra föreningen själv. Vad man däremot vill göra är att ge
        databashanteraren de bästa förutsättningarna för att själv kunna välja den mest lämpliga
        och effektivaste föreningsplanen, i slutändan kommer detta att påverka effektiviteten en
        hel del.

        I den förenklade modellen av en förening av två tabeller blir resultattabellens storlek
        produkten av de tabeller som förenades. Den förenar alltså alla rader i ena tabellen med
        alla rader i den andra tabellen. Om Tabell1 innehåller värdena {A, B} och Tabell2
        innehåller {C, D} så blir produkten { (A, C) (A, D) (B, C) (B, D) }. Realisten märker att
        en del av dessa rader troligtvis kommer att filtreras bort av ett villkor, som dessutom körs
        först efter att tabellerna multiplicerats. Det väsentliga i detta exempel är att visa på den
        enorma mängden med tillfälligt minne som behövs för att lagra den temporära tabellen.
        Det behövs alltså någon bättre algoritm för att utföra en förening i praktiken.

        Valet av vilken algoritm och plan som väljs beror på om tabellen har index, tabellens
        storlek samt på indexets selektivitet. Selektivitet är ett mått på olikheten mellan unika
        värden i ett index.




                                                                                           Sida 28 / 79
Finjustering och optimering av SQL databaser                                  02.04.2003
Krister Karlström

   5.2. Nested-Loop Joins
        ”Nested-Loop joins” kan översättas till ”förening med inbäddade upprepningar”, eller nåt i
        den stilen. Principen är två eller flera upprepningar inuti varandra där varje upprepning går
        igenom en specifik tabell. Alla algoritmer enligt ”Nested-Loop joins” principen baserar sig
        på någon variant av pseudokoden nedan:

        for (each row in Table1) {
          for (each row in Table2) {
            if (Table1 join column matches Table2 join column) pass
            else fail
          }
        }

        − Ordet “matches” betyder vanligtvis “lika med” eftersom majoriteten av alla föreningar
          är “lika- med-föreningar”, alltså sådana som använder lika med operatorn. Ett känt
          namn på dessa i det engelska språket är ”equijoins”.
        − Ordet ”pass” syftar på att raderna från bägge tabellerna skall tillfogas den temporära
          resultattabellen. Vanligtvis så sparar databashanterarna endast det ID som förknippas
          med raderna.

        Redan den här algoritmen är en stor förbättring jämfört med det grundscenario som
        beskrevs i introduktionen till detta kapitel. Nu behöver den temporära tabellen inte vara
        fullt så stor som produkten av bägge tabellerna. Men fortfarande så behövs ett gigantiskt
        antal jämförelser.

        Jämförelser är dock snabba operationer, speciellt om bägge talen som skall jämföras ligger
        i databashanterarens buffertminne. Genom att utöka den algoritm som nyss presenterades
        så att den tar sig an en sida i gången från en tabell kan prestandan höjas betydligt.
        Sidstorleken bestäms i databasserverns konfiguration, men typiska värden är 4 KB, 8 KB
        eller 16 KB. Genom att öka denna buffert så kan större sidor rymmas i buffertminnet och
        algoritmen körs snabbare.

        for (each page in Table1) {
          for (each page in Table2) {
            for (each row in Table1-page) {
              for (each row in Table2-page) {
                 if (join column matches) pass
                 else fail
              }
            }
          }
        }

        Denna algoritm utnyttjar buffertminnet genom att dela upp tabellerna i sidor och behandla
        en sida från varje tabell i gången. Om dessutom de kolumner som jämförs har index så
        behövs ingen skanning av hela sidan, det räcker med att slå upp värdet ur indexet. Den
        innersta upprepningen bör bestå av den enklare, alternativt den mindre, tabellen eller
        åtminstone ha ett index för att algoritmen skall bli ännu effektivare.



                                                                                        Sida 29 / 79
Finjustering och optimering av SQL databaser                                   02.04.2003
Krister Karlström
        Oftast finns det ett villkor med i spelet när man förenar tabeller med varandra, förutom det
        villkor som definierar föreningen. Den tabell som har ett filtreringsvillkor bör placeras
        som den yttre tabellen i upprepningen så att det totala antalet upprepningar av den inre
        upprepningen minskas, annars upprepas filtreringsvillkoret om och om igen på tok för
        många gånger.

        Databashanteraren väljer vilken tabell som blir den yttre och vilken som blir den inre på
        basen av följande punkter:

        − Den minsta tabellen skall vara i den inre upprepningen, speciellt om den är mycket
          liten.
        − Tabellen med det bästa indexet skall vara i den inre upprepningen.
        − Den tabell som har ett filtreringsvillkor skall vara i den yttre upprepningen.

        De här punkterna bör man tänka på när man skriver förfrågningar som förenar tabeller.
        Motverka inte databashanterarens vilja genom att placera ett filtreringsvillkor på fel tabell!
        Om tabell ett är mindre och har ett bättre index, placera då ett eventuellt filtreringsvillkor
        på tabell två istället – tabell två passar ju ändå bättre i den yttre upprepningen.

        Så här långt har allt handlat om enkla föreningar av typen Table1.column1 =
        Table2.column1. Förståss kan det bli lite mera komplicerat än så här. Det kan till
        exempel finns två föreningsvillkor som i följande exempel:

             SELECT * FROM Table1, Table2
                  WHERE Table1.column1 = Table2.column1
                  AND Table1.column2 = Table2.column2

        I det här fallet räcker det inte med att försäkra att column1 och column2 i den inre
        upprepningen är indexerade. Om ett sammansatt index skapas för column1 och
        column2 i den inre upprepningen, istället för två individuella index, sker den totala
        föreningen betydligt snabbare! Det är dessutom mycket viktigt att bägge kolumnerna som
        jämförs i den inre upprepningen har exakt samma datatyp och exakt samma storlek –
        annars kan det hända att databashanteraren inte alls utnyttjar indexet.




                                                                                         Sida 30 / 79
Finjustering och optimering av SQL databaser                                02.04.2003
Krister Karlström

   5.3. Sort-Merge Joins
        En ”Sort-Merge Join” är en aning mera komplicerad än en ”Nested-Loop Join”, men den
        går fortfarande att beskriva med ett par rader pseudokod:

        sort (Table1)
        sort (Table2)

        get first row (Table1)
        get first row (Table2)

        for (;;until no more rows in tables) {
            if(join-column in Table1 < join-column in Table2)
                 get next row (Table1)

             elseif (join-column in Table1 > join-column in Table2)
                  get next row (Table2)

             elseif (join-column in Table1 = join-column in Table2) {
                  pass
                  get next row (Table1)
                  get next row (Table2)
             }
        }

        Algoritmen börjar med att sortera de bägge tabellerna som skall förenas. Skedet kallas
        ”sorteringsskedet”, därifrån kommer den första halvan i namnet ”Sort-Merge”. Andra
        halvan kommer givetvis från nästa skede. I föreningsskedet (”Merge” skedet) av en “Sort-
        Merge Join” går databashanteraren alltid framåt i bägge tabellerna. Den behöver aldrig gå
        igenom en viss rad i en tabell mer än en gång – en stor förbättring jämfört med ”Nested-
        Loop Join”.

        Nackdelen är den sortering som krävs innan den fina algoritmen kan uträtta sitt arbete.
        Effektiva sorteringsalgoritmer borde ändå vara betydligt snabbare än jämförelser av allt
        mot allt (som är fallet i ”Nested-Loop Joins”). Det är bevisbart att ”Sort-Merge Join” är
        betydligt snabbare än ”Nested- Loop Join” om bägge tabellerna är väldigt stora.

        Trots alla fördelar så väljer databashanterarna väldigt ofta att hellre använda ”Nested-
        Loop Joins” än ”Sort-Merge Joins” därför att de kräver mindre mängd minne, är flexiblare
        och kräver inga extra tunga databehandlingar i startögonblicket. Följande exempel visar en
        idealisk situation för att utföra en ”Sort-Merge Join”:

             SELECT * FROM Table1, Table2
                  WHERE Table1.column1 = Table2.column1

        Det som gör denna förening ideal för ”Sort-Merge” är lika med operatorn och avsaknaden
        av filtreringsvillkor. En typisk SQL förfrågan för att generera en rapport. Om dessutom
        Table1 och Table2 är sorterade på förhand enligt samma nyckel så är det den perfekta
        situationen för en ”Sort-Merge”.



                                                                                      Sida 31 / 79
Finjustering och optimering av SQL databaser                                   02.04.2003
Krister Karlström
        Användningen av ”Sort-Merge Joins” kan innebära lite extra jobb för
        databasadministratorn. Hos vissa databashanterare måste nämligen ”Sort-Merge Joins”
        aktiveras manuellt. Dessutom kan det vara en god idé att se över storleken på serverns
        centralminne om man tänkt använda sig utav den här föreningstekniken.

   5.4. Hash Joins
        En ”Hash-Join” är ännu en algoritm för att skapa en förenad tabell. Processen att förena
        Table1 och Table2 går enligt följande modell:

        − Beräkna ett hash värde för varje rad i Table1. Spara hash värdet i en tillfällig tabell i
          minnet.
        − Beräkna ett hash värde för varje rad i Table2. Kontrollera om hash värdet redan
          finns i den tillfälliga tabellen i minnet. Om det finns, så finns det en koppling. Om det
          inte finns, så finns det ingen koppling.

        En ”Hash-Join” är med andra ord en variant av en ”Nested-Loop Join” vars inre tabells
        rader är uppslagna via hash värden istället för via index. De flesta databashanterare
        upprätthåller inga statiska tabeller med hash värden, så en temporär tabell med hash
        värden för Table1 behöver skapas innan föreningen kan inledas. Hash tabellen kan
        frigöras när den förenade tabellen är klar.

        Följande fyra krav måste alla vara uppfyllda för att en ”Hash-Join” skall fungera vettigt:

        1. Det finns tillräckligt med minne reserverat för den temporära tabellen innehållande
           alla hash värden. Inställningen är oftast konfigurerbar.
        2. Föreningen är en ”equijoin” (den använder lika med operatorn).
        3. Värdena i den inre tabellens föreningskolumn är unika och producerar unika hash
           värden.
        4. Den inre tabellen genomsöks många gånger eftersom den yttre tabellen är betydligt
           större och innehåller många rader som inte filtrerats ut utav något filtreringsvillkor.

        Krav nummer ett kan kompenseras för genom att dela upp den yttre tabellen i flera bitar
        och beräkna hash värden för varje bit i gången. Därefter utförs föreningensmomentet och
        när det är klart beräknas nya hash värden för nästa bit av den yttre tabellen o.s.v. Detta kan
        ge upphov till att den inre tabellen skannas många gånger, men det är i alla fall bättre än
        att ha en gigantisk hash tabell för den yttre tabellen, som dessutom kanske mappas ner på
        hårddisken om den inte ryms i centralminnet.

   5.5. Undvikning av Joins
        En förening av två tabeller kan ibland undvikas genom att transformera en SQL förfrågan
        till en enklare, utan förening, men ändå likvärdig. Följande exempel utnyttjar en av
        grundprinciperna i SQL optimering, spridning av konstanta värden. Uttryck ett kräver en
        förening av Table1 och Table2 medan uttryck två inte behöver någon förening. Ett
        krav är dock att bägge kolumnerna är indexerade.

        Uttryck #1
            SELECT * FROM Table1, Table2
                 WHERE Table1.column1 = Table2.column1
                 AND Table1.column1 = 55


                                                                                         Sida 32 / 79
Finjustering och optimering av SQL databaser                                  02.04.2003
Krister Karlström
        Uttryck #2
            SELECT * FROM Table1, Table2
                 WHERE Table1.column1 = 55
                 AND Table2.column1 = 55

        En sådan här situation uppstår alltid om lika med operatorn utgör filtreringsvillkoret och
        samma kolumn även ingår i föreningsvillkoret. Det är trots allt en väldigt liten del av alla
        föreningar som kan omformas till icke- föreningar. För att uttryck två över huvudtaget
        skall vara snabbare än uttryck ett krävs det att bägge tabellerna har index över column1.

   5.6. Gemensamma index för Joins
        En del databashanterare har stöd för något som kallas för ”Join Indexes”, bland annat
        Microsoft och Oracle stöder dem. Principen är att ha flera tabellers index i samma fysiska
        index.

           Index 1               Tabell 1              Index 2             Tabell 2
           A                    Rad 1.7                B                 Rad 14.53

           B                    Rad 6.3                C                 Rad 16.02

           C                    Rad 8.8                F                 Rad 19.08


        Bilderna ovan visar två vanliga index för Tabell ett och Tabell två.
        Bilden nedan visar ett gemensamt index (”Join Index”) för både Tabell ett och Tabell två:

                   Index                Tabell 1       Tabell 2
               A                          Rad 1.7

               B                          Rad 6.3

               B                          Rad 8.8

               C                                           Rad 14.53

               C                                           Rad 16.02

               F                                           Rad 19.08



        Antag att följande SQL förfrågan körs, med tillgång till ett gemensamt index för Tabell ett
        och två:

               SELECT * FROM Table1, Table2
                    WHERE Table1.column1 = Table2.column1




                                                                                        Sida 33 / 79
Finjustering och optimering av SQL databaser                                    02.04.2003
Krister Karlström
        Med tillgång till ett gemensamt index är uppgiften mer eller mindre trivial.
        Databashanteraren behöver endast skanna igenom den gemensamma indextabellen och för
        varje värde som pekar på en rad i tabell ett, kontrollera om nästa indexvärde är exakt
        likadant men pekar på en rad i tabell två. Om så är fallet har den hittat en koppling. Det
        här är faktiskt en ”Sort-Merge Join”, där redan både sorteringen och föreningen har utförts
        på förhand!

        Gemensamma index kan vara mycket användbara och kan underlätta föreningar av
        tabeller avsevärt, men det finns alltid en risk för att det blir råddigt när man börjar blanda
        ihop data från flera tabeller i samma fil. Om dessutom man behöver utföra enskilda
        sökningar i kolumnerna så går det långsammare på grund av all ”skräpdata” från den andra
        tabellen som ligger i indexet.

        En tumregel för gemensamma index är:

        Om mer än hälften av alla förfrågningar på två tabeller innebär en förening av dessa, och
        föreningskolumnerna ändrar nästan aldrig, och ingen av tabellerna innehåller interna
        grupperingar, skapa då ett gemensamt index för dessa två tabeller.

   5.7. Sammansatta tabeller
        Ett annat sätt att undvika föreningar (eller ”joins”) är att utnyttja något som ofta kallas för
        sammansatta tabeller (composite tables). Principen är mycket enkel, den sammansatta
        tabellen innehåller helt enkelt resultatet från en förening utförd på förhand. Principen
        beskrivs lättast med lite SQL:

             CREATE TABLE Table1 (
                  column1 INTEGER PRIMARY KEY,
                  column2 CHARACTER(50),
                  ...)

             CREATE TABLE Table2 (
                  column3 INTEGER PRIMARY KEY,
                  column4 CHARACTER(50)
                  ...)

             CREATE TABLE         Composite (
                  column1         INTEGER,
                  column3         INTEGER,
                  column2         CHARACTER(50),
                  column4         CHARACTER(50),
                  ...)

             CREATE UNIQUE INDEX Index1
                  ON Composite (column1, column3, column2, column4)

        Om någon rad i Table1 eller i Table2 ändras, eller om rader adderas eller raderas, så är
        den gemensamma tabellen inte längre synkroniserad. För att förhindra detta så måste även
        den gemensamma tabellen uppdateras, vilket är enkelt att göra med en triggningsfunktion.




                                                                                          Sida 34 / 79
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering
Sql optimering

More Related Content

Viewers also liked (6)

bogshirgo
bogshirgobogshirgo
bogshirgo
 
НҮБ
НҮБНҮБ
НҮБ
 
Plantae
PlantaePlantae
Plantae
 
Penyakit sosial
Penyakit sosialPenyakit sosial
Penyakit sosial
 
тест
тесттест
тест
 
Aris 9angi
Aris 9angiAris 9angi
Aris 9angi
 

Similar to Sql optimering

Ki Line32003 Sve
Ki Line32003 SveKi Line32003 Sve
Ki Line32003 Sveguest91254c
 
Sveriges potential för elproduktion från takmonterade solceller Sigrid Kamp
Sveriges potential för elproduktion från takmonterade solceller Sigrid KampSveriges potential för elproduktion från takmonterade solceller Sigrid Kamp
Sveriges potential för elproduktion från takmonterade solceller Sigrid KampSigrid Kamp
 
Utdrag ur boken "CSR i praktiken" av Per Grankvist
Utdrag ur boken "CSR i praktiken" av Per GrankvistUtdrag ur boken "CSR i praktiken" av Per Grankvist
Utdrag ur boken "CSR i praktiken" av Per GrankvistPer Grankvist
 
Det gröna manifestet
Det gröna manifestetDet gröna manifestet
Det gröna manifestetCenterpartiet
 
DN varumärke och grafisk plattform
DN varumärke och grafisk plattformDN varumärke och grafisk plattform
DN varumärke och grafisk plattformdn ide
 
Narkotikaspaning på Internet Projekt Nicks 2005
Narkotikaspaning på Internet Projekt Nicks 2005Narkotikaspaning på Internet Projekt Nicks 2005
Narkotikaspaning på Internet Projekt Nicks 2005Kim Nilvall
 
Projektdefinition Arbetsförmedlingen Hisingen
Projektdefinition Arbetsförmedlingen HisingenProjektdefinition Arbetsförmedlingen Hisingen
Projektdefinition Arbetsförmedlingen Hisingenguest87755d
 

Similar to Sql optimering (8)

Ki Line32003 Sve
Ki Line32003 SveKi Line32003 Sve
Ki Line32003 Sve
 
Sveriges potential för elproduktion från takmonterade solceller Sigrid Kamp
Sveriges potential för elproduktion från takmonterade solceller Sigrid KampSveriges potential för elproduktion från takmonterade solceller Sigrid Kamp
Sveriges potential för elproduktion från takmonterade solceller Sigrid Kamp
 
219270
219270219270
219270
 
Utdrag ur boken "CSR i praktiken" av Per Grankvist
Utdrag ur boken "CSR i praktiken" av Per GrankvistUtdrag ur boken "CSR i praktiken" av Per Grankvist
Utdrag ur boken "CSR i praktiken" av Per Grankvist
 
Det gröna manifestet
Det gröna manifestetDet gröna manifestet
Det gröna manifestet
 
DN varumärke och grafisk plattform
DN varumärke och grafisk plattformDN varumärke och grafisk plattform
DN varumärke och grafisk plattform
 
Narkotikaspaning på Internet Projekt Nicks 2005
Narkotikaspaning på Internet Projekt Nicks 2005Narkotikaspaning på Internet Projekt Nicks 2005
Narkotikaspaning på Internet Projekt Nicks 2005
 
Projektdefinition Arbetsförmedlingen Hisingen
Projektdefinition Arbetsförmedlingen HisingenProjektdefinition Arbetsförmedlingen Hisingen
Projektdefinition Arbetsförmedlingen Hisingen
 

Sql optimering

  • 1. Finjustering och optimering av SQL databaser Krister Karlström Administration och Design av Databassystem V2003 Arcada Polytechnic
  • 2. Finjustering och optimering av SQL databaser 02.04.2003 Krister Karlström INNEHÅLLSFÖRTECKNING 1. Introduktion till optimering.......................................................................................................... 5 1.1. Bakgrund .............................................................................................................................. 5 1.2. Finjustering och optimering ................................................................................................. 5 2. Enkla sökningar och operatorer ................................................................................................... 6 2.1. Begränsningar av kapitlet..................................................................................................... 6 2.2. Allmän optimering av villkor och sökningar ....................................................................... 6 2.3. Transformationer.................................................................................................................. 7 2.4. Datum................................................................................................................................... 8 2.5. Sammanslagning av flere konstanter ................................................................................... 9 2.6. Datatyper .............................................................................................................................. 9 2.7. AND..................................................................................................................................... 9 2.8. OR...................................................................................................................................... 10 2.9. AND plus OR..................................................................................................................... 10 2.10. NOT ............................................................................................................................... 11 2.11. IN ................................................................................................................................... 12 2.12. LIKE............................................................................................................................... 13 2.13. SIMILAR ....................................................................................................................... 14 2.14. UNION........................................................................................................................... 14 2.15. EXCEPT......................................................................................................................... 15 2.16. CASE ............................................................................................................................. 15 2.17. Syntax............................................................................................................................. 16 2.18. Datatypkonvertering (CAST)......................................................................................... 16 3. Sortering av data ........................................................................................................................ 17 3.1. Allmänt om sortering ......................................................................................................... 17 3.2. Sorteringseffektivitet.......................................................................................................... 17 3.3. Sorteringshastighet hos olika datatyper ............................................................................. 18 3.4. ORDER BY........................................................................................................................ 19 3.5. Att sortera eller att inte sortera........................................................................................... 19 3.6. Sorteringsnycklar ............................................................................................................... 20 3.7. Sortering med index........................................................................................................... 21 4. Gruppering av data..................................................................................................................... 22 4.1. GROUP BY ....................................................................................................................... 22 4.2. Optimal gruppering ............................................................................................................ 22 4.3. HAVING............................................................................................................................ 24 4.4. Alternativ till GROUP BY ................................................................................................. 24 4.5. Gruppering och sortering ................................................................................................... 25 4.6. Gruppering med index ....................................................................................................... 25 4.7. COUNT.............................................................................................................................. 26 4.8. SUM och AVG................................................................................................................... 26 4.9. MAX och MIN ................................................................................................................... 27 5. Joins (Kopplingar, Föreningar).................................................................................................. 28 5.1. Bakgrundsinformation om föreningar av tabeller .............................................................. 28 5.2. Nested-Loop Joins .............................................................................................................. 29 5.3. Sort-Merge Joins ................................................................................................................ 31 5.4. Hash Joins .......................................................................................................................... 32 5.5. Undvikning av Joins .......................................................................................................... 32 5.6. Gemensamma index för Joins ............................................................................................ 33 5.7. Sammansatta tabeller ......................................................................................................... 34 5.8. Kopplingar mellan tre eller flera tabeller ........................................................................... 35 Sida 2 / 79
  • 3. Finjustering och optimering av SQL databaser 02.04.2003 Krister Karlström 5.9. Outer Joins ......................................................................................................................... 35 6. Underförfrågningar (subqueries)................................................................................................ 36 6.1. Bakgrund ............................................................................................................................ 36 6.2. Föreningar eller underförfrågningar - Joins eller Subqueries? .......................................... 37 6.3. IN och ANY....................................................................................................................... 38 6.4. Flattening ........................................................................................................................... 39 6.5. DISTINCT ......................................................................................................................... 40 6.6. EXISTS .............................................................................................................................. 40 6.7. TOP .................................................................................................................................... 40 7. Kolumner ................................................................................................................................... 41 7.1. Datatyper ............................................................................................................................ 41 7.2. Fixerad eller varierbar längd? ............................................................................................ 41 7.3. Ögonblicksinformation ...................................................................................................... 42 7.4. Heltal.................................................................................................................................. 42 7.5. Flyttal ................................................................................................................................. 43 7.6. Decimaltal.......................................................................................................................... 44 7.7. Serienummer ...................................................................................................................... 44 7.8. Bitar.................................................................................................................................... 45 7.9. Stora objekt (LOB)............................................................................................................. 45 7.10. NULL............................................................................................................................. 46 7.11. Kolumnernas ordningsföljd i en tabell........................................................................... 46 8. Tabeller ...................................................................................................................................... 47 8.1. Bakgrund ............................................................................................................................ 47 8.2. Lagringsarkitektur .............................................................................................................. 47 8.3. Datahögar (heaps) .............................................................................................................. 49 8.4. Migration............................................................................................................................ 49 8.5. Fragmentering .................................................................................................................... 51 8.6. Grupperade tabeller............................................................................................................ 51 8.7. Normalisering..................................................................................................................... 51 8.8. Vyer.................................................................................................................................... 52 9. Index........................................................................................................................................... 53 9.1. Bakgrund ............................................................................................................................ 53 9.2. Binära träd.......................................................................................................................... 54 9.3. Vanliga index..................................................................................................................... 55 9.4. Sammansatta index ............................................................................................................ 55 9.5. Heltäckande index.............................................................................................................. 57 9.6. Unika index........................................................................................................................ 57 9.7. Grupperade index............................................................................................................... 57 9.8. Bitmap index...................................................................................................................... 57 9.9. Övriga index....................................................................................................................... 57 9.10. Indexnycklar, trunkering och komprimering ................................................................. 57 10. Begränsningar ........................................................................................................................ 57 10.1. Bakgrund ........................................................................................................................ 57 10.2. NOT NULL.................................................................................................................... 57 10.3. CHECK .......................................................................................................................... 57 10.4. FOREIGN KEY ............................................................................................................. 57 10.5. PRIMARY KEY ............................................................................................................ 57 10.6. UNIQUE ........................................................................................................................ 57 10.7. Triggningar..................................................................................................................... 57 10.8. Inaktivering av begränsningar........................................................................................ 57 10.9. Onödiga begränsningar i SELECT satser ...................................................................... 57 Sida 3 / 79
  • 4. Finjustering och optimering av SQL databaser 02.04.2003 Krister Karlström 11. Lagrade Procedurer (Stored Procedures) ............................................................................... 57 11.1. Bakgrund ........................................................................................................................ 57 11.2. Fördelar med Lagrade Procedurer.................................................................................. 57 12. Dataförändringar .................................................................................................................... 57 12.1. Bakgrund ........................................................................................................................ 57 12.2. Loggningar..................................................................................................................... 57 12.3. INSERT.......................................................................................................................... 57 12.4. UPDATE........................................................................................................................ 57 12.5. DELETE......................................................................................................................... 57 12.6. COMMIT och ROLLBACK.......................................................................................... 57 13. Källor ..................................................................................................................................... 57 Sida 4 / 79
  • 5. Finjustering och optimering av SQL databaser 02.04.2003 Krister Karlström 1. Introduktion till optimering 1.1. Bakgrund En dåligt designad databasprodukt kan orsaka fördröjningar för den enskilda användaren och även påverka andra applikationer som körs på samma dator eller i samma nätverk. Avsikten med denna föreläsning är att gå igenom de grundläggande detaljerna i optimering av SQL databaser samt att visa vilka fel och brister databasprogrammerare ofta gör och hur man kan förebygga dessa. Föreläsningen förutsätter grundläggande kunskap i SQL och kännedom om relationsdatabasmodellen. De exempel och eventuella rekommendationer som nämns i föreläsningen är främst SQL optimeringar och är inte specifika för någon viss databashanterare, om ej annat framgår. 1.2. Finjustering och optimering Det är möjligt att skilja på begreppen finjustering och optimering i databassammanhang. Finjustering är vad som görs åt en databas (exempelvis justering av buffertstorlekar, uppdatering av index) medan optimering är vad som oftast görs i ett program (exempelvis justering av SQL förfrågningar, effektivering av tillgängliga resurser, omskrivning av programkod). Med ordet finjustering avses ofta i databassammanhang en hastighetsökning. Hastighet kan definieras på två olika sätt: − Svarstid: Den tid som löper sen dess att klienten skickat en förfrågan till databashanteraren tills klienten fått svaret. − Genomströmning: Antalet operationer och förfrågningar databashanteraren hinner utföra under en viss tidsenhet. I ett stort månganvändarsystem är det databashanterarens genomströmningshastighet av förfrågningar som utgör grunden för svarstiden till de enskilda klienterna. En god förhandsoptimering av alla SQL förfrågningar ökar genomströmningshastigheten i databashanteraren och minskar samtidigt svarstiden till klienterna. Oavsett vilken databashanterare som används så kan prestanda alltid höjas genom att alltid göra de rätta sakerna och utföra de korrekta förfrågningarna. ”In our experience (confirmed by many industry experts) 80% of the performance gains on SQL Server come from making improvements in SQL code, not from devising crafty configuration adjustments or tweaking the operating system.” - Kevin Kline et al., Transact-SQL Programming, O’Reilly & Associates “Experience shows that 80 to 90 per cent of all tuning is done at the application level, not the database level.” - Thomas Kyte, Expert One on One: Oracle, Wrox Press Sida 5 / 79
  • 6. Finjustering och optimering av SQL databaser 02.04.2003 Krister Karlström Optimering av prestanda är ett brett område. Det omfattar bland annat: − Att kunna bygga SQL satser utan att göra sånt som är vedertaget känt för att vara resurskrävande − Att förstå den fysiska strukturen och tillvägagångssättet i en typisk databas − Att kunna lösa de verkliga problemen istället för de imaginära problemen. 2. Enkla sökningar och operatorer 2.1. Begränsningar av kapitlet I detta kapitel berättas hur man kan syntaxoptimera SQL satser. Många sökningsförfrågningar är det ingen idé att försöka optimera, eftersom det är endast vissa sökningsförfrågningar som har sådana parametrar som går att optimera. Fastän sökningar som består av flera tabeller med kopplingar (joins ) och underförfrågningar (subqueries) är de mest resurskrävande, tas de inte upp i det här kapitlet. För optimering av joins och subqueries finns det skilda kapitel, här tas endast enkla sökningar i en tabell upp. Dessutom behandlar detta kapitel endast sökvillkor med WHERE satsen. Satserna HAVING, IF och ON kommer senare. Kapitlet börjar med optimeringar som utförs på en mera generell nivå och i slutet av kapitlet finns specifika optimeringar för de vanligaste SQL kommandon som används. På grund av portabilitetsproblem så är en del av de lösningar och tips som presenteras i detta kapitel inte tillgängliga för alla databashanterare. Dessutom kan det finnas små variationer mellan olika plattformer. 2.2. Allmän optimering av villkor och sökningar I denna del av kapitlet nämns sådana saker som man bör hålla i minnet när man skriver enkla sökningar. De bästa sökningarna är de som påverkar det minsta antalet rader i en tabell och med sökvillkor som har en låg kostnad. Med låg kostnad avses låg komplexitet, enkelt utförande och snabb exekvering. Tabellen nedan visar en typisk rangordning av sökvillkor (tabellen baserar sig på tillverkarnas egna manualer och är vedertagen känd i databassammanhang). Desto högr e poäng en operator eller en operand har, desto effektivare är de att använda i sökningar. Operator Poäng Operand Poäng = 10 Ensam direkt given operand 10 > 5 Ensam kolumn 5 >= 5 Ensam parameter 5 < 5 Multioperand 3 <= 5 Exakt numerisk datatyp 2 LIKE 3 Annan numerisk datatyp 1 <> 0 Temporär datatyp 1 Textbaserad datatyp 0 NULL 0 Sida 6 / 79
  • 7. Finjustering och optimering av SQL databaser 02.04.2003 Krister Karlström Ur poängtabellen kan man läsa att den bästa och effektivaste sökningen skulle vara någonting i den här stilen: ... WHERE smallint_column = 12345 Det här exemplet skulle enligt poängtabellen få hela 27 poäng! Poängen har beräknats enligt följande: − 5 poäng för att kolumnen (smallint_column) är ensam på vänster sida − 2 poäng för kolumnens datatyp (exakt numerisk datatyp) − 10 poäng för likhetsoperatorn (=) − 10 poäng för den direkta operanden (12345) på höger sida Här kommer ett annat exempel: ... WHERE char_column >= varchar_column || ‘x’ Det här sökvillkoret ger enligt samma tabell endast 13 poäng. − 5 poäng för att kolumnen (char_column) är ensam på vänster sida − 0 poäng för den textbaserade datatypen (CHAR) på vänster sida − 5 poäng för operatorn större än eller lika med (>=) − 3 poäng för multioperand-uttrycket (varchar_column || ’x’) på höger sida − 0 poäng för den textbaserade datatypen (VARCHAR) på höger sida Den exakta poängen för alla operatorer och operander varierar något mellan olika tillverkare på databashanterare. Rangordningen är dock den samma för de flesta tillverkare. Med hjälp av dessa enkla kunskaper kan man bestämma sig för den mest optimala ordningen i uttrycken och om det lönar sig att byta ut en del av uttrycket mot något annat uttryck som gör samma sak. De flesta förfrågningar och sökningar kan skrivas på många olika sätt, men de får alla olika poäng. En del databashanterare kan själva optimera en dåligt skriven förfrågan innan den utförs, men om uttrycken är tämligen komplexa så kan det nog hända att databashanteraren inte klarar av att omforma uttrycket effektivare. 2.3. Transformationer En SQL förfrågan kan alltså oftast skrivas om på många olika sätt utan att resultatet förändras. För detta finns det en formell regel som man bör följa: IF (A <operator> B) IS TRUE AND (B <operator> C) IS TRUE THEN (A <operator> C) IS TRUE AND NOT (A <operator> C) IS FALSE Som operator duger en av de följande jämförelseoperatorerna: =, >, >=, <, <= Dessa operatorer duger inte i transformationslagen: <> och LIKE Transformationslagen säger alltså att man kan byta ut B till C utan att påverka resultatet. Om en transformation innebär att en konstant flyttas kallas processen konstantöverföring. Sida 7 / 79
  • 8. Finjustering och optimering av SQL databaser 02.04.2003 Krister Karlström Följande två exempel ger samma resultat, men det senare exemplet ger en högre poäng eftersom en kolumn har bytts ut till en konstant. Uttryck #1 ... WHERE column1 < column2 AND column2 = column3 AND column1 = 5 Uttryck #2 ... WHERE 5 < column2 AND column2 = column3 AND column1 = 5 Uttryck ett och två är transformationer av varandra och ger således exakt samma resultat, men uttryck två utför en effektivare och snabbare sökning, i alla fall i några databashanterare. Som tidigare nämnts i detta kapitel, så kan de flesta databashanterare göra enkla transformationer som denna. Däremot så försöker inte de flesta databashanterare utföra transformationer om uttrycket innehåller en eller flera nivåer av parenteser samt nyckelorder NOT. Detta exempel kan vara ett långsamt uttryck: SELECT * FROM Table1 WHERE column1 = 5 AND NOT (column3 = 7 OR column1 = column2) Genom att tillämpa transformationsprinciperna uppstår detta uttryck: SELECT * FROM Table1 WHERE column1 = 5 AND column3 <> 7 AND column2 <> 5 I de flesta databashanterare är det transformerade uttrycket betydligt snabbare. Det kan med andra ord ibland löna sig att själv tillämpa transformationslagen. I vissa fall kan det hända att konstantöverföringe n av ett flyttal inte medför någon ökning av prestandan i uttrycket, eftersom det på grund av avrundningar ibland är möjligt att en jämförelse, beroende på vad man jämför med, blir både större än och lika med samtidigt. 2.4. Datum En SQL förfrågan som innehåller dagens datum kan göras snabbare genom att inte använda databashanterarens globala variabel för dagens datum. Jämför förfrågan ett och två nedan: Förfrågan #1 SELECT * FROM Table1 WHERE date_column = CURRENT_DATE AND amount * 5 > 100.00 Sida 8 / 79
  • 9. Finjustering och optimering av SQL databaser 02.04.2003 Krister Karlström Förfrågan #2 SELECT * FROM Table1 WHERE date_column = DATE ’2003-03-26’ AND amount * 5 > 100.00 Denna optimering kräver att förfrågan skrivs om varje dag, och är således endast användbar om förfrågan genereras dynamiskt av ett applikationsgränssnitt. 2.5. Sammanslagning av flere konstanter Vid sammanslagning av flere konstanter i samma uttryck kan konstantöverföring uppstå. Därför är sammanslagning av konstanter en lönsam operation. Det enkla uttrycket ... WHERE a – 3 = 5 skall alltså hellre skrivas som ... WHERE a = 8 /* a – 3 = 5 -> a = 5 + 3 */ för att uppnå en högre poäng. 2.6. Datatyper I en 32-bitars processor fungerar aritmetiken snabbast om processorn kan jobba med 32- bitars ord. Därför är datatypen INTEGER (32-bitar brett ord med tecken) den snabbaste datatypen som kan användas av databashanterare vid jämföringar och aritmetiska uttryck. Datatyperna SMALLINT, DECIMAL och FLOAT är alltså i de flesta databashanterare långsammare eftersom databashanteraren inte kan arbeta med full ordlängd på 32-bitar. 2.7. AND När SQL uttrycket endast innehåller lika med operatorer så kommer de flesta databashanterare att utvärdera villkoren i den ordning de ges i SQL satsen. Databashanteraren kommer med andra ord att ställa upp alla villkor i en lista som den därefter går igenom från vänster till höger. Noteras bör att det inte finns några regler som säger att så här måste en databashanterare göra – de gör bara på det viset (undantag är Oracle som utvärderar villkoren från höger till vänster om den kostnadsbaserade förfrågningsoptimeraren är aktiv). Detta beteende kan utnyttjas genom att först ge det villkor som har minst sannolikhet att uppfyllas. Om alla villkor har samma sannolikhet sätts det minst komplexa villkoret först. Om uttrycket som blivit satt längst till vänster är falskt behövs således inga flera testningar av de övriga villkoren eftersom databashanteraren vet redan i det här skedet att slutresultatet blir falskt – oavsett om de övriga villkoren är falska eller sanna. Exempeluttrycket ... WHERE column1 = ’A’ AND column2 = ’B’ omformas till ... WHERE column2 = ‘B’ AND column1 = ‘A’ om det är mindre sannolikt att column2 är lika med ’B’ än att column1 är lika med ’A’. Sida 9 / 79
  • 10. Finjustering och optimering av SQL databaser 02.04.2003 Krister Karlström 2.8. OR När man skriver SQL satser med OR sätts det villkor som har störst sannolikhet att uppfyllas f rst. Detta är den exakta motsatsen till de givna råden för AND operationen ö eftersom OR operationen ger upphov till att flera tester utförs om det första villkoret är falskt, medan AND orsakar flera tester om första villkoret är sant. Exempeluttrycket ... WHERE column2 = ’B’ OR column1 = ’A’ omformas till ... WHERE column1 = ‘A’ OR column2 = ‘B’ om det är mera sannolikt att column1 är lika med ’A’ än att column2 är lika med ’B’. OR operationer är även snabbare om villkoren omfattar så få kolumner som möjligt eftersom det minskar på databashanterarens behov att slå upp i indextabeller. Därför bör villkor i en längre SQL sats bestående av flera OR operationer som berör samma kolumn komma efter varandra. Uttryck ett blir effektivare om det omformas till uttryck två i nedanstående exempel: Uttryck #1 ... WHERE column1 = 1 OR column2 = 3 OR column1 = 2 Uttryck #2 ... WHERE column1 = 1 OR column1 = 2 OR column2 = 3 2.9. AND plus OR Den distributiva lagen konstaterar följande: A AND (B OR C) = (A AND B) OR (A AND C) Antag att en SQL förfrågan behöver utföras som består av både AND och OR villkor. Förfrågningen är formulerad på sånt sätt att AND operationerna måste utföras före OR operationen kan utföras: SELECT * FROM Table1 WHERE (column1 = 1 AND column2 = ’A’) OR (column1 = 1 AND column2 = ‘B’) Sida 10 / 79
  • 11. Finjustering och optimering av SQL databaser 02.04.2003 Krister Karlström SQL förfrågningen görs mot följande tabell: Rad # column1 column2 1 3 A 2 2 B 3 1 C När databashanteraren gör sökningar som slås upp i index i den ordningen de nämns i SQL förfrågningen, kan det hända att den behöver gå igenom alla följande steg: 1. Indexsökning: column1 = 1. Resultat = {rad #3} 2. Indexsökning: column2 = ’A’. Resultat = {rad #1} 3. AND operationen körs för att förena de bägge delresultaten. Resultat = { } 4. Indexsökning: column1 = 1. Resultat = {rad #3} 5. Indexsökning: column2 = ‘B’. Resultat = {rad # 2} 6. AND operationen körs för att förena de bägge delresultaten. Resultat = { } 7. OR operationen körs för att förena resultaten av bägge AND operationerna. Resultat = {} Genom att tillämpa den distributiva lagen baklänges erhålls följande SQL förfrågan: SELECT * FROM Table1 WHERE column1 = 1 AND (column2 = ’A’ OR column2 = ’B’) När databashanteraren gör indexbaserade sökningar enligt det senare uttrycket kan det hända att endast följand e steg behövs: 1. Indexsökning: column2 = ’A’. Resultat = {rad #1} 2. Indexsökning: column2 = ’B’. Resultat = {rad #2} 3. OR operationen körs för att förena de bägge delresultaten. Resultat = {rad #1, rad #2} 4. Indexsökning: column1 = 1. Resultat = {rad #3} 5. AND operationen körs för att förena delresultaten. Resultat = { } Två delmoment kan alltså inbesparas genom att tillämpa den distributiva lagen baklänges. En del av de vanligaste databashanterarna klarar av att göra det här själva, men det skadar aldrig att färdigt optimera förfrågningarna. Den här optimeringen gäller inte om det finns kopplingar med i förfrågningen. 2.10. NOT Det är alltid bekvämt att skriva om NOT till något mera läsligare och lättförståeligare. Den mänskliga hjärnan har som tendens att bli råddig om uttrycket innehåller allt för mycket inverteringar av diverse delresultat. Ett enkelt uttryck kan förenklas genom att t.ex. byta riktning på jämförelseoperatorn: ... WHERE NOT (column1 > 5) transformeras enkelt till ... WHERE column1 <= 5 Sida 11 / 79
  • 12. Finjustering och optimering av SQL databaser 02.04.2003 Krister Karlström Ett mera komplicerat uttryck kräver försiktighet vid transformeringen. DeMorgan’s teorem kan tillämpas för att omforma uttryck med NOT. DeMorgan’s teorem består av två regler: NOT (A AND B) = (NOT A) OR (NOT B) NOT (A OR B) = (NOT A) AND (NOT B) Om DeMorgan’s teorem tillämpas på följande exempel ... WHERE NOT (column1 > 5 OR column2 = 7) uppstår följande uttryck: ... WHERE column1 <= 5 AND column2 <> 7 Den uppmärksamme kommer nu ihåg att inte lika med operatorn bör undvikas och i de flesta fall skulle en sådan här transformering ha en negativ effekt. På längre sikt, i vilken som helst godtycklig mängd av spridda värden och när det är fler än två rader inblandade, tar de förluster som uppstår av inte lika med operatorn ändå ut de vinster som erhålls a v lika med operatorerna. På grund av detta så använder inte vissa databashanterare indextabeller för att utföra jämförelser med inte lika med operatorn, men däremot använder de nog indextabeller för att utföra jämförelser med mindre än och större än operatorerna. Därför lönar sig följande transformation: ... WHERE NOT (bloodtype = ’O’) Transformeras till: ... WHERE bloodtype < ’O’ OR bloodtype > ’O’ 2.11. IN Många tror att det inte är någon skillnad på dessa två uttryck: Uttryck #1 ... WHERE column1 = 5 OR column1 = 6 Uttryck #2 ... WHERE column1 IN (5, 6) Dessa personer har en aningen fel. Hos några databashanterare är IN snabbare än OR. Det lönar sig alltså alltid att transformera OR till IN om möjligt. De databashanterare där IN inte är snabbare än OR kommer ändå att transformera tillbaka uttrycket till OR, så inget går förlorat på att använda IN. Sida 12 / 79
  • 13. Finjustering och optimering av SQL databaser 02.04.2003 Krister Karlström När en IN operator har en större mängd heltal (integers) som parametrar så är det klokare att fråga ”vad som är ute” än ”vad som är inne”. Därför bör uttrycket: ... WHERE column1 IN (1, 3, 4, 5) transformeras till det något effektivare ... WHERE column1 BETWEEN 1 AND 5 AND column1 <> 2 2.12. LIKE De flesta databashanterare använder index för att leta efter ett LIKE mönster om mönstret börjar med ett konkret tecken, men kommer att undvika index om mönstret börjar med ett jokertecken (antingen % eller _ ). Till exempel, om förfrågningen har ett sådant här villkor: ... WHERE column1 LIKE ’C_F%’ kommer databashanteraren att försöka leta efter matchande data genom att första leta efter alla indexnycklar som börjar med ett ‘C’. Därefter filtreras sådana nycklar bort som inte har ett ’F’ i position tre. Det finns med andra ord inga optimeringstips för en sådan här sökning. I vissa specialfall kan LIKE bytas ut mot en vanlig lika med operator. Exempelvis så är uttryck ett utbytbart mot uttryck två : Uttryck #1 ... WHERE column1 LIKE ’ABC’ Uttryck #2 ... WHERE column1 = ’ABC’ Det finns en liten fallgrop här, de är nämligen inte exakt samma uttryck. I standard SQL beaktar LIKE operatorn inledande tomrum medan lika med operatorn ignorerar inledande extra tomrum. Dessutom kan det hända att LIKE och lika med operatorn inte använder sig av samma teckentabell (collation). Om en kolumn endast är två eller tre tecken bred kan det vara frestande att använda SUBSTRING istället för LIKE. Hur som helst, så är SUBSTRING ett klassiskt exempel på en funktion som inte är effektiv om den får en kolumn som parameter. LIKE operatorn kommer alltid att klå multipla SUBSTRING funktioner. Uttryck ett skall med andra ord alltid omformas såsom uttryck två lyder: Uttryck #1 ... WHERE SUBSTRING(column1 FROM 1 FOR 1) = ‘F’ OR SUBSTRING(column1 FROM 2 FOR 1) = ‘F’ OR SUBSTRING(column1 FROM 3 FOR 1) = ‘F’ Uttryck #2 ... WHERE column1 LIKE ‘%F%’ Sida 13 / 79
  • 14. Finjustering och optimering av SQL databaser 02.04.2003 Krister Karlström Inom en mycket snar framtid så kommer vissa sökningar som idag görs med LIKE att bli onödiga, eftersom fulltextindex blir allt vanligare. 2.13. SIMILAR Operatorn SIMILAR introducerades i SQL3 (SQL:1999). Operatorns syntax och agerande påminner en aning om grep kommandot i Unix. I vissa fall kan användningen av SIMILAR istället för LIKE ge en prestandaökning, SIMILAR är i alla fall inte långsammare än LIKE och därför rekommenderas det ofta att använda SIMILAR istället för LIKE. Exemplet nedan visar ett fall där SIMILAR avsevärt förbättrar förfrågningens prestanda. Uttryck #1 .. WHERE column1 = ’A’ OR column1 = ’B’ OR column1 = ’K’ Uttryck #2 ... WHERE column1 SIMILAR TO ’[ABK]’ 2.14. UNION En UNION är i SQL ett sätt att förena två resultat till ett resultat. UNION operatorn tar hand om att inga dubbla rader uppstår i resultatet även om föreningen består av två sökningar från samma tabell. På grund av detta så är UNION en populär operator vid förening av data från flera olika sökningar. Men är operatorn verkligen det bästa sättet att göra detta på? Ta följande två exempel: Uttryck #1 SELECT * FROM Table1 WHERE column1 = 5 UNION SELECT * FROM Table1 WHERE column2 = 5 Uttryck #2 SELECT DISTINCT * FROM Table1 WHERE column1 = 5 OR column2 = 5 Bägge uttrycken ger upphov till samma slutresultat. Uttryck två kommer i så gott som alla fall att galant slå uttryck ett. Orsaken till detta varierar något mellan olika databashanterare, men den ligger alltid i databashanterarnas automatiska förfrågningsoptimerare. Ena orsaken är att de flesta optimerare endast klarar av att optimera inom samma WHERE sats. Operatorn UNION i uttryck ett kommer därför att separera de två WHERE satserna från varandra, vilket leder till att databashanteraren först skannar column1 för värden som är lika med fem och därefter skannar column2 för värden som är lika med fem. Databashanteraren hamnar alltså att utföra dubbla skanningar! (Med skanning avses en sökning genom hela tabellen, rad för rad.) Därför borde uttryck ett ta dubbelt så lång tid att Sida 14 / 79
  • 15. Finjustering och optimering av SQL databaser 02.04.2003 Krister Karlström utföra än uttryck två, om kolumnerna inte är indexerade. Om kolumnerna är indexerade så klarar de flesta databashanterare av att gottgöra denna prestandaförlust. Andra orsaken, som delvis talar för UNION operatorns fördel, är att några optimerare totalt kommer att vägra använda index om WHERE satsen innehåller OR. Detta försämrar dock inte prestandan mera än vad som vinns på att inte använda UNION. Rådet lyder alltså: Använd hellre OR istället för UNION, speciellt när kolumnerna inte är indexerade. 2.15. EXCEPT Vilket som helst uttryck i stilen A AND NOT B kan omformas till ett uttryck med EXCEPT, och tvärt om. I likhet med UNION så kommer EXCEPT att ge upphov till dubbla skanningar av en tabell eller flere. Dessutom är det väldigt få av de stora databashanterarna som överhuvudtaget stöder EXCEPT, eftersom operatorn har så dålig prestanda. Uttrycken nedan ger bägge samma resultat, uttryck ett rekommenderas. Uttryck #1 SELECT * FROM Table1 WHERE column1 = 7 AND NOT column2 = 8 Uttryck #2 SELECT * FROM Table1 WHERE column1 = 7 EXCEPT SELECT * FROM Table1 WHERE column2 = 8 2.16. CASE Om en förfrågan innehåller mer än ett långsamt funktionsanrop så kan CASE användas för att undvika att funktionsanropet händer två eller flera gånger. Uttryck två nedan visar hur dubbla funktionsanrop kan undvikas med CASE: Uttryck #1 ... WHERE slow_function(column1) = 3 OR slow_function(column1) = 5 Uttryck #2 ... WHERE 1 = CASE slow_function(column1) WHEN 3 THEN 1 WHEN 5 THEN 1 END Sida 15 / 79
  • 16. Finjustering och optimering av SQL databaser 02.04.2003 Krister Karlström 2.17. Syntax Det är även viktigt att hålla en konsekvent stil när man skriver SQL förfrågningar. Ta följande fyra uttryck som exempel: SELECT column1*4 FROM Table1 WHERE COLUMN1 = COLUMN2 + 7 SELECT Column1 * 4 FROM Table1 WHERE column1=(column2 + 7) Istället för att exekvera dessa två ovanstående uttryck bör man istället använda sig av dessa uttryck: SELECT column1 * 4 FROM Table1 WHERE column1 = column2 + 7 SELECT column1 * 4 FROM Table1 WHERE column1 = column2 + 7 Fastän alla dessa uttryck ger samma resultat, så kommer ibland det sista uttrycket att köra snabbare. Detta beror på att vissa databashanterare sparar vad som kan kallas förkompileringar av tidigare körda förfrågningar i en buffert, ibland även riktiga resultat om datan ännu inte har ändrats sen den senaste förfrågningen. En grundförutsättning för att denna buffert skall kunna utnyttjas är att förfrågningen ser exakt likadan ut som den tidigare, inklusive alla mellanslag och stora och små bokstäver. Förutom att förfrågningarna blir lättare att läsa och förstå, så kan alltså en konsekvent skrivstil även bidra till att höja förfrågningens prestanda. Här kommer några tips för en läsbar och entydig syntax: − Nyckelord med stora bokstäver men kolumner med små bokstäver − Tabellnamn har stor första bokstav − Enkla mellanslag mellan varje ord och runtom varje aritmetisk operator 2.18. Datatypkonvertering (CAST) Ibland hamnar man ut för att behöva konvertera en datatyp till en annan. Microsoft påstår i sin online dokumentation över SQL Server 2000 att man aldrig skall använda CAST funktioner om de verkligen inte är nödvändiga. Dessutom avråder de från att skriva ett uttryck som har samma kolumn på bägge sidorna om operatorn om den ena sidan innehåller en datakonvertering med CAST och kolumnen ingår som parameter. Antag att vi har en kolumn innehållande priser, skapad som DECIMAL(7, 2) (7 positioner för hela tal och två decimaler), och ett svar sökes på frågan ”Vilka priser är jämna priser?”. Nedan finns tre olika lösningar till detta problem: Uttryck #1 ... WHERE MOD(decimal_column, 1) = 0 Uttryck #2 ... WHERE CAST(decimal_column AS CHAR(7)) LIKE ’%.00%’ Sida 16 / 79
  • 17. Finjustering och optimering av SQL databaser 02.04.2003 Krister Karlström Uttryck #3 ... WHERE decimal_column = CAST(decimal_column AS INTEGER) Så vilket uttryck är det bästa? Om den kunskap som tidigare presenterats i detta kapitel tillämpas kan följande konstateras: Uttryck ett är definitivt det sämsta alternativet. Fastän det inte finns någon CAST operation så innehåller uttrycket en underförstådd och oundviklig datakonvertering från DECIMAL till INTEGER, eftersom MOD operatorn enbart fungerar med INTEGER. Uttrycket innehåller även en underförstådd division, i det här fallet division med ett. Uttryck två är det näst bästa uttrycket. Vissa databashanterare lagrar datatypen DECIMAL fysiskt som CHAR, vilket gör datakonverteringen vä ldigt enkel. Nackdelen är att LIKE operatorn börjar med ett jokertecken, vilket gör den mindre effektiv. Uttryck tre är den klara vinnaren, även om Microsoft troligtvis skulle hävda något annat. Om man jämför alla tre uttrycken och beräknar deras poäng enligt tabellen i stycke 2.2 så kommer uttryck tre att få den högsta prestandapoängen. Den höga poängen uppnås genom att uttrycket använder sig av lika med operatorn och har de enklaste operanderna. 3. Sortering av data 3.1. Allmänt om sortering Sortering av data är en av de vanligaste förekommande funktionerna som behövs i alla sammanhang. Trots att en mängd olika algoritmer, den ena bättre än den andra, har utvecklats är sortering än idag en resurs- och tidskrävande process. Man bör med andra ord verkligen vara på det säkra med att sortering verkligen behövs innan man slänger in en sorteringssats i SQL. Sortering av stora resultatmängder kräver enorma mängder minnesutrymme och cpu kraft, vilket leder till att även andra användare av databasservern känner av sorteringens bieffekter. Ibland inträffar sortering i databashanteraren utan att man kanske tänker på det eller över huvudtaget vet om det. SQL funktionerna GROUP BY, DISTINCT, CREATE [UNIQUE] INDEX och UNION kan kalla på samma sorteringsalgoritmer som används av ORDER BY. Mera sällsynt är de fall där databashanteraren väljer att sortera två listor när den behöver utföra en inner join eller outer join, men enstaka fall finns. I dessa fall är sorteringen endast en bieffekt, men det här kapitlet handlar främst om sorteringen som händer vid ORDER BY. 3.2. Sorteringseffektivitet Sortering har varit ett populärt ämne för forskning i flera decennier. Det är inte svårt att hitta en lämplig sorteringsalgoritm för datan som behöver sorteras. Även databashanterarna besitter en större repertoar över tillgängliga sorteringsalgoritmer. Det som påverkar sorteringseffektiviteten i en databashanterare är ganska långt hur komplext sorteringsvillkoret är och hur bred den data är som skall sorteras. Sist och slutligen så påverkar den osorterade datans ordning ganska så lite hur snabbt sorteringen blir klar. Sida 17 / 79
  • 18. Finjustering och optimering av SQL databaser 02.04.2003 Krister Karlström Det är först och främst tre saker som påverkar sorteringens hastighet. Nedan är de nämnda i viktighetsordning. 1. Antalet rader sorteringen omfattar 2. Antalet kolumner som nämns i ORDER BY satsen 3. Databredden på de kolumner som nämns i ORDER BY satsen Det lönar sig alltså att försöka omfatta så få rader som möjligt när man sorterar. Generellt kan man säga att om antalet rader i sorteringen ökar tiofalt så ökar sorteringstiden tjugofalt. En ökning av antalet kolumner i ORDER BY satsen ökar behovet av sorteringstid mera än vad motsvarande ökning i kolumnens databredd gör. Det lönar sig att slå ihop två kolumner före sorteringen, om det bara är möjligt, för att undvika två kolumner i ORDER BY satsen. Sorteringskolumnens databredd påverkar även sorteringstiden. Om det är möjligt kan man använda exempelvis SUBSTRING för att minska databredden på det data som skall sorteras. Det hjälper även om några av de första tecknen är unika sinsemellan eller om det redan finns någon form av ordning bland den osorterade datan. Genom att lägga till flera processorer och mera minne i databasservern kan man försnabba sorteringarna. De flesta sorteringsalgoritmer och databashanterare klarar nämligen av att utnyttja flera processorer och kan dela upp sorteringen i flera parallella trådar. Om dessutom centralminnet är tillräckligt stort så att all data ryms i minnet går det ännu snabbare. 3.3. Sorteringshastighet hos olika datatyper VARCHAR Det är alltid den definierade databredd som bestämmer hur lång tid det tar att sortera. En kolumn med varierbar databredd, såsom VARCHAR, har alltid en definierad bredd och en aktuell bredd. Om en VARCHAR(30) kolumn innehåller strängen ’ABC’ så är dess definierade bredd 30 tecken medan den aktuella bredden är tre tecken. Vid sortering så är det den definierade bredden på 30 tecken som bestämmer sorteringstiden. Detta beror troligen på att de flesta databashanterare redan på förhand allokerar det minnesutrymme sorteringen behöver. Minnet allokeras enligt det största möjliga behovet, vilket i det här fallet kan vara 30 tecken per rad. SMALLINT I Windows miljö, där en INTEGER är 32-bitar och en SMALLINT är 16-bitar, kunde man kanske tro att sortering av 16-bitars ord skulle vara snabbare än sortering av 32-bitars ord. Eftersom processorns ordstorlek i de flesta fall är 32-bitar, och databashanteraren kan utnyttja fulla bredden, är det vanligen snabbare att jämföra integers. Sida 18 / 79
  • 19. Finjustering och optimering av SQL databaser 02.04.2003 Krister Karlström CHAR Datatypen CHAR är som bekant 8-bitar. Om man skapar en kolumn som CHAR(4) så blir den totala bredden 4 x 8 = 32-bitar. INTEGER är också 32-bitar. Går det då lika snabbt att sortera en CHAR(4) kolumn som en INTEGER kolumn? Tyvärr inte, åtminstone inte i de flesta fall och hos de flesta databashanterare. Orsaken torde ligga i att en teckensträng inte kan jämföras 4 byte i gången. Däremot går det bra att jämföra 4 byte i gången när man jämför med INTEGER. INTEGER Den stora vinnaren är alltså datatypen INTEGER. Den sorteras alltid snabbast och passar perfekt in i 32-bitars arkitektur. När dessutom databashanteraren har sina knep för att utnyttja full databredd så är datatypen oslagbar i prestanda. Däremot tar datatypen fysiskt upp mera plats än 16-bitars SMALLINT och 8-bitars CHAR och vill man optimera databasens fysiska storlek skall man givetvis alltid välja så liten datatyp som möjligt. 3.4. ORDER BY Syntaxen för en ORDER BY sats är följande: SELECT <column list> FROM <Table list> ORDER BY <column expression> [ASC | DESC] [,...] I SQL-92 var ORDER BY lite mera begränsad än vad den är idag i SQL:1999. I SQL-92 var man tvungen att ha ett kolumnnamn som dessutom måste ingå i SELECT listan. I SQL:1999 är det tillåtet att ha ett helt uttryck som operand till ORDER BY. För att exempelvis sortera nummer i fallande ordning finns det idag två olika alternativ att göra detta på: SQL-92 SELECT numeric_column, numeric_column * -1 AS num FROM Table1 ORDER BY num SQL:1999 SELECT numeric_column FROM Table1 ORDER BY numeric_column * -1 Resultatet av ORDER BY numeric_column * -1 kan särskilja sig en aning från resultatet av ORDER BY numeric_column DESC. Skillnaden beror på om NULL värden existerar och hur databashanteraren behandlar dessa. 3.5. Att sortera eller att inte sortera ”Do not use ORDER BY if the query has a DISTINCT or GROUP BY on the same set of terms, because they have the side effect of ordering rows.” - Kevin Kline et al., Transact-SQL Programming, O’Reilly & Associates Det är ingen idé att sortera data om den redan är sorterad. Det finns en mängd situationer där så är fallet, där sådana operationer används vilka har som bieffekt att de sorterar data. Sida 19 / 79
  • 20. Finjustering och optimering av SQL databaser 02.04.2003 Krister Karlström Åtminstone följande satser returnerar data i sorterad ordning utan att innehålla en ORDER BY sats: − SELECT column1 FROM Table1 Returnerar sorterad data om Table1 är grupperad (clustered) och column1 är grupperingsnyckeln (cluster key) eller om tabellen annars råkar vara färdigt sorterad. − SELECT column1 FROM Table1 WHERE column1 > -32768 Returnerar sorterad data om column1 är indexerad i stigande ordning och databashanteraren använder index. − SELECT DISTINCT column1 FROM Table1 Returnerar sorterad data om column1 inte är UNIQUE. Om ORDER BY fogas till någon utav dessa SQL förfrågningar blir resultatet endast en försämring i prestanda. Databashanterarna tar heller inte automatiskt bort onödiga ORDER BY satser. 3.6. Sorteringsnycklar Utelämnande av ORDER BY satsen och påtvingande av sortering via teckentabeller och ordlistor är två olika metoder som kan användas för att snabba upp en sortering. Bägge alternativen är sådana lösningar som kanske inte stöds av alla databashanterare. Det finns tre saker man kan göra som försnabbar sortering utan att kräva speciellt stöd från databashanteraren: sorteringsnycklar, påtvingande av index och försortering. Om man behöver sortera data enligt exotiska teckenuppsättningar, eller annars bara behöver en djup sortering, sjunker sorteringshastigheten drastiskt. Bägge dessa problem är lösbara – addera en kolumn som innehåller sorteringsnycklar till tabellen. En sorteringsnyckel är en teckensträng bestående av en sekvens av en-bytes nummer. Sorteringsnyckeln representerar den relativa ordningsföljden mellan tecknen. Det finns en färdig funktion i MS Windows NT API för att konvertera vilken teckensträng som helst till en användbar sorteringsnyckel. Funktionen heter LCMapString. Funktionens indata kan vara vilka bokstäver som helst i någon av de teckenuppsättningar som stöds av Windows NT och funktionens utdata är en sorteringsnyckel automatiskt viktad enligt de lokaliserade inställningar som gjorts i systemet. Sida 20 / 79
  • 21. Finjustering och optimering av SQL databaser 02.04.2003 Krister Karlström Nedan finns ett skript för att skapa sorteringsnycklar för varje rad i en tabell. locale_id = an ID representing country/collation/etc. ... DECLARE Cursor1 CURSOR FOR SELECT character_column FROM Table1; OPEN Cursor1; for(;;) { FETCH Cursor1 INTO :character_string; if(NO_DATA_FOUND) break; LCMapString(locale_id, character_string, sort_key); UPDATE Table1 SET sort_key_column = :sort_key WHERE CURRENT OF Cursor1; } ... För att skriptet skall fungera bör en kolumn sort_key_column ha skapats på förhand med förhandsinställd binär sortering. Eftersom kolumnen använder sig utav binär sortering går det alltså lika snabbt att sortera teckensträngarna som med vilken binär sortering som helst, vilket är mycket snabbare än någon ordlistbaserad sortering. När tabellen innehåller en kolumn med sorteringsnycklar kan teckensträngarna hämtas i sorterad ordning med följande förfrågan: SELECT * FROM Table1 ORDER BY sort_key_column 3.7. Sortering med index Sorteringen kan snabbas upp om man kan uppmuntra databashanteraren att utnyttja eventuella index. Nyckelordet WHERE orsakar i de flesta databashanterare ett försök att hitta posten i ett index. Uttryck två kan vara snabbare än uttryck ett, om coulumn1 är indexerad: Uttryck #1 SELECT * FROM Table1 ORDER BY column1 Uttryck #2 SELECT * FROM Table1 WHERE column1 >= ’’ ORDER BY column1 Sida 21 / 79
  • 22. Finjustering och optimering av SQL databaser 02.04.2003 Krister Karlström I kapitel 3.5 konstaterades att en sökning med WHERE returnerar resultatet i sorterad ordning om kolumnen är indexerad. Detta kan dock inte tas för en självklarhet och ORDER BY kan därför inte lämnas bort. Hur som helst så blir hela sorteringen snabbare eftersom ORDER BY utförs snabbare om raderna redan är något så när sorterade. Ett par saker är värt att noteras om detta trick: − ORDER BY satsen kan inte utelämnas eftersom det inte finns några garantier för att databashanterare faktiskt använder indexsökningar. WHERE satsen skall närmast ses som en uppmuntran till användning av index. − WHERE satsen orsakar att alla NULL värden filtreras bort vilket kanske inte är ändamålet alltid. 4. Gruppering av data 4.1. GROUP BY Eftersom GROUP BY kan orsaka extra sorteringar så stämmer en stor del av de saker som nämndes for ORDER BY i föregående kapitel även för GROUP BY. Det lönar sig med andra ord att så långt som möjligt undvika GROUP BY och om de används bör de vara uppbyggda så enkla som möjligt. GROUP BY kan direkt användas i SQL förfrågningar: SELECT column1 FROM Table1 GROUP BY column1 Gruppering inträffar ibland även underförstått utan att själva nyckelordet GROUP BY finns med i klartext. För exempel, HAVING och dataihopsamlingsfunktioner såsom AVG, COUNT, MAX, MIN, SUM och andra ger upphov till gruppering av data: SELECT COUNT(*) FROM Table1 HAVING COUNT(*) = 5 Vissa databashanterare stöder extra parametrar och uttryck till GROUP BY och en del kan ha extra inbyggda funktioner för dataanalys, till exempel STDEV för standardavvikelse. 4.2. Optimal gruppering Precis som för ORDER BY så blir GROUP BY långsammare för varje extra kolumn som läggs till i listan över grupperingskolumner. Grupperingslistan kan hållas så kort som möjligt genom att se till att inga överflödiga kolumner sätts till. Följande exempel visar ett fall där en överflödig kolumn finns i grupperingslistan: SELECT secondary_key_column, primary_key_column, COUNT(*) FROM Table1 GROUP BY secondary_key_column, primary_key_column Sida 22 / 79
  • 23. Finjustering och optimering av SQL databaser 02.04.2003 Krister Karlström Eftersom kolumnen innehållande primärnyckeln är unik och inte kan innehålla NULL värden, är secondary_key_column i grupperingslistan överflödig och kunde uteslutas. Problemet är att om secondary_key_column tas bort ut grupperingslistan genereras ett felmeddelande. Alla databashanterare (förutom MySQL och Sybase) kommer att vägra ha secondary_key_column i selectlistan om den inte är med i grupperingslistan. En lösning, som är snabbare, kunde vara följande: SELECT MIN(secondary_key_column), primary_key_column, COUNT(*) FROM Table1 GROUP BY primary_key_column GROUP BY har som tendens att resultera i ett färre antal rader och JOIN tenderar att utöka antalet rader i ett resultat. Den korrekta ordningen vore alltså att första utföra alla grupperingsfunktioner (GROUP BY) innan föreningsfunktionerna (JOIN) utförs. Databashanteraren är tvungen att första utvärdera resultaten från FROM och WHERE innan grupperingsfunktioner kan inledas, vilket gör det svårt att utnyttja detta tänkande. I vissa fall är det möjligt att påverka ordningsföljden och således optimera förfrågningen. Om en databashanterare med stöd för mängdfunktioner (UNION, EXCEPT, INTERSECT) används så kan en SQL förfrågan som består av både GROUP BY och JOIN skrivas om till två förfrågningar som enbart utför GROUP BY, förenade av INTERSECT. Uttryck #1 SELECT SUM(Table1.column2), SUM(Table2.column2) FROM Table1 INNER JOIN Table2 ON Table1.column1 = Table2.column1 GROUP BY Table1.column1 Uttryck #2 SELECT column1, SUM(column2), 0 FROM Table1 GROUP BY column1 INTERSECT SELECT column1, 0, SUM(column2) FROM Table2 GROUP BY column1 Uttryck två utförs snabbare på grund av att ingen JOIN behöver utföras. Dessutom sparar operationen den mängd arbetsminne som behövs för att utföra förfrågningen. Här finns dock ett stort portabilitetsproblem. Många utav de stora tillverkarna för databashanterare stöder inte INTERSECT. Dessa är åtminstone Informix, Ingres, InterBase, Microsoft, MySQL och Sybase. För dessa databashanterare fungerar alltså inte den optimering som här presenterades. När man grupperar resultat som uppstått via en JOIN är det effektivast om grupperingskolumnen är från samma tabell som kolumnen på vilken en eventuell mängdfunktion tillämpas på. Det här tipset nämns i en del tillverkares manualer. Sida 23 / 79
  • 24. Finjustering och optimering av SQL databaser 02.04.2003 Krister Karlström Prestandan kan som bekant ökas genom att undvika JOIN. Eftersom GROUP BY inträffar vid användning av mängdfunktioner så kan uttryck ett skrivas om till uttryck två, om Table1.column1 är unik, för att undvika en JOIN. Uttryck #1 SELECT COUNT(*) FROM Table1, Table2 WHERE Table1.column1 = Table2.column1 Uttryck #2 SELECT COUNT(*) FROM Table2 WHERE Table2.column1 IN (SELECT Table1.column1 FROM Table1) 4.3. HAVING De flesta databashanterare sammanfogar inte WHERE och HAVING satser. Följande två uttryck ger exakt samma resultat, men uttryck två är snabbare. Uttryck #1 SELECT column1 FROM Table1 WHERE column2 = 5 GROUP BY column1 HAVING column1 > 6 Uttryck #2 SELECT column1 FROM Table1 WHERE column2 = 5 AND column1 > 6 GROUP BY column1 Endast sådana fall där jämförelsen av column1 är svår och tidskrävande kan dra nytta av att utföra jämföringen först i HAVING skedet. 4.4. Alternativ till GROUP BY Om man skriver en förfrågan som inte involverar mängdfunktioner kan man ofta använda DISTINCT som ett alternativ till GROUP BY. DISTINCT har tre fördelar framom GROUP BY: 1. Operatorn är mycket enkel 2. Operatorn kan användas i mera komplicerade uttryck 3. Databashanterare kör ofta DISTINCT snabbare än GROUP BY Det är effektivare att använda uttryck två istället för uttryck ett: Uttryck #1 SELECT column1 FROM Table1 GROUP BY column1 Uttryck #2 SELECT DISTINCT column1 FROM Table1 Sida 24 / 79
  • 25. Finjustering och optimering av SQL databaser 02.04.2003 Krister Karlström 4.5. Gruppering och sortering När en GROUP BY utförs så kommer databashanteraren att behöva leta efter dubbletter av data. Därför utförs nästan alltid en sortering före grupperingen kan utföras. Tabellen nedan visar två listor, en osorterad och en sorterad. Osorterad lista Sorterad lista Belgrad Belgrad Sofia Budapest Budapest Sofia Antag att en gruppering enligt ”Belgrad” skall utföras i den osorterade tabellen. Hur vet databashanteraren om det finns dubbletter av ”Belgrad”? Jo, databashanteraren är tvungen att gå igenom hela tabellen och jämför ”Belgrad” med både ”Sofia” och ”Budapest” innan den vet om det finns dubbletter. Den kan inte vara säker på att inga dubbletter finns förrän den har gått igenom hela tabellen, eftersom datan ligger i slumpmässig ordning. Om man däremot utför en gruppering enligt ”Belgrad” i den sorterade tabellen räcker det med att databashanteraren jämför ”Belgrad” med nästa rad, eftersom datan ligger i stigande alfabetisk ordning. Så fort som databashanteraren jämfört [n] med [n + 1] är den klar. Detta betyder alltså att en gruppering går snabbt om tabellen är sorterad. Ett annat effektivt sätt att utföra grupperingar är att använda en hash-lista för att snabbt kunna kontrollera om det finns likadana poster. Informix använder sig (enligt uppgifter) utav hash tekniken. Eftersom en GROUP BY föregås av en ORDER BY kan man alltså effektivera grupperingen genom att ge kolumnerna i samma ordningsföljd både i ORDER BY satsen och i GROUP BY satsen. Skriv inte en SQL förfrågan på det här viset: SELECT * FROM Table1 GROUP BY column1, column2 ORDER BY column1 Skriv den istället på det här viset: SELECT * FROM Table1 GROUP BY column1, column2 ORDER BY column1, column2 Den här optimeringen gäller för databashanterare som inte använder sig utav hash listor. Har man en sådan hanterare skall man använda sig utav den första varianten, alternativ två blir långsammare i ett hash-baserat system. 4.6. Gruppering med index De flesta databashanterare använder index vid datagrupperingar. Som redan tidigare nämndes, så underlättas en gruppering om tabellen är sorterad. Att kunna utnyttja index är egentligen samma sak som att ha tabellen sorterad, inte riktigt samma sak, men i alla fall är beteendet i det här sammanhanget väldigt lika. Här är det även värt att tänka på att GROUP BY har en lägre prioritet än JOIN och WHERE satser. GROUP BY kommer med andra ord att utföras efter en eventuell JOIN eller WHERE sats. Sida 25 / 79
  • 26. Finjustering och optimering av SQL databaser 02.04.2003 Krister Karlström Följande exempel kan inte utnyttja ett index för att snabba upp grupperingen: SELECT column1 FROM Table1 WHERE column2 = 55 GROUP BY column1 Orsaken till att index inte kan användas av GROUP BY torde vara ganska så klar. Databashanteraren utför WHERE satsen först och den jämförelsen görs via index över column2. Efter att detta är gjort fortsätter den med GROUP BY satsen, me n nu är det ingen idé längre att utnyttja index över column1, eftersom indexet är ett index över hela tabellen och inte över den data som WHERE satsen filtrerat fram. På grund av detta fenomen är index endast lämpliga för små och enkla grupperingar. Lyckligtvis är de flesta grupperingar som görs så pass enkla att ett index kan utnyttjas. Grupperingar där GROUP BY står ensamt och grupperingskolumnerna är i samma ordningsföljd som de indexerade kolumnerna i selectlistan kommer att utnyttja ett index. Följande exempel utför grupperingen snabbare, om column1 har ett index: SELECT column1 FROM Table1 GROUP BY coumn1 4.7. COUNT Funktionen COUNT kommer att utnyttja ett index om endast en kolumn (eller *) finns i selectlistan och om inga flera satser följer FROM satsen. Följande förfrågan utförs snabbare av vissa databashanterare om tabellen har ett index: SELECT COUNT(*) FROM Table1 COUNT har dessutom väldigt ofta databasspecifika specialoptimeringar, vilket kan göra det svårt att försöka tillföra ytterligare optimering. 4.8. SUM och AVG Index Även funktionerna SUM och AVG kommer att utnyttja ett index om endast en kolumn finns med i selectlistan och den kolumnen är indexerad. Tyvärr utförs inte funktionerna snabbare med hjälp av indexet – tvärtom, det tar ofta längre tid att utföra funktionerna om de utförs på indexerade kolumner. På längre sikt kan man ändå anse att de förluster som uppstår är väldigt små i förhållande till de vinster som uppnås via index hos COUNT, MAX och MIN. Följande exempel utförs ofta långsammare om column1 är indexerad: SELECT SUM(column1) FROM Table1 WHERE coumn1 > 5 Sida 26 / 79
  • 27. Finjustering och optimering av SQL databaser 02.04.2003 Krister Karlström Precision Precision kan även bli ett dilemma om man vill summera en större mängd flyttal av datatypen FLOAT. Speciellt om följande två fall förekommer kan det bli problem med precisionen: (a) talens exponenter varierar mycket sinsemellan och (b) när många subtraktionen inträffar på grund av negativa tal. Förståeliga förluster i precision kan även inträffa när man lagrar tal som avses vara exakta som FLOAT. Ta till exempel det ”exakta” talet 0.1 och lagra det i en FLOAT kolumn. Addition tio gånger av den kolumnen behöver nödvändigtvis inte ge slutresultatet 1.0, på grund av precisionsförluster. Högre flyttalsprecision kan uppnås genom att konvertera talen till en datatyp med högre precision innan summeringsfunktioner tillämpas, exempelvis till datatypen DECIMAL eller DOUBLE. Märk väl att det är viktigt att konvertera varje enskilt tal före additionen, inte själva additionsresultatet. Uttryck ett ger alltså ingen bättre precision, men uttryck två kan förbättra precisionen hos slutresultatet: Uttryck #1 SELECT CAST(SUM(column1) AS DECIMAL(10)) FROM Table1 Uttryck #2 SELECT SUM(CAST(column1 AS DECIMAL(10))) FROM Table1 Precisionen blir även en aning bättre om man använder SUM(x + y) istället för SUM(x) + SUM(y) eftersom det totala antalet additioner blir färre. Däremot blir precisionen bättre om man använder SUM(x) – SUM(y) istället för SUM(x - y) därför att det totala antalet subtraktioner blir färre. Med heltal kan precisionsförluster aldrig uppstå. Däremot kan man i långsökta fall få problem med begränsningar och spill (overflow) kan uppstå. Om man försöker addera två celler som bägge innehåller talet 2 miljarder så uppstår spill eftersom summan 4 miljarder kan inte representeras med 32-bitar. Problemet kan lösas med datatypkonvertering. För system som stöder datatypen BIGINT kan alternativ ett användas. Vill man hålla sig till standard SQL skall alternativ två användas. Alternativ #1 SELECT SUM(CAST(column1 AS BIGINT)) FROM Table1 Alternativ #2 SELECT SUM(CAST(column1 AS DECIMAL(10))) FROM Table1 4.9. MAX och MIN Funktionerna MAX och MIN kommer att utnyttja ett index om funktionen är ensam i selectlistan och inga andra satser följer efter FROM satsen. Om ett index existerar blir dessa två funktioner väldigt enkla och snabba att utföra. Funktionen MIN kan direkt läsa det minimala värdet från det första indexvärdet och funktionen MAX får direkt det maximala värdet genom att avläsa det sista indexet (eller tvärtom i ett fallande index, som i IBM). Databashanterarna löser följande problem väldigt lätt om tabellen har ett index: SELECT MIN(column1) FROM Table1 Sida 27 / 79
  • 28. Finjustering och optimering av SQL databaser 02.04.2003 Krister Karlström Men följande exempel är en betydligt svårare nöt att knäcka och tar betydligt längre tid att utföra: SELECT MIN(column1), MAX(column1) FROM Table1 Villkoret för indexanvändning var ju att funktionen skulle vara ensam i selectlistan, därför tar detta exempel väsentligt mera tid på sig att utföras. I sådana här situationer är det bättre att skriva förfrågningen som två skilda SELECT satser, eller förena dem med en UNION. Följande exempel rekommenderas: SELECT MIN(column1) FROM Table1 SELECT MAX(column1) FROM Table1 5. Joins (Kopplingar, Föreningar) 5.1. Bakgrundsinformation om föreningar av tabeller En gång i tiden fanns det en enkel regel för joins – indexera alla kolumner och föreningar av tabeller kommer att gå snabbt. Medan databashanterarna har utvecklats och blivit mera sofistikerade har flera olika strategier för olika typer av föreningar tagits fram. De flesta databashanterare innehar en ganska stor repertoar utav dessa och optimering av föreningar är idag mera raffinerande än förut. Det första man bör hålla i färskt minne när man planerar förfrågningar som förenar flera tabeller är att man inte vill utföra föreningen själv. Vad man däremot vill göra är att ge databashanteraren de bästa förutsättningarna för att själv kunna välja den mest lämpliga och effektivaste föreningsplanen, i slutändan kommer detta att påverka effektiviteten en hel del. I den förenklade modellen av en förening av två tabeller blir resultattabellens storlek produkten av de tabeller som förenades. Den förenar alltså alla rader i ena tabellen med alla rader i den andra tabellen. Om Tabell1 innehåller värdena {A, B} och Tabell2 innehåller {C, D} så blir produkten { (A, C) (A, D) (B, C) (B, D) }. Realisten märker att en del av dessa rader troligtvis kommer att filtreras bort av ett villkor, som dessutom körs först efter att tabellerna multiplicerats. Det väsentliga i detta exempel är att visa på den enorma mängden med tillfälligt minne som behövs för att lagra den temporära tabellen. Det behövs alltså någon bättre algoritm för att utföra en förening i praktiken. Valet av vilken algoritm och plan som väljs beror på om tabellen har index, tabellens storlek samt på indexets selektivitet. Selektivitet är ett mått på olikheten mellan unika värden i ett index. Sida 28 / 79
  • 29. Finjustering och optimering av SQL databaser 02.04.2003 Krister Karlström 5.2. Nested-Loop Joins ”Nested-Loop joins” kan översättas till ”förening med inbäddade upprepningar”, eller nåt i den stilen. Principen är två eller flera upprepningar inuti varandra där varje upprepning går igenom en specifik tabell. Alla algoritmer enligt ”Nested-Loop joins” principen baserar sig på någon variant av pseudokoden nedan: for (each row in Table1) { for (each row in Table2) { if (Table1 join column matches Table2 join column) pass else fail } } − Ordet “matches” betyder vanligtvis “lika med” eftersom majoriteten av alla föreningar är “lika- med-föreningar”, alltså sådana som använder lika med operatorn. Ett känt namn på dessa i det engelska språket är ”equijoins”. − Ordet ”pass” syftar på att raderna från bägge tabellerna skall tillfogas den temporära resultattabellen. Vanligtvis så sparar databashanterarna endast det ID som förknippas med raderna. Redan den här algoritmen är en stor förbättring jämfört med det grundscenario som beskrevs i introduktionen till detta kapitel. Nu behöver den temporära tabellen inte vara fullt så stor som produkten av bägge tabellerna. Men fortfarande så behövs ett gigantiskt antal jämförelser. Jämförelser är dock snabba operationer, speciellt om bägge talen som skall jämföras ligger i databashanterarens buffertminne. Genom att utöka den algoritm som nyss presenterades så att den tar sig an en sida i gången från en tabell kan prestandan höjas betydligt. Sidstorleken bestäms i databasserverns konfiguration, men typiska värden är 4 KB, 8 KB eller 16 KB. Genom att öka denna buffert så kan större sidor rymmas i buffertminnet och algoritmen körs snabbare. for (each page in Table1) { for (each page in Table2) { for (each row in Table1-page) { for (each row in Table2-page) { if (join column matches) pass else fail } } } } Denna algoritm utnyttjar buffertminnet genom att dela upp tabellerna i sidor och behandla en sida från varje tabell i gången. Om dessutom de kolumner som jämförs har index så behövs ingen skanning av hela sidan, det räcker med att slå upp värdet ur indexet. Den innersta upprepningen bör bestå av den enklare, alternativt den mindre, tabellen eller åtminstone ha ett index för att algoritmen skall bli ännu effektivare. Sida 29 / 79
  • 30. Finjustering och optimering av SQL databaser 02.04.2003 Krister Karlström Oftast finns det ett villkor med i spelet när man förenar tabeller med varandra, förutom det villkor som definierar föreningen. Den tabell som har ett filtreringsvillkor bör placeras som den yttre tabellen i upprepningen så att det totala antalet upprepningar av den inre upprepningen minskas, annars upprepas filtreringsvillkoret om och om igen på tok för många gånger. Databashanteraren väljer vilken tabell som blir den yttre och vilken som blir den inre på basen av följande punkter: − Den minsta tabellen skall vara i den inre upprepningen, speciellt om den är mycket liten. − Tabellen med det bästa indexet skall vara i den inre upprepningen. − Den tabell som har ett filtreringsvillkor skall vara i den yttre upprepningen. De här punkterna bör man tänka på när man skriver förfrågningar som förenar tabeller. Motverka inte databashanterarens vilja genom att placera ett filtreringsvillkor på fel tabell! Om tabell ett är mindre och har ett bättre index, placera då ett eventuellt filtreringsvillkor på tabell två istället – tabell två passar ju ändå bättre i den yttre upprepningen. Så här långt har allt handlat om enkla föreningar av typen Table1.column1 = Table2.column1. Förståss kan det bli lite mera komplicerat än så här. Det kan till exempel finns två föreningsvillkor som i följande exempel: SELECT * FROM Table1, Table2 WHERE Table1.column1 = Table2.column1 AND Table1.column2 = Table2.column2 I det här fallet räcker det inte med att försäkra att column1 och column2 i den inre upprepningen är indexerade. Om ett sammansatt index skapas för column1 och column2 i den inre upprepningen, istället för två individuella index, sker den totala föreningen betydligt snabbare! Det är dessutom mycket viktigt att bägge kolumnerna som jämförs i den inre upprepningen har exakt samma datatyp och exakt samma storlek – annars kan det hända att databashanteraren inte alls utnyttjar indexet. Sida 30 / 79
  • 31. Finjustering och optimering av SQL databaser 02.04.2003 Krister Karlström 5.3. Sort-Merge Joins En ”Sort-Merge Join” är en aning mera komplicerad än en ”Nested-Loop Join”, men den går fortfarande att beskriva med ett par rader pseudokod: sort (Table1) sort (Table2) get first row (Table1) get first row (Table2) for (;;until no more rows in tables) { if(join-column in Table1 < join-column in Table2) get next row (Table1) elseif (join-column in Table1 > join-column in Table2) get next row (Table2) elseif (join-column in Table1 = join-column in Table2) { pass get next row (Table1) get next row (Table2) } } Algoritmen börjar med att sortera de bägge tabellerna som skall förenas. Skedet kallas ”sorteringsskedet”, därifrån kommer den första halvan i namnet ”Sort-Merge”. Andra halvan kommer givetvis från nästa skede. I föreningsskedet (”Merge” skedet) av en “Sort- Merge Join” går databashanteraren alltid framåt i bägge tabellerna. Den behöver aldrig gå igenom en viss rad i en tabell mer än en gång – en stor förbättring jämfört med ”Nested- Loop Join”. Nackdelen är den sortering som krävs innan den fina algoritmen kan uträtta sitt arbete. Effektiva sorteringsalgoritmer borde ändå vara betydligt snabbare än jämförelser av allt mot allt (som är fallet i ”Nested-Loop Joins”). Det är bevisbart att ”Sort-Merge Join” är betydligt snabbare än ”Nested- Loop Join” om bägge tabellerna är väldigt stora. Trots alla fördelar så väljer databashanterarna väldigt ofta att hellre använda ”Nested- Loop Joins” än ”Sort-Merge Joins” därför att de kräver mindre mängd minne, är flexiblare och kräver inga extra tunga databehandlingar i startögonblicket. Följande exempel visar en idealisk situation för att utföra en ”Sort-Merge Join”: SELECT * FROM Table1, Table2 WHERE Table1.column1 = Table2.column1 Det som gör denna förening ideal för ”Sort-Merge” är lika med operatorn och avsaknaden av filtreringsvillkor. En typisk SQL förfrågan för att generera en rapport. Om dessutom Table1 och Table2 är sorterade på förhand enligt samma nyckel så är det den perfekta situationen för en ”Sort-Merge”. Sida 31 / 79
  • 32. Finjustering och optimering av SQL databaser 02.04.2003 Krister Karlström Användningen av ”Sort-Merge Joins” kan innebära lite extra jobb för databasadministratorn. Hos vissa databashanterare måste nämligen ”Sort-Merge Joins” aktiveras manuellt. Dessutom kan det vara en god idé att se över storleken på serverns centralminne om man tänkt använda sig utav den här föreningstekniken. 5.4. Hash Joins En ”Hash-Join” är ännu en algoritm för att skapa en förenad tabell. Processen att förena Table1 och Table2 går enligt följande modell: − Beräkna ett hash värde för varje rad i Table1. Spara hash värdet i en tillfällig tabell i minnet. − Beräkna ett hash värde för varje rad i Table2. Kontrollera om hash värdet redan finns i den tillfälliga tabellen i minnet. Om det finns, så finns det en koppling. Om det inte finns, så finns det ingen koppling. En ”Hash-Join” är med andra ord en variant av en ”Nested-Loop Join” vars inre tabells rader är uppslagna via hash värden istället för via index. De flesta databashanterare upprätthåller inga statiska tabeller med hash värden, så en temporär tabell med hash värden för Table1 behöver skapas innan föreningen kan inledas. Hash tabellen kan frigöras när den förenade tabellen är klar. Följande fyra krav måste alla vara uppfyllda för att en ”Hash-Join” skall fungera vettigt: 1. Det finns tillräckligt med minne reserverat för den temporära tabellen innehållande alla hash värden. Inställningen är oftast konfigurerbar. 2. Föreningen är en ”equijoin” (den använder lika med operatorn). 3. Värdena i den inre tabellens föreningskolumn är unika och producerar unika hash värden. 4. Den inre tabellen genomsöks många gånger eftersom den yttre tabellen är betydligt större och innehåller många rader som inte filtrerats ut utav något filtreringsvillkor. Krav nummer ett kan kompenseras för genom att dela upp den yttre tabellen i flera bitar och beräkna hash värden för varje bit i gången. Därefter utförs föreningensmomentet och när det är klart beräknas nya hash värden för nästa bit av den yttre tabellen o.s.v. Detta kan ge upphov till att den inre tabellen skannas många gånger, men det är i alla fall bättre än att ha en gigantisk hash tabell för den yttre tabellen, som dessutom kanske mappas ner på hårddisken om den inte ryms i centralminnet. 5.5. Undvikning av Joins En förening av två tabeller kan ibland undvikas genom att transformera en SQL förfrågan till en enklare, utan förening, men ändå likvärdig. Följande exempel utnyttjar en av grundprinciperna i SQL optimering, spridning av konstanta värden. Uttryck ett kräver en förening av Table1 och Table2 medan uttryck två inte behöver någon förening. Ett krav är dock att bägge kolumnerna är indexerade. Uttryck #1 SELECT * FROM Table1, Table2 WHERE Table1.column1 = Table2.column1 AND Table1.column1 = 55 Sida 32 / 79
  • 33. Finjustering och optimering av SQL databaser 02.04.2003 Krister Karlström Uttryck #2 SELECT * FROM Table1, Table2 WHERE Table1.column1 = 55 AND Table2.column1 = 55 En sådan här situation uppstår alltid om lika med operatorn utgör filtreringsvillkoret och samma kolumn även ingår i föreningsvillkoret. Det är trots allt en väldigt liten del av alla föreningar som kan omformas till icke- föreningar. För att uttryck två över huvudtaget skall vara snabbare än uttryck ett krävs det att bägge tabellerna har index över column1. 5.6. Gemensamma index för Joins En del databashanterare har stöd för något som kallas för ”Join Indexes”, bland annat Microsoft och Oracle stöder dem. Principen är att ha flera tabellers index i samma fysiska index. Index 1 Tabell 1 Index 2 Tabell 2 A Rad 1.7 B Rad 14.53 B Rad 6.3 C Rad 16.02 C Rad 8.8 F Rad 19.08 Bilderna ovan visar två vanliga index för Tabell ett och Tabell två. Bilden nedan visar ett gemensamt index (”Join Index”) för både Tabell ett och Tabell två: Index Tabell 1 Tabell 2 A Rad 1.7 B Rad 6.3 B Rad 8.8 C Rad 14.53 C Rad 16.02 F Rad 19.08 Antag att följande SQL förfrågan körs, med tillgång till ett gemensamt index för Tabell ett och två: SELECT * FROM Table1, Table2 WHERE Table1.column1 = Table2.column1 Sida 33 / 79
  • 34. Finjustering och optimering av SQL databaser 02.04.2003 Krister Karlström Med tillgång till ett gemensamt index är uppgiften mer eller mindre trivial. Databashanteraren behöver endast skanna igenom den gemensamma indextabellen och för varje värde som pekar på en rad i tabell ett, kontrollera om nästa indexvärde är exakt likadant men pekar på en rad i tabell två. Om så är fallet har den hittat en koppling. Det här är faktiskt en ”Sort-Merge Join”, där redan både sorteringen och föreningen har utförts på förhand! Gemensamma index kan vara mycket användbara och kan underlätta föreningar av tabeller avsevärt, men det finns alltid en risk för att det blir råddigt när man börjar blanda ihop data från flera tabeller i samma fil. Om dessutom man behöver utföra enskilda sökningar i kolumnerna så går det långsammare på grund av all ”skräpdata” från den andra tabellen som ligger i indexet. En tumregel för gemensamma index är: Om mer än hälften av alla förfrågningar på två tabeller innebär en förening av dessa, och föreningskolumnerna ändrar nästan aldrig, och ingen av tabellerna innehåller interna grupperingar, skapa då ett gemensamt index för dessa två tabeller. 5.7. Sammansatta tabeller Ett annat sätt att undvika föreningar (eller ”joins”) är att utnyttja något som ofta kallas för sammansatta tabeller (composite tables). Principen är mycket enkel, den sammansatta tabellen innehåller helt enkelt resultatet från en förening utförd på förhand. Principen beskrivs lättast med lite SQL: CREATE TABLE Table1 ( column1 INTEGER PRIMARY KEY, column2 CHARACTER(50), ...) CREATE TABLE Table2 ( column3 INTEGER PRIMARY KEY, column4 CHARACTER(50) ...) CREATE TABLE Composite ( column1 INTEGER, column3 INTEGER, column2 CHARACTER(50), column4 CHARACTER(50), ...) CREATE UNIQUE INDEX Index1 ON Composite (column1, column3, column2, column4) Om någon rad i Table1 eller i Table2 ändras, eller om rader adderas eller raderas, så är den gemensamma tabellen inte längre synkroniserad. För att förhindra detta så måste även den gemensamma tabellen uppdateras, vilket är enkelt att göra med en triggningsfunktion. Sida 34 / 79