IT-Academy Logo
Sign Up Login Help
Home - Programmieren - Datenbanken - SQL-DQL: Der SELECT-Befehl



SQL-DQL: Der SELECT-Befehl

In diesem Artikel wird der SQL-DQL-Befehl zur Abfrage von Daten - der SELECT-Befehl - vertieft betrachtet.


Autor: Patrick Bucher (paedubucher)
Datum: 03-07-2007, 19:35:09
Referenzen: SQL - kurz und gut, Jonathan Gennick, O'Reilly Verlag
Schwierigkeit: Fortgeschrittene
Ansichten: 13366x
Rating: 9 (1x bewertet)

Hinweis:

Für den hier dargestellte Inhalt ist nicht der Betreiber der Plattform, sondern der jeweilige Autor verantwortlich.
Falls Sie Missbrauch vermuten, bitten wir Sie, uns unter missbrauch@it-academy.cc zu kontaktieren.

[Druckansicht] [Als E-Mail senden] [Kommentar verfassen]



Die DQL (Data Query Language) ist der Teil von SQL, mit dem sich Daten aus Tabellen oder Views abfragen lassen. Die DQL umfasst den SELECT-Befehl, der im Folgenden näher erläutert wird.

Syntax:

SELECT Ausdrucksliste
  FROM Datenquelle
  WHERE Prädikate
  GROUP BY Prädikate
  HAVING Prädikate
  ORDER BY Sortierkriterien

Eine SELECT-Anweisung liefert eine Menge aus Zeilen und Spalten zurück. Diese Menge wird als Ergebnismenge bezeichnet.

Die einzelnen Syntaxelemente werden in den nächsten Abschnitten einzeln erläutert.

Die Ausdrucksliste

Jeder Ausdruck in der Ausdrucksliste wird bei erfolgter Abfrage zu einer Spalte in der Ergebnismenge. Es können beliebig viele Ausdrücke angegeben werden, diese werden dann jeweils durch ein Komma voneinander getrennt.

Beispiel:

SELECT vorname, nachname, wohnort FROM Datenquelle

Möchte man sämtliche Spalten einer Datenquelle abfragen, so kann dies mit der Wildcard * bewerkstelligt werden:

SELECT * FROM Datenquelle

Tabellennamen qualifizieren

Möchte man gleichnamige Spalten aus verschiedenen Datenquellen abfragen, so sind diese Spalten mit dem Tabellennamen zu qualifizieren. Diese Angabe hat der Syntax [tabelle.attribut] zu folgen. Gehen wir von folgender Entitätsstruktur aus:

  • mitarbeiter (m_id, name, vorname, ort);
  • firma (f_id, name, standort);

Die beiden Entitäten mitarbeiter und firma verfügen beide über ein Attribut (bzw. über eine Spalte) mit der Bezeichnung name. Eine Selektion über sämtliche Kriterien dieser beiden Entitäten erfordert eine Qualifikation der gemeinsamen Attribute mit dem Tabellennamen:

SELECT m_id, mitarbeiter.name, vorname, ort, f_id, firma.name, standort
FROM mitarbeiter, firma

Dabei müssen nur diejenigen Attribute mit dem Tabellennamen qualifiziert werden, die auch wirklich in beiden Tabellen vorkommen. Eine Angabe des Tabellennamen ist für die anderen Attribute zwar zulässig, jedoch nicht zwingend.

Ausdrücke

Oftmals ist es erwünscht oder sogar zwingend nötig, dass die Ergebnismenge nicht eins zu eins mit den Werten, die physisch auf der Datenbank abgespeichert sind, übereinstimmen sollen. Beispiele dafür wären Berechnungen, Verkettungen von Zeichenketten usw. SQL stellt für diesen Zweck Ausdrücke zur Verfügung:

SELECT name, preis * 1.10, abs(marge), ltrim(beschreibung) FROM artikel

In diesem Beispiel wird der Preis der selektierten Artikel mit dem Faktor 1.1 multipliziert (preis * 1.10), von der Marge des Artikels wird der Absolutwert genommen (abs(marge)) und bei der Beschreibung werden sämtliche führende Leerzeichen entfernt (ltrim(beschreibung)).

Neben den Rechenoperatoren (+, -, *, /) können verschiedene Funktionen für die einzelnen Spalten verwendet werden. Eine Übersicht über die wichtigsten Funktionen befindet sich im "Aggregatsfunktionen".

