====== 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}}