====== SQL-Builder ======
Der SQL-Builder (''OutlookSync.SqlBuilder.pas'') generiert SELECT-, INSERT- und UPDATE-Statements aus der Mapping-Konfiguration.
===== Alias-Konventionen =====
^ Alias ^ Bedeutung ^ Beispiel ^
| ''r'' | Root-Tabelle | ''FROM [STAP] r'' |
| ''jN'' | N-te Relation (Index in Relations-Array) | ''j4'' = Relations[4] |
| ''fN'' | N-ter gefilterter 1:N-JOIN | ''f1'' = erster ''fsRelated'' |
| ''_DbPK'' | Root-PK im SELECT | ''r.[IDSTAP] AS [_DbPK]'' |
| ''_PK_jN'' | Hidden PK einer Relation (für Write-Back) | ''j0.[IDSTAdr] AS [_PK_j0]'' |
| ''_PK_fN'' | Hidden PK eines gefilterten JOINs | ''f1.[IDSTTK] AS [_PK_f1]'' |
===== Generiertes SQL — Beispiel =====
Für ein Mapping mit Wurzeltabelle ''STAP'', einer Relation zu ''STAdr'' und gefilterten Feldern aus ''STTK'':
SELECT
r.[IDSTAP] AS [_DbPK],
r.[VN] AS [FirstName],
r.[NN] AS [LastName],
j0.[Str] AS [BusinessAddressStreet],
j0.[IDSTAdr] AS [_PK_j0],
f1.[B] AS [Email1Address],
f1.[IDSTTK] AS [_PK_f1]
FROM [STAP] r
LEFT JOIN [STAdr] j0
ON j0.[IDSTAP] = r.[IDSTAP]
OUTER APPLY (
SELECT TOP 1 t.[B], t.[IDSTTK]
FROM [STTK] t
WHERE t.[IDSTAP] = r.[IDSTAP]
AND t.[TKArt] = :P0
) f1
==== Erklärung ====
* **''r''** — Alias für die Wurzeltabelle ''STAP''
* **''j0''** — ''LEFT JOIN'' auf ''STAdr'' (1:1-Relation, Index 0)
* **''f1''** — ''OUTER APPLY'' mit ''TOP 1'' auf ''STTK'' (gefilterte 1:N-Relation)
* **'':P0''** — Parametrisierter Filter-Wert (''TKArt = 170'')
* **''_DbPK''**, **''_PK_j0''**, **''_PK_f1''** — Hidden PK-Spalten für Write-Back
----
===== INSERT mit OUTPUT INSERTED =====
INSERT INTO [STAP] ([VN], [NN])
OUTPUT INSERTED.[IDSTAP]
VALUES (:P0, :P1)
Der generierte Primärschlüssel wird über ''OUTPUT INSERTED'' zurückgegeben und für das Fill-Back verwendet.
----
===== CopyColumns — Denormalisierte Parent-Spalten =====
Manche Child-Tabellen enthalten NOT-NULL-Spalten, die denormalisiert aus der Wurzeltabelle stammen (z.B. ''STTK.IDST'' = ''STAP.IDST''). Das Mapping definiert diese über ''copyColumns'' im Relation-Objekt.
Beim INSERT in eine Child-Tabelle werden die CopyColumns per **Subquery** aus der Root-Tabelle aufgelöst:
INSERT INTO [STTK] ([IDST], [IDSTAP], [TKArt], [Wert])
VALUES (
(SELECT [IDST] FROM [STAP] WHERE [IDSTAP] = :FK),
:FK, :FV, :P0
)
==== Erklärung ====
* **'':FK''** — Primärschlüssel des Root-Datensatzes (wird vom INSERT-Kontext gesetzt)
* **'':FV''** — Filter-/Diskriminator-Wert (z.B. ''TKArt = 170'' für E-Mail)
* **Subquery** — ''(SELECT [IDST] FROM [STAP] WHERE [IDSTAP] = :FK)'' holt den denormalisierten Wert direkt aus dem Parent
==== Hintergrund ====
Ohne ''copyColumns'' schlägt der INSERT mit **SQL Server Error 515** fehl ("Cannot insert the value NULL into column … column does not allow NULLs"). Das betrifft typischerweise EAV-Tabellen (Entity-Attribute-Value), deren Child-Zeilen sowohl den Parent-FK als auch übergeordnete Gruppen-FKs tragen.
Alle CopyColumn-Bezeichner werden vor der SQL-Interpolation mit ''QuoteIdent'' validiert (CWE-89).
----
===== Mapping-Quelltypen =====
^ Typ ^ SQL-Pattern ^ Beispiel ^
| ''fsDirect'' | ''alias.[Col] AS [Field]'' | ''r.[VN] AS [FirstName]'' |
| ''fsRelated'' | ''LEFT JOIN'' / ''OUTER APPLY'' mit Diskriminator | ''f1.[B] AS [Email1] WHERE TKArt=170'' |
| ''fsExpression'' | Freies SQL-Fragment (Blacklist-geprüft) | ''CONCAT(r.[VN], ' ', r.[NN]) AS [FullName]'' |
----
===== SQL-Test und Debugging =====
{{wvds:image>de:int:olsync:MappingPane_SqlTest.png|width=100%|alt=SQL-Test im Mapping-Designer}}
- Öffnen Sie den **Mapping-Designer** → Schritt **Vorschau / SQL-Test**.
- Das **SQL-Memo** (oben) zeigt das generierte SELECT-Statement.
- Klicken Sie auf **Test-Abfrage** — bis zu 100 Zeilen werden im Ergebnis-Grid angezeigt.
- Bei Fehlern: Kopieren Sie das SQL in **SSMS** für detaillierte Analyse.
==== Häufige Probleme ====
^ Problem ^ Ursache ^ Lösung ^
| Spalte mehrdeutig | Gleicher Spaltenname in mehreren Tabellen | Alias-Qualifikation prüfen (''r.'', ''j0.'', ''f1.'') |
| Ergebnis vervielfacht | 1:N-Relation ohne ''singleRow: true'' | ''singleRow'' in der Relation aktivieren |
| Leere Ergebnisse | Filter-Wert stimmt nicht | ''filterValue'' im Feld-Mapping prüfen |
| SSMA CHECK-Constraint | Leere Strings statt NULL | Engine setzt automatisch NULL; Spalte prüfen |
----
===== Sicherheit (CWE-89) =====
Alle Tabellen- und Spaltennamen aus Mapping-Dateien werden **vor** der SQL-Interpolation validiert:
* **''IsValidIdent''** — prüft gegen ''[A-Za-z_][A-Za-z0-9_]*''
* **''QuoteIdent''** — klammert in ''[...]'' für SQL Server
* **Expression-Blacklist** — blockiert ''SELECT'', ''INSERT'', ''DROP'', ''EXEC'', ''xp_'' in ''fsExpression''-Feldern
* **Parametrisierung** — alle Werte über '':P0''...'':Pn'' Parameter gebunden
→ Details: [[.:security|Sicherheitsarchitektur]]
----
//Wolfgang van der Stille @ EMSR DATA d.o.o. — Outlook Sync//
{{tag>outlooksync entwickler sql-builder alias query debugging}}