Spaltennamen und Ausdrücke dürfen in der Ausdrucksliste nach Belieben durchmischt werden, so lange sie mit einem Komma voneinander getrennt sind.

Alias-Namen für Spalten und Ausdrücke

Oftmals möchte man in der Ergebnismenge nicht die gleichen Spaltennamen sehen, wie sie auf der Datenbank definiert sind. Dieses Bedürfnis kommt vor allem bei Ausdrücken zum tragen, da der Titel einer Ausdrucksspalte gleich lautet wie der Ausdruck selbst. Im obigen Beispiel würden die Spaltennamen in der Ergebnismenge beispielsweise "preis * 1.10", "abs(marge)" oder "ltrim(beschreibung)" lauten.

SQL erlaubt es darum, dass für Spalten und Ausdrücke Alias-Namen vergeben werden können. Dazu wird das Schlüsselwort AS verwendet. Hier das vorherige Beispiel unter der Berücksichtigung von Alias-Namen:

SELECT name, (preis * 1.10) AS preis, (abs(marge)) AS marge, (ltrim(beschreibung)) AS beschreibung
FROM artikel

Die Spalten in der Ergebnismenge würden in diesem Fall "name", "preis", "marge" und "beschreibung" lauten. Die Ausdrücke sollten bei der Vergabe von Alias-Namen immer mit runden Klammern umgeben werden.

ALL und DISTINCT

Oftmals enthält eine Datenmenge Zeilenduplikate. Dazu ein Beispiel einer Tabelle mit CDs:

Tabelle "cd"
idbezeichnungpreis
1Painkiller29.90
2The Number of the Beast15.90
3A Matter of Life and Death29.90
4Tyranny of Souls23.30

Auf den ersten Blick sind keine identischen Datensätze zu ermitteln. Selektiert man jedoch nur die Spalte preis aus dieser Tabelle, enthält diese Duplikate (Eintrag 1 und 3 haben den gleichen Preis). Ob diese Duplikate ausgegeben werden sollen oder nicht, kann mit dem Parameter ALL bzw. DISTINCT angegeben werden:

SELECT ALL preis FROM cd

In diesem Falle lautet die Ergebnismenge {29.90, 15.90, 29.90, 23.30}. Es werden also auch Duplikate ausgegeben. Die Angabe des Parameters ALL ist optional. Wird nichts angegeben, so werden sämtliche Duplikate selektiert.

SELECT DISTINCT preis FROM cd

Hier lautet die Ergebnismenge {29.90, 15.90, 23.30}, die Duplikate wurden unter Angabe des Parameters DISTINCT nicht selektiert.

Datenquellen (FROM)

Die Datenquelle einer Selektion ist in der Regel eine Tabelle oder eine View. Diese kann einfach mit ihrem Namen angegeben werden:

SELECT * FROM artikel

Möchte man Spalten von mehreren Tabellen/Views selektieren, so müssen selbstverständlich sämtliche verwendeten Datenquellen angegeben werden.

SELECT vorname, nachname, bezeichnung, preis FROM person, artikel

Wie bei den Spaltennamen und Ausdrücken können auch Datenquellen mit einem Alias-Namen selektiert werden:

SELECT * FROM TBL_ARTICLES AS artikel, TBL_PERSON AS person

Dies ist vor allem in der WHERE-Klausel von Nutzen, da fortan mit den Alias-Namen auf die Spalten zugegriffen werden kann (siehe Folgeabschnitt).

Die WHERE-Klausel

Bisher wurde die Auswahl der Daten nur anhand der zurückgelieferten Spalten (horizontal) eingeschränkt. Es ist jedoch auch möglich, die Auswahl vertikal einzuschränken und somit nur die Datensätze auszuwählen, die bestimmten Kriterien genügen.

Dazu ein Beispiel:

SELECT preis
FROM TBL_ARTICLES AS artikel
WHERE artikel.preis = 29.90

Es werden sämtliche Artikel selektiert, bei denen der Preis genau dem Wert 29.90 entspricht. Hier sieht man auch gleich den Vorteil eines Alias-Namens für die Tabelle. Statt den Vergleich mit TBL_ARTICLES.preis zu formulieren, kann die besser lesbare Variante artikel.preis verwendet werden. Diese Qualifikation bei der WHERE-Klausel ist nur dann von Nöten, wenn mehrere Spalten mit dem Namen "preis" zurückgeliefert werden.

