Queries
Aufbau der Queries
Innerhalb der Query-Befehle stehen eine Reihe von Funktionalitäten zur Verfügung, um die Auswahl der Datenpunkte bzw. Datenpunktattribute zu treffen und das Ergebnis zu behandeln. Dazu können verschiedene Befehlsteile verwendet werden, deren Reihenfolge festgelegt ist, da die Ausdrücke logisch miteinander verknüpft sind.
Beachten Sie Folgendes:
- Je kleiner die Tabelle, desto performanter (FROM- und WHERE _DPT, _DP-Filter verwenden) die Abfrage.
- Durch FROM-Klausel Tabelle möglichst stark einschränken.
- WHERE _DPT verwenden (aber nur mit “=“, also ohne LIKE und ohne OR), wenn durch FROM-Klausel nicht sowieso schon (fast) nur DPs eines Typs zurückgeliefert werden.
- FROM-Klausel: A* ist wesentlich schneller als *A (a la dpNames).
- In einer SQL-Abfrage, in der DP-Namen abgefragt werden, darf der FROM-Teil nur eine config und ein Attribut enthalten, aber keine zusätzlichen DP-Elemente.
Wenn es projektspezifisch notwendig ist, weitere Config-Attribute innerhalb derselben Abfrage abzufragen, dann sollten nur solche Configs/Attribute aufgenommen werden, die auch vom Event-Manager verarbeitet werden:
- _original and _online,
- _default (Ersatzwert),
- _pv_range ( WinCC OA Wertebereich),
- _alert_hdl,
- _alert_class (Meldeklasse),
- _auth (Berechtigung),
- _u_range (Benutzerwertebereich)
- _distrib,
- _dp_fct (DP-Funktion),
- _general und
- _connect
Andernfalls könnte der Event-Manager nicht die gesamte Abfrage selbst verarbeiten und der Manager müsste Teile der Abfrage an andere Manager (z. B. Data-Manager im Fall von "_offline"-Attributen) weiterleiten, was zu einer negativen Performance führen würde. Dies könnte sich auf die gesamte Abfrage auswirken, indem Zeit durch Roundtrip-Zeiten verschwendet wird.
BEISPIEL
Abfrage, die besser sein könnte:
dpQuery("SELECT '_online.._value' FROM '*.Devices.**' WHERE _DPT=\"Station\"", data);
Verbesserung: Station_*.Devices
statt *.Devices
(wenn
alle Stations mit Station_* beginnen)
BEISPIEL
Abfrage unnötig verwendet:
dpQueryConnectSingle("evStatusCB",
true, "evStatus", "SELECT '_online.._value' FROM '" +
reduManagerDpe + "' WHERE _DPT = \"_ReduManager\"");
SELECT
Auswählen von Daten in der Datenbank.
Beachten Sie, dass, wenn Sie Datenpunktelemente in einer SELECT-Abfrage verwenden, verwenden Sie die Datenpunkttyp-Klausel da WinCC OA ID's verwendet und der gleiche Datenpunktelement-Name verschiedene ID's für verschiedene DP-Typen haben könnte. Daher dürfen Abfragen wie:
string strQuery = "SELECT '.name:_original.._value,.Datapoint1.Element1:_original.._value'
FROM '*'";
nicht verwendet werden!
Verwenden Sie stattdessen:
string strQuery = "SELECT '.name:_original.._value,.Datapoint.Element1:_original.._value'
FROM '*' WHERE _DPT = \"ExampleDp_Float\"";
Der Datenpunkttyp muss auch angegeben werden, wenn die WHERE-Bedingung DP-Elementnamen enthält: z.B.: WHERE '.dpe' = 1!
[ALL]
Liefert alle Werte aus der Datenbank, welche dem FROM Statement der Query entsprechen. Sowohl aktuelle als auch historische Daten!
[ALERT]
In Zusammenhang mit SELECT werden Alarme ausgewählt (siehe auch _alert_hdl).
SELECT ALERT meldet sich nur auf Alarmattribute an, die man auch mit alertGet*() abfragen kann. Änderungen anderer Configs oder _alert_hdl-Attribute, die nicht in diese Liste fallen, lösen keinen Hotlink aus.
Die Configs _original und _online werden aus dem Prozessabbild übernommen, wenn sich seitdem der Alarm nicht geändert hat.
Event-Configs (_general, andere _alert_hdl, _u_range, etc.) werden vom Event-Manager übernommen.
Alle restlichen (_offline, keine Event-Configs, _original / _online die nicht zum Alarm gehören) werden vom Data-Manager per dpGetAsynch() abgefragt.
Wird bei SELECT ALERT in der WHERE Bedingung mit einer booleschen Variable verglichen, gilt es zu berücksichtigen dass folgender Vergleich nicht funktioniert:
"SELECT ALERT '_alert_hdl.._add_value_5' FROM 'myAlarm.' WHERE '_alert_hdl.._add_value_5' == TRUE"
Stattdessen muss das Statement in einer der beiden angeführten Varianten angegeben werden:
"SELECT ALERT '_alert_hdl.._add_value_5' FROM 'myAlarm.' WHERE '_alert_hdl.._add_value_5' == 1"
"SELECT ALERT '_alert_hdl.._add_value_5' FROM 'myAlarm.' WHERE '_alert_hdl.._add_value_5' LIKE \"TRUE\""
[ALERT SINGLE]
Im Zusammenhang mit SELECT wird jener Alarm ausgewählt, welcher zur letzten Änderung der Meldebehandlung führte (bei dpQueryConnectSingle()).
'Spalten' FROM 'Zeilen'
Festlegen der Daten, welche durch SELECT ausgewählt werden.
[FROM 'DPGROUP']
Einschränkung der Abfrage auf Datenpunktgruppen.
[REMOTE]
Hier wird bei verteilten Systemen der Systemname angegeben.
Folgendes Verhalten zeigen dpQueryConnect-Funktionen zur Laufzeit, wenn diese mit REMOTE <Systemname> ausgeführt werden:
- Werden die Funktionen auf ein System ausgeführt, dann muss dieses aktuell verbunden sein - sonst erhält man beim Connect einen Fehler.
- Wenn eine Verbindung zu einem System verloren geht, wird für dieses Ereignis kein Hotlink ausgelöst.
- Wird die Verbindung zu einem System hergestellt wurde, welches schon verbunden war, als das Connect gestartet wurde, dann wird ein Hotlink ausgelöst.
[REMOTE ALL]
Macht eine Query auf alle Systeme. Intern werden Messages zu allen bekannten und zukünftigen Systemen geschickt, einschließlich des eigenen Systems, wenn der Dist-Manager läuft. Wenn der Dist-Manager nicht läuft, wird eine Fehlermeldung angezeigt und es wird das Ergebnis des lokalen Systems geliefert
REMOTE_ALL funktioniert nur mit dpQueryConnect-Funktionen und nicht mit dpQuery! Es ist zu beachten, dass die Funktionen dpQueryConnectSingle() sowie dpQueryConnectAll()ohne dem Schlüsselwort REMOTE ALL nur mit EINEM System verbunden werden können!
Folgendes Verhalten zeigen dpQueryConnect-Funktionen zur Laufzeit, wenn diese mit REMOTE ALL ausgeführt werden:
- Die Funktionen werden auf alle verbundenen Systeme ausgeführt (Dist-Manager läuft).
- Wenn eine Verbindung zu einem System verloren geht, wird für dieses Ereignis kein Hotlink ausgelöst.
- Wenn die Verbindung zu einem System (wieder-)hergestellt wird (auch wenn das System zuvor noch nicht verbunden war!), dann wird ein Hotlink durch das System ausgelöst.
[ WHERE ]
Einschränkung der ausgewählten Datenmenge. (mehr dazu unter SQL-Schlüsselwörter)
[ TIMERANGE () ]
Zeitliche Abfrage der Informationen. (mehr dazu unter SQL-Schlüsselwörter)
[ SORT BY y1[, y2, ...]] oder [ORDER BY y1[,y2, ...]]
Sortieren der selektierten Daten. (mehr dazu unter SQL-Schlüsselwörter)
[ GROUP BY y1 [, y2, ...]]
Zusammenfassen der ausgewählten Informationen. (mehr dazu unter SQL-Schlüsselwörter)
[ FIRST | LAST x ]
Weitere Einschränkungen der selektierten Daten. (mehr dazu unter SQL-Schlüsselwörter)
Eine SQL-Abfrage in die DB muss immer aus einem SELECT und FROM Statement bestehen.
Die hier in [ ] angeführten Query-Befehle sind optional.
Eine Query-Abfrage genügt einer bestimmten Syntax. Schlüsselwörter wie SELECT werden groß geschrieben. Prinzipiell wird Groß- und Kleinschreibung beachtet. Strings kommen in doppelte Hochkommata ("), Datenpunktbezeichner in einfache Hochkommata ('). Es können Wildcards verwendet werden.
FILTERLANG 'de_AT.utf8' / FILTERLANG <globLangIndex>
Das Schlüsselwort FILTERLANG kann verwendet werden, um eine Projektsprache für eine Abfrage anzugeben. Wenn das Schlüsselwort verwendet wird, wird das Ergebnis der Abfrage nach der angegebenen Sprache gefiltert. Das Schlüsselwort muss als letztes Element der WHERE-Klausel angegeben werden. Beide, der Sprachbezeichner wie z.B. 'de_AT.utf8' oder der Integer-lang-Index wie z.B. 10000 können verwendet werden.
Wenn das Schlüsselwort nicht verwendet wird, wird das Ergebnis der Abfrage nach der Default-Projektsprache (erste Sprache der Config-Datei bzw. die Sprache, die über den Config-Eintrag "lang" definiert wurde) gefiltert. Das folgende Beispiel veranschaulicht wie das Schlüsselwort verwendet werden kann:
main()
{
int rc;
dyn_dyn_anytype ddt;
dyn_dyn_string dpt;
dyn_dyn_int dptVal;
string typeName;
string dpName;
langString val;
typeName = "testLangStringType";
dpName = "_testFilterLang";
if (dpExists(dpName)) //If a data point exists
dpDelete(dpName); //delete the data point
if (dpTypeExists(typeName)) //If the data point type exists
dpTypeDelete(typeName); //delete the type
dpt[1] = makeDynString(typeName);
dptVal[1] = makeDynInt(DPEL_LANGSTRING);
dpTypeCreate(dpt, dptVal); //Create a data point type
dpCreate(dpName, typeName); //Create a data point
setLangString(val, 0, "Deutsch"); //Set the texts for the data point
setLangString(val, 1, "English");
setLangString(val, 2, "Russian");
dpSet(dpName + ".:_original.._value", val);
// ------------------------------------------------------------------------------------
//Query the values by using the FILTERLANG keyword and output the result:
rc = dpQuery("SELECT '_original.._value' FROM '" + dpName + "' WHERE '_original.._value' = \"English\" FILTERLANG 'en_US.utf8'", ddt); //Result: English
DebugN("Return value:", rc, "Search result:", ddt);
rc = dpQuery("SELECT '_original.._value' FROM '" + dpName + "' WHERE '_original.._value' = \"Deutsch\" FILTERLANG " + getGlobalLangId(0), ddt); //Result: German, by using lang index for the query
DebugN("getGloablLangId:", getGlobalLangId(0));
DebugN("Return value:", rc, "Search result. Lang idx was used for the search:", ddt);
rc = dpQuery("SELECT '_original.._value' FROM '" + dpName + "' WHERE '_original.._value' = \"English\" FILTERLANG 'de_AT.utf8'", ddt); //No result since English is searched in German language
DebugN("Return value:", rc, "No result since English is searched in the German language:", ddt);
}
Ergebnis der Queries
Das Ergebnis einer Query-Abfrage wird als zweidimensionales Datenfeld zurückgeliefert und kann deshalb als Tabelle aufgefasst werden. Der Ausdruck nach dem Schlüsselwort SELECT spezifiziert eine Untermenge, welche in der Menge enthalten ist, die durch das Schlüsselwort FROM bestimmt ist, z.B.:
BEISPIEL
dpQuery("SELECT 'KF.DT.AT' FROM 'DP.EL'", tab);
Die Variable "tab" vom Typ dyn_dyn_anytype lässt Rückgabewerte beliebigen Typs zu.
Hierbei ergibt sich prinzipiell folgendes Ergebnis:
Spalte 1 | Spalte 2 | |
---|---|---|
Zeile 1 | KF.DT.AT | |
Zeile 2 | DP.EL | Wert von DP.EL: KF.DT.AT mit Listenindex 1 |
Zeile 3 | DP.EL | Wert von DP.EL: KF.DT.AT mit Listenindex 2 |
: | : | : |
Zeile x | DP.EL | Wert von DP.EL: KF.DT.AT mit Listenindex x |
Die erste Zeile nimmt eine Sonderstellung ein: die erste Spalte ist leer, ab der zweiten Spalte der ersten Zeile wird im Spaltenkopf die Ausprägung des SELECT-Teiles der Abfrage (Konfigs, eventuell mit Elementen) zurückgeliefert.
Die erfragte Wertemenge beginnt ab Zeile 2. Die Spalte 1 beinhaltet den Namen der abgefragten Wertemenge. In der zweiten Spalte sind indiziert die Werte der Abfrage gespeichert.
BEISPIEL
Folgende Beispiele verwenden einen Datenpunkt mld_bit vom Typ ExampleDp_Bit und einen Datenpunkt mld_float vom Typ ExampleDp_Float.
Hier eine Abfrage der Werte mld_float.:_online.._value und mld_float.:_online.._stime und die Darstellung des Ergebnisfeldes:
main()
{
string start='"'+"1998.10.27 00:00:00.000"+'"';
string stop='"'+"1998.10.27 14:50:35.000"+'"';
dyn_dyn_anytype tab;
int laenge, lauf;
dpQuery("SELECT '_online.._value','_online.._stime' FROM
'mld_float' TIMERANGE(\""+start+"\",\""+stop+"\",1,1)",tab);
laenge=dynlen(tab);
DebugN("Länge :",laenge);
for (lauf=1; lauf<=laenge;lauf++)
{
DebugN(tab[1]);
DebugN(tab[2]);
DebugN(tab[3]);
setValue("Tab2","appendLine",dynlen(tab),
"Spalte 1", tab[lauf][1],
"Zeit", tab[lauf][2],
"Wert",tab[lauf][3]);
}
}
Für die Verwendung des Befehls TIMERANGE und das Format der Variablen start und stop sei hier auf die entsprechenden Kapitel verwiesen.
1 | 2 | 3 | |
---|---|---|---|
1 | Type:0 Sys:0 Dp:0 El:0 Conf:29 Det:0 Attr:0 |
:_online.._value Type:0 Sys:0 Dp:0 El:0 Conf:29 Det:0 Attr:131173 |
:_online.._stime Type:0 Sys:0 Dp:0 El:0 Conf:29 Det:0 Attr:196710 |
2 |
System1: mld_float. Type:0 Sys:1 Dp:1651 El:1 Conf:0 Det:0 Attr:0 |
26 | Mon Jun 22 08:59:34 1998 473 |
3 |
System1: mld_float. Type:0 Sys:1 Dp:1651 El:1 Conf:0 Det: 0 Attr:0 |
24 | Mon Jun 22 08:59:35 1998 476 |
Unterschiede zwischen Daten und Meldungen
Im Gegensatz zu den Daten (z.B. dpQuery(SELECT...)) wird bei einer Abfrage der Meldungen (z.B. dpQuery(SELECT ALERT...)) eine Spalte mehr ausgegeben, da bei den Meldungen zusätzlich die Meldungszeit geliefert wird. Deshalb sind bei einer Abfrage von Daten die eigentlichen Werte in Spalte zwei zu finden, die bei einer Meldebehandlung in Spalte drei. Im letzten Fall ist der erste Wert folglich in der Zelle [2][3] zu finden, der nächste in Zelle [3][3], ..., [n][3] (mit n aus N+{ 2 <= n; n = letzte Zeile}).
BEISPIEL
main()
{
string start='"'+"1998.10.27 00:00:00.000"+'"';
string stop='"'+"1998.11.01 11:00:00.000"+'"';
dyn_dyn_anytype tab;
int laenge, lauf;
dpQuery("SELECT ALERT '_alert_hdl.._value', '_alert_hdl.._text'
FROM 'mld_float' TIMERANGE(\""+start+"\",\""+stop+"\",1,1)",tab);
laenge=dynlen(tab);
DebugN("Länge :",laenge);
for (lauf=1; lauf<=laenge;lauf++)
{
DebugN(tab[1]);
DebugN(tab[2]);
DebugN(tab[3]);
setValue("Tab2","appendLine",dynlen(tab),
"Spalte 1", tab[lauf][1],
"Zeit", tab[lauf][2],
"Wert",tab[lauf][3],"Text", tab[lauf][4] );
}
}
Für die Verwendung des Befehls TIMERANGE und das Format der Variablen start und stop sei hier auf die entsprechenden Kapitel verwiesen.
1 | 2 | 3 | 4 | |
---|---|---|---|---|
1 | Type:0 Sys:0 Dp:0 El:0 Conf:0 Det:0 Attr:0 | Type: 0 Sys: 0 Dp: 0 El: 0 Conf: 0 Det: 0 Attr: 0 | :_alert_hdl..value Type: 0 Sys: 0 Dp:0 El: 0 Conf: 23 Det: 0 Attr: 131675 | :_alert_hdl..text Type:0 Sys:0 Dp:0 El:0 Conf:23 Det:0 Attr:2621940 |
2 | System1:mld_float. Type:27 Sys:1 Dp:1651 El:1 Conf:0 Det:0 Attr:0 |
Mon Jun 22 09:20:34 1998 650 System1:mld_float.: meld_beh.3 Type:0 Sys:1 Dp:1651 El:1 Conf:23 Det:3 Attr:0 |
26 | 1 LANG:0 Text für Überschreitung |
3 | System1:mld_float. Type:27 Sys:1 Dp:1651 El:1 Conf:0 Det:0 Attr:0 |
Mon Jun 22 09:20:35 1998 680 System1:mld_float.: meld_beh.3 Type:0 Sys:1 Dp:1651 El:1 Conf:23 Det:3 Attr:0 |
24 | 1 LANG:0 Text für Unterschreitung |
Bei dieser Abfrage besaß der verwendete Datenpunkt das Config Meldebehandlung.
Datenpunktgruppen
Datenpunktgruppen können abgefragt werden. Syntax:
SELECT ... FROM 'DPGROUP(<gName>)' WHERE
...
SELECT ... FROM '{DPGROUP(<g1>),DPGROUP(<g2>)}'
WHERE ...
Für die Abfrage muss der interne Gruppendatenpunkt verwendet werden, Wildcards sind für Gruppen nicht zulässig. Kombinationen von Gruppen mit Datenpunkten sind möglich.
BEISPIEL
"SELECT '_original.._value' FROM 'DPGROUP(_DpGroup00006_Public)'"
Verteilte Systeme
Um SQL für verteilte Systeme zu verwenden, ist bei den Queries ein optionaler Eintrag zu machen:
...REMOTE 'sysname'
Der REMOTE Eintrag steht am Ende des SQL-Statements; es kann nur ein Systemname angegeben werden. Dieser muss zwischen einfachen Hochkommata stehen.
...REMOTE ALL
Der REMOTE ALL Eintrag steht am Ende des SQL-Statements; es wird eine Query auf alle Systeme gemacht. Intern werden Messages zu allen bekannten und zukünftigen Systemen geschickt, einschließlich des eigenen Systems, die work-Funktion (z.B. bei dpQueryConnectAll()) wird von jeder Antwort aufgerufen.
BEISPIEL
"SELECT '_original.._value,_online.._stime' FROM 'ExampleDP_Arg1.' REMOTE 'System2'"
BEISPIEL...
query = "SELECT '_original.._value' FROM 'ExampleDP_Arg*' REMOTE ALL";
dpQueryConnectAll("work",true,"Disttest",query);
Abarbeitung
Die interne Abarbeitung eines Query erfolgt in der Reihenfolge der Schlüsselwörter: Zunächst wird die Abfrage aus der Datenbank durchgeführt (SELECT oder SELECT ALERT), wobei Conditions, die Datenpunkttypen oder nur direkt in der Spaltenliste auftretende Attribute betreffen, sofort ausgewertet werden können. Im nächsten Schritt werden dann alle weiteren Conditions verifiziert. Die so erhaltene Zwischentabelle kann nun sortiert werden. Nach dem Sortieren wird dann bestimmt, wieviele Zeilen des Ergebnisses zurückgeliefert werden. Jede zurückgelieferte Zeile wird nach Möglichkeit immer komplett mit den gerade gültigen Werten aufgefüllt.
Im Falle einer Hot-Link-Verbindung zu aktuellen Werten findet die Evaluierung der Tabelle nach jedem Eintreffen eines neuen Wertes statt. Die Sortierung wird berücksichtigt, sofern als Ergebnis die ganze Tabelle verlangt wird. Zeilenzahl-Einschränkungen werden in diesem Modus jedoch nicht unterstützt.
Die GROUP BY Funktion wird nur dann ausgewertet, wenn keine Hot-Link-Verbindung besteht. Dazu wird die sortierte Ergebnistabelle zeilenweise abgearbeitet und für jede Gruppe ein Eintrag in der Gruppentabelle eingefügt. Wurde eine Gruppenfunktion im Argument vor FROM angeführt und keine GROUP BY-Klausel definiert, so liefert das Query genau eine Zeile mit der Zusammenfassung der gesamten Ergebnistabelle.
Im FROM-Teil können Zeichen "{" und "}" zur weiteren Einschränkung von Datenpunkten verwendet werden.
BEISPIEL
string start="1998.10.28 12.30.00.000"
string stop="1998.10.30 12.45.00.000"
dpQuery("SELECT 'SUM(_online.._value)' FROM '{alt_bit,alert_float}'
WHERE _DPT = \"ExampleDP_Float\"
TIMERANGE(\""+start+"\",\""+stop+"\", 1, 0)
ORDER BY 0 GROUP BY SECS(10)", tab);
In diesem Beispiel werden zunächst alle Datenpunkte vom Typ ExampleDP_Float im Zeitbereich start bis stop abgefragt und in einer internen Ergebnistabelle gespeichert. In einem zweiten Schritt wird die Summe der ermittelten Werte gebildet, die anschließend der Größe nach sortiert werden und in Gruppen mit Dt = 10 Sekunden abgelegt werden.
Erläuterungen zu den TIMERANGE- oder Gruppenfunktionen siehe im entsprechenden Kapitel dieses Abschnitts.
Siehe auch die Funktionen isAnswer() und isRefresh().