Sie sind nun wieder eingeladen, die Diskussion spezieller technischer Probleme mit zu verfolgen. Bitte schicken Sie Fragen, Anregungen oder Antworten zu den vorgestellten Themen – ebenso wie Ihre Kritik – an unsere e-Mail-Adressen: dieter.bender@MidrangeMagazin.de oder Redaktion@MidrangeMagazin.de Frage: Ich möchte per SQL eine gewisse Anzahl zufällig ausgewählter Datensätze aus einer Datei selektieren. In Access sieht das SQL-Statement wie folgt aus: SELECT TOP 10 FROM MyTable ORDER BY rnd([plz]). Ich habe bereits versucht – analog zur Access-Variante – mit der Funktion RAND zu arbeiten, bekomme aber immer dieselben Sätze angezeigt.

Antwort: Die von Ihnen skizzierte Access-Lösung muss zur Verwendung auf der iSeries an einigen Stellen modifiziert werden. Zunächst müssen in DB2/400 die Sortierfelder in der Ergebnistabelle enthalten sein; man muss also die Spalte mit der Zufallszahl mit in die SELECT-Klausel aufnehmen. Die RAND-Funktion ist auf der iSeries überladen, das heißt mit unterschiedlichen Parameterschnittstellen verwendbar.

Wenn ein Parameter übergeben wird, dann muss dieser nicht nur numerisch sein, sondern als SMALLINT oder INTEGER übergeben werden; dies kann auch durch Typumwandlung, durch so genanntes Casting erreicht werden. Die Funktion zur Umwandlung heißt INTEGER und verdaut so gut wie alles, was sich als Ganzzahl interpretieren lässt – selbst Alfa Werte werden versucht zu wandeln. Bei der Übergabe eines Wertes wird dieser als Startwert – auch nach dem englischen Begriff „seed“ genannt – für eine Zufallszahlenfolge verwendet. Die Implementierung ist doch recht schwach. Gleiche Startwerte liefern identische Folgen, was zu Ihrer Beobachtung führt, dass immer dieselben Sätze ausgewählt werden.

Die Variante ohne Parameter setzt keinen eigenen Startwert, sondern macht mit der „zufälligen“ Zahlenreihe dort weiter, wo beim letzten Mal (innerhalb des Jobs) aufgehört wurde – das ist für mehrfaches Ziehen im selben Job schon besser geeignet. Diese Variante hat dann in Ihrem Beispiel das folgende Aussehen:

select (rand() * 1) r, k.* 
     from kunde k          
     order by r            
     fetch first 10 rows only

Ohne Rechenoperation beim Ausdruck mit der RAND()-Funktion erkennt der Query Optimizer (zumindest unter V5R1 mit meinem PTF-Stand) nicht, dass diese Spalte sortierwürdig ist und ignoriert die ORDER BY-Klausel.

Diese Variante liefert bei mehrmaliger Ausführung im selben Job schon brauchbare Ergebnisse, sie liefert aber zu Beginn einer Sitzung immer wieder dieselben Ergebnismengen und ist also im Batch nicht verwendbar.
Verstärken kann man den Mechanismus, indem man einen hinreichend willkürlichen Startwert an die Funktion RAND übergibt. Hierzu bietet sich ein zeitbezogener Wert an, da dieser leicht zugänglich ist. Nimmt man Millisekunden, dann ist der Wert auch vom Benutzer nicht bewusst oder unbewusst steuerbar.

select (rand(microsecond(current_timestamp)) * 1) r, k.*
     from kunde k                                       
     order by r                                         

Die Funktion CURRENT_TIMESTAMP liefert eine auf sechs Stellen hinter dem Komma genaue Zeitmarke. Mit Hilfe der Funktion MICROSECOND wird daraus eine sechsstellige Integer-Zahl ermittelt, die nun als Startwert für RAND dient. Mit diesem Kniff ist die Selektion auch für Batch-Jobs verwendbar.

Die genauere Beschäftigung mit der DB2/400-Implementierung von RAND lässt allerdings doch einige Zweifel zurück, ob dieses Verfahren für echte statistische Zufallsauswahlen ausreichend stark ist. Es sind jedenfalls Anwendungen vorstellbar, für die ich einen eigenen Mechanismus vorziehen würde, der dann ja als so genannte User Defined Function (UDF) aus SQL genauso einfach wie jede SQL-Funktion verwendbar wäre.

Den Autor Dieter Bender erreichen Sie unter dieter.bender@midrangemagazin.de