Queries
Database Queries¶
Overview of the queries used to retrieve the needed personal data from the database. All of these queries can be found within the export_data.py
-file, each capsulated in a separate function for simplified expansion.
Basic Plan Data¶
SELECT
Prim AS 'plan_id',
RefPlanungseinheiten AS 'planning_unit',
VonDat AS 'from_date',
BisDat AS 'till_date'
FROM TPlan
WHERE RefPlanungseinheiten = {planning_unit}
AND VonDat = CONVERT(date,'{from_date}',23)
AND BisDat = CONVERT(date,'{till_date}',23)
We use the entity TPlan
to retrieve the plan identification number
of a corresponding schedule. The ID is the numeric primay key Prim
of the entity. It is unique by a given planning unit and a period of time.
RefPlanungseinheiten
is the reference to the underlying planning unit and stored in TPlanungseinheiten
.
To get a certain id VonDat
needs to be the first day of a month and BisDat
the last day of a month.
The CONVERT
-function uses the style code "23" that specifies the date format yyyy-mm-dd
.
Export shift_information.json¶
SELECT
RefDienste AS 'shift_id',
Kommt AS 'start',
Geht AS 'end'
FROM TDiensteSollzeiten
WHERE RefDienste = '{shift_ids["Frühschicht"]["id"]}'
OR RefDienste = '{shift_ids["Spätschicht"]["id"]}'
OR RefDienste = '{shift_ids["Nachtschicht"]["id"]}'
OR RefDienste = '{shift_ids["Zwischendienst"]["id"]}'
OR RefDienste = '{shift_ids["Sonderdienst"]["id"]}'
We use the entity TDiensteSollzeiten
to retrieve the start and end times for specific work shifts. To collect the time information a defined set of shift types (Frühschicht, Spätschicht, Nachtschicht, Zwischendienst und Sonderdienst) is used.
The unique identifier for a shift is a reference in TDiensteSollzeiten
and stored in RefDienste
as an integer.
The entity contains the start Kommt
and the end Geht
of a shift, referenced by their shift ID. The date format is yyyy-mm-dd hh-mi-ss
.
These values are further used to compute the shift duration, break duration and the total working minutes of a shift.
Export employees.json¶
SELECT
a.Prim,
a.Name,
a.Vorname,
a.PersNr,
t.Bezeichnung AS 'Beruf'
FROM TPlanPersonal b
JOIN TPersonal a ON b.RefPersonal = a.Prim
LEFT JOIN TBerufe t ON a.RefBerufe = t.Prim
WHERE RefPlan = {plan_id}
TPersonal
(a): stores detailed employee information
- TPlanPersonal
(b): links plannings entities
- TBerufe
(t): contains short and long descriptions of occupations
The Inner Join
ensures that only staff information with exisiting records are included. The Left Join
is used to receive the job description for each employees, if available. Employees without a job reference will still appear, with NULL
in the Beruf
column.
For a specific plan_id
we obtain the unique numeric primary key Prim
, the surname Name
and first name Vorname
, the personnel number PersNr
and the occupation Bezeichnung
of all employees within the underlying work schedule.
Export target_working_minutes.json¶
SELECT
p.PersNr,
p.Name AS 'name',
p.Vorname AS 'firstname',
pkt.RefKonten,
pkt.Wert2
FROM TPersonalKontenJeMonat pkt
JOIN TPersonal p ON pkt.RefPersonal = p.Prim
WHERE (pkt.RefKonten = 1 OR pkt.RefKonten = 19 OR pkt.RefKonten = 55)
AND pkt.Monat = '202411'
ORDER BY p.Name asc
We use the entity TPersonalKontenJeMonat
to retrieve the working hours of each employee per month.
We join this entity with TPersonal
to obtain the employee's Name
and PersNr
.
The RefKonten
field in the TPersonalKontenJeMonat
entity provides the relevant type of Konto (e.g., SOLL_Monat). All types of Konten can be found in the entity TKonten
.
In our case these are: 1. "1" := SOLL_Monat (first Column in TimeOffice) 2. "19" := Arbeitsstunden 3. "55" := Total
The tricky thing here is that the different Konten are only created when needed.
For example, if an employee has not yet been scheduled in the plan, only a "1" Konto is present in the entity TPersonalKontenJeMonat
.
The same applies to the "19" and "55" Konten.
Some employees only have a "19" Arbeitsstunden Konto, which represents the "Arbeitsstunden / IST Stunden" respectively.
Other employees have both a "19" and a "55" Konto.
In that case the "55" Konto represents the correct "IST Stunden" ("19" = "55" or "19" < "55").
Once we have identified the correct values using the entity RefKonten
, we can obtain the hours from Wert2
.
Since we are using working minutes and not hours, we must multiply by 60 to get the correct number of working minutes.
Export worked_sundays.json¶
SELECT
p.Prim,
p.Name AS name,
p.Vorname AS firstname,
COUNT(DISTINCT CAST(pkt.Datum AS DATE)) AS worked_sundays
FROM TPersonalKontenJeTag pkt
JOIN TPersonal p ON pkt.RefPersonal = p.Prim
WHERE pkt.RefKonten = 40
AND pkt.Datum BETWEEN {from_date} AND {till_date}
AND DATENAME(WEEKDAY, pkt.Datum) = 'Sonntag'
AND pkt.Wert > 0
GROUP BY
p.Prim,
p.Name,
p.Vorname
ORDER BY
worked_sundays DESC
We use the TPersonalKontenjeTag
table to retrieve the number of worked sunday shifts for each employee for the last 12 months. The table stores daily entries and account types per employee. The Inner Join
links this table with TPersonal
to receive the employees' unique primary key Prim
, as well as the surname Name
and the first name Vorname
. In the following the conditions are described:
- pkt.RefKonten = 40
: "40" is the account key for a sunday shift
- pkt.Datum BETWEEN {from_date} AND {till_date}
: defines the date range, in our case, of 12 months
- DATENAME(WEEKDAY, pkt.Datum) = 'Sonntag'
: filters the weekday, for this query it is sunday
- pkt.Wert > 0
: includes only days with positive values, means actual work days
Lastly we aggregate the worked days per employee with GROUP
and rank them by the number of worked shifts on a sunday with ORDER BY
.
Get plan dates¶
SELECT
CAST(VonDat AS DATE) AS 'START',
CAST(BisDat AS DATE) AS 'END'
FROM TPlan WHERE Prim = '{plan_id}'
We use the TPlan
table to get the start and the end dates of a specific plan defined by the given plan ID plan_id
. The date format is yyyy-mm-dd
.
Export free_shifts_and_vacation_days.json¶
Query for vacation days¶
SELECT
p.Prim AS Prim,
p.Name AS name,
p.Vorname AS firstname,
pkg.Datum AS vacation_days
FROM TPlanPersonalKommtGeht pkg
JOIN TPersonal p ON pkg.RefPersonal = p.Prim
WHERE pkg.Datum BETWEEN CONVERT(date,'{START_DATE}',23)
AND CONVERT(date,'{END_DATE}',23)
AND pkg.RefgAbw IN (20, 2434, 2435, 2091)
This SQL query retrieves a list of employees and the dates they took (additional) vacation days within a specified date range. To get these information we use the TPlanPersonalKommtGeht
table which stores the attendance and absence records, including absence types, for each employee. To obtain the surname Name
and the first name Vorname
we use the Inner Join
with the TPersonal
table. The last column vacation_days
stores the days when the vacation will take place.
In the WHERE
section, we first filter the records in the desired date period. The CONVERT
-function again uses the style code "23" that specifies the date format yyyy-mm-dd
. In addition, we filter the RefgAbw
for specific vacation types: "20", "2434", "2435" represent a standard vacation day Urlaub
, "2091" represents an additional vacation day Zusatzurlaub
. Full-day absence are stored in RefgAbw
.
Query for forbidden days¶
SELECT
p.Prim AS Prim,
p.Name AS 'name',
p.Vorname AS 'firstname',
pkg.Datum AS 'forbidden_days'
FROM TPlanPersonalKommtGeht pkg
JOIN TPersonal p ON pkg.RefPersonal = p.Prim
WHERE pkg.Datum BETWEEN CONVERT(date,'{START_DATE}',23)
AND CONVERT(date,'{END_DATE}',23)
AND pkg.RefgAbw NOT IN (20, 2434, 2435, 2091)
This query is very similar to the vacation query
. To get the information we again use the TPlanPersonalKommtGeht
table and obtain the surname Name
and the first name Vorname
with the Inner Join
with the TPersonal
table.
The difference is that for each employee, we want to retrieve the full-day absences from RefgAbw
that are not vacation days. Therefore, the last column forbidden_days
stores the days on which an employee is not be selected for a shift for reasons other than vacation. Other reasons could be, for example:
- parental leave (absence type "1078")
- pension (absence type "1086")
- day off (absence type "1089")
The distinction is necessary here because a vacation day has an impact on an employee's target working hours, while a day off, for example, has no impact.
Query for forbidden shifts¶
SELECT
p.Prim AS Prim,
p.Name AS 'name',
p.Vorname AS 'firstname',
pkg.Datum AS 'planned_shifts',
d.KurzBez AS 'dienst'
FROM TPlanPersonalKommtGeht pkg
JOIN TPersonal p ON pkg.RefPersonal = p.Prim
JOIN TDienste d ON pkg.RefDienste = d.Prim
WHERE pkg.Datum BETWEEN CONVERT(date,'{START_DATE}',23)
AND CONVERT(date,'{END_DATE}',23)
AND pkg.RefgAbw IS NULL
This query retrieves the already planned work shifts for other planning units for employees within a given time period. This means that an employee working in another planning unit is unavailable for our schedule.
To do this, we again use TPlanPersonalKommtGeht
and TPersonal
for the same information, as well as TDienste
to identify the already planned shift.
To obtain the desired records, i.e., the actual scheduled shifts, we exclude all absence records, with RefgAbw
set to NULL
. This means that the employees are already scheduled to work.
Query for accounting entries¶
SELECT
RefPersonal AS Prim,
Datum
FROM TPersonalKontenJeTag
WHERE RefPlanungsEinheiten = {planning_unit}
AND Datum BETWEEN CONVERT(date,'{START_DATE}',23)
AND CONVERT(date,'{END_DATE}',23)
We use TPersonalKontenJeTag
to retrieve the present days of the employees of a desired planning unit. The CONVERT
-function again uses the style code "23" that specifies the date format yyyy-mm-dd
.