Count (*) vs Count (1) - SQL Server

738

Nur frage mich , ob jemand von euch Menschen nutzen Count(1)über , Count(*)und wenn es einen spürbaren Unterschied in der Leistung ist oder wenn dies nur ein Vermächtnis Gewohnheit , die aus vergangenen Tagen Vergangenheit vorverlegt worden ist?

Die spezifische Datenbank ist SQL Server 2005.

super9
quelle
7
Ich weiß nichts über SQL Server, aber in MySQL gibt es keinen Unterschied. COUNT (Spalte) dagegen ist anders
Greg
118
Nicht wahr. COUNT (SomeColumn) gibt nur die Anzahl der Zeilen zurück, die Nicht-Null-Werte für SomeColumn enthalten. COUNT (*) und COUNT ('Foo') geben die Gesamtzahl der Zeilen in der Tabelle zurück.
Steve Broberg
1
Für weitere Details überprüfen Sie diese Auswahl Anzahl 1 vs Auswahl Anzahl * im Detail mit Grafik
Ali Adravi
4
Wow Steve und hier war ich 5 Jahre in TSQL, ohne count (*) vs Count (ColumnName) zu kennen. Vielen Dank
Harindaka
3
Beachten Sie auch die Antworten auf COUNT(*)vs COUNT(1)vs COUNT(pk)- was ist besser? . Es gibt auch COUNT(*)vs COUNT(column-name)- was ist korrekter? . Es kann durchaus andere Duplikate geben.
Jonathan Leffler

Antworten:

598

Es gibt keinen Unterschied.

Grund:

Online-Bücher sagen " COUNT ( { [ [ ALL | DISTINCT ] expression ] | * } )"

"1" ist ein Nicht-Null-Ausdruck. Es ist also dasselbe wie COUNT(*). Der Optimierer erkennt es als das, was es ist: trivial.

Das gleiche wie EXISTS (SELECT * ...oderEXISTS (SELECT 1 ...

Beispiel:

SELECT COUNT(1) FROM dbo.tab800krows
SELECT COUNT(1),FKID FROM dbo.tab800krows GROUP BY FKID

SELECT COUNT(*) FROM dbo.tab800krows
SELECT COUNT(*),FKID FROM dbo.tab800krows GROUP BY FKID

Gleiches IO, gleicher Plan, das funktioniert

Bearbeiten, August 2011

Ähnliche Frage auf DBA.SE .

Bearbeiten, Dezember 2011

COUNT(*)wird speziell in ANSI-92 erwähnt (suchen Sie nach " Scalar expressions 125")

Fall:

a) Wenn COUNT (*) angegeben ist, ist das Ergebnis die Kardinalität von T.

Das heißt, der ANSI-Standard erkennt, dass es offensichtlich blutet, was Sie meinen. COUNT(1)wurde aufgrund dieses Aberglaubens von RDBMS-Anbietern optimiert . Andernfalls würde es gemäß ANSI ausgewertet

b) Andernfalls sei TX die einspaltige Tabelle, die das Ergebnis der Anwendung des <Wertausdrucks> auf jede Zeile von T und der Eliminierung von Nullwerten ist. Wenn ein oder mehrere Nullwerte entfernt werden, wird eine Abschlussbedingung ausgelöst: warning-

gbn
quelle
73

In SQL Server ergeben diese Anweisungen dieselben Pläne.

Entgegen der landläufigen Meinung tun sie dies auch in Oracle.

SYS_GUID() in Oracle ist ziemlich rechenintensive Funktion.

In meiner Testdatenbank t_evenbefindet sich eine Tabelle mit 1,000,000Zeilen

Diese Abfrage:

SELECT  COUNT(SYS_GUID())
FROM    t_even

wird für 48Sekunden ausgeführt, da die Funktion jeden SYS_GUID()zurückgegebenen Wert auswerten muss , um sicherzustellen, dass es sich nicht um einen handelt NULL.

Diese Abfrage:

SELECT  COUNT(*)
FROM    (
        SELECT  SYS_GUID()
        FROM    t_even
        )

läuft nur für 2Sekunden, da es nicht einmal versucht zu bewerten SYS_GUID()(obwohl *es ein Argument dafür ist COUNT(*))

