In Kapitel 8 hatten wir die Basis-Kennzahlen festgelegt, die wir nachfolgend von der chess engine Stockfish für jede einzelne Schachstellung unserer analysierten Spiele berechnen ließen und in der Datenbank-Tabelle position_analysis gespeichert haben.
Wie bereits angekündigt, wollen wir davon weitere Kennzahlen ableiten, die ebenfalls in unserer Datenbank gespeichert werden sollen. Einige davon ergeben sich durch bloße numerische Umrechnung aus den Basis-Kennzahlen, andere durch Vergleiche der Bewertungen von jeweils einer Ausgangs-Position mit der entstandenen Ziel-Position nach einem ausgeführten Zug.
Wieder andere Kennzahlen wie z.B. ACPL sind als Durchschnittswerte statistischer Natur, auf eine gesamte Partie bezogen und daher Positions-übergreifend.
Wie erwähnt interessieren uns letztlich Messdaten für ausgewählte Spieler und deren Vergleich untereinander. Wir nutzen also unsere Positions-Kennzahlen zur Partien- und nachfolgend der Spieler-Bewertung.
Wir werden daher auf allen drei Ebenen Spieler, Partien und Positionen Werte berechnen. Demzufolge benötigen wir zu deren Speicherung weitere Tabellen in genau dieser Hierarchie (das Tabellenprefix ‚da‘ steht für engl. derived analytics).
Werfen wir zur Veranschaulichung nochmals einen Blick auf unsere bereits ermittelten Basis-Kennzahlen aus der Stockfish-Analyse. Wir betrachten dazu ein konkretes Spiel mit sämtlichen analysierten Postitionen anhand unserer Daten
.

