Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Datenbanken (https://www.delphipraxis.net/15-datenbanken/)
-   -   Delphi [FB 2.1] Schnelle Alternative zu Count(*) ? (https://www.delphipraxis.net/133871-%5Bfb-2-1%5D-schnelle-alternative-zu-count-%2A.html)

alzaimar 11. Mai 2009 09:40

Datenbank: Firebird • Version: 2.1 • Zugriff über: Egal

[FB 2.1] Schnelle Alternative zu Count(*) ?
 
Hi,

Ich habe eine Tabelle mit 1 Mio Einträgen. In einer App soll ständig die Anzahl der Zeilen dieser Tabelle angezeigt werden. Ein 'SELECT COUNT(*) FROM TABELLE' dauert ewig.

Frage: Gibt es eine schneller Alternative, um die Zeilenanzahl einer Tabelle zu ermitteln? Ich vermute, es geht irgendwie über die $RDB-Tabellen.

Hat jemand einen Hint?

mkinzler 11. Mai 2009 09:46

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
SQL-Code:
Select count(<pk>)
statt
SQL-Code:
select Count(*)

Elvis 11. Mai 2009 09:49

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Zitat:

Zitat von mkinzler
SQL-Code:
Select count(<pk>)
statt
SQL-Code:
select Count(*)

Das ist bei fast allen DBMS schneller, die leider nciht schlau genug sind um zu erkennen, dass hier einfach die Metadaten des PK-Indizes hergenommen werden können.
Tut aber auch nicht weh wenn man die SQLs mit MSSQL, Sybase oder Ora teilen will...

alzaimar 11. Mai 2009 12:53

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Hi Markus und Elvis,

Danke für den Tipp. Klappt nur leider nicht, d.h. es ist immer noch genauso lahm.

hazard999 11. Mai 2009 13:05

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Vielleicht reicht das ja:

http://www.firebirdfaq.org/faq5/

R2009 11. Mai 2009 13:43

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Hi alzaimar,

die Anzahl der Datensätze steht doch in deiner Datenbankkomponente (ADOquery.... zumindest bei denen die ich bisher verwendet habe).
Vielleicht lieg jetzt auch falsch, aber 3 Zeilen ist es Wert.

Viele Grüsse!

alzaimar 11. Mai 2009 13:44

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Hallo Hazard999,

nein, das ist nicht genau. Ich habe mir nun so beholfen, das ich die Anzahl in einer separaten Tabelle (1 Zeile, 1 Feld 'RowCount') per Trigger auf dem Laufenden halte.

@R2009: Du meinst die 'Recordcount'-Eigenschaft.
Rate mal, was die macht :zwinker:

[ ] Sie weiss einfach, wie viele Zeilen in der Tabelle stehen
[ ] Sie liest alle Zeilen ein (per SELECT * FROM) und zählt sie dann.

(Tipp: Nur eine Antwort ist richtig)

Bonusfrage: Geht das schneller? :mrgreen:

Elvis 11. Mai 2009 14:13

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Zitat:

Zitat von alzaimar
Hi Markus und Elvis,
Danke für den Tipp. Klappt nur leider nicht, d.h. es ist immer noch genauso lahm.

Das ist bitter.
Ernsthaft, manche fehlende Features sind böse genug, dass man sie als Bug ansehen muss.

Habe glücklicherweise noch nie ein uneingeschränktes Count(...) in FB gebraucht... :angle2:

mkinzler 11. Mai 2009 15:12

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Notfalls basteslt du dir halt eine eigene Systemtabelle, in welche du die Anzahl per Trigger updatest
( auch wenns der Normalisierung widerspricht)

Satty67 11. Mai 2009 16:05

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Meine, das Firebird den Index ohne WHERE gar nicht verwendet.
SQL-Code:
Select COUNT(<pk>) FROM <table> WHERE 1=1
Habe aber keine so große Testtabelle, um den Unterschied zu testen.

€: Scheint langsamer... Where kostet wohl mehr als es bringt (falls das mit dem Index s.o. stimmt)

dataspider 11. Mai 2009 16:30

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Hi,

ob man die Verwendung eines Index erzwingt oder nicht, macht IMHO keinen Unterschied.
Ein Count von 1.000.000 Datensätzen erzeugt mit und ohne Index 1.000.000 Reads.

Ich denke, die Variante mit den Triggern ist die wohl einzig machbare.

Cu, Frank

alzaimar 11. Mai 2009 16:58

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Hi Leute,

ich hab das jetzt mit den Triggern so umgesetzt. Mich wundert es nur ein wenig, denn Firebird selbst sollte doch wissen, wie viele Zeilen denn nun in der Tabelle sind bzw. Blätter im B-Baum des PK.

Die Trigger-Lösung ist leider nicht ganz so hübsch, weil ich mir dadurch eigentlich überflüssige Deadlock-Kandidaten einhandle bzw. unnötige Locks.

mjustin 11. Mai 2009 16:59

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Zitat:

Zitat von dataspider
Ich denke, die Variante mit den Triggern ist die wohl einzig machbare.

Könnte es bei vielen gleichzeitigen Inserts dabei nicht Probleme geben, auch wenn die Anwendungen mit Transaktionen arbeiten?

mkinzler 11. Mai 2009 17:27

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Dann müssen es aber verdammt viele sein.

mjustin 11. Mai 2009 17:42

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Zitat:

Zitat von alzaimar
Hi Leute,

ich hab das jetzt mit den Triggern so umgesetzt. Mich wundert es nur ein wenig, denn Firebird selbst sollte doch wissen, wie viele Zeilen denn nun in der Tabelle sind bzw. Blätter im B-Baum des PK.

Dann müsste FB das aber für jede Transaktion getrennt wissen - eine Repeatable Read Transaktion, die drei Tage läuft, müsste am Ende noch immer die ursprüngliche Anzahl vom Transaktionsstart sehen.

Zitat:

Zitat von alzaimar
Die Trigger-Lösung ist leider nicht ganz so hübsch, weil ich mir dadurch eigentlich überflüssige Deadlock-Kandidaten einhandle bzw. unnötige Locks.

Zielkonflikt: entweder eine genaue Anzahl, oder eine skalierbare / deadlockfreie Lösung :)

mjustin 11. Mai 2009 17:44

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Zitat:

Zitat von mkinzler
Dann müssen es aber verdammt viele sein.

Zwei Clients sind manchmal schon einer zuviel :)

