# [Oracle] count(*) Performance verbessern



## Der nette Mann (24. November 2004)

Hi,

ich habe ein Programm geschrieben, dass die Tabellen in ein Array schreibt, für die zu einer bestimmten Bedingung Einträge gefunden werden. Diesen Befehle wähle ich mit 

select count(*) from _Tabellenname_ where _Bedingung_ 

soweit so gut, leider haben einige Tabellen extrem viele Datensätze (auch mit Bedingung), so dass die Ausführung des Befehls für manche Tabellen über eine Minute dauert? Kann ich nicht irgendwie sagen, dass er z.B. beim ersten gefundenen Eintrag abbricht?

Danke für eure Antworten...


----------



## Exceptionfault (24. November 2004)

Um den COUNT(*) zu beschleunigen musst du einen FULL TABLE SCAN bei großen 
Tabellen verhindern. Der führt dazu dass Oracle alle Blöcke der Tabelle
ließt vollkommen egal ob Daten drin stehen oder nicht. Er ließt das komplette
Segment bis zur High Watermark. Wenn deine Tabelle sehr fragmentiert ist
kann ein COUNT(*) per FULL TABLE Scan mehrere Minuten dauern, obwohl nur 3
Datensätze gefunden werden.

Die einzige Möglichkeit das ganze zu beschleunigen ist ein Index auf die 
Suchbedingung zu legen. Dann kann er einen Index Range Scan machen, was um einiges
schneller geht. 

Wenn dich aber die Anzahl nicht interessiert, kannst du auch so abfragen

```
SELECT /*+ FIRST_ROWS(1) */ 1 
FROM xyz 
WHERE bedingung
AND ROWNUM = 1;
```

das sollte zumindest um einniges schneller sein als per COUNT(*)


----------



## Exceptionfault (24. November 2004)

Hab das ganze nochmal selbst getestet, einfach aus Interesse was so an Performancegewinn möglich ist. Ich habe das ganze an einer zweispaltigen Tabelle getestest, die aus dem Thread "[Oracle] bestimmten "Zeilenbereich" einer Tabelle anzeigen. " von Thomas. Habe sie mit 10 Mio Datensätzen gefüllt und nach jedem ausgeführten Statement den Cache der Datenbank mit 

```
ALTER SYSTEM FLUSH SHARED_POOL; 
ALTER SYSTEM FLUSH BUFFER_CACHE;
```
 wieder geleert um gleiche Bedingungen zu haben.

Hier meine Ergebnisse:


```
SQL> select count(*) from test where id = 10000000;

  COUNT(*)
----------
         1

Abgelaufen: 00:01:42.71

SQL> SELECT /*+ FIRST_ROWS(1) */ 1
  2  FROM test
  3  WHERE id = 10000000
  4  AND ROWNUM = 1;

         1
----------
         1

Abgelaufen: 00:00:06.31
```

Man sieht schonmal einen kleinen Unterscheid  
Jetzt hab ich ein Index über das Feld angelegt.


```
CREATE INDEX IDX_ID 
   ON TEST( ID ) 
   COMPUTE STATISTICS 
   TABLESPACE INDX 
   NOLOGGING;
   
Index wurde angelegt.

Abgelaufen: 00:02:13.84
```

Und jetzt alles nochmal...


```
1  SELECT /*+ FIRST_ROWS(1) */ 1
  2  FROM test
  3  WHERE id = 10000000
  4* AND ROWNUM = 1

         1
----------
         1

Abgelaufen: 00:00:00.00
```

Das sieht noch besser aus  Ich denke damit sollte deine Frage beantwortet sein, oder ?

Wen es interessiert ... Die Tabelle hat knapp 255 MB mit 10 Millionen Einträgen, der Index hat knapp 170 MB.


----------



## Thomas Darimont (25. November 2004)

Hallo!

Mal wieder Klasse gemacht @ ExceptionFault!
Mal noch eine Frage: sobald man auf eine Tabelle einen Index über eine oder mehrere Spalten anlegt erkauft man sich ja den Performancegewinn bei Select / Suchoperationen damit, dass einfüge / update Operationen etwas langsamer ablaufen (Da der Index ja u.U. wieder neu aufgebaut werden muss). Wie ermittelt man denn das "optimale" Verhältnis zwischen schneller Suche und vertretbar schnellen Einfüge/Update Operationen? Sprich um welchen Faktor werden denn Manipulationsanwesiungen durch Indizies verlangsamt (Am Beispiel einer Tabelle mit ähnlichen Dimensionen aus deinem Post (Nur mit ein wenig mehr Spalten ;-) )?