Quassnoi
quelle
es sollte SYS_GUID()mindestens (ich meine genau) einmal ausgewertet werden , damit die Unterabfrage das Ergebnis zurückgibt, oder?
Asgs
@asgs: warum denkst du so? Wie hängt es COUNT(*)von den Werten von ab SYS_GUID?
Quassnoi
Jetzt, wo du fragst, bin ich mir nicht sicher. Ich dachte für COUNT(*)die Ausführung, es braucht eine Tabelle, also sollte die Unterabfrage wie eine handeln. Ansonsten sehe ich keine Möglichkeit COUNT(*), einen aussagekräftigen Wert zurückzugeben
wie
1
@asgs: Vorausgesetzt, Sie wissen, was die mapMethode tut, sehen Sie, wie diese beiden Ausdrücke: t_even.map(() => sys_guid()).lengthund t_even.lengthwürden immer den gleichen Wert zurückgeben? Der Optimierer von Oracle ist intelligent genug, um das mapTeil zu erkennen und zu optimieren .
Quassnoi
1
@asgs genau. Nur eine kleine Korrektur: lengthhängt nicht ganz davon ab, woraus die Sammlung besteht, sondern nur von der Anzahl ihrer Elemente. Wenn diese Nummer in den Metadaten der Sammlung gespeichert ist (dies ist nicht bei Oracle oder den meisten anderen modernen RDBMS der Fall, sondern bei der alten MySQL-Speicher-Engine MyISAM), muss COUNT(*)nur der Wert aus den Metadaten übernommen werden.
Quassnoi
65

Klar COUNT(*)und COUNT(1)wird immer das gleiche Ergebnis zurückgeben. Wenn einer langsamer als der andere wäre, wäre dies effektiv auf einen Optimierungsfehler zurückzuführen. Da beide Formulare in Abfragen sehr häufig verwendet werden, wäre es für ein DBMS nicht sinnvoll, einen solchen Fehler nicht behoben zu lassen. Daher werden Sie feststellen, dass die Leistung beider Formulare (wahrscheinlich) in allen wichtigen SQL-DBMS identisch ist.

Tony Andrews
quelle
Ich würde es nicht als Fehler betrachten, wenn count (1) langsamer als count (*) wäre. Wenn Sie die DBMS bitten, Einsen zu generieren und diejenigen zu zählen, die nicht null sind, dann läuft es auf die Datensatzanzahl hinaus, aber Sie können nicht erwarten, dass die DBMS jeden Unsinn, den Sie schreiben, erkennen und für Sie umgehen.
Thorsten Kettner
1
Nun, ein Optimierer soll optimieren, und für eine Zählung müssen nur zwei Fälle berücksichtigt werden: Ausdruck, der null sein kann, Ausdruck, der niemals null sein wird: count (1) fällt in letzteren, sodass das DBMS dies nicht tun muss 1s "generieren", um die Frage zu beantworten. (Übrigens würde ich aus ästhetischen Gründen niemals etwas anderes als count (*) verwenden.)
Tony Andrews
46

Ich arbeite im SQL Server-Team und kann hoffentlich einige Punkte in diesem Thread klären (ich hatte es zuvor noch nicht gesehen, es tut mir leid, dass das Engineering-Team dies zuvor noch nicht getan hat).

Erstens gibt es keinen semantischen Unterschied zwischen select count(1) from tablevs. select count(*) from table. Sie geben in allen Fällen die gleichen Ergebnisse zurück (und es ist ein Fehler, wenn nicht). Wie in den anderen Antworten angegeben, select count(column) from tableist semantisch unterschiedlich und liefert nicht immer die gleichen Ergebnisse wie count(*).

