Unterstütztes SQL (Referenz)
Diese Referenz zeigt, welche SQL-Anweisungen der Treiber in OData-Anfragen übersetzt, wie er sie übersetzt und was er abweist. Sie brauchen diese Seite, wenn eine Abfrage aus Access, ADO.NET oder VBA nicht wie erwartet läuft oder wenn Sie eine Abfrage so schreiben wollen, dass sie ohne eigene Anweisungen auskommt. Den Überblick gibt das Konzept Wie der Treiber Anweisungen übersetzt. Welche Mengen und Spalten es überhaupt gibt und woher ihre Typen kommen, steht vorher unter Wie aus einem OData-Dienst ein relationales ODBC-Modell wird.
Syntax
SELECT <Spalten | Aggregate> FROM <Menge> [WHERE <Bedingung>] [GROUP BY <Spalten> [HAVING <Bedingung>]] [ORDER BY <Spalten> [OFFSET n ROWS [FETCH NEXT m ROWS ONLY]]] INSERT INTO <Menge> (…) VALUES (…) UPDATE <Menge> SET … WHERE <Schlüssel> = ? [AND <Zeilenversion> = ?] DELETE FROM <Menge> WHERE <Schlüssel> = ? [AND <Zeilenversion> = ?]
Eine übersetzte Anweisung liest genau eine Entitätsmenge. Alles andere geht an die Adresse für eigene Anweisungen oder wird abgewiesen.
Parameter
Bedingungen
| SQL | OData |
|---|---|
=, <>, <, <=, >, >=, IS NULL, IS NOT NULL | eq, ne, lt, le, gt, ge, eq null, ne null |
Spalte gegen Spalte derselben Tabelle (C.ParentId = C.Id), auch unter einem Tabellennamen-Alias | ParentId eq Id; Spalten verschiedener Typfamilien (Zahl gegen Text) weist der Treiber ab. Braucht einen Dienst, der Feld gegen Feld annimmt |
AND, OR, NOT, Klammern | and, or, not, Klammern |
IN (…), NOT IN (…) | Oder-Kette, verneint |
LIKE 'x%', '%x', '%x%' | startswith, endswith, contains |
LIKE mit einem Zeichen in eckigen Klammern ('B[_]%') | das Zeichen selbst, wie in SQL Server |
Parameter ? | gebundener Wert, als Literal in den Filter |
Gruppieren und Zusammenfassen
Access schickt Summen und Gruppen über eine verknüpfte Tabelle an den Treiber, gleich was
SQLGetInfo über ihn sagt, und rechnet sie nicht selbst, wenn der Treiber ablehnt – die
Abfrage scheitert dann mit „ODBC-Aufruf fehlgeschlagen“ (auch mit
SQL_ODBC_SQL_CONFORMANCE = 0). Mit einer Adresse für eigene Anweisungen
gehen sie unverändert an die Datenbank. Ohne sie übersetzt der Treiber sie nach $apply (OData
Data Aggregation 4.0) – aber nur, wenn das Schemadokument Org.OData.Aggregation.V1.ApplySupported
mit den nötigen Transformationen ankündigt. Ein Dienst ohne diese Ankündigung beantwortete
$apply bestenfalls mit einem Fehler, schlimmstenfalls – wenn er Unbekanntes übergeht – mit den Zeilen ohne
Gruppierung, die die Anwendung für die Gruppen hielte.
Access schickt AVG nicht, sondern SUM und COUNT(x) und teilt selbst; für
StDev und Var zusätzlich SUM(x * x); TOP n behält es; ORDER BY nennt die Spalte
voll qualifiziert ("dbo"."Artikel"."Gruppe"), auch innerhalb eines Aggregats; eine
Gruppierung nach Year(…) rechnet es selbst über die gelesene Spalte.
| Access schickt | Der Treiber fragt |
|---|---|
SELECT SUM("x") FROM "dbo"."t" "R" | t?$apply=aggregate(x with sum as gw_a1) |
SELECT "g", COUNT(*) FROM … WHERE ("id" > 3) GROUP BY "g" ORDER BY "dbo"."t"."g" DESC | $apply=filter(id gt 3)/groupby((g),aggregate($count as gw_a1))&$orderby=g desc |
… GROUP BY "g" HAVING (COUNT(*) > 1) | $filter=gw_a1 gt 1 auf die Gruppen |
… HAVING SUM("b") >= ? (Aggregat nicht ausgewählt) | angefragt als gw_a1, verglichen, aber nicht als Spalte gezeigt |
… ORDER BY SUM("dbo"."t"."id") DESC | dasselbe Aggregat wie in der Auswahl, $orderby=gw_a1 desc |
SELECT DISTINCT "g" FROM … | groupby((g)) |
SELECT COUNT("x"), SUM(("x" * "x")), SUM("x") FROM … | compute((x mul x) as gw_c1)/aggregate(x with Wkl.Store.V1.count as gw_a1,gw_c1 with sum as gw_a2,x with sum as gw_a3) |
SELECT COUNT(*) FROM … allein | weiter $count – das beantwortet jeder Dienst |
COUNT(x) – die Werte, die nicht fehlen – kennt das Protokoll nicht als Standardmethode; der
Treiber nutzt dafür Wkl.Store.V1.count, wenn der Dienst sie mit
Aggregation.CustomAggregationMethods ankündigt, und lehnt sonst ab. Die Aliasse gw_aN und
gw_cN wählt er so, dass keine Eigenschaft der Menge so heißt.
Spaltentypen wie bei Microsofts Treiber für SQL Server, damit Access dieselben Felder
anlegt: eine Anzahl ist int, die Summe ganzer Zahlen bis int ist int (ein größerer Wert
scheitert, wie dort am Überlauf), die von bigint ist bigint, die einer Dezimalzahl decimal(38, s);
MIN und MAX haben den Typ der Spalte. Eine Abweichung mit Absicht: Der Mittelwert ganzer
Zahlen ist float mit Nachkommastellen, wo SQL Server eine ganze Zahl liefert – der Dienst
rechnet den wahren Mittelwert, und ihn als ganze Zahl zu beschreiben schnitte ab, was er
schickt. Eine Spalte heißt wie ihr Alias in der Anweisung; ohne Alias hat sie keinen Namen, wie
beim ODBC Driver 18 (Access nennt sie dann selbst Expr1000, Expr1001 …).
Abgewiesen mit 42000 statt angenähert: Rechnen zwischen Aggregaten (SUM(a) - SUM(b)),
Division, andere Funktionen als SUM, MIN, MAX, AVG, COUNT, eine Spalte in der Auswahl,
die nicht gruppiert ist, DISTINCT in SUM oder AVG, ein Parameter innerhalb eines Aggregats,
* neben GROUP BY. Mit einer Adresse für eigene Anweisungen geht auch das an die Datenbank.
Werte und Feldtypen einer übersetzten Summenabfrage sind in Access dieselben wie über Microsofts
ODBC Driver 18. Läuft eine Summe über int über, scheitert sie wie dort.
Seitenweise lesen mit OFFSET/FETCH
Eine Anwendung, die ein großes Ergebnis Bildschirm für Bildschirm zeigt, fragt jede Seite mit
der Blätterform von SQL Server und des SQL-Standards an. Der Treiber übersetzt sie ohne
Adresse für eigene Anweisungen: OFFSET n wird $skip=n, FETCH NEXT m ROWS ONLY wird $top=m; Bedingung
und Sortierung gehen wie sonst als $filter und $orderby mit. Beispiel, wie ein Datengitter
es über eine eigene Verbindung mit SQLExecDirect schickt:
SELECT S."ID", S."Kunde", S."Umsatz" FROM "dbo"."Umsatz" S ORDER BY S."Umsatz" DESC, S."ID" OFFSET 500000 ROWS FETCH NEXT 200 ROWS ONLY
wird zu Umsatz?$select=ID,Kunde,Umsatz&$orderby=Umsatz desc,ID&$skip=500000&$top=200.
| Regel | Grund |
|---|---|
OFFSET nur hinter ORDER BY; ROW/ROWS, FIRST/NEXT gleichwertig; FETCH darf fehlen | Wie in SQL Server: ohne Ordnung gibt es keine feste Position |
Werte als ganze Zahl oder Parameter (?); OFFSET ab 0, FETCH ab 1 | Was SQL Server abweist (Fehler 10742, 10744), weist auch der Treiber ab |
TOP neben OFFSET wird abgewiesen | SQL Server weist eine solche Anweisung ebenfalls ab (Fehler 10741) |
Gruppierte Anweisungen (GROUP BY, DISTINCT) blättern genauso | Der Dienst blättert die Gruppen nach $apply mit $skip/$top |
SQL_ATTR_MAX_ROWS begrenzt FETCH zusätzlich | Wie bei TOP |
COUNT(*) mit Bedingung – auch die Rangbedingung für die Position eines Satzes – bleibt $count; COUNT(*) AS name behält den Namen | Die Spalte hat sonst keinen Namen, und ein Leser nach Namen fände sie nicht |
Die Wörter OFFSET, FETCH, ROWS, NEXT usw. erkennt der Treiber nur am Ende von
ORDER BY; eine Spalte namens Next bleibt unverändert schreibbar.
Wie teuer eine tiefe Seite ist, entscheidet der Dienst mit seiner Datenbank, nicht der Treiber. Der
Datendienst Wkl.Data.Gateway macht aus $skip ein OFFSET … ROWS, und SQL Server liest dafür alle
Zeilen davor. Eine flache Seite kostet über den Treiber zusätzlich den festen Aufschlag einer
HTTP-Anfrage.
Rückgabewert
Spaltennamen sind der Alias der Anweisung; ohne Alias ist der Name leer, wie beim ODBC Driver 18. Spaltentypen folgen den Regeln unter „Gruppieren und Zusammenfassen“.
Bemerkungen
LIKEgeht nur in den Formenx%,%x,%x%und ohne_als Platzhalter. Ein einzelnes Zeichen in eckigen Klammern ([_],[%]) oder hinter einem Fluchtzeichen (ESCAPE) gilt als dieses Zeichen und ist erlaubt. Eine Zeichenklasse wie[a-c]oder[^a]hat in OData keine Entsprechung.- Ausdrücke in der Feldliste übersetzt der Treiber nur innerhalb eines Aggregats.
- Verknüpfungen und Unterabfragen kündigt
SQLGetInfonicht an; Access schickt Verbünde dennoch, und sie laufen nur über die Adresse für eigene Anweisungen. - Der Cursor liest vorwärts, ohne Lesezeichen und ohne Sperren.
- GUID-Werte (
uniqueidentifier,Edm.Guid): als Literal in der Schreibweise von Access ({guid {33333333-…}}), als Text oder als Parameter vom TypGuidbzw. als 16 Byte; an den Dienst geht die Form von OData v4 ohne Typnamen. Für ODBC-2-Anwendungen wie Access liefert der Treiber eine GUID ohne angefragten Typ als Text, wie ODBC Driver 18. Das gilt in Access für Datenblatt, Formular, Unterformular und Nachschlageliste.
Beispiel
Access: SELECT ... FROM Artikel WHERE Gruppe = 'S' ORDER BY Nummer Dienst: GET .../Artikel?$filter=Gruppe eq 'S'&$orderby=Nummer&$select=...
Gilt für
Treiber Wkl OData, beide Bitbreiten; Gruppieren nur gegen einen Dienst, der $apply ankündigt
(siehe Anforderungen an einen OData-Dienst).
Ausnahmen
| SQLSTATE | Bedingung |
|---|---|
42000 | Die Anweisung lässt sich nicht übersetzen und es gibt keine Adresse für eigene Anweisungen; die Meldung nennt den Grund, etwa die Spalte oder das Konstrukt |