Gruß Tom


----------



## Exceptionfault (25. November 2004)

So, nach einem erheiternden Stromausfall im Geschäft versuch ich mich hier mal wieder mit erfreulicheren Problemen 

Eine generelle Formel gibt es für diese Frage (wie kann es anders sein) natürlich nicht. Es kommt auch ganz speziell auf das System an, sprich habe ich ein OLTP System mit vielen kleinen Transaktionen, oder betreibe ich ein Data Warehouse mit großen Batch Jobs und Ladeoperationen. Die beiden Systeme fordern komplett unterschiedliche Betrachtung des index Designs. 

Oralce liefert zwei grobe Faustformeln die man hier beachten sollte. Zum einen ist ein INSERT oder UPDATE Statement ca. 3x so Resourcenintensiv mit einem Index, als ohne. Sprich wenn ich 3 Indizes auf einer Tabelle habe brauche ich fast 10x so viel Resourcen bei einem Insert. Ein DELETE braucht nicht ganz so viel, da der Leaf Block des Indexes nicht entfernt sondern nur als gelöscht markiert wird.

Dies führt wiederum zu dem Problem das Indizes immer größer und langsamer werden je mehr Bewegung in meiner Tabelle ist. Daher die zweite Fausformel: Wurden ca. 10% der Tabelle geändert oder gelöscht oder neu eingefügt, sollte der Index neu aufgebaut werden.

Anhand dieser beiden Kriterien muss man nun überlegen was für sein System das Beste ist. Für ein Datawarehouse mit zahllosen Inserts ist es zum Beispiel üblich den Index vor dem Import zu droppen und danach neu aufzubauen. Das beschleunigt den Import um ein vielfaches.

OLTP Systeme hingegen sind meist Abfrage lastig, weshalb auf Indizes niemals verzichtet werden kann. Oft bestehen Tabellen sogar nur aus reinen Index Strukturen (IOT`s) um den Zugriff weiter zu beschleunigen. hier fallen die wenigen Änderungen dann kaum ins Gewicht. Wer also ein OLTP System betreibt steht nicht vor der Frage ob er Indizes benutzt, sondern wie viele. 

Also, wie schon gesagt, eine Fausformel gibt es dafür nicht, ich bin gerade in Webanwendungen immer sehr gut damit gefahren generell PK`s anzulegen und für Kreuztabellen (n:m) IOT`s zu verwenden. Ebenso wichtig ist es meiner Meinung nach alle FK Spalten mit Indizes zu belegen um Locks zu vermeiden. Das ist zwar in 9i nichtmehr so kritisch, kann aber dennoch nix schaden. Dann hab ich oft noch ein paar Unique Indizes für Spalten wie Username oder so, wenn ich als PK eine numerische ID habe. (Ich vermeide meist VARCHAR2 Felder als PK). Und ob weitere Indizes nötig sind wird im Testbetrieb ermittelt, wobei hier bei durchdachtem Tabellendesign so gut wie nie nachgebessert werden muss.

Im Datawarehouse Bereich würde ich dann eher auf Bitmap und Bitmap Join Indizes zurückgreifen, die werden nicht so groß und sind bei großen Datenmengen effizienter, ist aber wieder ein eigenes Kapitel da man bei Bitmap Indizes auch sehr viel Performance verlieren kann.


----------



## Der nette Mann (26. November 2004)

Hey, 

klasse Antwort Exceptionfault. Da ich gestern leider keine Zeit hatte das ganze zu testen, werde ich mich heute daran machen. Ich berichte dann noch von meinem Test...

Bis dahin... Vielen Dank

Der nette Mann


----------



## Der nette Mann (26. November 2004)

Hi,

hier meine Testergebnisse... 


```
meine größte Tabelle in der DB

vorher:
----------
Durchlaufzeit: 33,141 sec

nachher:
-------------
Durchlaufzeit: 2,229 sec
```

Da es an die 150 Tabellen sind (nicht alle brauchen so lange), macht sich das ganze deutlich in der Laufzeit bemerkbar.

Die Methode mit den Indizees hätte ich gerne ausprobiert, aber leider fehlen mir dazu die notwendigen Rechte  Bin ja nur ein kleiner Programmiere 

Trotzdem... vielen Dank 

Der nette Mann


----------

