Summenwert einer Reihe

Posted on Aug 18, 2004 von in SQL Server

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. 

Binärzahl in Dezimalzahl umwandeln

Posted on Aug 18, 2004 von in SQL Server

Normalerweise würde man solche Fragestellungen. welcher Dezimalzahl nun 1011001 entspricht, damit beantworten, in dem man auf den Client verweist. Was aber, wenn man einfach wissen will, wie so etwas in T-SQL aussehen könnte? Ob man es dann später einsetzt, ist ja eine andere Sache:

Ganze Geschichte »

Unterschiede zwischen MONEY und DECIMAL

Posted on Aug 16, 2004 von in SQL Server

SQL Server MVP Steve Kass hat dieses Beispiel in den englischen Newsgroups gepostet. Es zeigt, daß der Einsatz der Datentypen zur Speicherung monetärer Daten sorgfältig durchdacht sein sollte. Man sollte stets bedenken, welche Operationen mit diesen Daten durchgeführt werden.

Ganze Geschichte »

Determinismus von CHARINDEX und PATINDEX

Posted on Aug 12, 2004 von in SQL Server

In BOL werden CHARINDEX() und PATINDEX() als nichtdeterministische Stringfunktionen aufgelistet. Warum eigentlich? Man sollte meinen, das ceteris paribus auch bei diesen Funktionen stets ein identisches Ergebnis herauskommt. Richtig, und gleichzeitig nicht! Der Grund, warum beide Funktionen als nichtdeterministisch geführt werden, findet sich dann in den Erklärungen zu PATINDEX():

Ganze Geschichte »

Daten aus Excel abfragen

Posted on Aug 10, 2004 von in SQL Server

In fast allen Online Communities sieht man solche Fragen mit schöner Regelmässigkeit auftauchen. Die vielleicht einfachste Methode, diese Daten abzufragen, besteht in der Verwendung von OPENROWSET:

Ganze Geschichte »

Tags: , ,

Zahl rechts-oder linksbündig mit 0 formatieren

Posted on Aug 9, 2004 von in SQL Server

Die Präsentation von Informationen aus der Datenbank ist imho eher Sache des Clients als die des Servers. Solche Aufgaben gehören zum Handswerkzeug jedes Front-End und sind dort schnell und einfach erledigt.

Falls jedoch, aus welchen Gründen auch immer, dies auf dem Server erledigt werden muss, kann vielleicht folgendes Skript gute Dienst leisten:

DECLARE @MeineZahl INT
SET @MeineZahl = 99
SELECT 
	RIGHT(REPLICATE('0',10) + CAST(@MeineZahl AS VARCHAR(10)),10) AS Rechtsbündig
	, LEFT(CAST(@MeineZahl AS VARCHAR(10)) + REPLICATE('0',10) ,10) AS Linksbündig

Rechtsbündig Linksbündig 
------------ ----------- 
0000000099   9900000000

(1 row(s) affected)

Für den Lazycoder könnte das rechtbündige Auffüllen auch noch folgendermaßen aussehen:

DECLARE @MeineZahl INT
SET @MeineZahl = 99
SELECT REPLACE(STR(@MeineZahl,10), ' ', '0')

Rechtsbündig          
---------------------- 
0000000099

(1 row(s) affected)

N-te Wurzel einer Zahl

Posted on Aug 3, 2004 von in SQL Server

Tja, wieder so ein Beispiel, dass man mal in der Schule gelernt hat, aber immer genau dann vergisst, wenn man es braucht.

DECLARE @My1 FLOAT
DECLARE @My2 FLOAT

SELECT @My1 = 16, @My2 = 4
SELECT POWER(@My1, 1/@My2)
                                                      
----------------------------------------------------- 
2.0

(1 row(s) affected)

Oder als UDF-Version

CREATE FUNCTION nthroot(@My1 FLOAT, @My2 FLOAT) 
RETURNS FLOAT
	AS
		BEGIN
			RETURN POWER(@My1,1/@My2)
		END
GO
SELECT dbo.nthroot(16,4)
DROP FUNCTION dbo.nthroot
                                                      
----------------------------------------------------- 
2.0

(1 row(s) affected)

Kleinste gemeinsame Vielfache zweier Zahlen

Posted on Aug 3, 2004 von in SQL Server
CREATE FUNCTION dbo.kgv(@zahl1 int, @zahl2 int ) 
RETURNS INT
AS 
  BEGIN
   RETURN (@zahl1 * @zahl2) / dbo.ggt(@zahl1, @zahl2)
  END
 GO

SELECT dbo.kgv(24,36)
DROP FUNCTION dbo.kgv
            
----------- 
72

(1 row(s) affected)

Der Vollständigkeit halber hier noch einmal die Funktion zur Ermittlung des grösssten gemeinsamen Teilers:

CREATE FUNCTION dbo.ggt(@zahl1 int, @zahl2 int) 
RETURNS INT
AS
 BEGIN
   DECLARE @zahl3 INT
   SET @zahl3=1
   WHILE (@zahl3 <> 0)
    BEGIN        
     SET @zahl3=@zahl1 % @zahl2
     SET @zahl1=@zahl2
     SET @zahl2=@zahl3
    END
  RETURN @zahl1
 END
GO

Dies ist dies Adaption der Excel Funktion KGV.