Logische Verknüpfungen

Selbstverständlich kann man nicht nur eine einzige Bedingung definieren. Es ist auch möglich, verschiedene Bedingungen mit den Operatoren AND und OR zu verknüpfen. Die Auswertungsreihenfolge kann mit runden Klammern beeinflusst werden.

Vergleichsoperatoren

Die folgende Tabelle listet mögliche Vergleichsoperatoren auf:

OperatorBeschreibung
=Prüft auf Gleichheit
!=, <>Prüft auf Ungleichheit
<Prüft auf kleiner als
>Prüft auf grösser als
<=Prüft auf kleiner gleich
>=Prüft auf grösser gleich
LIKEPrüft, ob ein Wert einem Muster entspricht (Zeichenketten)
BETWEENPrüft, ob ein Wert im angegebenen Bereich liegt
INPrüft, ob der Wert in der angegebenen Wertmenge vorhanden ist
IS [NOT] NULLPrüft, ob ein Wert NULL bzw. nicht NULL entspricht

Dazu einige Beispiele:

SELECT lagerbestand, preis
FROM artikel
WHERE lagerbestand >= 1 AND preis < 30

In diesem Beispiel werden sämtliche Artikel selektiert, bei denen der Lagerbestand grösser oder gleich 1 und der Preis kleiner als 30 ist.

SELECT bezeichnung, lagerbestand, preis
FROM artikel
WHERE bezeichnung LIKE '%bier%' AND lagerbestand BETWEEN 100 AND 200

Es werden sämtliche Artikel selektiert, bei denen die Bezeichnung die Zeichenkette "bier" enthält. Dazu wird der LIKE-Operator mit den Wildcards % verwendet. Diese steht für keines, eines oder beliebig viele Zeichen. Weiter existiert die Wildcard _ (underscore), welche stellvertretend für kein oder ein Zeichen steht. Zudem muss der Lagerbestand der selektierten Artikel zwischen 100 und 200 liegen.

SELECT bezeichnung, lagerbestand, preis
FROM artikel
WHERE bezeichnung IN ('Korn', 'Bier', 'Schnaps', 'Wein', 'Aspirin')

In diesem Beispiel werden sämtliche Artikel selektiert, deren Bezeichnung einem Element in der definierten Menge ('Korn', 'Bier', usw.) entspricht. Es ist auch möglich, zu diesem Zweck eine Unterabfrage zu definieren:

SELECT bezeichnung, lagerbestand, preis
FROM artikel
WHERE bezeichnung IN (SELECT bezeichnung FROM Trinkutensilien)

In diesem Fall muss die Artikelbezeichnung einem Wert (genauer der Bezeichnung) eines Datensatzes aus der Tabelle Trinkutensilien entsprechen.

Die Operatoren LIKE, BETWEEN und IN (wie auch IS NULL, IS NOT NULL) können mit dem NOT-Operator negiert werden:

SELECT bezeichnung, lagerbestand, preis
FROM artikel
WHERE bezeichnung NOT LIKE '%bier%'
AND lagerbestand NOT BETWEEN 200 AND 500
AND preis NOT IN (SELECT preis FROM wucherei)

Für Operatoren wie >, <, >= usw. ist die Verwendung von NOT nicht zulässig bzw. nicht sinnvoll, da es für diese jeweils ein entsprechendes Pendant gibt (!=, <> und =, > und <, >= und <=).

Gruppierungen (GROUP BY)

SQL erlaubt es, Zeilen in verschiedenen Gruppen zusammenzufassen und dann nur eine dieser Zeilen zurückzuliefern. Um eine Ergebnismenge nach bestimmten Kriterien zu gruppieren, bestehen folgende Möglichkeiten:

  • Ein Gruppierungs-Kriterium wird in der "GROUP BY"-Klausel angegeben.
  • Weitere Spalten können anhand einer Aggregatsfunktion "zusammengeklappt" werden.

Eine Spalte muss sich entweder in der "GROUP BY"-Klausel befinden oder es muss eine Aggregatsfunktion auf sie angewendet werden. Ansonsten ist das Ergebnis der Gruppierung nicht definiert.