khh 11. Mai 2009 18:19

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Zitat:

Zitat von alzaimar
Hi Markus und Elvis,

Danke für den Tipp. Klappt nur leider nicht, d.h. es ist immer noch genauso lahm.

das entscheidende ist doch, dass ein index auf dem feld liegt


Gruss Kh

mkinzler 11. Mai 2009 18:25

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Zitat:

Zwei Clients sind manchmal schon einer zuviel Smile
das sollte nicht das Problem sein sondern eher, wenn sehr vile Insert/Deletes innerhalb kurzer Zeit passieren

dataspider 11. Mai 2009 18:32

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Zitat:

Zitat von khh
das entscheidende ist doch, dass ein index auf dem feld liegt

Überleg doch mal...
Wie soll ein Index beim Zählen aller Datensätze behilflich sein?

Erst bei einer Einschränkung der Ergebnismenge mit Where oder beim Sort wird ein Index benötigt.

Frank

mkinzler 11. Mai 2009 18:35

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Zitat:

Wie soll ein Index beim Zählen aller Datensätze behilflich sein?
Es müssen nur die Einträge im Index gezählt werden, was etwas schneller sein sollte

dataspider 11. Mai 2009 18:47

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Zitat:

Zitat von mkinzler
Es müssen nur die Einträge im Index gezählt werden, was etwas schneller sein sollte

Ich denke nicht, dass die DB - Entwickler für Count nur den Index durchlaufen.
Das Verfahren würde wirklich nur bei einem einfachen Count funktionieren.
Aber das ist jetzt wohl Spekulation und vielleicht hast du ja recht.
Ich habe mit IBExpert das Verhalten mal verglichen (bei ca. 700.000 Records).
Die Zeiten sind identisch, wenn ich select count(PK) from table bzw. select count(pk) from table where pk > 0 nehme.
Der Index wird im 2. Statement benutzt.

