By Frank Kalis
Dies ist ein beliebtes Beispiel für Informatikstudenten im Anfangsstadium, um die Auswirkungen effizienter Algorithmen zu demonstrieren. Also auch hier nicht unbedingt etwas, was man zwingend in einer Datenbank machen müßte, das sich aber durchaus mengenbasiert lösen läßt. Zu diesem Algorithmus gibt es eine kleinen Anekdote:
Dem "Entdecker" Carl-Friedrich Gauß wurde in der Schule die Aufgabe gestellt, die Summe aller Zahlen von 1 bis 100 zu berechnen. Alle Kinder rechneten los, Gauß schrieb kurz etwas auf seine Tafel und riss nach kurzer Zeit seinen Lehrer aus dessen Ruhepause. Die Formel stimmte natürlich! Wer es genauer nachlesen möchte, kann sich mal hier umschauen.
DECLARE @n BIGINT SET @n = 100 SELECT (@n+@n*@n)/2 -------------------- 5050 (1 row(s) affected)
oder als UDF
CREATE FUNCTION dbo.achtsieben(@n BIGINT) RETURNS BIGINT AS BEGIN RETURN (@n+@n*@n)/2 END GO SELECT dbo.achtsieben(100) DROP FUNCTION dbo.achtsieben -------------------- 5050 (1 row(s) affected)
Zu dem Namen, den ich der Funktion gegeben habe, gibt es auch eine Anekdote:
Als ich unsere Mathematiker nach der "richtigen" Bezeichnung für diese Formel gefragt habe, kam als Antwort:
"Wir nennen das die 78-er Regel, da die Summe der Monate eines Jahres 78 ist."
Ein kurzer Test ergibt:
DECLARE @n BIGINT SET @n = 12 SELECT (@n+@n*@n)/2 -------------------- 78 (1 row(s) affected)
Stimmt!
Nachtrag 27.08.2004: Auch im SQL Server kann man damit die Notwendigkeit zum Einsatz von effizienten Algorithmen demonstrieren. Dazu bauen wir uns mal folgendes Testskript zusammen:
DBCC FREEPROCCACHE DBCC DROPCLEANBUFFERS GO DECLARE @start DATETIME SET @start = GETDATE() DECLARE @n BIGINT SET @n = 200000 SELECT (@n+@n*@n)/2 SELECT GETDATE()-@start AS Zeit DBCC FREEPROCCACHE DBCC DROPCLEANBUFFERS GO DECLARE @start DATETIME DECLARE @n BIGINT DECLARE @result BIGINT SET @start = GETDATE() SET @n = 1 SET @result = 0 WHILE @n <= 200000 BEGIN SET @result = @result + @n SET @n = @n + 1 END SELECT @result SELECT GETDATE()-@start AS Zeit
Nach Ausführung erhält man folgendes Ergebnis:
... -------------------- 20000100000 (1 row(s) affected) Zeit ------------------------------------------------------ 1900-01-01 00:00:00.010 (1 row(s) affected) ... -------------------- 20000100000 (1 row(s) affected) Zeit ------------------------------------------------------ 1900-01-01 00:00:01.513 (1 row(s) affected)
Während der Gauß Algorithmus fast augenblicklich das Ergebnis zurückliefern, braucht die iterative Methode deutlich länger!
Vielleicht mag das nicht viel erscheinen, aber jetzt stelle ich mir die Auswirkungen auf ein System vor, in dem ein solcher algorithmus in einer stark frequentierten Prozedur implementiert wurde, die mehrere Tausend Male pro Stunde aufgerufen wird. Nun sieht die Sache schon etwas anders aus. Jetzt mag man natürlich mit Caching argumentieren, aber in diesem Fall wird die Ausführung ohne
DBCC FREEPROCCACHE DBCC DROPCLEANBUFFERS GO
zwischen zwei Läufen der iterativen Methode auch nicht wirklich schneller
Zeit ------------------------------------------------------ 1900-01-01 00:00:01.403 (1 row(s) affected)
Dies ist natürlich kein repräsentativer Test unter sterilen Testbedingungen, aber verdeutlicht doch die Notwendigkeit effizienter Algorithmen, auch, und vielleicht gerade, in T-SQL.