Zum besseren Verständnis ein Beispiel. Wir gehen von einer Tabelle mit Personen aus (die Spalte mit dem Alter der Person wurde absichtlich mit dem englischen Wort "age" benannt, da das deutsche Wort "Alter" ein SQL-Schlüsselwort darstellt und somit nicht für Spaltennamen verwendet werden kann):

Tabelle "person"
idvornamenachnameage
1AliceMeier87
2BarbaraHuber42
3ClaudioMeier12
4DanielMeier65
5ElviraHuber33
6FabianBirrer56

Wir erstellen eine Selektion, um das älteste Familienmitglied zu ermitteln (wir gehen davon aus, dass der gleiche Nachname eine Verwandschaft darstellt). Dazu soll nach der Spalte nachname gruppiert werden. Die Selektion dazu sieht folgendermassen aus:

SELECT nachname, MAX(age) AS age
FROM person
GROUP BY nachname

In der ersten Zeile wird mit MAX(age) angegeben, dass jeweils der grösste Alterswert ausgegeben wird. Dieser Ausdruck macht erst im Zusammenhang mit der Gruppierung in Zeile 3 Sinn. Dabei werden sämtliche Datensätze mit dem gleichen Nachnamen zu einer Gruppe hinzugefügt. Neben dem Nachnamen wird auch das Alter selektiert. Wir erhalten als Ergebnismenge somit eine Liste aller Nachnamen mit dem jeweiligen Höchstwert aus der Spalte age zu diesem Nachnamen. Die Ergebnismenge sieht dann folgendermassen aus:

nachnameage
Birrer56
Huber42
Meier87

Die HAVING-Klausel

Die HAVING-Klausel folgt der gleichen Syntax wie die WHERE-Klausel. Beide Klauseln haben die Funktion, die zu selektierende Datenmenge anhand bestimmter Kriterien einzuschränken. Im Unterschied zur WHERE-Klausel schränkt die HAVING-Klausel die Datenmenge nach einer vollzogenen Gruppierung ein.

  • WHERE schränkt die Datenmenge ein
  • Die Gruppierung (GROUP BY) reduziert die Ergebnismenge anhand von Gruppierungsattributen und Aggregatsfunktionen
  • HAVING schränkt die gruppiete Ergebnismenge weiter nach bestimmten Kriterien ein

Dazu betrachten wir am besten noch einmal das Beispiel aus dem vorhergehenden Abschnitt. Wir möchten diese Ergebnismenge nun nach Familiennamen einschränken, deren ältestes Familienmitglied das 50 Altersjahr schon hinter sich gelassen hat. Die Abfrage dazu sieht dann so aus:

SELECT nachname, MAX(age) AS age
FROM person
GROUP BY nachname
HAVING age > 50

Zu beachten ist in diesem Fall, dass für den Ausdruck MAX(age) ein Alias-Name definiert wurde (age). Der Vergleich in der HAVING-Klausel erfolgt dann anhand dieses Alias-Namens. In diesem Fall wird die Ergebnismenge um den Eintrag mit dem Nachnamen "Huber" und dem entsprechenden Alter 42 reduziert, was folgende Ergebnismenge zur Folge hat:

nachnameage
Huber42
Meier87

Nach Möglichkeit sollten so viele Kriterien wie möglich bereits in der WHERE-Klausel aufgeführt werden. Dies reduziert die Datenmenge, die das Datenbanksystem anschliessend gruppieren muss. In diesem Beispiel hätte die Bedingung [age > 50] auch schon in der WHERE-Klausel definiert werden können, die Ergebnismenge wäre die Gleiche geblieben.

Sortierung (ORDER BY)

Der SELECT-Befehl stellt die "ORDER BY"-Klausel zur Verfügung, um damit eine bestimmte Sortierung in der Ergebnismenge zu erreichen. Es können beliebig viele Spalten angegeben werden, welche wahlweise aufsteigend (ASC) oder absteigend (DESC) sortiert werden können. Betrachten wir dazu wieder das Beispiel mit der Personen-Tabelle:

Tabelle "person"
idvornamenachnameage
1AliceMeier87
2BarbaraHuber42
3ClaudioMeier12
4DanielMeier65
5ElviraHuber33
6FabianBirrer56