Frank

alzaimar 11. Mai 2009 18:49

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Zitat:

Zitat von dataspider
Wie soll ein Index beim Zählen aller Datensätze behilflich sein?

Oh, das ist ganz einfach:
Ohne Index muss man wirklich alle Zeilen durchlaufen (oder die geheime Tabelle mit den Metadaten finden).
Mit Index zähle ich nur die Anzahl der B-Tree-Seiten und multipliziere sie mit der Größe eines Indexelementes.
B-Tree-Seiten von Index-Strukturen wissen zudem, wie viele Elemente sie enthalten. Ich kenne zwar die FB-Implementierung nicht, kann mir aber nicht vorstellen, das FB bei B-Trees andere Wege schreitet, als der gängige und bewährte Standard. Der Weg über die B-Tree-Seiten macht also das Zählen um einen konstanten Faktor schneller (z.B. f=2000 für einen INTEGER-PK), denn ich muss nicht N Zeilen zählen, sondern nur N/f Seiten.

Weiterhin haben Indexstatistiken Informationen über die Gesamtanzahl der Einträge, sowie mindestens die Anzahl der unterschiedlichen Einträge (häufig noch zusätzliche Informationen), um beim Optimieren bzw. Erstellen des Queryplans den besser strukturierten Index zu bevorzugen.

Es müsste also theoretisch gehen.

Allerdings kann ich mir nicht vorstellen, einen solchen Sonderfall in der Erstellung des Queryplans für ein 'COUNT(*)' einzubauen. Kein TPC-Benchmark würde das würdigen.

Dann doch lieber die geheimen Metatabellen zugänglich machen. Irgendwo muss doch einfach stehen, wieviel Records in dieser Tabelle stehen. Ob nun mit oder ohne PK. Dammich, verdammt :wall: :stupid:

PS: Ich bekomm tatsächlich beim Programmstart ab und an ein Deadlock Problem. Einmal. Immer am Anfang. :gruebel: Muss an mir liegen. Ich schau mir das morgen nochmal an.

mkinzler 11. Mai 2009 18:51

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Zitat:

Die Zeiten sind identisch, wenn ich select count(PK) from table bzw. select count(pk) from table where pk > 0 nehme.
Und bei Count(*)?

dataspider 11. Mai 2009 19:00

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Zitat:

Zitat von mkinzler
Und bei Count(*)?

Ups... scheint doch schneller zu sein... :oops:

Frank

[EDIT]
Hmmm, habe jetzt noch mal count(*) gemacht, ist jetzt auch so schnell.
Wahrscheinlich hält FB die Daten auch nach einem Disconnect noch länger im Cache.
[/EDIT]

Chemiker 11. Mai 2009 19:00

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Hallo alzimar,

könnte man nicht den aktuellen Generator-Wert auslesen und die gelöschten Datensätze davon abziehen?

Bis bald Chemiker

mkinzler 11. Mai 2009 19:02

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Zitat:

könnte man nicht den aktuellen Generator-Wert auslesen und die gelöschten Datensätze davon abziehen?
Und wie bekommst du die gelöschten raus?

Chemiker 11. Mai 2009 19:07

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Hallo mkinzler,

z.B.: 2 Generator mitlaufen lassen für gelöschte Datensätze, oder in einem anderen Bereich des 1 Generators.

Ist nur so eine Idee.

Bis bald Chemiker

mjustin 11. Mai 2009 19:31

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Zitat:

Zitat von Chemiker
Hallo mkinzler,

z.B.: 2 Generator mitlaufen lassen für gelöschte Datensätze, oder in einem anderen Bereich des 1 Generators.

Ist nur so eine Idee.

Bis bald Chemiker

Hat einen Haken: Generatoren laufen ausserhalb von Transaktionen. Wenn also ein Satz gelöscht wird, wird der neue Generatorwert sofort für alle Transaktionen sichtbar - also schon vor dem Commit. Wenn die Transaktion dann ein Rollback macht, und damit das Löschen des Satzes (im Before Delete oder After Delete Trigger) verwirft, steht der falsche Wert noch im Generator. Auch bei After Delete ist die Transaktion noch nicht unbedingt committed.