Im oberen Bereich des Bildes sehen wir unsere SQL-Abfrage, mit der wir hier die für unseren Zweck wesentlichen Daten ermitteln.
- ) In der Spalte fen sind die einzelnen Schachpositionen kodiert, auf die sich die Kennzahlen wie centipawn beziehen. Zur FEN-Dekodierung und Anzeige der zugehörigen Position kann der Chess FEN Viewer benutzt werden.
- ) die Werte in der Spalte half_move_num sind aufsteigend nach Halbzug-Nummer sortiert. Für die Züge vor dem fünften Halbzug liegen keine Analyse-Ergebnisse vor. Offenbar handelt es sich dabei um bekannte Stellungen, die wir ja bewusst von einer Bewertung ausgeschlossen hatten.
- ) In den beiden Spalten move_white bzw. move_black befinden sich die einzelnen Züge, die zur bewerteten Position geführt haben.
- ) in der Spalte best_move_uci findet sich der aus Sicht der chess engine vorgeschlagene beste Zug aus dieser Position heraus.
- und 6. ) In der Schachposition von Zeile 7 wurde e5e4 als bester Zug (aus Sicht der engine) ermittelt. In Zeile 8 sehen wir, dass dieser Zug in der Partie von Schwarz tatsächlich auch ausgeführt wurde. Gleichzeitig wird damit verständlich, dass wir bestimmte Kennzahlen wie accuracy, welche ja aus der Differenz beider Bewertungen berechnet wird, nur durch Positions-übergreifende Betrachtung ermitteln können!
Abgeleitete Kennzahlen auf Positions-Ebene
Gehen wir der Reihe nach vor. Aus den Basis-Kennzahlen centipawn, wins, draws und losses, die wir aus der Partien-Analyse gewonnen haben, können wir direkt weitere Kennzahlen ableiten, die auf der Ebene der betreffenden Stellung bzw. des durchgeführten Zuges angesiedelt sind.
Hier nur ein Überblick – die konkrete Implementierung wird später gezeigt.
- White/Black winning chances
Die Gewinnwahrscheinlichkeit für eine Seite (Weiß oder Schwarz) kann direkt aus der Kennzahl centipawns abgeleitet werden.
Die genaue Formel für Win% wurde auf lichess.org definiert. - White/Black score rate
Die Score-Rate gibt die Wahrscheinlichkeit an, einen Punkt zu erzielen. Da es im Schach bei einem Unentschieden (remis) zu einer Punkteteilung kommt, ist dieser Fall ebenso zu berücksichtigen. Die Score-Rate kann direkt aus den WDL-Werten berechnet werden. - White/Black draw rate
Diese Kennzahl wird ebenfalls aus den WDL-Werten berechnet und gibt die Wahrscheinlichkeit für ein Remis ausgehend von der gegebenen Stellung an. - Accuracy (Zuggenauigkeit)
Die Genauigkeit bezeichnet den Grad der Abweichung des durchgeführten Zuges vom optimalen Zug aus Sicht der chess engine. Hierzu sind jeweils zwei aufeinanderfolgende Zeilen (Halbzüge) der Tabelle position_analysis zu berücksichtigen. Dabei wird die Gewinnwahrscheinlichkeit für die Ausgangsstellung (unter der Annahme, dass der optimale Zug gemacht wird) mit der Gewinnwahrscheinlichkeit für die Folgestellung, die durch den realen Spielzug entstanden ist, verglichen. Damit ist auch klar, dass jeweils für die erste Zeile der Tabelle position_analysis pro Partie der Wert accuracy nicht definiert ist! - Judgement (Zug-Klassifikation)
Die Kennzahl judgement soll die Qualität eines Zuges grob bewerten, um sie im Vergleich zur rein numerischen Kennzahl accuracy zu veranschaulichen. Die Kennzahl judgment kann vier verschiedene Werte annehmen, die über festgelegte Schwellwerte von der Kennzahl accuracy abgeleitet werden:- „ENGINE„
der durchgeführte Zug entspricht dem optimalen, von der engine vorgeschlagenen Zug. - „INACCURACY„
der durchgeführte Zug entspricht nicht dem optimalen Zug, kann aber auch noch nicht als klarer Fehler bezeichnet werden. - „MISTAKE„
ein klar schlechter Zug aus Sicht der engine. - „BLUNDER„
ein schwer wiegender Fehler („Patzer“) aus Sicht der engine.
- „ENGINE„
- Sharpness (Spielschärfe)
Umgangssprachlich ist relativ leicht zu erklären, was mit sharpness einer Schachstellung gemeint ist. Man sagt auch, ein Spiel stehe „auf Messers Schneide“ oder „Spitz auf Knopf“ und meint damit eine fragile Schachposition mit beiderseitigen hohen Chancen und Risiken. Oft liegen in diesen Situationen unmittelbare und gravierende Drohungen wie etwa Matt oder Figurenverlust in der Luft. Diese Schlüsselstellungen sind dadurch geprägt, dass nur wenige Züge zur Auswahl stehen, um zu gewinnen oder wenigstens das Gleichgewicht zu halten. Auf der anderen Seite sind in solchen meist sehr taktisch geprägten Positionen auch unmittelbare Verlustzüge möglich. Im Schach spricht man dann auch von einer „geringen Remisbreite„. Als Beispiel für eine Stellung mit einer großen Remisbreite ließe sich die Anfangsstellung im Schach anführen – hier sind viele Züge möglich ohne unmittelbare Aussicht auf Partie-Gewinn oder die Gefahr des Partie-Verlusts.
Die Hauptgrund, diese Kennzahl in Hinblick auf die Spielstärke eines Schachmeisters überhaupt betrachten zu wollen, liegt in der These begründet, dass starke Spieler auf Gewinn spielen, d.h. sie suchen die schärfsmögliche Fortsetzung, um ihren Gegner unter Druck zu setzen und fehlerhafte Gegenzüge zu provozieren. Tatsächlich entsprachen viele bekannte Meister diesem Typus des Angriffsspielers – Paul Morphy, Alexander Aljechin, Michail Tal, Bobby Fischer und Garri Kasparov, um nur einige zu nennen. Doch es gibt auch Gegenbeispiele – die Weltmeister José Raúl Capablanca und Tigran Petrosjan blieben in ihren Partien bevorzugt auf der sicheren Seite und gaben sich oft lieber mit einem Remis zufrieden, als einen Partieverlust zu riskieren. Und das durchaus sehr erfolgreich!
Trotzdem verbinden die meisten Schachspieler kühnes und gleichzeitig korrektes Angriffspiel mit Spielstärke. Im Gegensatz dazu gelten Remispartien meist als uninspiriert und langweilig (Ausnahmen bestätigen die Regel!). Deshalb interessiert uns die Kennzahl sharpness ebenfalls, auch wenn sie objektiv betrachtet vielleicht nur ein schwaches Indiz für Spielstärke sein mag.
Aus der oben erwähnten „Remisbreite“ lässt sich unmittelbar eine Idee entwickeln, wie wir dafür eine Kennzahl konstruieren können. Eine geringe Remisbreite mit gleichzeitig hohen Gewinnchancen für beide Seiten würden wir demnach als „scharfe Stellung“ einschätzen. Und genau diese Information liegt uns in Form der WDL-Werte vor!
Durch geschickte Wahl einer geeigneten Formel für diese Kennzahl, so wie auf chess-journal.com ausführlich erläutert, erhalten wir einen aussagekräftigen Wert dafür.
Wie in den Beispielen dort vorgeführt, funktioniert das Verfahren ziemlich gut. Die Sache hat nur einen Haken – die WDL-Werte der beiden chess engines Stockfish und Leela Chess Zero unterscheiden sich sehr stark und das, obwohl beide Schachprogramme mit ihrer Spielstärke eng beieinander liegen.
Sehen wir uns dazu das obige Beispiel aus der Partie Caruana-Vachier-Lagrave, 2021 an, erkennen wir auf einen Blick, dass die beiderseitigen Gewinnchancen einerseits relativ ausgewogen und andererseits die Chance auf Remis etwas geringer ist, auch wenn die Werte je nach Rechentiefe leicht schwanken.
Wir konnten die dort angegebenen WDL-Werte 244-239-517 mit Hilfe von Nibbler und Leela Chess Zero grob nachvollziehen, zum Beispiel mit der Knotenzahl 5100 (Maß für die Suchtiefe). Leela Chess Zero sieht Schwarz mit der centipawn-Bewertung von -0,41 dabei leicht im Vorteil. Das passt auch zu den zugehörigen WDL-Werten.