Zweitens gibt es in Bezug auf die Leistung zwei Aspekte, die in SQL Server (und SQL Azure) von Bedeutung sind: Kompilierungszeitarbeit und Ausführungszeitarbeit. Die Kompilierungszeit ist eine trivial kleine Menge zusätzlicher Arbeit in der aktuellen Implementierung. In einigen Fällen wird das * auf alle Spalten erweitert, gefolgt von einer Reduzierung auf 1 Spalte, die ausgegeben wird, da einige der internen Operationen beim Binden und Optimieren funktionieren. Ich bezweifle, dass es in einem messbaren Test auftauchen würde, und es würde wahrscheinlich im Rauschen all der anderen Dinge verloren gehen, die unter der Decke passieren (wie automatische Statistiken, xevent-Sitzungen, Overhead des Abfragespeichers, Trigger usw.). Es sind vielleicht ein paar tausend zusätzliche CPU-Anweisungen. Damit, count (1) erledigt während der Kompilierung ein wenig weniger Arbeit (was normalerweise einmal vorkommt und der Plan über mehrere nachfolgende Ausführungen zwischengespeichert wird). Für die Ausführungszeit sollte es unter der Annahme, dass die Pläne gleich sind, keinen messbaren Unterschied geben. (Eines der früheren Beispiele zeigt einen Unterschied - es ist höchstwahrscheinlich auf andere Faktoren an der Maschine zurückzuführen, wenn der Plan identisch ist).

Wie der Plan möglicherweise anders sein kann. Dies ist äußerst unwahrscheinlich, aber in der Architektur des aktuellen Optimierers möglicherweise möglich. Das Optimierungsprogramm von SQL Server funktioniert als Suchprogramm (denken Sie: Computerprogramm, das Schach spielt und verschiedene Alternativen für verschiedene Teile der Abfrage durchsucht und die Alternativen berechnet, um den günstigsten Plan in angemessener Zeit zu finden). Diese Suche hat einige Einschränkungen hinsichtlich der Funktionsweise, um die Fertigstellung der Abfragekompilierung in angemessener Zeit zu gewährleisten. Für Abfragen, die über das Trivialste hinausgehen, gibt es Phasen der Suche, und sie behandeln Tranchen von Abfragen, basierend darauf, wie kostspielig der Optimierer die Ausführung der Abfrage für möglich hält. Es gibt drei Hauptsuchphasen, und in jeder Phase können aggressivere (teurere) Heuristiken ausgeführt werden, um einen günstigeren Plan als bei jeder früheren Lösung zu finden. Letztendlich gibt es am Ende jeder Phase einen Entscheidungsprozess, der versucht zu bestimmen, ob der bisher gefundene Plan zurückgegeben oder weiter gesucht werden soll. Bei diesem Prozess wird die bisher benötigte Gesamtzeit im Vergleich zu den geschätzten Kosten des besten bisher gefundenen Plans verwendet. Auf verschiedenen Computern mit unterschiedlichen CPU-Geschwindigkeiten ist es daher möglich (wenn auch selten), unterschiedliche Pläne zu erhalten, da in einer früheren Phase mit einem Plan eine Zeitüberschreitung auftritt und nicht in die nächste Suchphase übergegangen wird. Es gibt auch einige ähnliche Szenarien, die sich auf das Auslaufen der letzten Phase und möglicherweise auf den Speicher bei sehr, sehr teuren Abfragen beziehen, die den gesamten Speicher auf dem Computer belegen (normalerweise kein Problem bei 64-Bit, aber es war ein größeres Problem zurück auf 32-Bit-Servern). Wenn Sie einen anderen Plan erhalten, würde sich die Leistung zur Laufzeit letztendlich unterscheiden. Ich nicht

Net-Net: Bitte verwenden Sie eines der beiden gewünschten Elemente, da dies in keiner praktischen Form von Bedeutung ist. (Es gibt weitaus größere Faktoren, die die Leistung in SQL über dieses Thema hinaus ehrlich beeinflussen).

Ich hoffe das hilft. Ich habe ein Buchkapitel über die Funktionsweise des Optimierers geschrieben, aber ich weiß nicht, ob es angemessen ist, es hier zu veröffentlichen (da ich immer noch winzige Lizenzgebühren bekomme, glaube ich). Anstatt zu veröffentlichen, dass ich einen Link zu einem Vortrag bei SQLBits in Großbritannien veröffentlichen werde, in dem es darum geht, wie das Optimierungsprogramm auf hohem Niveau funktioniert, können Sie die verschiedenen Hauptphasen der Suche auf Wunsch etwas detaillierter sehen darüber zu lernen. Hier ist der Videolink: https://sqlbits.com/Sessions/Event6/inside_the_sql_server_query_optimizer