Chemiker 11. Mai 2009 20:30

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Hallo mjustin,

und wie ist das bei COUNT?

Bis bald Chemiker

mjustin 11. Mai 2009 20:50

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Zitat:

Zitat von Chemiker
Hallo mjustin,

und wie ist das bei COUNT?

Bis bald Chemiker

Count läuft innerhalb der Transaktion. Verschiedene Transaktionen können daher zu unterschiedlichen Ergebnissen (Anzahl Sätze) kommen. 'Die' Anzahl Sätze ist also relativ zur Transaktion zu sehen. Wenn ein Programm sich nur mit der Datenbank verbindet, eine Anfrage mit Select Count(*) macht und dann die Verbindung trennt, ist die erhaltene Information in der nächsten Nanosekunde natürlich schon wieder wertlos. Aber das Problem ist ja, wenn ich den Thread richtig lese, die lange Ausführungszeit eines Count(*).



Wie wäre es, z.B. anhand eines Zeitstempels (Datum der Satzanlage) nur die aktuellen Sätze abzufragen?

Also wenn z.B. die Daten bis April 2009 sich nicht mehr allzusehr ändern, macht man ein

Select count(*) from tabelle where creationdate >= '01.05.2009'

Und addiert eine bereits bekannte Satzanzahl für alle davor liegenden Sätze auf.


Oder man geht etwas weg von der Datenbank, erzeugt in der Anwendung eine 'Satz erzeugt' oder 'Satz gelöscht' Message an einen zentralen Prozess (Application Server, simplen Telnetserver, whatever) und dieser sammelt die Messages und aktualisiert die Anzahl, entweder in einer Datenbanktabelle die nur einen Satz enthält, oder indem er in einer Antwortmessage die aktuelle Zahl zurückliefert.

dataspider 12. Mai 2009 14:11

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Hallo,

ich wollte der Vollständigkeit halber doch noch mal meine Tesergebnisse zu count zusammenfassen.
Ich wollte es einfach wissen.
Es war etwas kompliziert, da der erste Aufruf immer am längsten dauert. Dann greift der DB Cache.
Für eine zweiten Versuch müsste man den Rechner neu starten, da auch nach ShutDown und Neustart des Servers
die Abfragen schneller gehn. Wahrscheinlich greift hier der System Cache vom OS.

Die Ausführungsgeschwindigkeit für

1. select count(*)
2. select count(pk)
3. select count(pk) where pk > 0

ist in etwa gleich.
Tabelle hatte ca. 750.000 Datensätze.

Test 1 und 2 lag zwischen 500 un 540 ms.
test 3 lag etwa bei 620 ms.

Ich habe das ganze 30 mal durchgeführt.

So, wie es aussieht, verlangsamt die zusätzliche Verwendung des Indexes (Test 3) den Prozess sogar noch.


Frank

mschaefer 12. Mai 2009 15:36

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Zitat:

Zitat von alzaimar
Dann doch lieber die geheimen Metatabellen zugänglich machen.
Irgendwo muss doch einfach stehen, wieviel Records in dieser
Tabelle stehen. Ob nun mit oder ohne PK. Dammich, verdammt

Wenn das DBMS dies führen würde, dann wäre wohl auch das Count schneler.
Würde einem DBMS-Optimierer jedenfalls zutrauaen, dass er selbst auf die
geheimen Tabellen zugreifen würde, wenn da Zahlen geführt würden.

Grüße // Martin

hoika 12. Mai 2009 15:44

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Hallo,

wie schon weiter oben gesagt wurde,
läuft das Count(*) immer innerhalb einer Transaktion.
Durch die MGA von Firebird gibt es keine "feste Recordzahl".

Inwieweit man dem Nutzer sagen will 751.345, 751.346 ... Einträge sind drin,
steht zur Frage.


Heiko

Elvis 12. Mai 2009 15:50

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Zitat:

Zitat von hoika
wie schon weiter oben gesagt wurde,
läuft das Count(*) immer innerhalb einer Transaktion.
Durch die MGA von Firebird gibt es keine "feste Recordzahl".

Das gibt es in keinem DBMS, MGA oder nicht.