Als erstes Kriterium soll nach dem Nachnamen gruppiert werden. Da aber der gleiche Nachname bei verschiedenen Personen eingetragen ist, lässt sich die Sortierung weiter verfeinern. Die Personen sollen zusätzlich nach ihrem Alter sortiert werden. Der SELECT-Befehl dafür sieht dann folgendermassen aus:

SELECT * FROM person
ORDER BY nachname ASC, age DESC

Die "ORDER BY"-Klausel besagt in diesem Fall, dass die Spalte nachname aufsteigend (d.h. anhand des Alphabets, von A bis Z) sortiert werden soll. Weiter wird absteigend nach der Spalte age sortiert, sodass innerhalb der gleichen Familie zuerst die Familienmitglieder höheren Alters aufgelistet werden. Das Ergebnis dieser Abfrage präsentiert sich dann folgendermassen:

Tabelle "person"
idvornamenachnameage
6FabianBirrer56
2BarbaraHuber42
5ElviraHuber33
1AliceMeier87
4DanielMeier65
3ClaudioMeier12

Die Angabe der Sortierreihenfolge ASC ist optional. Wird keine Sortierreihenfolge angegeben, so wird automatisch ASC angenommen. Somit ist nur die explizite Angabe von DESC erforderlich.

Abfragen über mehrere Tabellen (JOIN)

Nicht immer befinden sich alle relevanten Daten für eine Abfrage in einer einzigen Tabelle. Eine Abfrage muss somit über mehrere Tabellen hinweg erfolgen. Um die Daten der verschiedenen Tabellen aber dennoch zu einer einzigen Ergebnismenge zusammenfügen zu können, müssen die Tabellen anhand bestimmter Kriterien verknüpft werden. Zu diesem Zweck eignen sich Fremdschlüsselfelder, Datensätze verschiedener Tabellen können über Fremdschlüssel miteinander "verbunden" werden.

In den folgenden Abschnitten wird von der folgenden Tabellenstruktur ausgegangen:

Tabelle "auto"
idmarkemodellfk_besitzer
1OpelKadett2
2AudiA33
3VWGolf2
4BMW3erNULL
5FiatPanda5

Tabelle "person"
idname
1Hans Meier
2Sepp Birrer
3Alice Huber
4Barbara Kunz
5Martin Duss

Die Assoziation zwischen den einzelnen Datensätzen ist in diesem Falle durch den Fremdschlüssel fk_besitzer der Tabelle auto auf das Feld id der Tabelle person zu erstellen.

Die einfachste Möglichkeit, diese beiden Tabellen miteinander zu verknüpfen, ist die Angabe beider Tabellennamen in der FROM-Klausel:

SELECT * FROM autos, person

Auf die Angabe der Ergebnismenge möchte ich in diesem Fall verzichten, da diese Selektion nichts anderes macht, als ein karthesisches Produkt zwischen den Datensätzen dieser beiden Tabellen herzustellen. Somit wird jeder Datensatz aus der Tabelle autos mit jedem Datensatz aus der Tabelle person angezeigt. Diese Ergebnismenge hat kaum eine praktische Verwendung.

Erinnern wir uns an die WHERE-Klausel. Diese erlaubt es nicht nur, Datensätze anhand bestimmter Bedingungen zu selektieren, wir können damit auch Datensätze "zuordnen". Betrachten wir dazu folgendes Beispiel:

SELECT auto.marke, auto.modell, person.name AS besitzer
FROM auto, person 
WHERE auto.fk_besitzer = person.id

Die WHERE-Klausel besagt, dass die Datensätze der Tabelle auto mit den Datensätzen der Tabelle person assoziiert werden sollen. Die Bedingung dafür lautet, dass das Feld auto.fk_besitzer jeweils dem Feld person.id entsprechen muss. Wir erhalten somit als Ergebnis eine Zuordnung der Autos zu ihren Besitzern:

markemodellbesitzer
OpelKadettSepp Birrer
AudiA3Alice Huber
VWGolfSepp Birrer
FiatPandaMartin Duss

Diese Abfrage lässt sich auch noch anders formulieren, mit einem sog. JOIN. Die Syntax dafür lautet folgendermassen:

SELECT Spalten FROM Datenquelle
INNER JOIN Datenquelle ON Bedingung

Übertragen auf unser Beispiel ergibt dies folgenden SQL-Befehl:

SELECT auto.marke, auto.modell, person.name AS besitzer
FROM auto
INNER JOIN person ON auto.fk_besitzer = person.id