Conor Cunningham MSFT
quelle
2
Meiner Meinung nach 1erfährt das auch die gleiche Erweiterung. Ich stütze mich dabei auf die Perf-Tests hier. Stackoverflow.com/questions/1597442/… siehe auch das Beispiel in dieser Antwort auf eine Abfrage, bei 1der unerwartet ein Fehler auftritt, wenn Berechtigungen auf Spaltenebene im Spiel sind
Martin Smith,
21

Bedeutet im SQL-92-Standard COUNT(*)speziell "die Kardinalität des Tabellenausdrucks" (kann eine Basistabelle, "VIEW", eine abgeleitete Tabelle, ein CTE usw. sein).

Ich denke, die Idee war, dass COUNT(*)das leicht zu analysieren ist. Jede Äußerung unter Verwendung bedarf der Parser es keine Spalten verweisen , um sicherzustellen , ( COUNT('a')wo aeine wörtliche ist und COUNT(a)wo aeine Spalte kann zu unterschiedlichen Ergebnissen führen).

Ebenso COUNT(*)kann es von einem mit den SQL-Standards vertrauten menschlichen Programmierer leicht ausgewählt werden. Dies ist eine nützliche Fähigkeit, wenn Sie mit dem SQL-Angebot mehrerer Anbieter arbeiten.

Im speziellen Fall SELECT COUNT(*) FROM MyPersistedTable;wird davon ausgegangen, dass das DBMS wahrscheinlich Statistiken für die Kardinalität der Tabelle enthält.

Deshalb benutze ich , weil COUNT(1)und COUNT(*)semantisch äquivalent sind COUNT(*).

eines Tages, wenn
quelle
1
SQL-92 Text aus meiner Antwort auf DBA.SE verlinkt: dba.stackexchange.com/questions/2511/…
gbn
17

COUNT(*)und COUNT(1)sind im Falle von Ergebnis und Leistung gleich.

Nakul Chaudhary
quelle
12

Ich würde erwarten, dass der Optimierer sicherstellt, dass es außerhalb seltsamer Randfälle keinen wirklichen Unterschied gibt.

Wie bei allem ist die einzige Möglichkeit, dies zu erkennen, die Messung Ihrer spezifischen Fälle.

Das heißt, ich habe immer verwendet COUNT(*).

Richard
quelle
Gemäß der akzeptierten Antwort gilt dies nicht für MS SQL - es gibt tatsächlich keinen Unterschied zwischen den beiden.
David Manheim
10

Da diese Frage immer wieder auftaucht, gibt es hier noch eine Antwort. Ich hoffe, dass ich hier etwas für Anfänger hinzufügen kann, die sich über "Best Practice" wundern.

SELECT COUNT(*) FROM something zählt Datensätze, was eine einfache Aufgabe ist.

SELECT COUNT(1) FROM something Ruft eine 1 pro Datensatz ab und zählt dann die 1s, die nicht null sind, was im Wesentlichen Datensätze zählt, nur komplizierter.

Trotzdem: Gute DBMS bemerken, dass die zweite Anweisung dieselbe Anzahl wie die erste Anweisung ergibt, und interpretieren sie entsprechend neu, um keine unnötige Arbeit zu leisten. In der Regel führen beide Anweisungen zum gleichen Ausführungsplan und benötigen dieselbe Zeit.

Aus Gründen der Lesbarkeit sollten Sie jedoch die erste Anweisung verwenden. Sie möchten Datensätze zählen, also Datensätze, keine Ausdrücke. Verwenden Sie COUNT (Ausdruck) nur, wenn Sie Vorkommen von etwas zählen möchten, die nicht null sind.

Thorsten Kettner
quelle
8

Ich habe einen Schnelltest unter SQL Server 2012 auf einer 8-GB-RAM-Hyper-V-Box durchgeführt. Sie können die Ergebnisse selbst sehen. Ich habe während der Ausführung dieser Tests keine andere Fensteranwendung außer SQL Server Management Studio ausgeführt.

Mein Tabellenschema:

CREATE TABLE [dbo].[employee](
    [Id] [bigint] IDENTITY(1,1) NOT NULL,
    [Name] [nvarchar](50) NOT NULL,
 CONSTRAINT [PK_employee] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

Gesamtzahl der Datensätze in der EmployeeTabelle: 178090131 (~ 178 Millionen Zeilen)

Erste Abfrage:

Set Statistics Time On
Go    
Select Count(*) From Employee
Go    
Set Statistics Time Off
Go

Ergebnis der ersten Abfrage:

 SQL Server parse and compile time: 
 CPU time = 0 ms, elapsed time = 35 ms.

 (1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 10766 ms,  elapsed time = 70265 ms.
 SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

Zweite Abfrage:

    Set Statistics Time On
    Go    
    Select Count(1) From Employee
    Go    
    Set Statistics Time Off
    Go

Ergebnis der zweiten Abfrage:

 SQL Server parse and compile time: 
   CPU time = 14 ms, elapsed time = 14 ms.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 11031 ms,  elapsed time = 70182 ms.
 SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

Sie können feststellen, dass es einen Unterschied von 83 (= 70265 - 70182) Millisekunden gibt, der leicht auf den genauen Systemzustand zum Zeitpunkt der Ausführung von Abfragen zurückgeführt werden kann. Außerdem habe ich einen einzelnen Lauf durchgeführt, sodass dieser Unterschied genauer wird, wenn ich mehrere Läufe und eine Mittelung durchführe. Wenn für einen so großen Datensatz der Unterschied weniger als 100 Millisekunden beträgt, können wir leicht den Schluss ziehen, dass die beiden Abfragen keinen Leistungsunterschied aufweisen, den die SQL Server Engine aufweist.

Hinweis : RAM wird in beiden Läufen nahezu zu 100% ausgelastet. Ich habe den SQL Server-Dienst neu gestartet, bevor ich beide Läufe gestartet habe.

RBT
quelle
7
SET STATISTICS TIME ON

select count(1) from MyTable (nolock) -- table containing 1 million records. 

SQL Server-Ausführungszeiten:
CPU-Zeit = 31 ms, verstrichene Zeit = 36 ms.

select count(*) from MyTable (nolock) -- table containing 1 million records. 

SQL Server-Ausführungszeiten:
CPU-Zeit = 46 ms, verstrichene Zeit = 37 ms.

Ich habe dies hunderte Male ausgeführt und den Cache jedes Mal geleert. Die Ergebnisse variieren von Zeit zu Zeit, da die Serverlast variiert, haben aber fast immer count(*)eine höhere CPU-Zeit.

Eyal Z.
quelle
14
Ich kann das nicht reproduzieren. count(*)und count(1)Rück Ergebnisse innerhalb weniger ms voneinander, auch wenn eine Tabelle mit 4.500.000 Zeilen zu zählen, in meiner SQL 2008 - Instanz.
Jeff Atwood
2
In einigen Systemen wird die zuerst ausgeführte Anweisung manchmal immer schneller ausgeführt. Haben Sie die Reihenfolge, in der sie ausgeführt werden, zufällig ausgewählt?
JosephDoggie
@JosephDoggie sollte der SQL Server-Dienst immer neu gestartet werden, bevor jede Abfrage ausgeführt wird, während solche Messungen / Statistiken durchgeführt werden. Wenn Sie gerade den SQL Server-Dienst gestartet haben, wird jeder Lauf völlig unabhängig und die Reihenfolge der Abfragen sollte keine Rolle spielen. Wenn Sie andererseits den SQL Server-Dienst nicht neu starten und die Engine eine Art Caching von Ausführungsplänen durchführt, sollte die später ausgeführte Abfrage schneller ausgeführt werden als die erste.
RBT
Die Ausführungszeiten müssen bei Vergleichen die genauen Abfragepläne berücksichtigen. Wenn sie unterschiedlich sind (z. B. Hash-Aggregat vs. Sortieren + Stream-Aggregat), sind die Ergebnisse nicht vergleichbar. Ich fordere daher zur Vorsicht auf, hier ohne weitere Daten Schlussfolgerungen zu ziehen.
Conor Cunningham MSFT
3

Es gibt einen Artikel zeigt , dass die COUNT(1)auf Oracle ist nur ein Alias COUNT(*), mit einem Nachweis darüber.

Ich werde einige Teile zitieren:

Es gibt einen Teil der Datenbanksoftware namens "The Optimizer", der in der offiziellen Dokumentation als "Integrierte Datenbanksoftware, die den effizientesten Weg zur Ausführung einer SQL-Anweisung bestimmt" definiert ist.

Eine der Komponenten des Optimierers heißt „der Transformator“, dessen Aufgabe es ist, zu bestimmen, ob es vorteilhaft ist, die ursprüngliche SQL-Anweisung in eine semantisch äquivalente SQL-Anweisung umzuschreiben, die effizienter sein könnte.

Möchten Sie sehen, was der Optimierer tut, wenn Sie eine Abfrage mit COUNT (1) schreiben?

Mit einem Benutzer mit ALTER SESSIONBerechtigungen können Sie eine setzen tracefile_identifier, die Optimierungsverfolgung aktivieren und die COUNT(1)Auswahl ausführen , wie : SELECT /* test-1 */ COUNT(1) FROM employees;.

Danach müssen Sie die Trace-Dateien lokalisieren, was getan werden kann SELECT VALUE FROM V$DIAG_INFO WHERE NAME = 'Diag Trace';. Später in der Datei finden Sie:

SELECT COUNT(*) COUNT(1)” FROM COURSE”.”EMPLOYEES EMPLOYEES

Wie Sie sehen können, ist es nur ein Alias ​​für COUNT(*).

Ein weiterer wichtiger Kommentar: Das COUNT(*)war vor zwei Jahrzehnten auf Oracle vor Oracle 7.3 wirklich schneller :

Count (1) wurde seit 7.3 in count (*) umgeschrieben, da Oracle mythische Aussagen gerne automatisch abstimmt. In früheren Oracle7 musste Oracle (1) für jede Zeile als Funktion auswerten, bevor DETERMINISTIC und NON-DETERMINISTIC existieren.

Vor zwei Jahrzehnten war count (*) also schneller

Für andere Datenbanken als SQL Server sollte es für jede einzelne einzeln recherchiert werden.

Ich weiß, dass diese Frage spezifisch für SQL Server ist, aber die anderen Fragen zu SO zu demselben Thema, ohne die Datenbank zu erwähnen, wurden geschlossen und als aus dieser Antwort dupliziert markiert.

Dherik
quelle
1

In allen RDBMS sind die beiden Zählmethoden hinsichtlich des Ergebnisses gleichwertig. In Bezug auf die Leistung habe ich keinen Leistungsunterschied in SQL Server festgestellt, aber es kann erwähnenswert sein, dass einige RDBMS, z. B. PostgreSQL 11, weniger optimale Implementierungen aufweisen, COUNT(1)da sie die Nullbarkeit des Argumentausdrucks überprüfen, wie in diesem Beitrag zu sehen ist .

Ich habe beim Ausführen einen Leistungsunterschied von 10% für 1 Million Zeilen festgestellt:

-- Faster
SELECT COUNT(*) FROM t;

-- 10% slower
SELECT COUNT(1) FROM t;
Lukas Eder
quelle
0

COUNT (1) unterscheidet sich, wenn überhaupt, nicht wesentlich von COUNT (*). In Bezug auf die Zählung von NULL-fähigen Spalten kann dies unkompliziert sein, um die Unterschiede zwischen COUNT (*) und COUNT (<einige Spalten>) zu demonstrieren.

USE tempdb;
GO

IF OBJECT_ID( N'dbo.Blitzen', N'U') IS NOT NULL DROP TABLE dbo.Blitzen;
GO

CREATE TABLE dbo.Blitzen (ID INT NULL, Somelala CHAR(1) NULL);

INSERT dbo.Blitzen SELECT 1, 'A';
INSERT dbo.Blitzen SELECT NULL, NULL;
INSERT dbo.Blitzen SELECT NULL, 'A';
INSERT dbo.Blitzen SELECT 1, NULL;

SELECT COUNT(*), COUNT(1), COUNT(ID), COUNT(Somelala) FROM dbo.Blitzen;
GO

DROP TABLE dbo.Blitzen;
GO
Graeme
quelle