Vergleichen wir nun exakt dieselbe Stellung mit der Bewertung durch Stockfish, erwarten wir aufgund der ebenbürtigen Spielstärke der beiden engines ähnliche Werte.
c:\git_projects\chessAnalyzer\various python scripts>python analyse_Caruana-Vachier-Lagrave_2021_move_19.py
r n b . k . . r
. p . n . p p .
p . . . p . . p
. . b . . . . .
. . q N N . . .
. . P . Q . B .
. . . . . . P P
. . . R K . . R
centipawn: -24
wins: 7
draws: 947
losses: 46
c:\git_projects\chessAnalyzer\various python scripts>
Auch hier sieht die engine (in diesem Fall Stockfish) Schwarz leicht im Vorteil, wenn auch etwas weniger ausgeprägt.
Was uns aber völlig überrascht, ist die komplett verschiedene Gewichtung der WDL-Werte im Vergleich zu Leela Chess Zero! Wie kann das sein?
Nun, Stockfish rechnet bekanntlich intern mit der centipawn-Bewertung und konvertiert daraus am Ende die WDL-Werte, d.h. alle Positionen mit identischen centipawn-Werten erhalten auch die selben WDL-Werte, egal auf welche Weise diese zustande gekommen sind. Im Gegensatz dazu rechnet Leela Chess Zero intern bereits auf Basis der WDL-Werte und destilliert daraus erst am Ende eine centipawn-Bewertung. Deshalb kann es bereits während der Analyse gute Chancen auf Gewinn bzw. Verlust von klaren Remis-Stellungen unterscheiden.
Nichtsdestotrotz werden wir im ersten Schritt die sharpness-Kennzahl aus den WDL-Werten von Stockfish nach der obigen Formel berechnen und abspeichern. Ob damit ein Aussagegehalt verbunden ist, können wir später genauer prüfen.
Unberücksichtigte Kennzahlen
Es gibt viele weitere interessante Merkmale, Schachstellungen oder einzelne Züge betreffend, die wir hier nur der Vollständigkeit halber erwähnen wollen. Sie sind entweder nicht klar definiert, kompliziert zu berechnen oder zu wenig relevant zur Beurteilung der Stärke eines Spielers.
- Brillianz
Solch ein Zug muss nach allgemeinem Verständnis der stärkste mögliche Zug sein. Dazu darf er nicht offensichtlich sein, sondern muss eine gewisse Tiefe aufweisen, die in einer überraschenden Pointe mündet.
Der Zug muss nicht unbedingt zum Sieg führen, aber ob damit ein Figurenopfer verbunden sein soll, daran scheiden sich bereits die Geister! Dazu lassen sie sich als taktisch oder strategisch brillante Züge kategorisieren. Die Diskussionen um die richtige Einordnung halten jedenfalls weiter an. - heat map
Es geht dabei im Prinzip um den Vergleich, welche Seite mehr Felder auf dem Spielbrett kontrolliert. Der Begriff „heat map“ leitet sich davon ab, dass diese Felder anhand ihres Status (von Weiß/Schwarz kontrolliert, neutral, umstritten) leicht visuell dargestellt werden können. Natürlich ließe sich das auch in eine Kennzahl umrechnen. - peace activity
gemäß Paul Morphy’s Leitsatz „Hilf deinen Figuren, damit sie dir helfen“ ist Figurenaktivität sicher eine der interessanteren Kennzahlen, vergleiche etwa hier. Allerdings berücksichtigt eine chess engine dieses Merkmal bereits in seiner internen Stellungsbewertung. Wir sehen daher von einer gesonderten Betrachtung ab. - Stellungskomplexität
Dass eine übersichtlichere Stellung leichter zu spielen ist, liegt auf der Hand. Dementsprechend ist es viel schwieriger, in einer sehr komplizierten Stellung mit vielen Figuren auf dem Brett und gespickt mit taktischen Fallen, Drohungen und Fesselungen, einen guten Zug zu finden. Daher klingt die Aufgabe, komplexe Stellungen als solche zu identifizieren, bereits äußerst kompliziert!
Ein interessanter Ansatz, relativ einfach zumindest ein starkes Indiz für eine komplizierte Stellung zu finden, wurde in einem Artikel von Matej Guid und Ivan Bratko beschrieben.
Demnach lassen sich komplizierte Stellungen auch dadurch charakterisieren, indem man die Bewertungen der besten Züge bei unterschiedlichen Suchtiefen ermittelt. Sind diese Einzelbewertungen nun starken Schwankungen unterworfen, kann daraus geschlossen werden, dass die untersuchte Stellung auf jeden Fall schwer einzuschätzen ist, was wiederum eine plausible Definition für eine komplizierte Stellung sein kann.
Wir wollen diesen Gedanken nicht weiter vertiefen, auch weil damit weiterer Rechenaufwand und damit zusätzliche Rechenzeit verbunden sind.
Zwischenfazit
Wir haben nun die abgeleiteten Kennzahlen auf Positionsebene definiert, die wir in einer eigenen Datenbank-Tabelle speichern werden. Die zugehörigen Werte leiten sich von den Basis-Kennzahlen ab. Somit entsteht aus jedem Datensatz der Tabelle position_analysis ein zugehöriger Datensatz in einer neuen Tabelle, die wir da_position nennen (das Präfix „da“ steht für „derived analytics“, wie oben erwähnt). Im Gegensatz zu den mit viel Rechenaufwand von Stockfish ermittelten Analyse-Kennzahlen sind die abgeleiteten Kennzahlen in wenigen Minuten berechnet. Deshalb speichern wir diese auch in einer separaten Tabelle, die wir jederzeit erweitern, ggf. korrigieren und neu berechnen lassen können können.
Implementierung
Als Erstes erstellen wir die Tabelle da_position mit den abgeleiteten Kennzahlen. Jede Zeile dieser Tabelle steht in einer eindeutigen Beziehung zu einer bestimmten Spielstellung und deren Basis-Kennzahlen. Dementsprechend erfolgt die Fremdschlüssel-Verknüpfung über die Spalte position.id wie unten dargestellt.

Da die Berechnung der abgeleiteten Kennzahlen mathematisch relativ simpel und die Programmlogik Datenbank-zentriert ist, verwenden wir statt Python PL/SQL-Code. und schreiben ein Package da, das den gesamten Code für alle abgeleiteten Kennzahlen enthalten soll. Momentan enthält es nur Code zur Berechnung der Positions-bezogenen abgeleiteten Kennzahlen. Zusätzlich erstellen wir ein Hilfs-PL/SQL-Package für ein Logging und eine zugehörige Logtabelle, in die wir ggf. Fehler- und Diagnose-Daten schreiben. Da MariaDB nicht über eine autonome Commit-Funktion wie z.B. Oracle verfügt und wir das Logging aber vom Transaktionsverhalten der eigentlichen Programmlogik entkoppeln wollen, verwenden wir für die Tabelle logtable die spezielle MariaDB engine Aria.
CREATE TABLE `logtable` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`dat` datetime NOT NULL DEFAULT current_timestamp(),
`msg` varchar(2000) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=Aria DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci CHECKSUM=1 PAGE_CHECKSUM=1 TRANSACTIONAL=1;
Log-Tabelle logtable
SET SESSION SQL_MODE='ORACLE';
DELIMITER //
CREATE DEFINER="chess_user"@"%" PACKAGE "logging" AS
-- must be declared as public!
PROCEDURE log(p_msg IN varchar2);
END
//
CREATE DEFINER="chess_user"@"%" PACKAGE BODY "logging" as
procedure log(p_msg IN varchar2)
as
begin
insert into logtable (msg) values (p_msg);
end log;
end
//
LOGGING Package Spezifikation und Body
SET SESSION SQL_MODE='ORACLE';
DELIMITER //
CREATE OR REPLACE PACKAGE da AS
-- must be declared as public!
PROCEDURE gen_da_position(p_player_id NUMBER(20));
END da;
//
CREATE OR REPLACE PACKAGE BODY da as
function calc_win_percent(p_centipawns in double) return double
as
v_win_percent double;
begin
v_win_percent := 50.0 + 50.0 * (2.0 / (1.0 + exp(-0.00368208 * p_centipawns)) - 1.0);
return v_win_percent;
end calc_win_percent;
function calc_accuracy (p_winpercent_before in double, p_winpercent_after in double) return double
as
v_win_diff double;
v_accuracy double;
begin
if p_winpercent_after >= p_winpercent_before then
return (100.0);
end if;
v_win_diff := p_winpercent_before - p_winpercent_after;
v_accuracy := 103.1668 * exp(-0.04354 * (v_win_diff)) - 3.1669;
v_accuracy := greatest(v_accuracy, 0.0);
v_accuracy := least(v_accuracy, 100.0);
return(v_accuracy);
end calc_accuracy;
function calc_judgement (p_winpercent_before in double, p_winpercent_after in double) return varchar2
as
v_win_diff double;
v_judgement varchar2(100);
begin
if p_winpercent_after >= p_winpercent_before then
return ('ENGINE');
end if;
v_win_diff := p_winpercent_before - p_winpercent_after;
if v_win_diff >= 30.0 then
v_judgement := 'BLUNDER';
elsif v_win_diff >= 20.0 then
v_judgement := 'MISTAKE';
elsif v_win_diff >= 10.0 then
v_judgement := 'INACCURACY';
end if;
return(v_judgement);
end calc_judgement;
function calc_sharpness (p_win in integer, p_loose in integer) return double
as
v_win_rescaled double;
v_loose_rescaled double;
v_sharpness double;
begin
if p_win = 0 or p_loose = 0 then
return null;
end if;
v_win_rescaled := p_win / 1000.0;
v_loose_rescaled := p_loose / 1000.0;
v_sharpness := pow(2.0 / (log(1.0 / v_win_rescaled - 1.0) + log(1.0 / v_loose_rescaled - 1.0)), 2);
return v_sharpness;
end calc_sharpness;
procedure upsert_da_position(p_da_position_rec in da_position%rowtype)
as
begin
insert into da_position values (p_da_position_rec.position_id,
p_da_position_rec.white_winning_chances,
p_da_position_rec.white_score_rate,
p_da_position_rec.white_draw_rate,
p_da_position_rec.black_winning_chances,
p_da_position_rec.black_score_rate,
p_da_position_rec.black_draw_rate,
p_da_position_rec.accuracy,
p_da_position_rec.judgement,
p_da_position_rec.sharpness
)
on duplicate key
update white_winning_chances = p_da_position_rec.white_winning_chances,
white_score_rate = p_da_position_rec.white_score_rate,
white_draw_rate = p_da_position_rec.white_draw_rate,
black_winning_chances = p_da_position_rec.black_winning_chances,
black_score_rate = p_da_position_rec.black_score_rate,
black_draw_rate = p_da_position_rec.black_draw_rate,
accuracy = p_da_position_rec.accuracy,
judgement = p_da_position_rec.judgement,
sharpness = p_da_position_rec.sharpness;
end upsert_da_position;
PROCEDURE gen_da_position(p_player_id NUMBER(20))
as
v_total_prv, v_total, v_win_rate_prv, v_win_rate, v_white_winning_chances_prv, v_white_winning_chances, v_score double;
v_score_rate, v_draw_rate, v_loss_rate, v_white_score_rate, v_black_score_rate double;
v_loss_rate_prv, v_accuracy, v_black_winning_chances_prv, v_black_winning_chances double;
v_white_draw_rate, v_black_draw_rate double;
v_judgement varchar2(100);
v_da_position_rec da_position%rowtype;
begin
for v_rec in
(
select
lag(g.id) over (order by g.id, p.half_move_num) as game_id_prv,
g.id as game_id,
pa.position_id,
p.half_move_num,
decode(p.move_white, '', null, p.move_white) move_white,
decode(p.move_black, '', null, p.move_black) move_black,
lag(pa.best_move_uci) over (order by g.id, p.half_move_num) as best_move_uci_prv,
pa.best_move_uci,
lag(pa.centipawn) over (order by g.id, p.half_move_num) as centipawn_prv,
pa.centipawn,
lag(pa.wins) over (order by g.id, p.half_move_num) as wins_prv,
pa.wins,
lag(pa.draws) over (order by g.id, p.half_move_num) as draws_prv,
pa.draws,
lag(pa.losses) over (order by g.id, p.half_move_num) as losses_prv,
pa.losses
from
game g
join player pw on
g.white_player_id = pw.id
join player pb on
g.black_player_id = pb.id
join position p on
(g.id = p.game_id)
join position_analysis pa on
(p.id = pa.position_id)
where
pw.id = p_player_id
or pb.id = p_player_id
order by g.id, p.half_move_num
)
loop
if v_rec.best_move_uci_prv is null or v_rec.game_id <> v_rec.game_id_prv
then
continue; -- no move evaluation available
end if;
v_da_position_rec := null;
v_total_prv := v_rec.wins_prv + v_rec.draws_prv + v_rec.losses_prv;
v_win_rate_prv := v_rec.wins_prv / v_total_prv;
v_loss_rate_prv := v_rec.losses_prv / v_total_prv;
v_white_winning_chances_prv := calc_win_percent(v_rec.centipawn_prv);
v_black_winning_chances_prv := 100 - v_white_winning_chances_prv;
v_total := v_rec.wins + v_rec.draws + v_rec.losses;
v_score := v_rec.wins + v_rec.draws / 2;
v_score_rate := v_score / v_total;
v_win_rate := v_rec.wins / v_total;
v_draw_rate := v_rec.draws / v_total;
v_loss_rate := v_rec.losses / v_total;
v_white_winning_chances := calc_win_percent(v_rec.centipawn);
v_black_winning_chances := 100 - v_white_winning_chances;
-- logging.log('v_white_winning_chances: '||v_white_winning_chances);
-- logging.log('v_black_winning_chances: '||v_black_winning_chances);
if (v_rec.half_move_num MOD 2 = 0)
then -- black moved
v_black_score_rate := 100 * v_score_rate;
v_white_score_rate := 100 * (1 - v_score_rate);
if (v_rec.move_black = v_rec.best_move_uci_prv) then
v_accuracy := 100.0;
v_judgement := 'ENGINE';
else
v_accuracy := calc_accuracy(v_black_winning_chances_prv, v_black_winning_chances);
v_judgement := calc_judgement(v_black_winning_chances_prv, v_black_winning_chances);
end if;
else -- white moved
v_white_score_rate := 100 * v_score_rate;
v_black_score_rate := 100 * (1 - v_score_rate);
if (v_rec.move_white = v_rec.best_move_uci_prv) then
v_accuracy := 100.0;
v_judgement := 'ENGINE';
else
v_accuracy := calc_accuracy(v_white_winning_chances_prv, v_white_winning_chances);
v_judgement := calc_judgement(v_white_winning_chances_prv, v_white_winning_chances);
end if;
end if;
v_white_draw_rate := 100 * v_draw_rate;
v_black_draw_rate := 100 * v_draw_rate;
--
-- set record data
--
v_da_position_rec.position_id := v_rec.position_id;
v_da_position_rec.white_winning_chances := v_white_winning_chances;
v_da_position_rec.white_score_rate := v_white_score_rate;
v_da_position_rec.white_draw_rate := v_white_draw_rate;
v_da_position_rec.black_winning_chances := v_black_winning_chances;
v_da_position_rec.black_score_rate := v_black_score_rate;
v_da_position_rec.black_draw_rate := v_black_draw_rate;
v_da_position_rec.accuracy := v_accuracy;
v_da_position_rec.judgement := v_judgement;
v_da_position_rec.sharpness := calc_sharpness(v_rec.wins, v_rec.losses);
--
-- upsert record
--
upsert_da_position(v_da_position_rec);
end loop;
end gen_da_position;
end da;
//
DELIMITER ;
DA Package Spezifikation und Body
Neben internen Hilfsfunktionen zur Berechnung der abgeleiteten Kennzahlen und dem Speichern in der Datenbank enthält das Package da die Prozedur gen_da_position mit dem Parameter p_player, für welchen die entsprechenden Kennzahlen erzeugt werden. Eventuell bereits vorhandene Daten in der Tabelle da_position werden bei erneutem Aufruf der Prozedur überschrieben.
Generierung der abgeleiteten Kennzahlen
Um die abgeleiteten Kennzahlen für alle Partien eines bestimmten Spielers zu erzeugen, rufen wir die Package-Prozedur mit dem entsprechenden Wert der Spalte id aus der Tabelle player auf. Wie unten gezeigt führen wir die Datenbank-Prozedur im Rahmen eines PL/SQL-Blocks für jede einzelne Spieler-ID separat aus und schreiben die Transaktion am Ende mit commit fest (der konkrete ID-Wert ist natürlich Datenbank-spezifisch). Die Ausführungszeit dafür beträgt weniger als eine Minute.