Wichtig ist, dass die FROM-Klausel nur eine einzige Tabelle umfasst (Tabelle auto). Die weiteren Tabellen werden dann mit der JOIN-Klausel assoziiert. Die Ergebnismenge bleibt die Gleiche.

Dieser JOIN kann über beliebig viele Tabellen weiter gezogen werden. So könnte man z.B. Datensätze der Tabelle ort in die Abfrage miteinbeziehen (nehmen wir an, die Tabelle person enthält ein Feld namens fk_ort, das auf die Tabelle ort verweist):

SELECT auto.marke, auto.modell, person.name AS besitzer, ort.name AS ort
FROM auto 
INNER JOIN person ON auto.fk_besitzer = person.id
INNER JOIN ort ON person.fk_ort = ort.id

Neben dem INNER JOIN gibt es noch weitere Möglichkeiten für JOINs. Erwähnenswert sind folgende JOINs:

  • LEFT JOIN
    • Es werden sämtliche Datensätze der Tabelle selektiert, die als erstes aufgeführt wird.
    • Kann zu einem Datensatz der "linken" Tabelle eine Assoziation zu einem Datensatz der "rechten" Tabelle erstellt werden, so wird dieser ebenfalls selektiert.
  • RIGHT JOIN
    • Es werden sämtliche Datensätze der Tabelle selektiert, die als zweites aufgeführt wird.
    • Kann zu einem Datensatz der "rechten" Tabelle eine Assoziation zu einem Datensatz der "linken" Tabelle erstellt werden, so wird dieser ebenfalls selektiert.

Aggregatsfunktionen

Der SQL-Standard stellt folgende Aggregatsfunktionen zur Verfügung:

FunktionBeschreibung
COUNT()Liefert die Anzahl der Werte in der Ergebnismenge einer SELECT-Abfrage bzw. einer Gruppierung.
COUNT(DISTINCT)Liefert die Anzahl der unterschiedlichen Werte in der Ergebnismenge einer SELECT-Abfrage bzw. einer Gruppierung.
AVG()Liefert den Durchschnittswert eines Datenfeldes einer SELECT-Abfrage bzw. einer Gruppierung.
MIN()Liefert den kleinsten Wert eines Datenfeldes einer SELECT-Abfrage bzw. einer Gruppierung.
MAX()Liefert den grössten Wert eines Datenfeldes einer SELECT-Abfrage bzw. einer Gruppierung.
SUM()Liefert die Summe der Werte eines Datenfeldes einer SELECT-Abfrage bzw. einer Gruppierung.


dreamer
Expert
Beitrag vom:
16-02-2008, 10:06:59

Ein umfangreicher Text zum 1992-er SQL-Standard ist verfügbar unter http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt

-----------------------------------------------------


paedubucher
Professonial
Beitrag vom:
06-07-2007, 18:26:15

Funktionen

Ich kann nur sagen, dass die Funktionen, die unter dem Abschnitt "Aggregatsfunktionen" aufgelistet sind dem Standard entsprechen. Über ltrim() und dergleichen kann ich leider keine Aussage machen...

Beim Zusammenstellen dieses Artikels habe ich einige Zeit dafür verwendet, den offiziellen SQL-Standard im Internet zu finden; vergebens. Wenn mir jemand einen Link darauf zusenden könnte, wäre ich sehr dankbar!

-----------------------------------------------------


dreamer
Expert
Beitrag vom:
05-07-2007, 22:37:01

Sind alle aufgelistete Funktionen wie z.B. ltrim() standard, bzw. gehören sie zum algemeinen SQL-Standard? Oder werden sie nur von bestimmte Datenbanken unterstützt?

-----------------------------------------------------


[back to top]



Userdaten
User nicht eingeloggt

Gesamtranking
Werbung
Datenbankstand
Autoren:04508
Artikel:00815
Glossar:04116
News:13565
Userbeiträge:16552
Queueeinträge:06246
News Umfrage
Ihre Anforderungen an ein Online-Zeiterfassungs-Produkt?
Mobile Nutzung möglich (Ipone, Android)
Externe API Schnittstelle/Plugins dritter
Zeiterfassung meiner Mitarbeiter
Exportieren in CSV/XLS
Siehe Kommentar



[Results] | [Archiv] Votes: 1154
Comments: 0