EntwicklerMitarbeiter-Handbuch

SQL-Konventionen

Die SQL-Queries in einem Plugin sind T-SQL (Microsoft SQL Server / JTL-Wawi eazybusiness). Sie werden über ADO.NET-Parameter ausgeführt.

Entwickler-Handbuch · Plugin-Authoring

Grundregeln

RegelBegründung
Genau ein SELECT-Statement pro DateiValidator lehnt Batches ab
Keine SemikolonsWerden als Multi-Statement interpretiert
Keine Kommentare (--, /* */)Werden beim Packen entfernt
Parametrisiert via @{name}Vermeidet SQL-Injection
Nur SELECT, kein INSERT/UPDATE/DELETE (außer automation-Plugins)Schutz vor Datenmutation
Timeout = 30s, Row-Limit = 10.000 (überschreibbar)Performance-Schutz

Parameter

Parameter werden im SQL als @{paramName} referenziert. Beim Packen ersetzt der Hub sie durch @paramName — das ist exakt die ADO.NET-Syntax, kein zusätzlicher Templating-Schritt.

Beispiel

Manifest:

jsonmanifest.json
{
  "id": "sales-by-period",
  "parameters": [
    { "name": "fromDate", "type": "date", "required": true },
    { "name": "minAmount", "type": "number", "required": false }
  ]
}

SQL (sql/sales-by-period.sql):

sqlsql/sales-by-period.sql
SELECT
  YEAR(dErstellt) AS Jahr,
  MONTH(dErstellt) AS Monat,
  SUM(fGesamtsumme) AS Umsatz
FROM tBestellung
WHERE dErstellt >= @{fromDate}
  AND (@{minAmount} IS NULL OR fGesamtsumme >= @{minAmount})
GROUP BY YEAR(dErstellt), MONTH(dErstellt)
ORDER BY Jahr DESC, Monat DESC

Der Hub macht daraus zur Laufzeit:

sqlsql/sales-by-period.sql
SELECT
  YEAR(dErstellt) AS Jahr,
  MONTH(dErstellt) AS Monat,
  SUM(fGesamtsumme) AS Umsatz
FROM tBestellung
WHERE dErstellt >= @fromDate
  AND (@minAmount IS NULL OR fGesamtsumme >= @minAmount)
GROUP BY YEAR(dErstellt), MONTH(dErstellt)
ORDER BY Jahr DESC, Monat DESC

Und übergibt die Parameter typisiert als ADO.NET- SqlParameter:

csharpProgram.cs
new SqlParameter("@fromDate", SqlDbType.Date) { Value = fromDate },
new SqlParameter("@minAmount", SqlDbType.Decimal) { Value = (object?)minAmount ?? DBNull.Value }

NULL-Handling

Wenn ein Parameter required: false ist und der User ihn leer lässt:

  • string DBNull.Value
  • number DBNull.Value
  • date DBNull.Value
  • bool false (kein Nullable)
  • article/customer DBNull.Value

Prüfe im SQL immer explizit auf NULL, wenn der Parameter optional ist:

sqlsql/example.sql
AND (@{minAmount} IS NULL OR fGesamtsumme >= @{minAmount})

Spalten-Aliasse

Die Spaltennamen im Resultat werden genau so an die DataTable weitergegeben, wie sie im SQL stehen:

sqlsql/example.sql
SELECT
  cName AS Artikelname,
  fPreis AS Netto,
  fVKNetto AS Brutto
FROM tArtikel

→ DataTable-Header: Artikelname, Netto, Brutto

Faustregel: Verwende sprechende AS-Aliasse, keine technischen Spaltennamen aus JTL.

Sortierung

Die DataTable ist clientseitig sortierbar (TanStack Table). Trotzdem: Im SQL eine sinnvolle Default-Sortierung setzen — der CSV-Export nutzt die Reihenfolge aus der DB.

sqlsql/example.sql
ORDER BY Umsatz DESC

Performance-Tipps

TippWarum
TOP N oder OFFSET ... FETCH NEXTVerhindert riesige Results
Aggregat-Operationen in SQL, nicht im FrontendSchneller, weniger Traffic
EXISTS statt IN für Sub-QueriesBesser optimierbar
Indizes nutzenJTL hat die wichtigsten schon — siehe IX_tBestellung_kKunde etc.

Was NICHT funktioniert

PatternWarum abgelehnt
SELECT 1; SELECT 2Multi-Statement
-- KommentarWerden entfernt
/* Kommentar */Werden entfernt
EXEC sp_whoSystem-Prozeduren verboten
xp_cmdshell '...'Sicherheits-Risiko
OPENROWSET(...)Verbindung nach außen
INSERT INTO ... (in report-Plugins)Nur Lesezugriff
BULK INSERTSicherheits-Risiko

Mehr dazu in Security-Richtlinien.

Testen vor dem Packen

Teste Queries immer zuerst in SSMS mit dem echten JTL-Mandanten. Erst wenn sie dort laufen, kommen sie ins Plugin.

sqlssms-test.sql
DECLARE @fromDate DATE = '2026-01-01'
DECLARE @minAmount DECIMAL(18,2) = NULL

SELECT
  YEAR(dErstellt) AS Jahr,
  MONTH(dErstellt) AS Monat,
  SUM(fGesamtsumme) AS Umsatz
FROM tBestellung
WHERE dErstellt >= @fromDate
  AND (@minAmount IS NULL OR fGesamtsumme >= @minAmount)
GROUP BY YEAR(dErstellt), MONTH(dErstellt)
ORDER BY Jahr DESC, Monat DESC

Wenn die Query im SSMS läuft, ist sie plugin-ready.