Auf der oben verlinkten FB Page wurde da schon auf sehr verzweifelte Hacks zurückgegriffen, also wird FB hier wohl nix bieten.

Die Frage ist halt, ob Trigger für jedes Delete und Insert auf Row-Ebene vertetbar wären.
Dann könnte sich Alzaimar selbst eine Meta table führen, in der zu jeder Tabelle die Records in der aktuellen Transaktion stehen.
Ob es dadurch zu mehr Deadlocks kommt bezweifle ich, schließlich schreibt man da ja nur, wenn man eh schon schreibt (insert/delete).

Trotzdem ganz schön bitter, IMO...

alzaimar 12. Mai 2009 19:21

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Zitat:

Zitat von mschaefer
Wenn das DBMS dies führen würde, dann wäre wohl auch das Count schneller. Würde einem DBMS-Optimierer jedenfalls zutrauen, dass er selbst auf die geheimen Tabellen zugreifen würde, wenn da Zahlen geführt würden.

Selbst MSSQL macht es nicht. Dort greift man die Metatabellen ab:
SQL-Code:
SELECT rows FROM sysindexes WHERE id = OBJECT_ID('table_name') AND indid < 2
Der Optimizer, der sich sonst einen Wolf optimiert (Hä? Wie? 'Der mit dem Wolf optimiert?' :gruebel: ), erspart sich das, vermutlich aus den von mir genannten Gründen.

Ich verstehe die Argumentation mit MGA nicht. Genauso, wie MGA mir eine (meine) Sicht auf alle Daten liefert, könnte das doch genauso mit der Zeilenanzahl funktionieren. Schließlich gibt es Tabellen für Tabellen, Felder, Views usw. Wieso steht in der Tabellentabelle nicht auch die Tabellengröße drin? Ich kapiere es nicht.

mkinzler 12. Mai 2009 19:32

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Wegen der Versionierung wären dann auch mehrere Metadatenversionen von Nöten

mjustin 12. Mai 2009 19:42

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Zitat:

Zitat von Elvis
Ob es dadurch zu mehr Deadlocks kommt bezweifle ich, schließlich schreibt man da ja nur, wenn man eh schon schreibt (insert/delete).

Wenn zwei Clients gleichzeitig einen neuen Satz in der gleichen Tabelle einfügen, und der Insert-Trigger dann die Satzzahl in der Satzzahl-Hilfstabelle um eins erhöhen will, dann sind das zwar zwei getrennte Inserts (daher natürlich auch kein Deadlockrisiko), aber dennoch zwei konkurrierende Updates auf einen Satz der Hilfstabelle. (Es sei denn, die Hilfstabelle enthält für jede *Transaktion* einen Satz.)

Es kommt zwar nicht zu einem Deadlock, aber sobald zwei Insert-Trigger gleichzeitig die alte Satzzahl lesen, und der erste Trigger diese Zahl (erhöht ums eins) erfolgreich zurückschreibt und committed, kann der zweite Insert Trigger den ursprünglichen Satz nicht mehr updaten (da er sich dadurch verändert hat), und muss aufgeben...

mkinzler 12. Mai 2009 19:48

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Da die Trigger in Transkationen laufen, sehe ich das Problem nicht. Allerdings wäre diese tabelle dann auch "versioniert" (mehrere konkurrierende Zugriffe -> mehrere Datensätze/Werte)

alzaimar 12. Mai 2009 20:07

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Ich hatte aber Deadlocks, und zwar immer beim Programmstart. Heute war ich out of office, aber morgen gehts der Deadlocke an den Sack. Meine Lösung besteht aus einer Tabelle mit einer Zeile und einem Feld ('RowSize') sowie zwei Triggern (BeforeInsert, BeforeDelete). Am Anfang der SW kommt manchmal genau 1x ein Deadlock. Danach nicht nochmal.

hoika 13. Mai 2009 11:43

Re: [FB 2.1] Schnelle Alternative zu Count(*) ?
 
Hallo,

was passiert, wenn nach dem Insert ein Rollback gemacht wird ? ?
Dann wird natürlich kein OnDelete-Trigger aufgerufen.


Heiko


Alle Zeitangaben in WEZ +1. Es ist jetzt 18:31 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz