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.
VORSICHT: ALERT SELECT–Abfragen sind an die Alarmdatenbank gerichtet (verarbeitet vom Event-Manager) und sollten für eine effektive und schnelle Verarbeitung immer nur Attribute der Config "_alert_hdl" enthalten.

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:

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.

VORSICHT:

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!

Anmerkung: Alle Datenpunktelemente aller Datenpunkte z.B. eines spezifischen Typs können über (*.**) abgefragt werden. Für ein Beispiel siehe SQL Schlüsselwörter.

[ALL]

Liefert alle Werte aus der Datenbank, welche dem FROM Statement der Query entsprechen. Sowohl aktuelle als auch historische Daten!

VORSICHT: Dies kann aufgrund einer großen Anzahl an Daten zu hohen Wartezeiten führen! Um diese zu reduzieren, kann mittels TIMERANGE eine Eingrenzung der Ergebnisse vorgenommen werden. Das ALL Statement sollte nur in speziellen Anwendungsfällen verwendet werden!

[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.

Anmerkung:

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()).

Anmerkung: Es werden nur KAM und GING Meldungen berücksichtigt, Acknowledge-Werte werden nicht zurückgeliefert.

'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.

VORSICHT: Wenn das Keyword REMOTE verwendet wird, muss es vor dem Keyword WHERE verwendet werden!

Folgendes Verhalten zeigen dpQueryConnect-Funktionen zur Laufzeit, wenn diese mit REMOTE <Systemname> ausgeführt werden:

  1. Werden die Funktionen auf ein System ausgeführt, dann muss dieses aktuell verbunden sein - sonst erhält man beim Connect einen Fehler.
  2. Wenn eine Verbindung zu einem System verloren geht, wird für dieses Ereignis kein Hotlink ausgelöst.
  3. 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

VORSICHT:

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:

  1. Die Funktionen werden auf alle verbundenen Systeme ausgeführt (Dist-Manager läuft).
  2. Wenn eine Verbindung zu einem System verloren geht, wird für dieses Ereignis kein Hotlink ausgelöst.
  3. 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)

VORSICHT:

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.

VORSICHT: Bei der Angabe mehrerer Attribute (z.B. '_original.._value,_alert_hdl.._act_state_color,_online.._stime') beim SELECT-Statement, darf nach dem Beistrich kein Leerzeichen folgen! Auch im FROM-Teil gilt es, kein Leerzeichen zu verwenden!

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:

Tabelle 1. Query-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.

Abbildung 1. Datenpunkte mld_bit und mld_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.

Tabelle 2. Tabelle: Query-Ergebnis
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.

Tabelle 3. Tabelle: Query-Ergebnis
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)'"
Einschränkung: Nach dem Komma, welches zwei Datenpunktgruppen bzw. Datenpunkte trennt, sind keine Leerzeichen erlaubt.

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.

VORSICHT: Wenn ein dpQueryConnectSingle() mit einem REMOTE-State verwendet wird, muss in der Config-Datei ein DIST-System konfiguriert sein.

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);
Anmerkung: Sie können auch innerhalb der { } Liste Wildcards verwenden, also z.B Example*{Arg?}.

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().