USE [DD_SYS]
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_WARNINGS ON
GO
SET ANSI_PADDING ON
GO

-- [PRDD_SYNC_DATABASE]
-- =================================================================
-- Schema- und optionale Daten-Synchronisation zwischen zwei bestehenden Datenbanken
-- Fokus: programmierbare Objekte, Schemata, Tabellen (additiv), Keys, FKs, Indizes, Synonyme
-- Optionale Datenabgleiche pro Tabelle (nur bei Abweichungen)
-- Minimum requirement: MS SQL Server 2016
--
-- Returns: INT Value - 0 = Everything worked well, 1 = Error
-- =================================================================
-- NICHT UNTERSTÜTZT / NOT SUPPORTED (in dieser Version):
-- - Extended Properties (sp_addextendedproperty)
-- - Service Broker Objects (Queues, Services, Contracts)
-- - User-Defined Types (CREATE TYPE)
-- - Full-Text Indexes (CREATE FULLTEXT INDEX)
-- - XML Schema Collections (CREATE XML SCHEMA COLLECTION)
-- - User-Defined Statistics (CREATE STATISTICS)
-- - Columnstore Indexes (type 5,6 - nur Clustered/Nonclustered werden synchronisiert)
-- - Partitions und Partition Schemes
-- - Change Data Capture (CDC)
-- - Change Tracking
-- - CLR Assemblies, CLR Functions, CLR Procedures (CREATE ASSEMBLY)
-- - Encrypted Objects (WITH ENCRYPTION)
-- =================================================================
-- GEPLANTE FEATURES / PLANNED FEATURES (Future):
-- - @pBATCHSIZE: Batch-basiertes MERGE für große Tabellen (Parameter validiert, Implementierung ausstehend)
-- =================================================================
-- Copyright (c) 2025 by Digital Data GmbH
--
-- Digital Data GmbH • Ludwig-Rinn-Strasse 16 • D-35452 Heuchelheim
-- Tel.: 0641/202360 • E-Mail: info-flow@digitaldata.works
-- =================================================================
-- Creation Date / Author:    24.12.2025 / MK
-- Version Date / Editor:     29.12.2025 / MK
-- Version Number:            0.6.4 (Final)
-- =================================================================
-- History:
-- 29.12.2025 / MK - v0.6.4: Bugfix: Views+Functions+Procedures robust (case-insensitive, whitespace, placeholder)
-- 29.12.2025 / MK - v0.6.3: Code Review: 34 Punkte behoben (CRITICAL, HIGH, MEDIUM, LOW)
-- 29.12.2025 / MK - v0.6.2: Validation auch bei @EXECUTE=0 (DRY-RUN/Preview)
-- 29.12.2025 / MK - v0.6.1: OBJECT DETAILS nur bei @DEBUG=1 (weniger Output)
-- 29.12.2025 / MK - v0.6.0: Views/Procedures/Functions use CREATE OR ALTER (no more DROP required)
-- 29.12.2025 / MK - v0.5.9: Cycle Detection for Views/Functions Dependency CTEs
-- 29.12.2025 / MK - v0.5.8: Code Review Fixes + Bidirectional Mismatch Report
-- 29.12.2025 / MK - v0.5.7: Feature: @pDROPEXISTING Parameter zur Steuerung von DROP-Statements
-- 29.12.2025 / MK - v0.5.6: Bugfix: DROP Phases sort before CREATE, Comprehensive Collation-Fix
-- 29.12.2025 / MK - v0.5.4: Feature: @pUSETRANSACTION Parameter, Transaction Safety-Net
-- 29.12.2025 / MK - v0.5.3: Code Review: 23-Punkte-Plan (Security, Performance, Validation)
-- 29.12.2025 / MK - v0.5.2: Feature: Collation-Check, Temp-Tables mit Server-Collation
-- 29.12.2025 / MK - v0.5.1: Bugfix: Temp-Tabellen dynamisch mit Quell-DB Collation
-- 29.12.2025 / MK - v0.5.0: Feature: DROP + CREATE for programmable objects
-- 29.12.2025 / MK - v0.4.x: Diverse Bugfixes und Code Reviews
-- 28.12.2025 / MK - v0.3.x: Collation-Fixes, Cursors LOCAL, Stats
-- 24.12.2025 / MK - v0.2.0: IncludeData-Option, Logging-Tabelle, Hash-Vergleich
-- =================================================================
CREATE OR ALTER PROCEDURE [dbo].[PRDD_SYNC_DATABASE] (
    @pSOURCEDATABASE           NVARCHAR(128)    = NULL,   -- Quell-Datenbank (Referenz). Schema- und Datenvergleich-Basis. Nicht NULL erforderlich.
    @pTARGETDATABASE           NVARCHAR(128)    = NULL,   -- Ziel-Datenbank (zu aktualisieren). Erhält alle Schema- und Datenänderungen. Nicht NULL erforderlich.
    @pEXECUTE                  BIT              = 1,      -- Ausführungsmodus: 1 = Änderungen ausführen, 0 = Nur Plan ausgeben/Test (Dry-Run)
    @pEXCLUDEOBJECTS           NVARCHAR(MAX)    = NULL,   -- Optionale Objekt-Ausschlüsse. Komma-getrennt. Unterstützt Wildcards (*). Beispiel: 'test*,temp_*'
    @pSYNCOBJECTS              NVARCHAR(500)    = 'ALL',  -- Objekttypen für Sync. 'ALL' oder kommasepariert: SCHEMAS,TABLES/TABS,VIEWS,PROCEDURES/PROCS,FUNCTIONS/FUNCS,TRIGGERS,INDEXES,SYNONYMS,SEQUENCES
    @pLOGLEVEL                 NVARCHAR(25)     = 'INFO', -- Log-Schwellenwert: 'INFO' = alle, 'WARN' = Warnungen+Fehler, 'ERROR' = nur Fehler, NULL = kein Logging
    @pDEBUG                    BIT              = 0,      -- Debug-Modus: 1 = ausführliche Ausgabe (SQL-Statements, Hashes), 0 = minimal
    @pINCLUDEDATA              BIT              = 0,      -- Daten-Synchronisation: 0 = nur Schema, 1 = Schema + Daten mit Hash-Vergleich
    @pDELETEMISSING            BIT              = 0,      -- Lösch-Strategie für Target-Daten: 0 = behalten, 1 = fehlende Zeilen im Target löschen
    @pSYNCSECURITY             BIT              = 0,      -- Sicherheitssync: 1 = Benutzer/Rollen/Grants synchronisieren, 0 = überspringen (Standard)
    @pVALIDATION_MODE          NVARCHAR(20)     = 'NONE', -- Validierungsmodus: 'NONE' = keine, 'SCHEMA' = nur Schema, 'DATA' = nur Daten, 'ALL' = beides
    @pBATCHSIZE                INT              = 10000,  -- [FUTURE] Batch-Größe für Data-Sync (validiert, aber noch nicht implementiert)
    @pUSETRANSACTION           BIT              = 0,      -- Transaction-Modus: 1 = atomare Ausfuehrung mit Rollback bei Fehler, 0 = ohne Transaction
    @pDROPEXISTING             BIT              = 0       -- DROP-Strategie fuer programm. Objekte (Views/Triggers/Functions/Procs): 1 = DROP vor CREATE, 0 = nur CREATE OR ALTER (Standard)
)
AS
BEGIN

    --===============================================-- Set session options --================================================--
    SET NOCOUNT ON;
    SET XACT_ABORT ON;
    ----------------------------------------------------------------------------------------------------------------------------

    --=========================================-- Define constants --========================================--
    DECLARE @CONST_MIN_BATCHSIZE        INT = 100,
            @CONST_MAX_BATCHSIZE        INT = 1000000,
            @CONST_DEFAULT_BATCHSIZE    INT = 10000,
            @CONST_MAX_EXCLUDES         INT = 1000;  -- Maximum number of exclude patterns (overflow protection, 1000 is sufficient for most use cases without memory impact)
    ----------------------------------------------------------------------------------------------------------------------------

    --=========================================-- Parameter copies (avoid sniffing) --========================================--
    DECLARE @SOURCEDATABASE             SYSNAME             = NULLIF(LTRIM(RTRIM(@pSOURCEDATABASE)), N''),
            @TARGETDATABASE             SYSNAME             = NULLIF(LTRIM(RTRIM(@pTARGETDATABASE)), N''),
            @EXECUTE                    BIT                 = ISNULL(@pEXECUTE, 0),
            @LOGLEVEL                   NVARCHAR(25)        = CASE WHEN @pLOGLEVEL IS NULL THEN NULL ELSE UPPER(LTRIM(RTRIM(@pLOGLEVEL))) END,
            @DEBUG                      BIT                 = ISNULL(@pDEBUG, 0),
            @EXCLUDEOBJECTS             NVARCHAR(MAX)       = CASE WHEN @pEXCLUDEOBJECTS IS NULL THEN NULL ELSE LTRIM(RTRIM(@pEXCLUDEOBJECTS)) END,
            @SYNCOBJECTS                NVARCHAR(500)       = CASE WHEN @pSYNCOBJECTS IS NULL THEN N'ALL' ELSE UPPER(LTRIM(RTRIM(@pSYNCOBJECTS))) END,
            @INCLUDEDATA                BIT                 = ISNULL(@pINCLUDEDATA, 0),
            @DELETEMISSING              BIT                 = ISNULL(@pDELETEMISSING, 0),
            @SYNCSECURITY               BIT                 = ISNULL(@pSYNCSECURITY, 0),
            @VALIDATION_MODE            NVARCHAR(20)        = CASE WHEN @pVALIDATION_MODE IS NULL THEN 'NONE' ELSE UPPER(LTRIM(RTRIM(@pVALIDATION_MODE))) END,
            @BATCHSIZE                  INT                 = NULL,  -- Wird nach Konstanten-Definition gesetzt
            @USETRANSACTION             BIT                 = ISNULL(@pUSETRANSACTION, 0),
            @DROPEXISTING               BIT                 = ISNULL(@pDROPEXISTING, 0);

    -- Object type flags for granular control (derived from @SYNCOBJECTS)
    DECLARE @SYNC_SCHEMAS               BIT = 0,
            @SYNC_TABLES                BIT = 0,
            @SYNC_VIEWS                 BIT = 0,
            @SYNC_PROCEDURES            BIT = 0,
            @SYNC_FUNCTIONS             BIT = 0,
            @SYNC_TRIGGERS              BIT = 0,
            @SYNC_INDEXES               BIT = 0,
            @SYNC_SYNONYMS              BIT = 0,
            @SYNC_SEQUENCES             BIT = 0;

    -- Parse @SYNCOBJECTS and set flags (with delimiter check to avoid partial string matches)
    DECLARE @SYNCOBJECTS_PADDED NVARCHAR(510) = N',' + REPLACE(@SYNCOBJECTS, N' ', N'') + N',';

    IF @SYNCOBJECTS = N'ALL' OR @SYNCOBJECTS = N'' OR @SYNCOBJECTS IS NULL BEGIN
        -- ALL: Enable all object types
        SET @SYNC_SCHEMAS = 1; SET @SYNC_TABLES = 1; SET @SYNC_VIEWS = 1;
        SET @SYNC_PROCEDURES = 1; SET @SYNC_FUNCTIONS = 1; SET @SYNC_TRIGGERS = 1;
        SET @SYNC_INDEXES = 1; SET @SYNC_SYNONYMS = 1; SET @SYNC_SEQUENCES = 1;
    END ELSE BEGIN
        -- Individual object types from comma-separated list (with delimiter check)
        IF CHARINDEX(N',SCHEMAS,', @SYNCOBJECTS_PADDED) > 0 SET @SYNC_SCHEMAS = 1;
        IF CHARINDEX(N',TABLES,', @SYNCOBJECTS_PADDED) > 0 OR CHARINDEX(N',TABS,', @SYNCOBJECTS_PADDED) > 0 SET @SYNC_TABLES = 1;
        IF CHARINDEX(N',VIEWS,', @SYNCOBJECTS_PADDED) > 0 SET @SYNC_VIEWS = 1;
        IF CHARINDEX(N',PROCEDURES,', @SYNCOBJECTS_PADDED) > 0 OR CHARINDEX(N',PROCS,', @SYNCOBJECTS_PADDED) > 0 SET @SYNC_PROCEDURES = 1;
        IF CHARINDEX(N',FUNCTIONS,', @SYNCOBJECTS_PADDED) > 0 OR CHARINDEX(N',FUNCS,', @SYNCOBJECTS_PADDED) > 0 SET @SYNC_FUNCTIONS = 1;
        IF CHARINDEX(N',TRIGGERS,', @SYNCOBJECTS_PADDED) > 0 SET @SYNC_TRIGGERS = 1;
        IF CHARINDEX(N',INDEXES,', @SYNCOBJECTS_PADDED) > 0 SET @SYNC_INDEXES = 1;
        IF CHARINDEX(N',SYNONYMS,', @SYNCOBJECTS_PADDED) > 0 SET @SYNC_SYNONYMS = 1;
        IF CHARINDEX(N',SEQUENCES,', @SYNCOBJECTS_PADDED) > 0 SET @SYNC_SEQUENCES = 1;

        -- Warning if no object types recognized
        IF @SYNC_SCHEMAS = 0 AND @SYNC_TABLES = 0 AND @SYNC_VIEWS = 0 AND @SYNC_PROCEDURES = 0
           AND @SYNC_FUNCTIONS = 0 AND @SYNC_TRIGGERS = 0 AND @SYNC_INDEXES = 0
           AND @SYNC_SYNONYMS = 0 AND @SYNC_SEQUENCES = 0 BEGIN
            PRINT '[WARN] No valid object types recognized in @pSYNCOBJECTS: ' + @SYNCOBJECTS;
            PRINT '[WARN] Valid values: ALL, SCHEMAS, TABLES/TABS, VIEWS, PROCEDURES/PROCS, FUNCTIONS/FUNCS, TRIGGERS, INDEXES, SYNONYMS, SEQUENCES';
        END;
    END;

    -- BATCHSIZE mit Konstanten validieren (#1, #18)
    SET @BATCHSIZE = CASE
        WHEN @pBATCHSIZE IS NULL OR @pBATCHSIZE < 0 THEN @CONST_DEFAULT_BATCHSIZE
        WHEN @pBATCHSIZE > 0 AND @pBATCHSIZE < @CONST_MIN_BATCHSIZE THEN @CONST_MIN_BATCHSIZE
        WHEN @pBATCHSIZE > @CONST_MAX_BATCHSIZE THEN @CONST_MAX_BATCHSIZE
        ELSE @pBATCHSIZE
    END;

    -- Warning if BATCHSIZE was automatically adjusted
    IF @BATCHSIZE <> ISNULL(@pBATCHSIZE, @CONST_DEFAULT_BATCHSIZE) BEGIN
        PRINT '[WARN] @pBATCHSIZE adjusted from ' + CONVERT(NVARCHAR(10), ISNULL(@pBATCHSIZE, -1)) + ' to ' + CONVERT(NVARCHAR(10), @BATCHSIZE);
    END;

    -- Validate VALIDATION_MODE and set to default if invalid
    IF @VALIDATION_MODE NOT IN (N'NONE', N'SCHEMA', N'DATA', N'ALL') BEGIN
        PRINT '[WARN] Invalid @pVALIDATION_MODE value, defaulting to NONE';
        SET @VALIDATION_MODE = N'NONE';
    END;

    --=================================================-- Declare runtime variables --==========================================--
    DECLARE @MY_PROCEDURE_NAME          NVARCHAR(128)       = OBJECT_NAME(@@PROCID);
    DECLARE @PRODUCTVERSION             sql_variant,
            @PRODUCTMAINVERSION         INT,
            @PRODUCTLEVEL               sql_variant,
            @PRODUCTEDITION             sql_variant,
            @SERVERNAME                 sql_variant,
            @COMPUTERNETBIOS            sql_variant,
            @MACHINENAME                sql_variant,
            @INSTANCENAME               sql_variant,
            @MYSESSIONID                NVARCHAR(50)        = CONVERT(NVARCHAR(50),@@SPID),
            @PLACEHOLDERSOURCEDBQUOTED  NVARCHAR(32)        = N'{#SOURCEDB_Q#}',
            @PLACEHOLDERTARGETDBQUOTED  NVARCHAR(32)        = N'{#TARGETDB_Q#}',
            @PLACEHOLDERSOURCEDB        NVARCHAR(32)        = N'{#SOURCEDB#}',
            @PLACEHOLDERTARGETDB        NVARCHAR(32)        = N'{#TARGETDB#}',
            @PLACEHOLDERCOLLATION       NVARCHAR(32)        = N'{#COLLATION#}',
            @PLACEHOLDERTARGETCOLLATION NVARCHAR(32)        = N'{#TARGET_COLLATION_NAME#}',
            @PLACEHOLDERSOURCECOLLATIONNAME NVARCHAR(32) = N'{#SOURCECOLLATION_NAME#}',
            @PLACEHOLDERTARGETCOLLATIONNAME NVARCHAR(32) = N'{#TARGETCOLLATION_NAME#}',
            @PLACEHOLDERDROPEXISTING    NVARCHAR(32)        = N'{#DROPEXISTING_FILTER#}',
            @DROPEXISTING_FILTER        NVARCHAR(20)        = CASE WHEN @DROPEXISTING = 1 THEN N'' ELSE N'AND 1=0' END,
            @SOURCEDATABASEQUOTED       NVARCHAR(260)       = QUOTENAME(@SOURCEDATABASE),
            @TARGETDATABASEQUOTED       NVARCHAR(260)       = QUOTENAME(@TARGETDATABASE),
            @SOURCECOLLATION            NVARCHAR(128)       = NULL,
            @TARGETCOLLATION            NVARCHAR(128)       = NULL,
            @SERVERCOLLATION            NVARCHAR(128)       = NULL,
            @COMPARECOLLATION           NVARCHAR(200)       = NULL,
            @DYNAMICSQL                 NVARCHAR(MAX)       = NULL,
            @SERVERINFOMSG              NVARCHAR(MAX)       = NULL,
            @DELCLAUSE                  NVARCHAR(100)       = NULL,
            @STEP                       INT                 = NULL,
            @PHASE                      NVARCHAR(50)        = NULL,
            @COMMAND                    NVARCHAR(MAX)       = NULL,
            @SEVERITY                   NVARCHAR(10)        = 'INFO',
            @ERRMSG                     NVARCHAR(MAX)       = NULL,
            @VALIDATION_PASSED          BIT                 = 1,
            @VALIDATION_ERRORS          INT                 = 0,
            @VALIDATION_WARNINGS        INT                 = 0,
            @VALIDATION_SQL             NVARCHAR(MAX)       = NULL,
            @VALIDATION_CURSOR_SQL      NVARCHAR(MAX)       = NULL,
            @VALIDATION_SOURCE_COUNT    INT                 = 0,
            @VALIDATION_TARGET_COUNT    INT                 = 0,
            @VALIDATION_SOURCE_ROWS     BIGINT              = 0,
            @VALIDATION_TARGET_ROWS     BIGINT              = 0,
            @VALIDATION_SCHEMA_NAME     NVARCHAR(128)       = NULL,
            @VALIDATION_TABLE_NAME      NVARCHAR(128)       = NULL,
            @RETURN_STATUS              INT                 = 0,
            @RETURN_STATUS_TEXT         NVARCHAR(MAX)       = N'START PROCEDURE [' + @MY_PROCEDURE_NAME + N'] @ ' + CONVERT(NVARCHAR(50),GETDATE(),120),
            -- Error output variables (used in execution loop)
            @ERRPOS                     INT                 = 1,
            @ERRCHUNK                   INT                 = 4000;

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

    --=================================================-- Output parameters --================================================--
    PRINT '====================================================================================================';
    PRINT @RETURN_STATUS_TEXT;
    PRINT '';
    PRINT 'PARAMETER01 - @pSOURCEDATABASE:    ' + ISNULL(@SOURCEDATABASE,'<NULL>');
    PRINT 'PARAMETER02 - @pTARGETDATABASE:    ' + ISNULL(@TARGETDATABASE,'<NULL>');
    PRINT 'PARAMETER03 - @pEXECUTE:           ' + CONVERT(NVARCHAR(1),@EXECUTE);
    PRINT 'PARAMETER04 - @pEXCLUDEOBJECTS:    ' + ISNULL(@EXCLUDEOBJECTS,'<NULL>');
    PRINT 'PARAMETER05 - @pSYNCOBJECTS:       ' + ISNULL(@SYNCOBJECTS,'<NULL>');
    PRINT 'PARAMETER06 - @pLOGLEVEL:          ' + ISNULL(@LOGLEVEL,'<NULL>');
    PRINT 'PARAMETER07 - @pDEBUG:             ' + CONVERT(NVARCHAR(1),@DEBUG);
    PRINT 'PARAMETER08 - @pINCLUDEDATA:       ' + CONVERT(NVARCHAR(1),@INCLUDEDATA);
    PRINT 'PARAMETER09 - @pDELETEMISSING:     ' + CONVERT(NVARCHAR(1),@DELETEMISSING);
    PRINT 'PARAMETER10 - @pSYNCSECURITY:      ' + CONVERT(NVARCHAR(1),@SYNCSECURITY);
    PRINT 'PARAMETER11 - @pVALIDATION_MODE:   ' + ISNULL(@VALIDATION_MODE,'<NULL>');
    PRINT 'PARAMETER12 - @pBATCHSIZE:         ' + CONVERT(NVARCHAR(10),@BATCHSIZE);
    PRINT 'PARAMETER13 - @pUSETRANSACTION:    ' + CONVERT(NVARCHAR(1),@USETRANSACTION);
    PRINT 'PARAMETER14 - @pDROPEXISTING:     ' + CONVERT(NVARCHAR(1),@DROPEXISTING);
    PRINT '';
    ----------------------------------------------------------------------------------------------------------------------------

    --=================================================-- Get server infos --==================================================--
    SELECT @PRODUCTVERSION = SERVERPROPERTY('productversion'), @PRODUCTLEVEL = SERVERPROPERTY ('productlevel'), @PRODUCTEDITION = SERVERPROPERTY ('edition');
    SELECT @MACHINENAME = SERVERPROPERTY('MachineName'), @INSTANCENAME = SERVERPROPERTY('InstanceName');
    SELECT @SERVERNAME = SERVERPROPERTY('ServerName');
    SELECT @COMPUTERNETBIOS = SERVERPROPERTY('ComputerNamePhysicalNetBIOS');
    SET @PRODUCTMAINVERSION = ISNULL(LEFT(CONVERT(NVARCHAR(100),@PRODUCTVERSION), CHARINDEX('.', CONVERT(NVARCHAR(100),@PRODUCTVERSION)) - 1),0);

    -- SQL Server 2016+ required (version 13+) for STRING_SPLIT, HASHBYTES SHA2_256, etc.
    IF @PRODUCTMAINVERSION < 13 BEGIN
        RAISERROR('SQL Server 2016 or higher required (current version: %d)', 16, 1, @PRODUCTMAINVERSION);
        RETURN 1;
    END;

    -- Get Collations: Server, Source and Target (not DD_SYS!)
    SELECT @SERVERCOLLATION = CONVERT(NVARCHAR(128), SERVERPROPERTY('Collation'));
    DECLARE @COLLATION_SQL NVARCHAR(1000); SET @COLLATION_SQL = N'USE ' + @SOURCEDATABASEQUOTED + N'; SELECT @col = CONVERT(NVARCHAR(128), DATABASEPROPERTYEX(DB_NAME(), ''Collation''))'; EXEC sys.sp_executesql @COLLATION_SQL, N'@col NVARCHAR(128) OUTPUT', @SOURCECOLLATION OUTPUT;
    SET @COLLATION_SQL = N'USE ' + @TARGETDATABASEQUOTED + N'; SELECT @col = CONVERT(NVARCHAR(128), DATABASEPROPERTYEX(DB_NAME(), ''Collation''))'; EXEC sys.sp_executesql @COLLATION_SQL, N'@col NVARCHAR(128) OUTPUT', @TARGETCOLLATION OUTPUT;
    -- COMPARISONS: Use Source collation for cross-database object name comparisons
    -- NEW OBJECTS: Use Target collation for new columns (Target-DB defines collation)
    SET @COMPARECOLLATION = N' COLLATE ' + ISNULL(@SOURCECOLLATION, ISNULL(@SERVERCOLLATION, N'Latin1_General_CI_AS'));

    PRINT 'Informations about this Server:';
    PRINT '@MYSESSIONID:        ' + CONVERT(NVARCHAR(100),@MYSESSIONID);
    PRINT '@SERVERNAME:         ' + CONVERT(NVARCHAR(100),@SERVERNAME);
    PRINT '@COMPUTERNETBIOS:    ' + CONVERT(NVARCHAR(100),@COMPUTERNETBIOS);
    PRINT '@MACHINENAME:        ' + CONVERT(NVARCHAR(100),@MACHINENAME);
    PRINT '@INSTANCENAME:       ' + ISNULL(CONVERT(NVARCHAR(100),@INSTANCENAME), N'<DEFAULT>');
    PRINT '@PRODUCTVERSION:     ' + CONVERT(NVARCHAR(100),@PRODUCTVERSION);
    PRINT '@PRODUCTMAINVERSION: ' + CONVERT(NVARCHAR(100),@PRODUCTMAINVERSION);
    PRINT '@PRODUCTLEVEL:       ' + CONVERT(NVARCHAR(100),@PRODUCTLEVEL);
    PRINT '@PRODUCTEDITION:     ' + CONVERT(NVARCHAR(100),@PRODUCTEDITION);
    PRINT '';
    PRINT 'Collation Information:';
    PRINT '@SERVERCOLLATION:    ' + ISNULL(@SERVERCOLLATION, N'<NULL>');
    PRINT '@SOURCECOLLATION:    ' + ISNULL(@SOURCECOLLATION, N'<NULL>');
    PRINT '@TARGETCOLLATION:    ' + ISNULL(@TARGETCOLLATION, N'<NULL>') + N' (used for new columns)';
    PRINT '@COMPARECOLLATION:   ' + ISNULL(@COMPARECOLLATION, N'<NULL>') + N' (for object name comparisons)';
    ----------------------------------------------------------------------------------------------------------------------------

    --=================================================-- Logging-Tabelle im aktuellen Kontext sicherstellen --================================================--
    IF @LOGLEVEL IS NOT NULL BEGIN
      IF EXISTS (SELECT * FROM [INFORMATION_SCHEMA].[TABLES] WHERE [TABLE_NAME] = N'TBDD_SYNC_DATABASE_LOG') BEGIN
          PRINT '[INFO] Log table already exists';
      END
      ELSE BEGIN
          PRINT '[INFO] Log table does not exists, trying to create...';

          CREATE TABLE [dbo].[TBDD_SYNC_DATABASE_LOG](
              [LOG_ID]     BIGINT IDENTITY(1,1) NOT NULL,
              [LOG_LEVEL]  NVARCHAR(10)         NOT NULL,
              [MESSAGE1]   NVARCHAR(MAX)        NULL,
              [MESSAGE2]   NVARCHAR(MAX)        NULL,
              [MESSAGE3]   NVARCHAR(MAX)        NULL,
              [ADDED_WHO]  SYSNAME              NOT NULL CONSTRAINT [DF_TBDD_SYNC_DATABASE_LOG_ADDED_WHO] DEFAULT (SUSER_SNAME()),
              [ADDED_WHEN] DATETIME2(0)         NOT NULL CONSTRAINT [DF_TBDD_SYNC_DATABASE_LOG_ADDED_WHEN] DEFAULT (GETDATE()),
				CONSTRAINT [PK_TBDD_SYNC_DATABASE_LOG_LOGID] PRIMARY KEY CLUSTERED
			(
				[LOG_ID] ASC
			) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, DATA_COMPRESSION = PAGE) ON [PRIMARY]
			) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY];

		END;
	END
	ELSE BEGIN
		PRINT '';
		PRINT '[WARN] Skipping Logging to log table!';
	END;
	-----------------------------------------------------------------------------------------------------------------------------

    --=================================================-- Log server infos to table --================================================--
    IF @LOGLEVEL IS NOT NULL BEGIN
        SET @SERVERINFOMSG =
            N'ServerInfo: ServerName=' + CONVERT(NVARCHAR(100),@SERVERNAME) +
            N'; ComputerNetBIOS=' + CONVERT(NVARCHAR(100),@COMPUTERNETBIOS) +
            N'; Machine=' + CONVERT(NVARCHAR(100),@MACHINENAME) +
            N'; Instance=' + ISNULL(CONVERT(NVARCHAR(100),@INSTANCENAME),N'<DEFAULT>') +
            N'; Version=' + CONVERT(NVARCHAR(100),@PRODUCTVERSION) +
            N'; MainVersion=' + CONVERT(NVARCHAR(100),@PRODUCTMAINVERSION) +
            N'; Level=' + CONVERT(NVARCHAR(100),@PRODUCTLEVEL) +
            N'; Edition=' + CONVERT(NVARCHAR(100),@PRODUCTEDITION) +
            N'; SessionID=' + CONVERT(NVARCHAR(100),@MYSESSIONID);

        --================================================-- Log server info to table --=================================================--
        IF (UPPER(@LOGLEVEL) IN (N'INFO',N'WARN',N'ERROR')) BEGIN
            INSERT INTO [TBDD_SYNC_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1],    [MESSAGE2],                           [MESSAGE3],                    [ADDED_WHO],        [ADDED_WHEN])
                                             VALUES ('INFO',       '00-Server',    CONVERT(NVARCHAR(MAX),@SERVERNAME), @SERVERINFOMSG,               @MY_PROCEDURE_NAME, GETDATE());
        END;
        -----------------------------------------------------------------------------------------------------------------------------
    END;
    -----------------------------------------------------------------------------------------------------------------------------


    IF @SOURCEDATABASE IS NULL OR DB_ID(@SOURCEDATABASE) IS NULL BEGIN
        RAISERROR('Source database invalid/missing',16,1); RETURN 1; END;
    IF @TARGETDATABASE IS NULL OR DB_ID(@TARGETDATABASE) IS NULL BEGIN
        RAISERROR('Target database must exist',16,1); RETURN 1; END;
    IF @SOURCEDATABASE = @TARGETDATABASE BEGIN RAISERROR('Source and target must differ',16,1); RETURN 1; END;

    -- Check if databases are online (uses state instead of state_desc for performance)
    IF EXISTS (SELECT 1 FROM sys.databases WHERE name = @SOURCEDATABASE AND state <> 0) BEGIN
        RAISERROR('Source database is not online (state <> 0)',16,1); RETURN 1; END;
    IF EXISTS (SELECT 1 FROM sys.databases WHERE name = @TARGETDATABASE AND state <> 0) BEGIN
        RAISERROR('Target database is not online (state <> 0)',16,1); RETURN 1; END;
    IF @DEBUG = 1 PRINT '[DEBUG] Validating @LOGLEVEL: ' + ISNULL(@LOGLEVEL, '<NULL>');
    IF @LOGLEVEL IS NOT NULL AND UPPER(@LOGLEVEL) NOT IN (N'INFO',N'WARN',N'ERROR') BEGIN RAISERROR('Invalid @pLOGLEVEL',16,1); RETURN 1; END;
    IF @DEBUG = 1 PRINT '[DEBUG] Validating @VALIDATION_MODE: ' + ISNULL(@VALIDATION_MODE, '<NULL>');
    -- VALIDATION_MODE is already validated in line 101-104 and set to default

    -- @SYNCOBJECTS Info-Ausgabe
    PRINT '[INFO] Object types to sync: ' + @SYNCOBJECTS;
    PRINT '[INFO] Sync flags: SCHEMAS=' + CONVERT(NVARCHAR(1), @SYNC_SCHEMAS) +
          ', TABLES=' + CONVERT(NVARCHAR(1), @SYNC_TABLES) +
          ', VIEWS=' + CONVERT(NVARCHAR(1), @SYNC_VIEWS) +
          ', PROCEDURES=' + CONVERT(NVARCHAR(1), @SYNC_PROCEDURES) +
          ', FUNCTIONS=' + CONVERT(NVARCHAR(1), @SYNC_FUNCTIONS) +
          ', TRIGGERS=' + CONVERT(NVARCHAR(1), @SYNC_TRIGGERS) +
          ', INDEXES=' + CONVERT(NVARCHAR(1), @SYNC_INDEXES) +
          ', SYNONYMS=' + CONVERT(NVARCHAR(1), @SYNC_SYNONYMS) +
          ', SEQUENCES=' + CONVERT(NVARCHAR(1), @SYNC_SEQUENCES);

    -- Warnings for potentially problematic combinations
    IF @SYNC_INDEXES = 1 AND @SYNC_TABLES = 0 BEGIN
        PRINT '[WARN] INDEXES enabled without TABLES - indexes may reference non-existing tables';
    END;
    IF @SYNC_VIEWS = 1 AND @SYNC_TABLES = 0 BEGIN
        PRINT '[WARN] VIEWS enabled without TABLES - views may reference non-existing tables';
    END;
    IF @SYNC_TRIGGERS = 1 AND @SYNC_TABLES = 0 BEGIN
        PRINT '[WARN] TRIGGERS enabled without TABLES - triggers may reference non-existing tables';
    END;
    IF @VALIDATION_MODE IN (N'DATA', N'ALL') AND @SYNC_TABLES = 0 BEGIN
        PRINT '[WARN] Data validation requested but TABLES sync is disabled';
    END;
    IF @INCLUDEDATA = 1 AND @SYNC_TABLES = 0 BEGIN
        PRINT '[WARN] @pINCLUDEDATA=1 but TABLES sync is disabled - data sync will be limited';
    END;

    IF @DEBUG = 1 PRINT '[DEBUG] All validations passed';

    -- Security #17: Extended permission check for DDL operations
    IF @EXECUTE = 1 BEGIN
        IF HAS_PERMS_BY_NAME(@TARGETDATABASE, 'DATABASE', 'ALTER') = 0 BEGIN
            RAISERROR('Insufficient permissions: ALTER permission required on target database',16,1);
            RETURN 1;
        END;
        IF HAS_PERMS_BY_NAME(@TARGETDATABASE, 'DATABASE', 'CREATE TABLE') = 0 BEGIN
            RAISERROR('Insufficient permissions: CREATE TABLE permission required on target database',16,1);
            RETURN 1;
        END;
        IF HAS_PERMS_BY_NAME(@SOURCEDATABASE, 'DATABASE', 'VIEW DEFINITION') = 0 BEGIN
            RAISERROR('Insufficient permissions: VIEW DEFINITION permission required on source database',16,1);
            RETURN 1;
        END;
    END;
    IF @DEBUG = 1 PRINT '[DEBUG] Permissions validated';

        --===============================================-- Log result to table --=================================================--
    IF @DEBUG = 1 PRINT '[DEBUG] Logging to table...';
        IF UPPER(@LOGLEVEL) IN (N'INFO',N'WARN',N'ERROR') BEGIN
            INSERT INTO [TBDD_SYNC_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1],    [MESSAGE2],             [MESSAGE3],                    [ADDED_WHO],        [ADDED_WHEN])
                                            VALUES ('INFO',  '01-Validation', 'Parametern validiert', 'Source: '+@SOURCEDATABASE+', Target: '+@TARGETDATABASE, @MY_PROCEDURE_NAME, GETDATE());
        END;
    IF @DEBUG = 1 PRINT '[DEBUG] Log entry created successfully';

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

    -- Prepare excludes
    IF @DEBUG = 1 PRINT '[DEBUG] Creating excludes temp table...';
    -- Create temp table with SERVER collation (use COLLATE in queries)
    -- Line removed
    CREATE TABLE #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS(Pattern NVARCHAR(256) NOT NULL, IsWildcard BIT NOT NULL DEFAULT 0);
    CREATE NONCLUSTERED INDEX IX_Pattern ON #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS(Pattern, IsWildcard);
    CREATE NONCLUSTERED INDEX IX_IsWildcard_Pattern ON #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS(IsWildcard, Pattern);  -- #7: Performance-Index
    IF @DEBUG = 1 PRINT '[DEBUG] Excludes temp table created';

    IF @EXCLUDEOBJECTS IS NOT NULL BEGIN
        IF @DEBUG = 1 PRINT '[DEBUG] Processing EXCLUDEOBJECTS...';
        INSERT INTO #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS(Pattern, IsWildcard)
        SELECT TOP (@CONST_MAX_EXCLUDES)  -- Limit to max patterns (overflow protection)
            REPLACE(LTRIM(RTRIM(value)), N'*', N'%'),
            CASE WHEN value LIKE '%*%' THEN 1 ELSE 0 END
        FROM STRING_SPLIT(@EXCLUDEOBJECTS, N',') WHERE LTRIM(RTRIM(value))<>N'';

        DECLARE @INSERTED_EXCLUDES INT = @@ROWCOUNT;
    IF @DEBUG = 1 PRINT '[DEBUG] EXCLUDEOBJECTS processed, count: ' + CONVERT(NVARCHAR(10), @INSERTED_EXCLUDES);

        -- Warning if limit reached
        IF @INSERTED_EXCLUDES >= @CONST_MAX_EXCLUDES BEGIN
            PRINT '[WARN] EXCLUDEOBJECTS limit reached (' + CONVERT(NVARCHAR(10), @CONST_MAX_EXCLUDES) + '). Additional patterns ignored!';
        END;
    END;

        --===============================================-- Log result to table --=================================================--
    IF @DEBUG = 1 PRINT '[DEBUG] Counting excludes...';
        DECLARE @EXCLUDECOUNT INT = (SELECT COUNT(*) FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS);
        IF (UPPER(@LOGLEVEL) IN (N'INFO',N'WARN',N'ERROR')) BEGIN
            INSERT INTO [TBDD_SYNC_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1],    [MESSAGE2],             [MESSAGE3],                    [ADDED_WHO],        [ADDED_WHEN])
                                            VALUES ('INFO',  '02-Preparation', 'Temp-Tabellen erstellt', 'Excludes: '+CONVERT(NVARCHAR(10),@EXCLUDECOUNT), @MY_PROCEDURE_NAME, GETDATE());
        END;

        -----------------------------------------------------------------------------------------------------------------------------
    -- Commands temp table erstellen (Phase with server collation)
    -- Line removed
    CREATE TABLE #TBDD_SYNC_DATABASE_COMMANDS(Step INT IDENTITY(1,1) NOT NULL, Phase NVARCHAR(50) NOT NULL, Command NVARCHAR(MAX) NOT NULL, CONSTRAINT PK_Commands PRIMARY KEY CLUSTERED (Step));
    CREATE NONCLUSTERED INDEX IX_Phase ON #TBDD_SYNC_DATABASE_COMMANDS(Phase) INCLUDE(Step);
    IF @DEBUG = 1 PRINT '[DEBUG] Commands temp table created';

        --=================================================-- Security Sync (optional) --==================================================--
    IF @DEBUG = 1 PRINT '[DEBUG] Checking @SYNCSECURITY: ' + CONVERT(NVARCHAR(1), @SYNCSECURITY);
        IF @SYNCSECURITY = 1 BEGIN
        IF @DEBUG = 1 PRINT '[DEBUG] Security sync is enabled';
                --===============================================-- Log result to table --=================================================--
                IF (UPPER(@LOGLEVEL) IN (N'INFO',N'WARN',N'ERROR')) BEGIN
                    INSERT INTO [TBDD_SYNC_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1],    [MESSAGE2],             [MESSAGE3],                    [ADDED_WHO],        [ADDED_WHEN])
                                                    VALUES ('INFO',  '03-Security', 'Security-Sync aktiviert', 'Sync Rollen, Benutzer, Grants', @MY_PROCEDURE_NAME, GETDATE());
                END;

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

                -- Rollen anlegen
                SET @DYNAMICSQL = N'
INSERT INTO #TBDD_SYNC_DATABASE_COMMANDS(Phase, Command)
SELECT * FROM (
SELECT N''01-Security-Roles'',
             N''USE {#TARGETDB_Q#}; IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name=N'''''' + REPLACE(r.name, '''''''', '''''''''''') + N'''''' AND type=''''R'''') CREATE ROLE ''+QUOTENAME(r.name)+N'';''
FROM {#SOURCEDB_Q#}.sys.database_principals r
WHERE r.type=''R'' AND ISNULL(r.is_fixed_role,0)=0 AND r.name{#COLLATION#} NOT IN (N''public'')) AS X(Phase, Command)
WHERE X.Command IS NOT NULL AND LEN(X.Command)>0;'
                SET @DYNAMICSQL = REPLACE(REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);
                BEGIN TRY
                    EXEC sys.sp_executesql @DYNAMICSQL;
    IF @DEBUG = 1 PRINT '[DEBUG] Security-Roles commands built successfully';
                END TRY
                BEGIN CATCH
                    PRINT '[ERROR] Failed to build Security-Roles commands!';
                    PRINT '[ERROR] Error Message: ' + ERROR_MESSAGE();
                    PRINT '[ERROR] Dynamic SQL (first 8000 chars):';
                    PRINT SUBSTRING(@DYNAMICSQL, 1, 8000);
                    IF LEN(@DYNAMICSQL) > 8000 PRINT '[ERROR] (continued): ' + SUBSTRING(@DYNAMICSQL, 8001, 8000);
                    THROW;
                END CATCH;

                -- Benutzer anlegen (nur wenn Login existiert)
                SET @DYNAMICSQL = N'
INSERT INTO #TBDD_SYNC_DATABASE_COMMANDS(Phase, Command)
SELECT * FROM (
SELECT N''01-Security-Users'',
             N''USE {#TARGETDB_Q#}; IF (EXISTS (SELECT 1 FROM sys.server_principals WHERE name=N'''''' + REPLACE(dp.name, '''''''', '''''''''''') + N'''''' ) AND NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name=N'''''' + REPLACE(dp.name, '''''''', '''''''''''') + N'''''' ))
                     BEGIN CREATE USER ''+QUOTENAME(dp.name)+N'' FOR LOGIN ''+QUOTENAME(dp.name)+N'' WITH DEFAULT_SCHEMA='' + QUOTENAME(ISNULL(dp.default_schema_name,N''dbo'')) + N''; END''
FROM {#SOURCEDB_Q#}.sys.database_principals dp
WHERE dp.type IN (''S'',''U'')
    AND dp.name{#COLLATION#} NOT IN (N''dbo'',N''guest'',N''INFORMATION_SCHEMA'',N''sys'',N''public'')) AS X(Phase, Command)
WHERE X.Command IS NOT NULL AND LEN(X.Command)>0;';
                SET @DYNAMICSQL = REPLACE(REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);
                BEGIN TRY
                    EXEC sys.sp_executesql @DYNAMICSQL;
    IF @DEBUG = 1 PRINT '[DEBUG] Security-Users commands built successfully';
                END TRY
                BEGIN CATCH
                    PRINT '[ERROR] Failed to build Security-Users commands!';
                    PRINT '[ERROR] Error Message: ' + ERROR_MESSAGE();
                    PRINT '[ERROR] Dynamic SQL (first 8000 chars):';
                    PRINT SUBSTRING(@DYNAMICSQL, 1, 8000);
                    IF LEN(@DYNAMICSQL) > 8000 PRINT '[ERROR] (continued): ' + SUBSTRING(@DYNAMICSQL, 8001, 8000);
                    THROW;
                END CATCH;

                -- Rollenmitgliedschaften angleichen
                SET @DYNAMICSQL = N'
INSERT INTO #TBDD_SYNC_DATABASE_COMMANDS(Phase, Command)
SELECT * FROM (
SELECT N''01-Security-RoleMembers'',
             N''USE {#TARGETDB_Q#}; IF (EXISTS (SELECT 1 FROM sys.database_principals WHERE name=N'''''' + REPLACE(r.name, '''''''', '''''''''''') + N'''''' AND type=''''R'''')
                     AND EXISTS (SELECT 1 FROM sys.database_principals WHERE name=N'''''' + REPLACE(m.name, '''''''', '''''''''''') + N'''''' )
                     AND NOT EXISTS (SELECT 1 FROM sys.database_role_members drm JOIN sys.database_principals rp ON rp.principal_id=drm.role_principal_id JOIN sys.database_principals mp ON mp.principal_id=drm.member_principal_id WHERE rp.name=N'''''' + REPLACE(r.name, '''''''', '''''''''''') + N'''''' AND mp.name=N'''''' + REPLACE(m.name, '''''''', '''''''''''') + N'''''' ))
                     BEGIN ALTER ROLE ''+QUOTENAME(r.name)+N'' ADD MEMBER ''+QUOTENAME(m.name)+N''; END''
FROM {#SOURCEDB_Q#}.sys.database_role_members drm
JOIN {#SOURCEDB_Q#}.sys.database_principals r ON r.principal_id=drm.role_principal_id
JOIN {#SOURCEDB_Q#}.sys.database_principals m ON m.principal_id=drm.member_principal_id
WHERE r.name{#COLLATION#} NOT IN (N''public'')
    AND m.name{#COLLATION#} NOT IN (N''dbo'',N''guest'',N''INFORMATION_SCHEMA'',N''sys'',N''public'')) AS X(Phase, Command)
WHERE X.Command IS NOT NULL AND LEN(X.Command)>0;';
                SET @DYNAMICSQL = REPLACE(REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);
                BEGIN TRY
                    EXEC sys.sp_executesql @DYNAMICSQL;
    IF @DEBUG = 1 PRINT '[DEBUG] Security-RoleMembers commands built successfully';
                END TRY
                BEGIN CATCH
                    PRINT '[ERROR] Failed to build Security-RoleMembers commands!';
                    PRINT '[ERROR] Error Message: ' + ERROR_MESSAGE();
                    PRINT '[ERROR] Dynamic SQL (first 8000 chars):';
                    PRINT SUBSTRING(@DYNAMICSQL, 1, 8000);
                    IF LEN(@DYNAMICSQL) > 8000 PRINT '[ERROR] (continued): ' + SUBSTRING(@DYNAMICSQL, 8001, 8000);
                    THROW;
                END CATCH;

                -- Database-/Schema-/Objekt-/Spalten-Grants (GRANT/DENY) nur wenn im Target nicht vorhanden oder anderes state
                SET @DYNAMICSQL = N'
INSERT INTO #TBDD_SYNC_DATABASE_COMMANDS(Phase, Command)
SELECT N''01-Security-Grants'',
             N''USE {#TARGETDB_Q#}; '' +
             CASE dp.state_desc WHEN ''DENY'' THEN N''DENY '' WHEN ''GRANT_WITH_GRANT_OPTION'' THEN N''GRANT '' ELSE N''GRANT '' END + dp.permission_name +
             CASE dp.class WHEN 0 THEN N'' TO ''+QUOTENAME(pr.name)
                                         WHEN 3 THEN N'' ON SCHEMA::''+QUOTENAME(s.name)+N'' TO ''+QUOTENAME(pr.name)
                                         WHEN 4 THEN N'' ON OBJECT::''+QUOTENAME(sc.name)+N''.''{#COLLATION#}+QUOTENAME(obj.name)+N'' (''+QUOTENAME(col.name)+N'') TO ''+QUOTENAME(pr.name)
                                         ELSE N'' ON ''+QUOTENAME(sc.name)+N''.''{#COLLATION#}+QUOTENAME(obj.name)+N'' TO ''+QUOTENAME(pr.name) END +
             CASE WHEN dp.state_desc=''GRANT_WITH_GRANT_OPTION'' THEN N'' WITH GRANT OPTION'' ELSE N'''' END + N'';''
FROM {#SOURCEDB_Q#}.sys.database_permissions dp
JOIN {#SOURCEDB_Q#}.sys.database_principals pr ON pr.principal_id=dp.grantee_principal_id
LEFT JOIN {#SOURCEDB_Q#}.sys.objects obj ON obj.object_id=dp.major_id
LEFT JOIN {#SOURCEDB_Q#}.sys.schemas sc ON sc.schema_id=obj.schema_id
LEFT JOIN {#SOURCEDB_Q#}.sys.schemas s ON s.schema_id=dp.major_id AND dp.class=3
LEFT JOIN {#SOURCEDB_Q#}.sys.columns col ON col.object_id=dp.major_id AND dp.class=4 AND col.column_id=dp.minor_id
LEFT JOIN {#TARGETDB_Q#}.sys.database_principals tpr ON tpr.name{#COLLATION#}=pr.name{#COLLATION#}
LEFT JOIN {#TARGETDB_Q#}.sys.schemas tsc ON tsc.name{#COLLATION#}=sc.name{#COLLATION#}
LEFT JOIN {#TARGETDB_Q#}.sys.objects tobj ON tobj.name{#COLLATION#}=obj.name{#COLLATION#} AND tobj.schema_id=tsc.schema_id
LEFT JOIN {#TARGETDB_Q#}.sys.columns tcol ON tcol.object_id=tobj.object_id AND tcol.name{#COLLATION#}=col.name{#COLLATION#}
LEFT JOIN {#TARGETDB_Q#}.sys.database_permissions tdp ON tdp.grantee_principal_id=tpr.principal_id
        AND tdp.permission_name{#COLLATION#}=dp.permission_name{#COLLATION#}
        AND tdp.class=dp.class
        AND ISNULL(tdp.state_desc,N''''){#COLLATION#}=ISNULL(dp.state_desc,N''''){#COLLATION#}
        AND (
                (dp.class=0 AND tdp.major_id=0)
             OR (dp.class=3 AND tdp.major_id=tsc.schema_id)
             OR (dp.class=1 AND tdp.major_id=tobj.object_id)
             OR (dp.class=4 AND tdp.major_id=tobj.object_id AND tdp.minor_id=tcol.column_id)
            )
WHERE pr.name{#COLLATION#} NOT IN (N''dbo'',N''guest'',N''INFORMATION_SCHEMA'',N''sys'',N''public'')
    AND dp.permission_name NOT IN (N''CONTROL'')
    AND dp.state_desc IN (''GRANT'',''GRANT_WITH_GRANT_OPTION'',''DENY'')
    AND tdp.major_id IS NULL;';
                SET @DYNAMICSQL = REPLACE(REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);
                IF @DEBUG=1 BEGIN
                    DECLARE @DbgPos INT = 1, @DbgChunk INT = 4000;
                    PRINT '===== DEBUG Security-Grants DynamicSql (length: ' + CONVERT(NVARCHAR(10), LEN(@DYNAMICSQL)) + '):';
                    WHILE @DbgPos <= LEN(@DYNAMICSQL) BEGIN
                        PRINT SUBSTRING(@DYNAMICSQL, @DbgPos, @DbgChunk);
                        SET @DbgPos = @DbgPos + @DbgChunk;
                    END;
                END;
                BEGIN TRY
                    EXEC sys.sp_executesql @DYNAMICSQL;
    IF @DEBUG = 1 PRINT '[DEBUG] Security-Grants commands built successfully';
                END TRY
                BEGIN CATCH
                    PRINT '[ERROR] Failed to build Security-Grants commands!';
                    PRINT '[ERROR] Error Message: ' + ERROR_MESSAGE();
                    PRINT '[ERROR] Dynamic SQL (first 8000 chars):';
                    PRINT SUBSTRING(@DYNAMICSQL, 1, 8000);
                    IF LEN(@DYNAMICSQL) > 8000 PRINT '[ERROR] (continued): ' + SUBSTRING(@DYNAMICSQL, 8001, 8000);
                    THROW;
                END CATCH;
        END;
        -----------------------------------------------------------------------------------------------------------------------------


        --=================================================-- Schemas (nur anlegen wenn fehlend) --================================================--
        IF @SYNC_SCHEMAS = 1 BEGIN
        IF @DEBUG = 1 PRINT '[DEBUG] Building Schemas commands...';
                SET @DYNAMICSQL = N'
INSERT INTO #TBDD_SYNC_DATABASE_COMMANDS(Phase, Command)
SELECT N''02-Schemas'',
       N''USE {#TARGETDB_Q#}; IF NOT EXISTS (SELECT 1 FROM sys.schemas WHERE name = N'''''' + REPLACE(s.name, '''''''', '''''''''''') + N'''''' ) BEGIN EXEC(N''''CREATE SCHEMA '' + QUOTENAME(s.name) + N'';''''); END;''
FROM {#SOURCEDB_Q#}.sys.schemas s
WHERE s.name{#COLLATION#} NOT IN (N''sys'',N''INFORMATION_SCHEMA'',N''dbo'',N''guest'',N''db_owner'',N''db_accessadmin'',N''db_securityadmin'',N''db_ddladmin'',N''db_backupoperator'',N''db_datareader'',N''db_datawriter'',N''db_denydatareader'',N''db_denydatawriter'')
    AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard=1 AND s.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#}) OR (ex.IsWildcard=0 AND s.name{#COLLATION#}=ex.Pattern{#COLLATION#}));';
                SET @DYNAMICSQL = REPLACE(REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);
                BEGIN TRY
                    EXEC sys.sp_executesql @DYNAMICSQL;
    IF @DEBUG = 1 PRINT '[DEBUG] Schemas commands built successfully';
                END TRY
                BEGIN CATCH
                    PRINT '[ERROR] Failed to build Schemas commands!';
                    PRINT '[ERROR] Error Message: ' + ERROR_MESSAGE();
                    PRINT '[ERROR] Dynamic SQL (first 8000 chars):';
                    PRINT SUBSTRING(@DYNAMICSQL, 1, 8000);
                    IF LEN(@DYNAMICSQL) > 8000 PRINT '[ERROR] (continued): ' + SUBSTRING(@DYNAMICSQL, 8001, 8000);
                    THROW;
                END CATCH;
        END ELSE PRINT '[INFO] Skipping Schemas (disabled by @pSYNCOBJECTS)';
    -----------------------------------------------------------------------------------------------------------------------------

        --=================================================-- Tables anlegen (fehlend) --================================================--
        IF @SYNC_TABLES = 1 BEGIN
        IF @DEBUG = 1 PRINT '[DEBUG] Building Tables commands...';
        SET @DYNAMICSQL = N'
INSERT INTO #TBDD_SYNC_DATABASE_COMMANDS(Phase, Command)
SELECT N''03-Tables'',
       CASE WHEN tgt.object_id IS NULL THEN
            N''USE {#TARGETDB_Q#}; CREATE TABLE '' + QUOTENAME(sc.name) + N''.'' + QUOTENAME(t.name) + N''('' +
            STUFF((
                SELECT N'','' + CHAR(10) + N''  '' +
                       CASE WHEN c.is_computed = 1 THEN QUOTENAME(c.name) + N'' AS '' + cc.definition + CASE WHEN cc.is_persisted=1 THEN N'' PERSISTED'' ELSE N'''' END
                            ELSE QUOTENAME(c.name) + N'' '' +
                                 CASE WHEN ty.is_user_defined=1 THEN QUOTENAME(tys.name) + N''.'' + QUOTENAME(ty.name)
                                      WHEN ty.name{#COLLATION#} IN (N''varchar'',N''char'',N''varbinary'',N''binary'') THEN ty.name + N''('' + CASE WHEN c.max_length=-1 THEN N''max'' ELSE CONVERT(nvarchar(10), c.max_length) END + N'')''
                                      WHEN ty.name{#COLLATION#} IN (N''nvarchar'',N''nchar'') THEN ty.name + N''('' + CASE WHEN c.max_length=-1 THEN N''max'' ELSE CONVERT(nvarchar(10), c.max_length/2) END + N'')''
                                      WHEN ty.name{#COLLATION#} IN (N''decimal'',N''numeric'') THEN ty.name + N''('' + CONVERT(nvarchar(10), c.precision) + N'','' + CONVERT(nvarchar(10), c.scale) + N'')''
                                      WHEN ty.name{#COLLATION#} IN (N''datetime2'',N''datetimeoffset'',N''time'') THEN ty.name + N''('' + CONVERT(nvarchar(10), c.scale) + N'')''
                                      ELSE ty.name END +
                                 CASE WHEN ty.is_user_defined=0 AND ty.name{#COLLATION#} IN (N''char'',N''varchar'',N''nchar'',N''nvarchar'',N''text'',N''ntext'') THEN N'' COLLATE {#TARGET_COLLATION_NAME#}'' ELSE N'''' END +
                                 CASE WHEN ic.object_id IS NOT NULL THEN N'' IDENTITY('' + CONVERT(nvarchar(30), ic.seed_value) + N'','' + CONVERT(nvarchar(30), ic.increment_value) + N'')'' ELSE N'''' END +
                                 CASE WHEN c.is_nullable=1 THEN N'' NULL'' ELSE N'' NOT NULL'' END
                       END
                FROM {#SOURCEDB_Q#}.sys.columns c
                LEFT JOIN {#SOURCEDB_Q#}.sys.identity_columns ic ON ic.object_id=c.object_id AND ic.column_id=c.column_id
                LEFT JOIN {#SOURCEDB_Q#}.sys.computed_columns cc ON cc.object_id=c.object_id AND cc.column_id=c.column_id
                JOIN {#SOURCEDB_Q#}.sys.types ty ON ty.user_type_id=c.user_type_id
                LEFT JOIN {#SOURCEDB_Q#}.sys.schemas tys ON tys.schema_id=ty.schema_id
                WHERE c.object_id=t.object_id
                ORDER BY c.column_id
                FOR XML PATH(''''), TYPE).value(''.'', ''nvarchar(max)''),1,2,N'''') + N''
);''
       ELSE
            N''-- Table structure differs: '' + QUOTENAME(sc.name) + N''.'' + QUOTENAME(t.name) + N'' (manual review required)''
       END
FROM {#SOURCEDB_Q#}.sys.tables t
JOIN {#SOURCEDB_Q#}.sys.schemas sc ON sc.schema_id=t.schema_id
LEFT JOIN {#TARGETDB_Q#}.sys.schemas tsc ON tsc.name{#COLLATION#}=sc.name{#COLLATION#}
LEFT JOIN {#TARGETDB_Q#}.sys.tables tt ON tt.name{#COLLATION#}=t.name{#COLLATION#} AND tt.schema_id = tsc.schema_id
LEFT JOIN {#TARGETDB_Q#}.sys.objects tgt ON tgt.object_id = tt.object_id
WHERE sc.name{#COLLATION#}<>N''sys''{#COLLATION#} AND t.is_ms_shipped=0
  AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard=1 AND (t.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (sc.name + N''.'' + t.name){#COLLATION#} LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard=0 AND (t.name{#COLLATION#}=ex.Pattern{#COLLATION#} OR (sc.name + N''.'' + t.name){#COLLATION#}=ex.Pattern{#COLLATION#})));';
        SET @DYNAMICSQL = REPLACE(REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);
        SET @DYNAMICSQL = REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDB, @SOURCEDATABASE), @PLACEHOLDERTARGETDB, @TARGETDATABASE);
        SET @DYNAMICSQL = REPLACE(@DYNAMICSQL, @PLACEHOLDERTARGETCOLLATION, @TARGETCOLLATION);

        BEGIN TRY
            EXEC sys.sp_executesql @DYNAMICSQL;
    IF @DEBUG = 1 PRINT '[DEBUG] Tables commands executed successfully';
        END TRY
        BEGIN CATCH
            PRINT '[ERROR] Failed to execute Tables command!';
            PRINT '[ERROR] Error Message: ' + ERROR_MESSAGE();
            PRINT '[ERROR] Error Line: ' + CONVERT(NVARCHAR(10), ERROR_LINE());
            PRINT '[ERROR] Dynamic SQL (first 8000 chars):';
            PRINT SUBSTRING(@DYNAMICSQL, 1, 8000);
            IF LEN(@DYNAMICSQL) > 8000 BEGIN
                PRINT '[ERROR] Dynamic SQL (continuation):';
                PRINT SUBSTRING(@DYNAMICSQL, 8001, 8000);
            END;
            THROW;
        END CATCH;
    -----------------------------------------------------------------------------------------------------------------------------

        --=================================================-- Tabellen-ALTER: fehlende Spalten anlegen --================================================--
        SET @DYNAMICSQL = N'
INSERT INTO #TBDD_SYNC_DATABASE_COMMANDS(Phase, Command)
SELECT N''03-Table-AddColumns'',
             N''USE {#TARGETDB_Q#}; ALTER TABLE ''+QUOTENAME(sc.name)+N''.''{#COLLATION#}+QUOTENAME(t.name)+N'' ADD ''+
             CASE WHEN c.is_computed=1 THEN QUOTENAME(c.name) + N'' AS '' + cc.definition + CASE WHEN cc.is_persisted=1 THEN N'' PERSISTED'' ELSE N'''' END
                        ELSE QUOTENAME(c.name) + N'' '' +
                                 CASE WHEN ty.is_user_defined=1 THEN QUOTENAME(tys.name) + N''.'' + QUOTENAME(ty.name)
                                            WHEN ty.name{#COLLATION#} IN (N''varchar'',N''char'',N''varbinary'',N''binary'') THEN ty.name + N''('' + CASE WHEN c.max_length=-1 THEN N''max'' ELSE CONVERT(nvarchar(10), c.max_length) END + N'')''
                                            WHEN ty.name{#COLLATION#} IN (N''nvarchar'',N''nchar'') THEN ty.name + N''('' + CASE WHEN c.max_length=-1 THEN N''max'' ELSE CONVERT(nvarchar(10), c.max_length/2) END + N'')''
                                            WHEN ty.name{#COLLATION#} IN (N''decimal'',N''numeric'') THEN ty.name + N''('' + CONVERT(nvarchar(10), c.precision) + N'','' + CONVERT(nvarchar(10), c.scale) + N'')''
                                            WHEN ty.name{#COLLATION#} IN (N''datetime2'',N''datetimeoffset'',N''time'') THEN ty.name + N''('' + CONVERT(nvarchar(10), c.scale) + N'')''
                                            ELSE ty.name END +
                                 CASE WHEN ty.is_user_defined=0 AND ty.name{#COLLATION#} IN (N''char'',N''varchar'',N''nchar'',N''nvarchar'',N''text'',N''ntext'') THEN N'' COLLATE {#TARGET_COLLATION_NAME#}'' ELSE N'''' END +
                                 CASE WHEN c.is_sparse=1 THEN N'' SPARSE'' ELSE N'''' END +
                                 CASE WHEN ic.object_id IS NOT NULL THEN N'' IDENTITY('' + CONVERT(nvarchar(30), ic.seed_value) + N'','' + CONVERT(nvarchar(30), ic.increment_value) + N'')'' ELSE N'''' END +
                                 CASE WHEN c.is_nullable=1 THEN N'' NULL'' ELSE N'' NOT NULL'' END
             END
FROM {#SOURCEDB_Q#}.sys.tables t
JOIN {#SOURCEDB_Q#}.sys.schemas sc ON sc.schema_id=t.schema_id
JOIN {#SOURCEDB_Q#}.sys.columns c ON c.object_id=t.object_id
LEFT JOIN {#SOURCEDB_Q#}.sys.identity_columns ic ON ic.object_id=c.object_id AND ic.column_id=c.column_id
LEFT JOIN {#SOURCEDB_Q#}.sys.computed_columns cc ON cc.object_id=c.object_id AND cc.column_id=c.column_id
JOIN {#SOURCEDB_Q#}.sys.types ty ON ty.user_type_id=c.user_type_id
LEFT JOIN {#SOURCEDB_Q#}.sys.schemas tys ON tys.schema_id=ty.schema_id
LEFT JOIN {#TARGETDB_Q#}.sys.schemas tsc ON tsc.name{#COLLATION#}=sc.name{#COLLATION#}
LEFT JOIN {#TARGETDB_Q#}.sys.tables tt ON tt.name{#COLLATION#}=t.name{#COLLATION#} AND tt.schema_id=tsc.schema_id
LEFT JOIN {#TARGETDB_Q#}.sys.columns tc ON tc.object_id=tt.object_id AND tc.name{#COLLATION#}=c.name{#COLLATION#}
WHERE sc.name{#COLLATION#}<>N''sys''{#COLLATION#} AND t.is_ms_shipped=0
    AND tt.object_id IS NOT NULL  -- Target table must exist
    AND tc.object_id IS NULL      -- Column must not exist
    AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard=1 AND (t.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (sc.name{#COLLATION#}+N''.''{#COLLATION#}+t.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard=0 AND (t.name{#COLLATION#}=ex.Pattern{#COLLATION#} OR (sc.name{#COLLATION#}+N''.''{#COLLATION#}+t.name{#COLLATION#})=ex.Pattern{#COLLATION#})));';
        SET @DYNAMICSQL = REPLACE(REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);
        SET @DYNAMICSQL = REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDB, @SOURCEDATABASE), @PLACEHOLDERTARGETDB, @TARGETDATABASE);
        SET @DYNAMICSQL = REPLACE(@DYNAMICSQL, @PLACEHOLDERTARGETCOLLATION, @TARGETCOLLATION);

        BEGIN TRY
            EXEC sys.sp_executesql @DYNAMICSQL;
    IF @DEBUG = 1 PRINT '[DEBUG] Table-AddColumns commands executed successfully';
        END TRY
        BEGIN CATCH
            PRINT '[ERROR] Failed to execute Table-AddColumns command!';
            PRINT '[ERROR] Error Message: ' + ERROR_MESSAGE();
            PRINT '[ERROR] Dynamic SQL (first 8000 chars):';
            PRINT SUBSTRING(@DYNAMICSQL, 1, 8000);
            THROW;
        END CATCH;
        -----------------------------------------------------------------------------------------------------------------------------

        --=================================================-- Table ALTER: Modify columns (Type/Nullability/Collation/Sparse) --================================================--
        SET @DYNAMICSQL = N'
INSERT INTO #TBDD_SYNC_DATABASE_COMMANDS(Phase, Command)
SELECT N''03-Table-AlterColumns'',
             N''USE {#TARGETDB_Q#}; ALTER TABLE ''+QUOTENAME(sc.name)+N''.''{#COLLATION#}+QUOTENAME(t.name)+N'' ALTER COLUMN ''+
             QUOTENAME(c.name) + N'' ''+
             CASE WHEN ty.is_user_defined=1 THEN QUOTENAME(tys.name) + N''.'' + QUOTENAME(ty.name)
                        WHEN ty.name{#COLLATION#} IN (N''varchar'',N''char'',N''varbinary'',N''binary'') THEN ty.name + N''('' + CASE WHEN c.max_length=-1 THEN N''max'' ELSE CONVERT(nvarchar(10), c.max_length) END + N'')''
                        WHEN ty.name{#COLLATION#} IN (N''nvarchar'',N''nchar'') THEN ty.name + N''('' + CASE WHEN c.max_length=-1 THEN N''max'' ELSE CONVERT(nvarchar(10), c.max_length/2) END + N'')''
                        WHEN ty.name{#COLLATION#} IN (N''decimal'',N''numeric'') THEN ty.name + N''('' + CONVERT(nvarchar(10), c.precision) + N'','' + CONVERT(nvarchar(10), c.scale) + N'')''
                        WHEN ty.name{#COLLATION#} IN (N''datetime2'',N''datetimeoffset'',N''time'') THEN ty.name + N''('' + CONVERT(nvarchar(10), c.scale) + N'')''
                        ELSE ty.name END +
             CASE WHEN ty.is_user_defined=0 AND ty.name{#COLLATION#} IN (N''char'',N''varchar'',N''nchar'',N''nvarchar'',N''text'',N''ntext'') THEN N'' COLLATE {#TARGET_COLLATION_NAME#}'' ELSE N'''' END +
             CASE WHEN c.is_sparse=1 THEN N'' SPARSE'' ELSE N'''' END +
             CASE WHEN c.is_nullable=1 THEN N'' NULL'' ELSE N'' NOT NULL'' END
FROM {#SOURCEDB_Q#}.sys.tables t
JOIN {#SOURCEDB_Q#}.sys.schemas sc ON sc.schema_id=t.schema_id
JOIN {#SOURCEDB_Q#}.sys.columns c ON c.object_id=t.object_id
JOIN {#SOURCEDB_Q#}.sys.types ty ON ty.user_type_id=c.user_type_id
LEFT JOIN {#SOURCEDB_Q#}.sys.schemas tys ON tys.schema_id=ty.schema_id
LEFT JOIN {#SOURCEDB_Q#}.sys.identity_columns ic_src ON ic_src.object_id=c.object_id AND ic_src.column_id=c.column_id
LEFT JOIN {#TARGETDB_Q#}.sys.schemas tsc ON tsc.name{#COLLATION#}=sc.name{#COLLATION#}
LEFT JOIN {#TARGETDB_Q#}.sys.tables tt ON tt.name{#COLLATION#}=t.name{#COLLATION#} AND tt.schema_id=tsc.schema_id
LEFT JOIN {#TARGETDB_Q#}.sys.columns tc ON tc.object_id=tt.object_id AND tc.name{#COLLATION#}=c.name{#COLLATION#}
LEFT JOIN {#TARGETDB_Q#}.sys.types tty ON tty.user_type_id=tc.user_type_id
WHERE sc.name{#COLLATION#}<>N''sys''{#COLLATION#} AND t.is_ms_shipped=0 AND c.is_computed=0 AND tc.object_id IS NOT NULL
    AND ic_src.object_id IS NULL
    AND (
                c.is_nullable<>tc.is_nullable OR c.is_sparse<>tc.is_sparse OR c.max_length<>tc.max_length OR c.precision<>tc.precision OR c.scale<>tc.scale OR ty.name{#COLLATION#}<>tty.name{#COLLATION#} OR ty.is_user_defined<>tty.is_user_defined
            )
    AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard=1 AND (t.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (sc.name{#COLLATION#}+N''.''{#COLLATION#}+t.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard=0 AND (t.name{#COLLATION#}=ex.Pattern{#COLLATION#} OR (sc.name{#COLLATION#}+N''.''{#COLLATION#}+t.name{#COLLATION#})=ex.Pattern{#COLLATION#})));';
        SET @DYNAMICSQL = REPLACE(REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);
        SET @DYNAMICSQL = REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDB, @SOURCEDATABASE), @PLACEHOLDERTARGETDB, @TARGETDATABASE);
        SET @DYNAMICSQL = REPLACE(@DYNAMICSQL, @PLACEHOLDERTARGETCOLLATION, @TARGETCOLLATION);
        BEGIN TRY
            EXEC sys.sp_executesql @DYNAMICSQL;
    IF @DEBUG = 1 PRINT '[DEBUG] Table-AlterColumns commands executed successfully';
        END TRY
        BEGIN CATCH
            PRINT '[ERROR] Failed to execute Table-AlterColumns command!';
            PRINT '[ERROR] Error Message: ' + ERROR_MESSAGE();
            PRINT '[ERROR] Dynamic SQL (first 8000 chars):';
            PRINT SUBSTRING(@DYNAMICSQL, 1, 8000);
            IF LEN(@DYNAMICSQL) > 8000 PRINT '[ERROR] (continued): ' + SUBSTRING(@DYNAMICSQL, 8001, 8000);
            THROW;
        END CATCH;
        -----------------------------------------------------------------------------------------------------------------------------

        --=================================================-- Tabellen-ALTER: Default Constraints synchronisieren --================================================--
        SET @DYNAMICSQL = N'
INSERT INTO #TBDD_SYNC_DATABASE_COMMANDS(Phase, Command)
SELECT N''03-Table-Defaults'',
             N''USE {#TARGETDB_Q#}; '' +
             CASE WHEN tgtDC.object_id IS NOT NULL THEN N''ALTER TABLE ''+QUOTENAME(sc.name)+N''.''{#COLLATION#}+QUOTENAME(t.name)+N'' DROP CONSTRAINT ''+QUOTENAME(tgtDC.name)+N''; '' ELSE N'''' END +
             CASE WHEN srcdc.name IS NOT NULL THEN N''ALTER TABLE ''+QUOTENAME(sc.name)+N''.''{#COLLATION#}+QUOTENAME(t.name)+N'' ADD CONSTRAINT ''+QUOTENAME(srcdc.name)+N'' DEFAULT ''+ISNULL(srcdc.definition,N'''')+N'' FOR ''+QUOTENAME(c.name)+N'';'' ELSE N'''' END
FROM {#SOURCEDB_Q#}.sys.tables t
JOIN {#SOURCEDB_Q#}.sys.schemas sc ON sc.schema_id=t.schema_id
JOIN {#SOURCEDB_Q#}.sys.columns c ON c.object_id=t.object_id
LEFT JOIN {#SOURCEDB_Q#}.sys.default_constraints srcdc ON srcdc.parent_object_id=c.object_id AND srcdc.parent_column_id=c.column_id
LEFT JOIN {#TARGETDB_Q#}.sys.schemas tsc ON tsc.name{#COLLATION#}=sc.name{#COLLATION#}
LEFT JOIN {#TARGETDB_Q#}.sys.tables tt ON tt.name{#COLLATION#}=t.name{#COLLATION#} AND tt.schema_id=tsc.schema_id
LEFT JOIN {#TARGETDB_Q#}.sys.columns tc ON tc.object_id=tt.object_id AND tc.name{#COLLATION#}=c.name{#COLLATION#}
LEFT JOIN {#TARGETDB_Q#}.sys.default_constraints tgtDC ON tgtDC.parent_object_id=tc.object_id AND tgtDC.parent_column_id=tc.column_id
WHERE sc.name{#COLLATION#}<>N''sys''{#COLLATION#} AND t.is_ms_shipped=0 AND c.is_computed=0 AND tc.object_id IS NOT NULL
    AND ( (srcdc.definition IS NOT NULL AND (tgtDC.definition IS NULL OR tgtDC.definition{#COLLATION#}<>srcdc.definition{#COLLATION#}))
                OR (srcdc.definition IS NULL AND tgtDC.definition IS NOT NULL) )
    AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard=1 AND (t.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (sc.name{#COLLATION#}+N''.''{#COLLATION#}+t.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard=0 AND (t.name{#COLLATION#}=ex.Pattern{#COLLATION#} OR (sc.name{#COLLATION#}+N''.''{#COLLATION#}+t.name{#COLLATION#})=ex.Pattern{#COLLATION#})));';
        SET @DYNAMICSQL = REPLACE(REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);
        SET @DYNAMICSQL = REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDB, @SOURCEDATABASE), @PLACEHOLDERTARGETDB, @TARGETDATABASE);
        BEGIN TRY
            EXEC sys.sp_executesql @DYNAMICSQL;
    IF @DEBUG = 1 PRINT '[DEBUG] Table-Defaults commands executed successfully';
        END TRY
        BEGIN CATCH
            PRINT '[ERROR] Failed to execute Table-Defaults command!';
            PRINT '[ERROR] Error Message: ' + ERROR_MESSAGE();
            PRINT '[ERROR] Dynamic SQL (first 8000 chars):';
            PRINT SUBSTRING(@DYNAMICSQL, 1, 8000);
            IF LEN(@DYNAMICSQL) > 8000 PRINT '[ERROR] (continued): ' + SUBSTRING(@DYNAMICSQL, 8001, 8000);
            THROW;
        END CATCH;
        -----------------------------------------------------------------------------------------------------------------------------

        --=================================================-- Tabellen-ALTER: Check Constraints synchronisieren --================================================--
        SET @DYNAMICSQL = N'
INSERT INTO #TBDD_SYNC_DATABASE_COMMANDS(Phase, Command)
SELECT N''03-Table-Checks'',
             N''USE {#TARGETDB_Q#}; '' + CASE WHEN tgtck.object_id IS NOT NULL THEN N''ALTER TABLE ''+QUOTENAME(sc.name)+N''.''{#COLLATION#}+QUOTENAME(t.name)+N'' DROP CONSTRAINT ''+QUOTENAME(tgtck.name)+N''; '' ELSE N'''' END +
             N''ALTER TABLE ''+QUOTENAME(sc.name)+N''.''{#COLLATION#}+QUOTENAME(t.name)+N'' WITH CHECK ADD CONSTRAINT ''+QUOTENAME(ck.name)+N'' CHECK ''+ISNULL(ck.definition,N'''')
FROM {#SOURCEDB_Q#}.sys.check_constraints ck
JOIN {#SOURCEDB_Q#}.sys.tables t ON t.object_id=ck.parent_object_id
JOIN {#SOURCEDB_Q#}.sys.schemas sc ON sc.schema_id=t.schema_id
LEFT JOIN {#TARGETDB_Q#}.sys.schemas tsc ON tsc.name{#COLLATION#}=sc.name{#COLLATION#}
LEFT JOIN {#TARGETDB_Q#}.sys.tables tt ON tt.name{#COLLATION#}=t.name{#COLLATION#} AND tt.schema_id=tsc.schema_id
LEFT JOIN {#TARGETDB_Q#}.sys.check_constraints tgtck ON tgtck.parent_object_id=tt.object_id AND tgtck.name{#COLLATION#}=ck.name{#COLLATION#}
WHERE sc.name{#COLLATION#}<>N''sys''{#COLLATION#} AND t.is_ms_shipped=0
    AND (tgtck.object_id IS NULL OR tgtck.definition{#COLLATION#}<>ck.definition{#COLLATION#})
    AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard=1 AND (t.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (sc.name{#COLLATION#}+N''.''{#COLLATION#}+t.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard=0 AND (t.name{#COLLATION#}=ex.Pattern{#COLLATION#} OR (sc.name{#COLLATION#}+N''.''{#COLLATION#}+t.name{#COLLATION#})=ex.Pattern{#COLLATION#})));';
        SET @DYNAMICSQL = REPLACE(REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);
        SET @DYNAMICSQL = REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDB, @SOURCEDATABASE), @PLACEHOLDERTARGETDB, @TARGETDATABASE);
        BEGIN TRY
            EXEC sys.sp_executesql @DYNAMICSQL;
    IF @DEBUG = 1 PRINT '[DEBUG] Table-Checks commands executed successfully';
        END TRY
        BEGIN CATCH
            PRINT '[ERROR] Failed to execute Table-Checks command!';
            PRINT '[ERROR] Error Message: ' + ERROR_MESSAGE();
            PRINT '[ERROR] Dynamic SQL (first 8000 chars):';
            PRINT SUBSTRING(@DYNAMICSQL, 1, 8000);
            IF LEN(@DYNAMICSQL) > 8000 PRINT '[ERROR] (continued): ' + SUBSTRING(@DYNAMICSQL, 8001, 8000);
            THROW;
        END CATCH;
        -----------------------------------------------------------------------------------------------------------------------------

        --=================================================-- Tabellen-ALTER: Unique Constraints synchronisieren --================================================--
        SET @DYNAMICSQL = N'
INSERT INTO #TBDD_SYNC_DATABASE_COMMANDS(Phase, Command)
SELECT * FROM (
SELECT N''03-Table-Unique'',
             N''USE {#TARGETDB_Q#}; '' + CASE WHEN tgtuq.object_id IS NOT NULL THEN N''ALTER TABLE ''+QUOTENAME(sc.name)+N''.''{#COLLATION#}+QUOTENAME(t.name)+N'' DROP CONSTRAINT ''+QUOTENAME(tgtuq.name)+N''; '' ELSE N'''' END +
             N''ALTER TABLE ''+QUOTENAME(sc.name)+N''.''{#COLLATION#}+QUOTENAME(t.name)+N'' ADD CONSTRAINT ''+QUOTENAME(uq.name)+N'' UNIQUE '' + CASE WHEN i.type=1 THEN N''CLUSTERED '' ELSE N''NONCLUSTERED '' END +
             N''(''+STUFF((SELECT '',''{#COLLATION#}+QUOTENAME(cs.name)
                                        FROM {#SOURCEDB_Q#}.sys.index_columns ic
                                        JOIN {#SOURCEDB_Q#}.sys.columns cs ON cs.object_id=ic.object_id AND cs.column_id=ic.column_id
                                        WHERE ic.object_id=i.object_id AND ic.index_id=i.index_id
                                        ORDER BY ic.key_ordinal
                                        FOR XML PATH(''''), TYPE).value(''.'',''nvarchar(max)''),1,1,N'''')+N'')''
FROM {#SOURCEDB_Q#}.sys.key_constraints uq
JOIN {#SOURCEDB_Q#}.sys.tables t ON t.object_id=uq.parent_object_id
JOIN {#SOURCEDB_Q#}.sys.schemas sc ON sc.schema_id=t.schema_id
JOIN {#SOURCEDB_Q#}.sys.indexes i ON i.object_id=uq.parent_object_id AND i.index_id=uq.unique_index_id
LEFT JOIN {#TARGETDB_Q#}.sys.schemas tsc ON tsc.name{#COLLATION#}=sc.name{#COLLATION#}
LEFT JOIN {#TARGETDB_Q#}.sys.tables tt ON tt.name{#COLLATION#}=t.name{#COLLATION#} AND tt.schema_id=tsc.schema_id
LEFT JOIN {#TARGETDB_Q#}.sys.key_constraints tgtuq ON tgtuq.parent_object_id=tt.object_id AND tgtuq.name{#COLLATION#}=uq.name{#COLLATION#} AND tgtuq.type{#COLLATION#}=''UQ''{#COLLATION#}
OUTER APPLY (
                SELECT HASHBYTES(''SHA2_256'',
                                (SELECT cs.name{#COLLATION#}, ic.key_ordinal
                                 FROM {#SOURCEDB_Q#}.sys.index_columns ic
                                 JOIN {#SOURCEDB_Q#}.sys.columns cs ON cs.object_id=ic.object_id AND cs.column_id=ic.column_id
                                 WHERE ic.object_id=i.object_id AND ic.index_id=i.index_id
                                 ORDER BY ic.key_ordinal
                                 FOR XML PATH(''''), TYPE).value(''.'',''nvarchar(max)''){#COLLATION#}) AS src_hash
) hsrc
OUTER APPLY (
                SELECT HASHBYTES(''SHA2_256'',
                                (SELECT cs.name{#COLLATION#}, ic.key_ordinal
                                 FROM {#TARGETDB_Q#}.sys.index_columns ic
                                 JOIN {#TARGETDB_Q#}.sys.columns cs ON cs.object_id=ic.object_id AND cs.column_id=ic.column_id
                                 WHERE ic.object_id=tgtuq.parent_object_id AND ic.index_id=tgtuq.unique_index_id
                                 ORDER BY ic.key_ordinal
                                 FOR XML PATH(''''), TYPE).value(''.'',''nvarchar(max)''){#COLLATION#}) AS tgt_hash
) htgt
WHERE uq.type{#COLLATION#}=''UQ''{#COLLATION#} AND sc.name{#COLLATION#}<>N''sys''{#COLLATION#}
    AND (tgtuq.object_id IS NULL OR hsrc.src_hash<>htgt.tgt_hash)
    AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard=1 AND (t.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (sc.name{#COLLATION#}+N''.''{#COLLATION#}+t.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard=0 AND (t.name{#COLLATION#}=ex.Pattern{#COLLATION#} OR (sc.name{#COLLATION#}+N''.''{#COLLATION#}+t.name{#COLLATION#})=ex.Pattern{#COLLATION#})))
) AS X(Phase, Command)
WHERE X.Command IS NOT NULL AND LEN(X.Command)>0;';
        SET @DYNAMICSQL = REPLACE(REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);
        SET @DYNAMICSQL = REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDB, @SOURCEDATABASE), @PLACEHOLDERTARGETDB, @TARGETDATABASE);
        BEGIN TRY
            EXEC sys.sp_executesql @DYNAMICSQL;
    IF @DEBUG = 1 PRINT '[DEBUG] Table-Unique commands built successfully';
        END TRY
        BEGIN CATCH
            PRINT '[ERROR] Failed to build Table-Unique commands!';
            PRINT '[ERROR] Error Message: ' + ERROR_MESSAGE();
            PRINT '[ERROR] Dynamic SQL (first 8000 chars):';
            PRINT SUBSTRING(@DYNAMICSQL, 1, 8000);
            IF LEN(@DYNAMICSQL) > 8000 PRINT '[ERROR] (continued): ' + SUBSTRING(@DYNAMICSQL, 8001, 8000);
            THROW;
        END CATCH;
        END ELSE PRINT '[INFO] Skipping Tables (disabled by @pSYNCOBJECTS)';
        -----------------------------------------------------------------------------------------------------------------------------

        --=================================================-- Foreign Keys vor PK-Anpassung droppen (Target referenziert PK-Tabellen) --================================================--
        IF @SYNC_TABLES = 1 BEGIN -- FK-Drop for PK belongs to TABLES
        SET @DYNAMICSQL = N'
        INSERT INTO #TBDD_SYNC_DATABASE_COMMANDS(Phase, Command)
        SELECT * FROM (
        SELECT N''04-FK-DropForPK'',
            N''USE {#TARGETDB_Q#}; ALTER TABLE ''+QUOTENAME(ts.name)+N''.''{#COLLATION#}+QUOTENAME(tt.name)+N'' DROP CONSTRAINT ''+QUOTENAME(tfk.name)+N'';''
    FROM {#SOURCEDB_Q#}.sys.key_constraints pk
    JOIN {#SOURCEDB_Q#}.sys.tables st ON st.object_id=pk.parent_object_id
    JOIN {#SOURCEDB_Q#}.sys.schemas ss ON ss.schema_id=st.schema_id
    JOIN {#TARGETDB_Q#}.sys.schemas ts ON ts.name{#COLLATION#}=ss.name{#COLLATION#}
    JOIN {#TARGETDB_Q#}.sys.tables tt ON tt.name{#COLLATION#}=st.name{#COLLATION#} AND tt.schema_id=ts.schema_id
    JOIN {#TARGETDB_Q#}.sys.foreign_keys tfk ON tfk.referenced_object_id=tt.object_id
            WHERE pk.type{#COLLATION#}=''PK''{#COLLATION#} AND ss.name{#COLLATION#}<>N''sys''{#COLLATION#}
                AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard=1 AND (st.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (ss.name{#COLLATION#}+N''.''{#COLLATION#}+st.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard=0 AND (st.name{#COLLATION#}=ex.Pattern{#COLLATION#} OR (ss.name{#COLLATION#}+N''.''{#COLLATION#}+st.name{#COLLATION#})=ex.Pattern{#COLLATION#})))
            ) AS X(Phase, Command)
            WHERE X.Command IS NOT NULL AND LEN(X.Command)>0;';
        SET @DYNAMICSQL = REPLACE(REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);
        BEGIN TRY
            EXEC sys.sp_executesql @DYNAMICSQL;
    IF @DEBUG = 1 PRINT '[DEBUG] FK-DropForPK commands built successfully';
        END TRY
        BEGIN CATCH
            PRINT '[ERROR] Failed to build FK-DropForPK commands!';
            PRINT '[ERROR] Error Message: ' + ERROR_MESSAGE();
            PRINT '[ERROR] Dynamic SQL (first 8000 chars):';
            PRINT SUBSTRING(@DYNAMICSQL, 1, 8000);
            IF LEN(@DYNAMICSQL) > 8000 PRINT '[ERROR] (continued): ' + SUBSTRING(@DYNAMICSQL, 8001, 8000);
            THROW;
        END CATCH;
        END ELSE PRINT '[INFO] Skipping FK-DropForPK (disabled by @pSYNCOBJECTS)';

        --=================================================-- Primary Keys (Drop/Create bei Abweichungen) --================================================--
        IF @SYNC_TABLES = 1 BEGIN -- Primary Keys belongs to TABLES
        SET @DYNAMICSQL = N'
        INSERT INTO #TBDD_SYNC_DATABASE_COMMANDS(Phase, Command)
        SELECT * FROM (
        SELECT N''05-PrimaryKeys'',
              N''USE {#TARGETDB_Q#}; '' +
              CASE WHEN tp.object_id IS NOT NULL THEN N''ALTER TABLE ''+QUOTENAME(sc.name)+N''.''{#COLLATION#}+QUOTENAME(t.name)+'' DROP CONSTRAINT ''+QUOTENAME(tp.name)+''; '' ELSE N'''' END +
              N''ALTER TABLE ''+QUOTENAME(sc.name)+N''.''{#COLLATION#}+QUOTENAME(t.name)+'' ADD CONSTRAINT ''+QUOTENAME(pk.name)+'' PRIMARY KEY '' +
              CASE WHEN i.type=1 THEN N''CLUSTERED '' ELSE N''NONCLUSTERED '' END +
              N''('' +
              STUFF((SELECT '',''{#COLLATION#}+QUOTENAME(cs.name)
                  FROM {#SOURCEDB_Q#}.sys.index_columns ic
                  JOIN {#SOURCEDB_Q#}.sys.columns cs ON cs.object_id=ic.object_id AND cs.column_id=ic.column_id
                  WHERE ic.object_id=i.object_id AND ic.index_id=i.index_id
                  ORDER BY ic.key_ordinal
                  FOR XML PATH(''''), TYPE).value(''.'',''nvarchar(max)''),1,1,N'''') + N'')''
    FROM {#SOURCEDB_Q#}.sys.key_constraints pk
    JOIN {#SOURCEDB_Q#}.sys.tables t ON t.object_id=pk.parent_object_id
    JOIN {#SOURCEDB_Q#}.sys.schemas sc ON sc.schema_id=t.schema_id
    JOIN {#SOURCEDB_Q#}.sys.indexes i ON i.object_id=pk.parent_object_id AND i.index_id=pk.unique_index_id
    OUTER APPLY (
        SELECT tp.object_id, tp.name, tp.unique_index_id
        FROM {#TARGETDB_Q#}.sys.key_constraints tp
        JOIN {#TARGETDB_Q#}.sys.tables tt ON tt.object_id=tp.parent_object_id
        JOIN {#TARGETDB_Q#}.sys.schemas tsc ON tsc.schema_id=tt.schema_id
        WHERE tp.type{#COLLATION#}=''PK''{#COLLATION#} AND tsc.name{#COLLATION#}=sc.name{#COLLATION#} AND tt.name{#COLLATION#}=t.name{#COLLATION#}
    ) tp
    OUTER APPLY (
        SELECT HASHBYTES(''SHA2_256'',
            (SELECT cs.name{#COLLATION#}, ic.key_ordinal
             FROM {#SOURCEDB_Q#}.sys.index_columns ic
             JOIN {#SOURCEDB_Q#}.sys.columns cs ON cs.object_id=ic.object_id AND cs.column_id=ic.column_id
             WHERE ic.object_id=i.object_id AND ic.index_id=i.index_id
             ORDER BY ic.key_ordinal
             FOR XML PATH(''''), TYPE).value(''.'',''nvarchar(max)''){#COLLATION#}) AS src_hash
    ) hsrc
    OUTER APPLY (
        SELECT HASHBYTES(''SHA2_256'',
            (SELECT cs.name{#COLLATION#}, ic.key_ordinal
             FROM {#TARGETDB_Q#}.sys.index_columns ic
             JOIN {#TARGETDB_Q#}.sys.columns cs ON cs.object_id=ic.object_id AND cs.column_id=ic.column_id
             WHERE ic.object_id=tp.object_id AND ic.index_id=tp.unique_index_id
             ORDER BY ic.key_ordinal
             FOR XML PATH(''''), TYPE).value(''.'',''nvarchar(max)''){#COLLATION#}) AS tgt_hash
    ) htgt
    WHERE pk.type{#COLLATION#}=''PK''{#COLLATION#} AND sc.name{#COLLATION#}<>N''sys''{#COLLATION#}
        AND (tp.object_id IS NULL OR hsrc.src_hash<>htgt.tgt_hash)
        AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard=1 AND (t.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (sc.name{#COLLATION#}+N''.''{#COLLATION#}+t.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard=0 AND (t.name{#COLLATION#}=ex.Pattern{#COLLATION#} OR (sc.name{#COLLATION#}+N''.''{#COLLATION#}+t.name{#COLLATION#})=ex.Pattern{#COLLATION#})))
    ) AS X(Phase, Command)
    WHERE X.Command IS NOT NULL AND LEN(X.Command)>0;';
        SET @DYNAMICSQL = REPLACE(REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);
        SET @DYNAMICSQL = REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDB, @SOURCEDATABASE), @PLACEHOLDERTARGETDB, @TARGETDATABASE);
        BEGIN TRY
            EXEC sys.sp_executesql @DYNAMICSQL;
    IF @DEBUG = 1 PRINT '[DEBUG] PrimaryKeys commands built successfully';
        END TRY
        BEGIN CATCH
            PRINT '[ERROR] Failed to build PrimaryKeys commands!';
            PRINT '[ERROR] Error Message: ' + ERROR_MESSAGE();
            PRINT '[ERROR] Dynamic SQL (first 8000 chars):';
            PRINT SUBSTRING(@DYNAMICSQL, 1, 8000);
            IF LEN(@DYNAMICSQL) > 8000 PRINT '[ERROR] (continued): ' + SUBSTRING(@DYNAMICSQL, 8001, 8000);
            THROW;
        END CATCH;
        END ELSE PRINT '[INFO] Skipping Primary Keys (disabled by @pSYNCOBJECTS)';
        -----------------------------------------------------------------------------------------------------------------------------

        --=================================================-- Foreign Keys (fehlend oder abweichend -> Drop/Create) --================================================--
        IF @SYNC_TABLES = 1 BEGIN -- Foreign Keys belongs to TABLES
        SET @DYNAMICSQL = N'
     INSERT INTO #TBDD_SYNC_DATABASE_COMMANDS(Phase, Command)
     SELECT * FROM (
     SELECT N''06-ForeignKeys'',
              N''USE {#TARGETDB_Q#}; '' +
              CASE WHEN tfk.object_id IS NOT NULL THEN N''ALTER TABLE ''+QUOTENAME(ts.name)+N''.''{#COLLATION#}+QUOTENAME(t.name)+N'' DROP CONSTRAINT ''+QUOTENAME(tfk.name)+N''; '' ELSE N'''' END +
              N''ALTER TABLE ''+QUOTENAME(ts.name)+N''.''{#COLLATION#}+QUOTENAME(t.name)+
              N'' WITH CHECK ADD CONSTRAINT ''+QUOTENAME(fk.name)+N'' FOREIGN KEY (''+
              STUFF((SELECT '',''{#COLLATION#}+QUOTENAME(c.name)
                  FROM {#SOURCEDB_Q#}.sys.foreign_key_columns fkc
                  JOIN {#SOURCEDB_Q#}.sys.columns c ON c.object_id=fkc.parent_object_id AND c.column_id=fkc.parent_column_id
                  WHERE fkc.constraint_object_id=fk.object_id
                  ORDER BY fkc.constraint_column_id
                  FOR XML PATH(''''), TYPE).value(''.'',''nvarchar(max)''),1,1,N'''') + N'') REFERENCES ''+
              QUOTENAME(rs.name)+N''.''{#COLLATION#}+QUOTENAME(rt.name)+N''(''+
              STUFF((SELECT '',''{#COLLATION#}+QUOTENAME(rc.name)
                  FROM {#SOURCEDB_Q#}.sys.foreign_key_columns fkc
                  JOIN {#SOURCEDB_Q#}.sys.columns rc ON rc.object_id=fkc.referenced_object_id AND rc.column_id=fkc.referenced_column_id
                  WHERE fkc.constraint_object_id=fk.object_id
                  ORDER BY fkc.constraint_column_id
                  FOR XML PATH(''''), TYPE).value(''.'',''nvarchar(max)''),1,1,N'''') + N'')'' +
              CASE WHEN fk.delete_referential_action=1 THEN N'' ON DELETE CASCADE'' WHEN fk.delete_referential_action=2 THEN N'' ON DELETE SET NULL'' WHEN fk.delete_referential_action=3 THEN N'' ON DELETE SET DEFAULT'' ELSE N'''' END +
              CASE WHEN fk.update_referential_action=1 THEN N'' ON UPDATE CASCADE'' WHEN fk.update_referential_action=2 THEN N'' ON UPDATE SET NULL'' WHEN fk.update_referential_action=3 THEN N'' ON UPDATE SET DEFAULT'' ELSE N'''' END +
              CASE WHEN fk.is_not_for_replication=1 THEN N'' NOT FOR REPLICATION'' ELSE N'''' END + N'';''
    FROM {#SOURCEDB_Q#}.sys.foreign_keys fk
    JOIN {#SOURCEDB_Q#}.sys.tables t ON t.object_id=fk.parent_object_id
    JOIN {#SOURCEDB_Q#}.sys.tables rt ON rt.object_id=fk.referenced_object_id
    JOIN {#SOURCEDB_Q#}.sys.schemas ts ON ts.schema_id=t.schema_id
    JOIN {#SOURCEDB_Q#}.sys.schemas rs ON rs.schema_id=rt.schema_id
    LEFT JOIN {#TARGETDB_Q#}.sys.schemas tts ON tts.name{#COLLATION#}=ts.name{#COLLATION#}
    LEFT JOIN {#TARGETDB_Q#}.sys.tables tt ON tt.name{#COLLATION#}=t.name{#COLLATION#} AND tt.schema_id=tts.schema_id
    LEFT JOIN {#TARGETDB_Q#}.sys.foreign_keys tfk ON tfk.name{#COLLATION#}=fk.name{#COLLATION#} AND tfk.parent_object_id=ISNULL(tt.object_id, -1)
    OUTER APPLY (
        SELECT HASHBYTES(''SHA2_256'',
            (SELECT fkc.constraint_column_id, pc.name{#COLLATION#}, rc.name{#COLLATION#}, fk.delete_referential_action, fk.update_referential_action, fk.is_not_for_replication
             FROM {#SOURCEDB_Q#}.sys.foreign_key_columns fkc
             JOIN {#SOURCEDB_Q#}.sys.columns pc ON pc.object_id=fkc.parent_object_id AND pc.column_id=fkc.parent_column_id
             JOIN {#SOURCEDB_Q#}.sys.columns rc ON rc.object_id=fkc.referenced_object_id AND rc.column_id=fkc.referenced_column_id
             WHERE fkc.constraint_object_id=fk.object_id
             ORDER BY fkc.constraint_column_id
             FOR XML PATH(''''), TYPE).value(''.'',''nvarchar(max)''){#COLLATION#}) AS src_hash
    ) hsrc
    OUTER APPLY (
        SELECT HASHBYTES(''SHA2_256'',
            (SELECT fkc.constraint_column_id, pc.name{#COLLATION#}, rc.name{#COLLATION#}, tfk.delete_referential_action, tfk.update_referential_action, tfk.is_not_for_replication
             FROM {#TARGETDB_Q#}.sys.foreign_key_columns fkc
             JOIN {#TARGETDB_Q#}.sys.columns pc ON pc.object_id=fkc.parent_object_id AND pc.column_id=fkc.parent_column_id
             JOIN {#TARGETDB_Q#}.sys.columns rc ON rc.object_id=fkc.referenced_object_id AND rc.column_id=fkc.referenced_column_id
             WHERE fkc.constraint_object_id=tfk.object_id
             ORDER BY fkc.constraint_column_id
             FOR XML PATH(''''), TYPE).value(''.'',''nvarchar(max)''){#COLLATION#}) AS tgt_hash
    ) htgt
    WHERE fk.is_ms_shipped=0 AND ts.name{#COLLATION#}<>N''sys''{#COLLATION#}
        AND (tfk.object_id IS NULL OR hsrc.src_hash<>htgt.tgt_hash)
        AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard=1 AND (t.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (ts.name{#COLLATION#}+N''.''{#COLLATION#}+t.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard=0 AND (t.name{#COLLATION#}=ex.Pattern{#COLLATION#} OR (ts.name{#COLLATION#}+N''.''{#COLLATION#}+t.name{#COLLATION#})=ex.Pattern{#COLLATION#})))
    ) AS X(Phase, Command)
    WHERE X.Command IS NOT NULL AND LEN(X.Command)>0;';
        SET @DYNAMICSQL = REPLACE(REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);
        BEGIN TRY
            EXEC sys.sp_executesql @DYNAMICSQL;
    IF @DEBUG = 1 PRINT '[DEBUG] ForeignKeys commands built successfully';
        END TRY
        BEGIN CATCH
            PRINT '[ERROR] Failed to build ForeignKeys commands!';
            PRINT '[ERROR] Error Message: ' + ERROR_MESSAGE();
            PRINT '[ERROR] Dynamic SQL (first 8000 chars):';
            PRINT SUBSTRING(@DYNAMICSQL, 1, 8000);
            IF LEN(@DYNAMICSQL) > 8000 PRINT '[ERROR] (continued): ' + SUBSTRING(@DYNAMICSQL, 8001, 8000);
            THROW;
        END CATCH;
        END ELSE PRINT '[INFO] Skipping Foreign Keys (disabled by @pSYNCOBJECTS)';
        -----------------------------------------------------------------------------------------------------------------------------

        --=================================================-- Indexe (NC/Clustered) fehlend oder abweichend -> Drop/Create --================================================--
        IF @SYNC_INDEXES = 1 BEGIN
        SET @DYNAMICSQL = N'
    INSERT INTO #TBDD_SYNC_DATABASE_COMMANDS(Phase, Command)
    SELECT * FROM (
    SELECT N''07-Indexes'',
           N''USE {#TARGETDB_Q#}; '' +
           CASE WHEN ti.index_id IS NOT NULL THEN N''DROP INDEX ''+QUOTENAME(i.name)+N'' ON ''+QUOTENAME(s.name)+N''.''{#COLLATION#}+QUOTENAME(t.name)+N''; '' ELSE N'''' END +
           N''CREATE '' + CASE WHEN i.is_unique=1 THEN N''UNIQUE '' ELSE N'''' END +
           CASE WHEN i.type=1 THEN N''CLUSTERED '' WHEN i.type=2 THEN N''NONCLUSTERED '' ELSE N'''' END +
           N''INDEX '' + QUOTENAME(i.name) + N'' ON '' + QUOTENAME(s.name)+N''.''{#COLLATION#}+QUOTENAME(t.name)+N''(''+
           STUFF((SELECT '',''{#COLLATION#}+QUOTENAME(c.name) + CASE WHEN ic.is_descending_key=1 THEN N'' DESC'' ELSE N'''' END
                FROM {#SOURCEDB_Q#}.sys.index_columns ic
                JOIN {#SOURCEDB_Q#}.sys.columns c ON c.object_id=ic.object_id AND c.column_id=ic.column_id
                WHERE ic.object_id=i.object_id AND ic.index_id=i.index_id AND ic.is_included_column=0
                ORDER BY ic.key_ordinal
                FOR XML PATH(''''), TYPE).value(''.'',''nvarchar(max)''),1,1,N'''') + N'')'' +
           CASE WHEN EXISTS (SELECT 1 FROM {#SOURCEDB_Q#}.sys.index_columns ic WHERE ic.object_id=i.object_id AND ic.index_id=i.index_id AND ic.is_included_column=1)
            THEN N'' INCLUDE(''+STUFF((SELECT '',''{#COLLATION#}+QUOTENAME(c.name)
                             FROM {#SOURCEDB_Q#}.sys.index_columns ic
                             JOIN {#SOURCEDB_Q#}.sys.columns c ON c.object_id=ic.object_id AND c.column_id=ic.column_id
                             WHERE ic.object_id=i.object_id AND ic.index_id=i.index_id AND ic.is_included_column=1
                             ORDER BY ic.index_column_id
                     FOR XML PATH(''''), TYPE).value(''.'',''nvarchar(max)''),1,1,N'''')+N'')''
            ELSE N'''' END +
           N'' WITH (PAD_INDEX = ''+CASE WHEN i.is_padded=1 THEN N''ON'' ELSE N''OFF'' END+
           N'', ALLOW_ROW_LOCKS = ''+CASE WHEN i.allow_row_locks=1 THEN N''ON'' ELSE N''OFF'' END+
           N'', ALLOW_PAGE_LOCKS = ''+CASE WHEN i.allow_page_locks=1 THEN N''ON'' ELSE N''OFF'' END+
           N'', IGNORE_DUP_KEY = ''+CASE WHEN i.ignore_dup_key=1 THEN N''ON'' ELSE N''OFF'' END+
           N'', FILLFACTOR = ''+CONVERT(nvarchar(3), CASE WHEN i.fill_factor=0 THEN 100 ELSE i.fill_factor END)+N'')'' +
           CASE WHEN i.has_filter=1 THEN N'' WHERE ''+ISNULL(i.filter_definition,N'''') ELSE N'''' END
    FROM {#SOURCEDB_Q#}.sys.indexes i
    JOIN {#SOURCEDB_Q#}.sys.tables t ON t.object_id=i.object_id
    JOIN {#SOURCEDB_Q#}.sys.schemas s ON s.schema_id=t.schema_id
    LEFT JOIN {#TARGETDB_Q#}.sys.schemas ts ON ts.name{#COLLATION#}=s.name{#COLLATION#}
    LEFT JOIN {#TARGETDB_Q#}.sys.tables tt ON tt.schema_id=ts.schema_id AND tt.name{#COLLATION#}=t.name{#COLLATION#}
    LEFT JOIN {#TARGETDB_Q#}.sys.indexes ti ON ti.object_id=tt.object_id AND ti.name{#COLLATION#}=i.name{#COLLATION#}
    OUTER APPLY (
        SELECT HASHBYTES(''SHA2_256'',
            (SELECT ic.key_ordinal, ic.is_included_column, ic.is_descending_key, c.name{#COLLATION#}, i.has_filter, i.filter_definition{#COLLATION#}, i.is_padded, i.allow_row_locks, i.allow_page_locks, i.ignore_dup_key, i.fill_factor
             FROM {#SOURCEDB_Q#}.sys.index_columns ic
             JOIN {#SOURCEDB_Q#}.sys.columns c ON c.object_id=ic.object_id AND c.column_id=ic.column_id
             WHERE ic.object_id=i.object_id AND ic.index_id=i.index_id
             ORDER BY ic.index_column_id
             FOR XML PATH(''''), TYPE).value(''.'',''nvarchar(max)''){#COLLATION#}) AS src_hash,
               i.is_unique AS src_unique,
               i.type AS src_type
    ) hsrc
    OUTER APPLY (
        SELECT HASHBYTES(''SHA2_256'',
            (SELECT ic.key_ordinal, ic.is_included_column, ic.is_descending_key, c.name{#COLLATION#}, ti.has_filter, ti.filter_definition{#COLLATION#}, ti.is_padded, ti.allow_row_locks, ti.allow_page_locks, ti.ignore_dup_key, ti.fill_factor
             FROM {#TARGETDB_Q#}.sys.index_columns ic
             JOIN {#TARGETDB_Q#}.sys.columns c ON c.object_id=ic.object_id AND c.column_id=ic.column_id
             WHERE ic.object_id=tt.object_id AND ic.index_id=ti.index_id
             ORDER BY ic.index_column_id
             FOR XML PATH(''''), TYPE).value(''.'',''nvarchar(max)''){#COLLATION#}) AS tgt_hash,
               ti.is_unique AS tgt_unique,
               ti.type AS tgt_type
    ) htgt
        WHERE i.is_primary_key=0 AND i.is_unique_constraint=0 AND i.is_hypothetical=0 AND i.type IN (1,2) AND t.is_ms_shipped=0 AND s.name{#COLLATION#}<>N''sys''{#COLLATION#}
            AND (ti.index_id IS NULL OR hsrc.src_hash<>htgt.tgt_hash OR hsrc.src_unique<>htgt.tgt_unique OR hsrc.src_type<>htgt.tgt_type)
                        AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard=1 AND (t.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#}+N''.''{#COLLATION#}+t.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard=0 AND (t.name{#COLLATION#}=ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#}+N''.''{#COLLATION#}+t.name{#COLLATION#})=ex.Pattern{#COLLATION#})))
    ) AS X(Phase, Command)
    WHERE X.Command IS NOT NULL AND LEN(X.Command)>0;';
        SET @DYNAMICSQL = REPLACE(REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);
        BEGIN TRY
            EXEC sys.sp_executesql @DYNAMICSQL;
    IF @DEBUG = 1 PRINT '[DEBUG] Index commands built successfully';
        END TRY
        BEGIN CATCH
            PRINT '[ERROR] Failed to build Index commands!';
            PRINT '[ERROR] Error Message: ' + ERROR_MESSAGE();
            PRINT '[ERROR] Dynamic SQL (first 8000 chars):';
            PRINT SUBSTRING(@DYNAMICSQL, 1, 8000);
            IF LEN(@DYNAMICSQL) > 8000 PRINT '[ERROR] (continued): ' + SUBSTRING(@DYNAMICSQL, 8001, 8000);
            THROW;
        END CATCH;
        END ELSE PRINT '[INFO] Skipping Indexes (disabled by @pSYNCOBJECTS)';
        -----------------------------------------------------------------------------------------------------------------------------

        --=================================================-- Programmierbare Objekte (Triggers/Views/Functions/Procedures) --================================================--
        -- Triggers (table-level)
    IF @SYNC_TRIGGERS = 1 BEGIN
    SET @DYNAMICSQL = N'
INSERT INTO #TBDD_SYNC_DATABASE_COMMANDS(Phase, Command)
SELECT * FROM (
-- First: Generate DROP statements for existing triggers that will be recreated
SELECT N''10A-Triggers-Drop'', N''EXEC {#TARGETDB_Q#}.sys.sp_executesql N''''DROP TRIGGER '' + QUOTENAME(ts.name) + N''.'' + QUOTENAME(ttr.name) + N'''''';''
FROM {#SOURCEDB_Q#}.sys.triggers tr
JOIN {#SOURCEDB_Q#}.sys.objects o ON o.object_id=tr.object_id
JOIN {#SOURCEDB_Q#}.sys.sql_modules m ON m.object_id=tr.object_id
JOIN {#SOURCEDB_Q#}.sys.schemas s ON s.schema_id=o.schema_id
JOIN {#SOURCEDB_Q#}.sys.tables pt ON pt.object_id=tr.parent_id
JOIN {#SOURCEDB_Q#}.sys.schemas ps ON ps.schema_id=pt.schema_id
JOIN {#TARGETDB_Q#}.sys.triggers ttr ON 1=1
JOIN {#TARGETDB_Q#}.sys.objects tobj ON tobj.object_id=ttr.object_id
JOIN {#TARGETDB_Q#}.sys.sql_modules tgtm ON tgtm.object_id=ttr.object_id
JOIN {#TARGETDB_Q#}.sys.schemas ts ON ts.schema_id=tobj.schema_id AND ts.name{#COLLATION#}=s.name{#COLLATION#} AND ttr.name{#COLLATION#}=tr.name{#COLLATION#}
CROSS APPLY (SELECT HASHBYTES(''SHA2_256'', REPLACE(REPLACE(CAST(m.definition AS NVARCHAR(max)){#COLLATION#}, N''[''+ N''{#SOURCEDB#}'' +N'']'', N''[''+ N''{#TARGETDB#}'' +N'']''), N''{#SOURCEDB#}''+N''.'', N''{#TARGETDB#}''+N''.'')) AS src_hash, HASHBYTES(''SHA2_256'', CAST(tgtm.definition AS NVARCHAR(max)){#COLLATION#}) AS tgt_hash) hcmp
WHERE s.name{#COLLATION#}<>N''sys''{#COLLATION#} AND tr.parent_id IS NOT NULL AND m.definition IS NOT NULL
    AND (N''{#SOURCECOLLATION_NAME#}'' COLLATE Latin1_General_CI_AS<>N''{#TARGETCOLLATION_NAME#}'' COLLATE Latin1_General_CI_AS OR hcmp.src_hash<>hcmp.tgt_hash)
    AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard=1 AND (pt.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (ps.name{#COLLATION#}+N''.''{#COLLATION#}+pt.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard=0 AND (pt.name{#COLLATION#}=ex.Pattern{#COLLATION#} OR (ps.name{#COLLATION#}+N''.''{#COLLATION#}+pt.name{#COLLATION#})=ex.Pattern{#COLLATION#}))
                                                                                OR (ex.IsWildcard=1 AND (tr.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#}+N''.''{#COLLATION#}+tr.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard=0 AND (tr.name{#COLLATION#}=ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#}+N''.''{#COLLATION#}+tr.name{#COLLATION#})=ex.Pattern{#COLLATION#}))){#DROPEXISTING_FILTER#}
UNION ALL
-- Second: Generate CREATE statements for all triggers
SELECT N''10B-Triggers'',
       N''EXEC {#TARGETDB_Q#}.sys.sp_executesql N'''''' +
         REPLACE(
            REPLACE(
                REPLACE(
                    -- First normalize spaces (8->1, 4->1, 2->1 twice for 3+ spaces), then replace CREATE/ALTER
                    REPLACE(REPLACE(
                        REPLACE(REPLACE(REPLACE(REPLACE(CAST(m.definition AS NVARCHAR(max)){#COLLATION#}, N''        '', N'' ''), N''    '', N'' ''), N''  '', N'' ''), N''  '', N'' ''),
                        N''ALTER TRIGGER'', N''CREATE TRIGGER''), N''CREATE OR ALTER TRIGGER'', N''CREATE TRIGGER''),
                    N''[''+ N''{#SOURCEDB#}'' +N'']'', N''[''+ N''{#TARGETDB#}'' +N'']''),
                N''{#SOURCEDB#}''+N''.'', N''{#TARGETDB#}''+N''.''),
            CHAR(39), CHAR(39)+CHAR(39)) +
         '''''';''
FROM {#SOURCEDB_Q#}.sys.triggers tr
JOIN {#SOURCEDB_Q#}.sys.objects o ON o.object_id=tr.object_id
JOIN {#SOURCEDB_Q#}.sys.sql_modules m ON m.object_id=tr.object_id
JOIN {#SOURCEDB_Q#}.sys.schemas s ON s.schema_id=o.schema_id
JOIN {#SOURCEDB_Q#}.sys.tables pt ON pt.object_id=tr.parent_id
JOIN {#SOURCEDB_Q#}.sys.schemas ps ON ps.schema_id=pt.schema_id
CROSS APPLY (SELECT HASHBYTES(''SHA2_256'', REPLACE(REPLACE(CAST(m.definition AS NVARCHAR(max)){#COLLATION#}, N''[''+ N''{#SOURCEDB#}'' +N'']'', N''[''+ N''{#TARGETDB#}'' +N'']''), N''{#SOURCEDB#}''+N''.'', N''{#TARGETDB#}''+N''.'')) AS src_hash) hsrc
OUTER APPLY (
    SELECT HASHBYTES(''SHA2_256'', CAST(tgtm.definition AS NVARCHAR(max)){#COLLATION#}) AS tgt_hash
    FROM {#TARGETDB_Q#}.sys.triggers ttr
    JOIN {#TARGETDB_Q#}.sys.objects tobj ON tobj.object_id=ttr.object_id
    JOIN {#TARGETDB_Q#}.sys.sql_modules tgtm ON tgtm.object_id=ttr.object_id
    JOIN {#TARGETDB_Q#}.sys.schemas ts ON ts.schema_id=tobj.schema_id
    WHERE ts.name{#COLLATION#}=s.name{#COLLATION#} AND tobj.name{#COLLATION#}=tr.name{#COLLATION#}
) htgt
WHERE s.name{#COLLATION#}<>N''sys''{#COLLATION#} AND tr.parent_id IS NOT NULL
  AND m.definition IS NOT NULL  -- Skip encrypted objects (WITH ENCRYPTION)
  AND (N''{#SOURCECOLLATION_NAME#}'' COLLATE Latin1_General_CI_AS<>N''{#TARGETCOLLATION_NAME#}'' COLLATE Latin1_General_CI_AS OR htgt.tgt_hash IS NULL OR hsrc.src_hash<>htgt.tgt_hash)
        AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard=1 AND (pt.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (ps.name{#COLLATION#}+N''.''{#COLLATION#}+pt.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard=0 AND (pt.name{#COLLATION#}=ex.Pattern{#COLLATION#} OR (ps.name{#COLLATION#}+N''.''{#COLLATION#}+pt.name{#COLLATION#})=ex.Pattern{#COLLATION#}))
                                                                                OR (ex.IsWildcard=1 AND (tr.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#}+N''.''{#COLLATION#}+tr.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard=0 AND (tr.name{#COLLATION#}=ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#}+N''.''{#COLLATION#}+tr.name{#COLLATION#})=ex.Pattern{#COLLATION#}))
                 )
) AS X(Phase, Command)
WHERE X.Command IS NOT NULL AND LEN(X.Command)>0;';
    SET @DYNAMICSQL = REPLACE(REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);
    SET @DYNAMICSQL = REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDB, @SOURCEDATABASE), @PLACEHOLDERTARGETDB, @TARGETDATABASE);
    SET @DYNAMICSQL = REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCECOLLATIONNAME, @SOURCECOLLATION), @PLACEHOLDERTARGETCOLLATIONNAME, @TARGETCOLLATION);
    SET @DYNAMICSQL = REPLACE(@DYNAMICSQL, @PLACEHOLDERDROPEXISTING, @DROPEXISTING_FILTER);
    BEGIN TRY
        EXEC sys.sp_executesql @DYNAMICSQL;
    IF @DEBUG = 1 PRINT '[DEBUG] Trigger commands built successfully';
    END TRY
    BEGIN CATCH
        PRINT '[ERROR] Failed to build Trigger commands!';
        PRINT '[ERROR] Error Message: ' + ERROR_MESSAGE();
        PRINT '[ERROR] Dynamic SQL (first 8000 chars):';
        PRINT SUBSTRING(@DYNAMICSQL, 1, 8000);
        IF LEN(@DYNAMICSQL) > 8000 PRINT '[ERROR] (continued): ' + SUBSTRING(@DYNAMICSQL, 8001, 8000);
        THROW;
    END CATCH;
    END ELSE PRINT '[INFO] Skipping Triggers (disabled by @pSYNCOBJECTS)';
        -----------------------------------------------------------------------------------------------------------------------------

        --=================================================-- Views --================================================--
    IF @SYNC_VIEWS = 1 BEGIN
    SET @DYNAMICSQL = N'
;WITH ViewDeps AS (
  SELECT v.object_id, 0 dep, CAST(CONCAT(N'','',v.object_id,N'','') AS NVARCHAR(MAX)) AS visited
  FROM {#SOURCEDB_Q#}.sys.views v
  WHERE NOT EXISTS (SELECT 1 FROM {#SOURCEDB_Q#}.sys.sql_expression_dependencies d JOIN {#SOURCEDB_Q#}.sys.views rv ON rv.object_id=d.referenced_id WHERE d.referencing_id=v.object_id)
  UNION ALL
  SELECT v.object_id, vd.dep+1, CAST(CONCAT(vd.visited,v.object_id,N'','') AS NVARCHAR(MAX))
  FROM {#SOURCEDB_Q#}.sys.views v
  JOIN {#SOURCEDB_Q#}.sys.sql_expression_dependencies d ON d.referencing_id=v.object_id
  JOIN ViewDeps vd ON vd.object_id=d.referenced_id
  WHERE vd.visited NOT LIKE CONCAT(N''%,'',v.object_id,N'',%'') AND vd.dep < 50
), MaxLevel AS (SELECT object_id, MAX(dep) dep FROM ViewDeps GROUP BY object_id)
INSERT INTO #TBDD_SYNC_DATABASE_COMMANDS(Phase, Command)
SELECT X.Phase, X.Command FROM (
-- First: Generate DROP statements for views that need recreation (collation mismatch OR hash mismatch)
SELECT N''11A-Views-Drop'', N''EXEC {#TARGETDB_Q#}.sys.sp_executesql N''''DROP VIEW '' + QUOTENAME(ts.name) + N''.'' + QUOTENAME(tv.name) + N'''''';'', 0
FROM {#SOURCEDB_Q#}.sys.views v
JOIN {#SOURCEDB_Q#}.sys.sql_modules m ON m.object_id=v.object_id
JOIN {#SOURCEDB_Q#}.sys.schemas s ON s.schema_id=v.schema_id
JOIN {#TARGETDB_Q#}.sys.views tv ON 1=1
JOIN {#TARGETDB_Q#}.sys.sql_modules tgtm ON tgtm.object_id=tv.object_id
JOIN {#TARGETDB_Q#}.sys.schemas ts ON ts.schema_id=tv.schema_id AND ts.name{#COLLATION#}=s.name{#COLLATION#} AND tv.name{#COLLATION#}=v.name{#COLLATION#}
CROSS APPLY (SELECT HASHBYTES(''SHA2_256'', REPLACE(REPLACE(CAST(m.definition AS NVARCHAR(max)){#COLLATION#}, N''[''+ N''{#SOURCEDB#}'' +N'']'', N''[''+ N''{#TARGETDB#}'' +N'']''), N''{#SOURCEDB#}''+N''.'', N''{#TARGETDB#}''+N''.'')) AS src_hash, HASHBYTES(''SHA2_256'', CAST(tgtm.definition AS NVARCHAR(max)){#COLLATION#}) AS tgt_hash) hcmp
WHERE s.name{#COLLATION#}<>N''sys''{#COLLATION#} AND m.definition IS NOT NULL
    AND (N''{#SOURCECOLLATION_NAME#}'' COLLATE Latin1_General_CI_AS<>N''{#TARGETCOLLATION_NAME#}'' COLLATE Latin1_General_CI_AS OR hcmp.src_hash<>hcmp.tgt_hash)
    AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard=1 AND (v.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#}+N''.''{#COLLATION#}+v.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard=0 AND (v.name{#COLLATION#}=ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#}+N''.''{#COLLATION#}+v.name{#COLLATION#})=ex.Pattern{#COLLATION#}))){#DROPEXISTING_FILTER#}
UNION ALL
-- Second: Generate CREATE statements for all views (robust: handles varying whitespace and case-insensitive)
SELECT N''11B-Views'',
       N''EXEC {#TARGETDB_Q#}.sys.sp_executesql N'''''' +
    REPLACE(
        REPLACE(
            REPLACE(
                -- Case-insensitive REPLACE chain on normalized definition:
                -- 1. CREATE OR ALTER VIEW -> placeholder (preserve existing)
                -- 2. ALTER VIEW -> CREATE OR ALTER VIEW
                -- 3. CREATE VIEW -> CREATE OR ALTER VIEW
                -- 4. placeholder -> CREATE OR ALTER VIEW (restore)
                REPLACE(
                    REPLACE(
                        REPLACE(
                            REPLACE(norm.def COLLATE Latin1_General_CI_AS,
                                N''CREATE OR ALTER VIEW'' COLLATE Latin1_General_CI_AS, N''<#COA_VIEW#>''),
                            N''ALTER VIEW'' COLLATE Latin1_General_CI_AS, N''CREATE OR ALTER VIEW''),
                        N''CREATE VIEW'' COLLATE Latin1_General_CI_AS, N''CREATE OR ALTER VIEW''),
                    N''<#COA_VIEW#>'', N''CREATE OR ALTER VIEW''),
                N''[''+ N''{#SOURCEDB#}'' +N'']'', N''[''+ N''{#TARGETDB#}'' +N'']''),
            N''{#SOURCEDB#}''+N''.'', N''{#TARGETDB#}''+N''.''),
        CHAR(39), CHAR(39)+CHAR(39)) + '''''';'',
       ISNULL(ml.dep,999)
FROM {#SOURCEDB_Q#}.sys.views v
JOIN {#SOURCEDB_Q#}.sys.sql_modules m ON m.object_id=v.object_id
JOIN {#SOURCEDB_Q#}.sys.schemas s ON s.schema_id=v.schema_id
-- Normalize whitespace: collapse 8->1, 4->1, 2->1 (twice for 3+ spaces)
CROSS APPLY (SELECT REPLACE(REPLACE(REPLACE(REPLACE(CAST(m.definition AS NVARCHAR(max)){#COLLATION#}, N''        '', N'' ''), N''    '', N'' ''), N''  '', N'' ''), N''  '', N'' '') AS def) norm
CROSS APPLY (SELECT HASHBYTES(''SHA2_256'', REPLACE(REPLACE(CAST(m.definition AS NVARCHAR(max)){#COLLATION#}, N''[''+ N''{#SOURCEDB#}'' +N'']'', N''[''+ N''{#TARGETDB#}'' +N'']''), N''{#SOURCEDB#}''+N''.'', N''{#TARGETDB#}''+N''.'')) AS src_hash) hsrc
OUTER APPLY (
        SELECT HASHBYTES(''SHA2_256'', CAST(tm.definition AS NVARCHAR(max)){#COLLATION#}) AS tgt_hash
        FROM {#TARGETDB_Q#}.sys.views tv
        JOIN {#TARGETDB_Q#}.sys.sql_modules tm ON tm.object_id=tv.object_id
        JOIN {#TARGETDB_Q#}.sys.schemas ts ON ts.schema_id=tv.schema_id
        WHERE ts.name{#COLLATION#}=s.name{#COLLATION#} AND tv.name{#COLLATION#}=v.name{#COLLATION#}
) htgt
LEFT JOIN MaxLevel ml ON ml.object_id=v.object_id
WHERE s.name{#COLLATION#}<>N''sys''{#COLLATION#}
  AND m.definition IS NOT NULL  -- Skip encrypted objects (WITH ENCRYPTION)
  AND (N''{#SOURCECOLLATION_NAME#}'' COLLATE Latin1_General_CI_AS<>N''{#TARGETCOLLATION_NAME#}'' COLLATE Latin1_General_CI_AS OR htgt.tgt_hash IS NULL OR hsrc.src_hash<>htgt.tgt_hash)
    AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard=1 AND (v.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#}+N''.''{#COLLATION#}+v.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard=0 AND (v.name{#COLLATION#}=ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#}+N''.''{#COLLATION#}+v.name{#COLLATION#})=ex.Pattern{#COLLATION#})))
) AS X(Phase, Command, SortOrder)
WHERE X.Command IS NOT NULL AND LEN(X.Command)>0
ORDER BY X.SortOrder
OPTION(MAXRECURSION 100);';
    SET @DYNAMICSQL = REPLACE(REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);
    SET @DYNAMICSQL = REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDB, @SOURCEDATABASE), @PLACEHOLDERTARGETDB, @TARGETDATABASE);
    SET @DYNAMICSQL = REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCECOLLATIONNAME, @SOURCECOLLATION), @PLACEHOLDERTARGETCOLLATIONNAME, @TARGETCOLLATION);
    SET @DYNAMICSQL = REPLACE(@DYNAMICSQL, @PLACEHOLDERDROPEXISTING, @DROPEXISTING_FILTER);
    BEGIN TRY
        EXEC sys.sp_executesql @DYNAMICSQL;
    IF @DEBUG = 1 PRINT '[DEBUG] View commands built successfully';
    END TRY
    BEGIN CATCH
        PRINT '[ERROR] Failed to build View commands!';
        PRINT '[ERROR] Error Message: ' + ERROR_MESSAGE();
        PRINT '[ERROR] Dynamic SQL (first 8000 chars):';
        PRINT SUBSTRING(@DYNAMICSQL, 1, 8000);
        IF LEN(@DYNAMICSQL) > 8000 PRINT '[ERROR] (continued): ' + SUBSTRING(@DYNAMICSQL, 8001, 8000);
        THROW;
    END CATCH;
    END ELSE PRINT '[INFO] Skipping Views (disabled by @pSYNCOBJECTS)';
        -----------------------------------------------------------------------------------------------------------------------------

        --=================================================-- Functions --================================================--
    IF @SYNC_FUNCTIONS = 1 BEGIN
    SET @DYNAMICSQL = N'
;WITH FuncDeps AS (
  SELECT o.object_id, 0 dep, CAST(CONCAT(N'','',o.object_id,N'','') AS NVARCHAR(MAX)) AS visited
  FROM {#SOURCEDB_Q#}.sys.objects o WHERE o.type IN (N''FN'',N''TF'',N''IF'',N''FS'',N''FT'')
    AND NOT EXISTS(SELECT 1 FROM {#SOURCEDB_Q#}.sys.sql_expression_dependencies d JOIN {#SOURCEDB_Q#}.sys.objects ro ON ro.object_id=d.referenced_id AND ro.type IN (N''FN'',N''TF'',N''IF'',N''FS'',N''FT'') WHERE d.referencing_id=o.object_id)
  UNION ALL
  SELECT o.object_id, fd.dep+1, CAST(CONCAT(fd.visited,o.object_id,N'','') AS NVARCHAR(MAX))
  FROM {#SOURCEDB_Q#}.sys.objects o
  JOIN {#SOURCEDB_Q#}.sys.sql_expression_dependencies d ON d.referencing_id=o.object_id
  JOIN FuncDeps fd ON fd.object_id=d.referenced_id
  WHERE fd.visited NOT LIKE CONCAT(N''%,'',o.object_id,N'',%'') AND fd.dep < 50
), MaxLevel AS (SELECT object_id, MAX(dep) dep FROM FuncDeps GROUP BY object_id)
INSERT INTO #TBDD_SYNC_DATABASE_COMMANDS(Phase, Command)
SELECT X.Phase, X.Command FROM (
-- First: Generate DROP statements for functions that need recreation (collation mismatch OR hash mismatch)
SELECT N''12A-Functions-Drop'', N''EXEC {#TARGETDB_Q#}.sys.sp_executesql N''''DROP FUNCTION '' + QUOTENAME(ts.name) + N''.'' + QUOTENAME(tobj.name) + N'''''';'', 0
FROM {#SOURCEDB_Q#}.sys.objects o
JOIN {#SOURCEDB_Q#}.sys.sql_modules m ON m.object_id=o.object_id
JOIN {#SOURCEDB_Q#}.sys.schemas s ON s.schema_id=o.schema_id
JOIN {#TARGETDB_Q#}.sys.objects tobj ON tobj.type IN (N''FN'',N''TF'',N''IF'',N''FS'',N''FT'')
JOIN {#TARGETDB_Q#}.sys.sql_modules tgtm ON tgtm.object_id=tobj.object_id
JOIN {#TARGETDB_Q#}.sys.schemas ts ON ts.schema_id=tobj.schema_id AND ts.name{#COLLATION#}=s.name{#COLLATION#} AND tobj.name{#COLLATION#}=o.name{#COLLATION#}
CROSS APPLY (SELECT HASHBYTES(''SHA2_256'', REPLACE(REPLACE(CAST(m.definition AS NVARCHAR(max)){#COLLATION#}, N''[''+ N''{#SOURCEDB#}'' +N'']'', N''[''+ N''{#TARGETDB#}'' +N'']''), N''{#SOURCEDB#}''+N''.'', N''{#TARGETDB#}''+N''.'')) AS src_hash, HASHBYTES(''SHA2_256'', CAST(tgtm.definition AS NVARCHAR(max)){#COLLATION#}) AS tgt_hash) hcmp
WHERE s.name{#COLLATION#}<>N''sys''{#COLLATION#} AND o.type IN (N''FN'',N''TF'',N''IF'',N''FS'',N''FT'') AND m.definition IS NOT NULL
    AND (N''{#SOURCECOLLATION_NAME#}'' COLLATE Latin1_General_CI_AS<>N''{#TARGETCOLLATION_NAME#}'' COLLATE Latin1_General_CI_AS OR hcmp.src_hash<>hcmp.tgt_hash)
    AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard=1 AND (o.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#}+N''.''{#COLLATION#}+o.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard=0 AND (o.name{#COLLATION#}=ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#}+N''.''{#COLLATION#}+o.name{#COLLATION#})=ex.Pattern{#COLLATION#}))){#DROPEXISTING_FILTER#}
UNION ALL
-- Second: Generate CREATE statements for all functions (robust: handles varying whitespace and case-insensitive)
SELECT N''12B-Functions'',
       N''EXEC {#TARGETDB_Q#}.sys.sp_executesql N'''''' +
        REPLACE(
            REPLACE(
                REPLACE(
                    -- Case-insensitive REPLACE chain on normalized definition:
                    -- 1. CREATE OR ALTER FUNCTION -> placeholder (preserve existing)
                    -- 2. ALTER FUNCTION -> CREATE OR ALTER FUNCTION
                    -- 3. CREATE FUNCTION -> CREATE OR ALTER FUNCTION  
                    -- 4. placeholder -> CREATE OR ALTER FUNCTION (restore)
                    REPLACE(
                        REPLACE(
                            REPLACE(
                                REPLACE(norm.def COLLATE Latin1_General_CI_AS,
                                    N''CREATE OR ALTER FUNCTION'' COLLATE Latin1_General_CI_AS, N''<#COA_FUNC#>''),
                                N''ALTER FUNCTION'' COLLATE Latin1_General_CI_AS, N''CREATE OR ALTER FUNCTION''),
                            N''CREATE FUNCTION'' COLLATE Latin1_General_CI_AS, N''CREATE OR ALTER FUNCTION''),
                        N''<#COA_FUNC#>'', N''CREATE OR ALTER FUNCTION''),
                    N''[''+ N''{#SOURCEDB#}'' +N'']'', N''[''+ N''{#TARGETDB#}'' +N'']''),
                N''{#SOURCEDB#}''+N''.'', N''{#TARGETDB#}''+N''.''),
            CHAR(39), CHAR(39)+CHAR(39)) + '''''';'',
       ISNULL(ml.dep,999)
FROM {#SOURCEDB_Q#}.sys.objects o
JOIN {#SOURCEDB_Q#}.sys.sql_modules m ON m.object_id=o.object_id
JOIN {#SOURCEDB_Q#}.sys.schemas s ON s.schema_id=o.schema_id
-- Normalize whitespace: collapse 8->1, 4->1, 2->1 (twice for 3+ spaces)
CROSS APPLY (SELECT REPLACE(REPLACE(REPLACE(REPLACE(CAST(m.definition AS NVARCHAR(max)){#COLLATION#}, N''        '', N'' ''), N''    '', N'' ''), N''  '', N'' ''), N''  '', N'' '') AS def) norm
CROSS APPLY (SELECT HASHBYTES(''SHA2_256'', REPLACE(REPLACE(CAST(m.definition AS NVARCHAR(max)){#COLLATION#}, N''[''+ N''{#SOURCEDB#}'' +N'']'', N''[''+ N''{#TARGETDB#}'' +N'']''), N''{#SOURCEDB#}''+N''.'', N''{#TARGETDB#}''+N''.'')) AS src_hash) hsrc
OUTER APPLY (
        SELECT HASHBYTES(''SHA2_256'', CAST(tm.definition AS NVARCHAR(max)){#COLLATION#}) AS tgt_hash
        FROM {#TARGETDB_Q#}.sys.objects tobj
        JOIN {#TARGETDB_Q#}.sys.sql_modules tm ON tm.object_id=tobj.object_id
        JOIN {#TARGETDB_Q#}.sys.schemas ts ON ts.schema_id=tobj.schema_id
        WHERE ts.name{#COLLATION#}=s.name{#COLLATION#} AND tobj.name{#COLLATION#}=o.name{#COLLATION#} AND tobj.type IN (N''FN'',N''TF'',N''IF'',N''FS'',N''FT'')
) htgt
LEFT JOIN MaxLevel ml ON ml.object_id=o.object_id
WHERE s.name{#COLLATION#}<>N''sys''{#COLLATION#} AND o.type IN (N''FN'',N''TF'',N''IF'',N''FS'',N''FT'')
    AND m.definition IS NOT NULL  -- Skip encrypted objects (WITH ENCRYPTION)
    AND (N''{#SOURCECOLLATION_NAME#}'' COLLATE Latin1_General_CI_AS<>N''{#TARGETCOLLATION_NAME#}'' COLLATE Latin1_General_CI_AS OR htgt.tgt_hash IS NULL OR hsrc.src_hash<>htgt.tgt_hash)
    AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard=1 AND (o.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#}+N''.''{#COLLATION#}+o.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard=0 AND (o.name{#COLLATION#}=ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#}+N''.''{#COLLATION#}+o.name{#COLLATION#})=ex.Pattern{#COLLATION#})))
) AS X(Phase, Command, SortOrder)
WHERE X.Command IS NOT NULL AND LEN(X.Command)>0
ORDER BY X.SortOrder
OPTION(MAXRECURSION 100);';
    SET @DYNAMICSQL = REPLACE(REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);
    SET @DYNAMICSQL = REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDB, @SOURCEDATABASE), @PLACEHOLDERTARGETDB, @TARGETDATABASE);
    SET @DYNAMICSQL = REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCECOLLATIONNAME, @SOURCECOLLATION), @PLACEHOLDERTARGETCOLLATIONNAME, @TARGETCOLLATION);
    SET @DYNAMICSQL = REPLACE(@DYNAMICSQL, @PLACEHOLDERDROPEXISTING, @DROPEXISTING_FILTER);
    BEGIN TRY
        EXEC sys.sp_executesql @DYNAMICSQL;
    IF @DEBUG = 1 PRINT '[DEBUG] Function commands built successfully';
    END TRY
    BEGIN CATCH
        PRINT '[ERROR] Failed to build Function commands!';
        PRINT '[ERROR] Error Message: ' + ERROR_MESSAGE();
        PRINT '[ERROR] Dynamic SQL (first 8000 chars):';
        PRINT SUBSTRING(@DYNAMICSQL, 1, 8000);
        IF LEN(@DYNAMICSQL) > 8000 PRINT '[ERROR] (continued): ' + SUBSTRING(@DYNAMICSQL, 8001, 8000);
        THROW;
    END CATCH;
    END ELSE PRINT '[INFO] Skipping Functions (disabled by @pSYNCOBJECTS)';
    -----------------------------------------------------------------------------------------------------------------------------

    --=================================================-- Procedures --================================================--
    IF @SYNC_PROCEDURES = 1 BEGIN
    SET @DYNAMICSQL = N'
INSERT INTO #TBDD_SYNC_DATABASE_COMMANDS(Phase, Command)
SELECT * FROM (
-- First: Generate DROP statements for procedures that need recreation (collation mismatch OR hash mismatch)
SELECT N''13A-Procedures-Drop'', N''EXEC {#TARGETDB_Q#}.sys.sp_executesql N''''DROP PROCEDURE '' + QUOTENAME(ts.name) + N''.'' + QUOTENAME(tp.name) + N'''''';''
FROM {#SOURCEDB_Q#}.sys.procedures p
JOIN {#SOURCEDB_Q#}.sys.sql_modules m ON m.object_id=p.object_id
JOIN {#SOURCEDB_Q#}.sys.schemas s ON s.schema_id=p.schema_id
JOIN {#TARGETDB_Q#}.sys.procedures tp ON 1=1
JOIN {#TARGETDB_Q#}.sys.sql_modules tgtm ON tgtm.object_id=tp.object_id
JOIN {#TARGETDB_Q#}.sys.schemas ts ON ts.schema_id=tp.schema_id AND ts.name{#COLLATION#}=s.name{#COLLATION#} AND tp.name{#COLLATION#}=p.name{#COLLATION#}
CROSS APPLY (SELECT HASHBYTES(''SHA2_256'', REPLACE(REPLACE(CAST(m.definition AS NVARCHAR(max)){#COLLATION#}, N''[''+ N''{#SOURCEDB#}'' +N'']'', N''[''+ N''{#TARGETDB#}'' +N'']''), N''{#SOURCEDB#}''+N''.'', N''{#TARGETDB#}''+N''.'')) AS src_hash, HASHBYTES(''SHA2_256'', CAST(tgtm.definition AS NVARCHAR(max)){#COLLATION#}) AS tgt_hash) hcmp
WHERE s.name{#COLLATION#}<>N''sys''{#COLLATION#} AND m.definition IS NOT NULL
    AND (N''{#SOURCECOLLATION_NAME#}'' COLLATE Latin1_General_CI_AS<>N''{#TARGETCOLLATION_NAME#}'' COLLATE Latin1_General_CI_AS OR hcmp.src_hash<>hcmp.tgt_hash)
    AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard=1 AND (p.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#}+N''.''{#COLLATION#}+p.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard=0 AND (p.name{#COLLATION#}=ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#}+N''.''{#COLLATION#}+p.name{#COLLATION#})=ex.Pattern{#COLLATION#}))){#DROPEXISTING_FILTER#}
UNION ALL
-- Second: Generate CREATE statements for all procedures (robust: handles varying whitespace and case-insensitive)
SELECT N''13B-Procedures'',
       N''EXEC {#TARGETDB_Q#}.sys.sp_executesql N'''''' +
        REPLACE(
            REPLACE(
                REPLACE(
                    -- Case-insensitive REPLACE chain on normalized definition:
                    -- 1. CREATE OR ALTER PROCEDURE -> placeholder (preserve existing)
                    -- 2. ALTER PROCEDURE -> CREATE OR ALTER PROCEDURE
                    -- 3. CREATE PROCEDURE -> CREATE OR ALTER PROCEDURE
                    -- 4. placeholder -> CREATE OR ALTER PROCEDURE (restore)
                    REPLACE(
                        REPLACE(
                            REPLACE(
                                REPLACE(norm.def COLLATE Latin1_General_CI_AS,
                                    N''CREATE OR ALTER PROCEDURE'' COLLATE Latin1_General_CI_AS, N''<#COA_PROC#>''),
                                N''ALTER PROCEDURE'' COLLATE Latin1_General_CI_AS, N''CREATE OR ALTER PROCEDURE''),
                            N''CREATE PROCEDURE'' COLLATE Latin1_General_CI_AS, N''CREATE OR ALTER PROCEDURE''),
                        N''<#COA_PROC#>'', N''CREATE OR ALTER PROCEDURE''),
                    N''[''+ N''{#SOURCEDB#}'' +N'']'', N''[''+ N''{#TARGETDB#}'' +N'']''),
                N''{#SOURCEDB#}''+N''.'', N''{#TARGETDB#}''+N''.''),
            CHAR(39), CHAR(39)+CHAR(39)) +
        '''''';''
FROM {#SOURCEDB_Q#}.sys.procedures p
JOIN {#SOURCEDB_Q#}.sys.sql_modules m ON m.object_id=p.object_id
JOIN {#SOURCEDB_Q#}.sys.schemas s ON s.schema_id=p.schema_id
-- Normalize whitespace: collapse 8->1, 4->1, 2->1 (twice for 3+ spaces)
CROSS APPLY (SELECT REPLACE(REPLACE(REPLACE(REPLACE(CAST(m.definition AS NVARCHAR(max)){#COLLATION#}, N''        '', N'' ''), N''    '', N'' ''), N''  '', N'' ''), N''  '', N'' '') AS def) norm
CROSS APPLY (SELECT HASHBYTES(''SHA2_256'', REPLACE(REPLACE(CAST(m.definition AS NVARCHAR(max)){#COLLATION#}, N''[''+ N''{#SOURCEDB#}'' +N'']'', N''[''+ N''{#TARGETDB#}'' +N'']''), N''{#SOURCEDB#}''+N''.'', N''{#TARGETDB#}''+N''.'')) AS src_hash) hsrc
OUTER APPLY (
        SELECT HASHBYTES(''SHA2_256'', CAST(tm.definition AS NVARCHAR(max)){#COLLATION#}) AS tgt_hash
        FROM {#TARGETDB_Q#}.sys.procedures tp
        JOIN {#TARGETDB_Q#}.sys.sql_modules tm ON tm.object_id=tp.object_id
        JOIN {#TARGETDB_Q#}.sys.schemas ts ON ts.schema_id=tp.schema_id
        WHERE ts.name{#COLLATION#}=s.name{#COLLATION#} AND tp.name{#COLLATION#}=p.name{#COLLATION#}
) htgt
WHERE s.name{#COLLATION#}<>N''sys''{#COLLATION#}
    AND m.definition IS NOT NULL  -- Skip encrypted objects (WITH ENCRYPTION)
    AND (N''{#SOURCECOLLATION_NAME#}'' COLLATE Latin1_General_CI_AS<>N''{#TARGETCOLLATION_NAME#}'' COLLATE Latin1_General_CI_AS OR htgt.tgt_hash IS NULL OR hsrc.src_hash<>htgt.tgt_hash)
        AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard=1 AND (p.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#}+N''.''{#COLLATION#}+p.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard=0 AND (p.name{#COLLATION#}=ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#}+N''.''{#COLLATION#}+p.name{#COLLATION#})=ex.Pattern{#COLLATION#})))
        ) AS X(Phase, Command)
    WHERE X.Command IS NOT NULL AND LEN(X.Command)>0;';
    SET @DYNAMICSQL = REPLACE(REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);
    SET @DYNAMICSQL = REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDB, @SOURCEDATABASE), @PLACEHOLDERTARGETDB, @TARGETDATABASE);
    SET @DYNAMICSQL = REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCECOLLATIONNAME, @SOURCECOLLATION), @PLACEHOLDERTARGETCOLLATIONNAME, @TARGETCOLLATION);
    SET @DYNAMICSQL = REPLACE(@DYNAMICSQL, @PLACEHOLDERDROPEXISTING, @DROPEXISTING_FILTER);
    BEGIN TRY
        EXEC sys.sp_executesql @DYNAMICSQL;
    IF @DEBUG = 1 PRINT '[DEBUG] Procedure commands built successfully';
    END TRY
    BEGIN CATCH
        PRINT '[ERROR] Failed to build Procedure commands!';
        PRINT '[ERROR] Error Message: ' + ERROR_MESSAGE();
        PRINT '[ERROR] Dynamic SQL (first 8000 chars):';
        PRINT SUBSTRING(@DYNAMICSQL, 1, 8000);
        IF LEN(@DYNAMICSQL) > 8000 PRINT '[ERROR] (continued): ' + SUBSTRING(@DYNAMICSQL, 8001, 8000);
        THROW;
    END CATCH;
    END ELSE PRINT '[INFO] Skipping Procedures (disabled by @pSYNCOBJECTS)';
    -----------------------------------------------------------------------------------------------------------------------------

    --=================================================-- Sequences (fehlend oder abweichend -> Drop/Create) --================================================--
    IF @SYNC_SEQUENCES = 1 BEGIN
    SET @DYNAMICSQL = N'
INSERT INTO #TBDD_SYNC_DATABASE_COMMANDS(Phase, Command)
SELECT * FROM (
SELECT N''14-Sequences'',
       N''USE {#TARGETDB_Q#}; '' +
       CASE WHEN tseq.object_id IS NOT NULL THEN N''DROP SEQUENCE '' + QUOTENAME(s.name)+N''.'' + QUOTENAME(seq.name) + N''; '' ELSE N'''' END +
       N''CREATE SEQUENCE '' + QUOTENAME(s.name) + N''.'' + QUOTENAME(seq.name) + N'' AS '' + ty.name +
       N'' START WITH '' + CONVERT(NVARCHAR(50), ISNULL(seq.current_value, seq.start_value)) +
       N'' INCREMENT BY '' + CONVERT(NVARCHAR(50), seq.increment) +
       CASE WHEN seq.minimum_value IS NOT NULL THEN N'' MINVALUE '' + CONVERT(NVARCHAR(50), seq.minimum_value) ELSE N'' NO MINVALUE'' END +
       CASE WHEN seq.maximum_value IS NOT NULL THEN N'' MAXVALUE '' + CONVERT(NVARCHAR(50), seq.maximum_value) ELSE N'' NO MAXVALUE'' END +
       CASE WHEN seq.is_cycling=1 THEN N'' CYCLE'' ELSE N'' NO CYCLE'' END +
       CASE WHEN seq.is_cached=1 THEN N'' CACHE '' + CONVERT(NVARCHAR(50), ISNULL(seq.cache_size, 50)) ELSE N'' NO CACHE'' END + N'';''
FROM {#SOURCEDB_Q#}.sys.sequences seq
JOIN {#SOURCEDB_Q#}.sys.schemas s ON s.schema_id=seq.schema_id
JOIN {#SOURCEDB_Q#}.sys.types ty ON ty.user_type_id=seq.user_type_id
LEFT JOIN {#TARGETDB_Q#}.sys.schemas ts ON ts.name{#COLLATION#}=s.name{#COLLATION#}
LEFT JOIN {#TARGETDB_Q#}.sys.sequences tseq ON tseq.schema_id=ts.schema_id AND tseq.name{#COLLATION#}=seq.name{#COLLATION#}
LEFT JOIN {#TARGETDB_Q#}.sys.types tty ON tty.user_type_id=tseq.user_type_id
WHERE s.name{#COLLATION#}<>N''sys''{#COLLATION#}
    AND (tseq.object_id IS NULL
         OR seq.increment<>tseq.increment
         OR seq.minimum_value<>tseq.minimum_value
         OR seq.maximum_value<>tseq.maximum_value
         OR seq.is_cycling<>tseq.is_cycling
         OR ty.name{#COLLATION#}<>tty.name{#COLLATION#})
    AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard=1 AND (seq.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#}+N''.''{#COLLATION#}+seq.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard=0 AND (seq.name{#COLLATION#}=ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#}+N''.''{#COLLATION#}+seq.name{#COLLATION#})=ex.Pattern{#COLLATION#})))
) AS X(Phase, Command)
WHERE X.Command IS NOT NULL AND LEN(X.Command)>0;';
    SET @DYNAMICSQL = REPLACE(REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);
    SET @DYNAMICSQL = REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDB, @SOURCEDATABASE), @PLACEHOLDERTARGETDB, @TARGETDATABASE);
    BEGIN TRY
        EXEC sys.sp_executesql @DYNAMICSQL;
    IF @DEBUG = 1 PRINT '[DEBUG] Sequence commands built successfully';
    END TRY
    BEGIN CATCH
        PRINT '[ERROR] Failed to build Sequence commands!';
        PRINT '[ERROR] Error Message: ' + ERROR_MESSAGE();
        PRINT '[ERROR] Dynamic SQL (first 8000 chars):';
        PRINT SUBSTRING(@DYNAMICSQL, 1, 8000);
        IF LEN(@DYNAMICSQL) > 8000 PRINT '[ERROR] (continued): ' + SUBSTRING(@DYNAMICSQL, 8001, 8000);
        THROW;
    END CATCH;
    END ELSE PRINT '[INFO] Skipping Sequences (disabled by @pSYNCOBJECTS)';
    -----------------------------------------------------------------------------------------------------------------------------

    --=================================================-- Synonyms (Drop/Create bei Abweichungen) --================================================--
    IF @SYNC_SYNONYMS = 1 BEGIN
    SET @DYNAMICSQL = N'
INSERT INTO #TBDD_SYNC_DATABASE_COMMANDS(Phase, Command)
SELECT * FROM (
SELECT N''15-Synonyms'',
       N''USE {#TARGETDB_Q#}; '' +
       CASE WHEN tgt.object_id IS NOT NULL THEN N''DROP SYNONYM '' + QUOTENAME(s.name)+N''.'' + QUOTENAME(sn.name) + N''; '' ELSE N'''' END +
       N''CREATE SYNONYM '' + QUOTENAME(s.name) + N''.'' + QUOTENAME(sn.name) + N'' FOR '' + sn.base_object_name
FROM {#SOURCEDB_Q#}.sys.synonyms sn
JOIN {#SOURCEDB_Q#}.sys.schemas s ON s.schema_id=sn.schema_id
OUTER APPLY (
    SELECT tsn.object_id, tsn.base_object_name
    FROM {#TARGETDB_Q#}.sys.synonyms tsn
    JOIN {#TARGETDB_Q#}.sys.schemas ts ON ts.schema_id=tsn.schema_id
    WHERE ts.name{#COLLATION#}=s.name{#COLLATION#} AND tsn.name{#COLLATION#}=sn.name{#COLLATION#}
) tgt
WHERE s.name{#COLLATION#}<>N''sys''{#COLLATION#}
    AND (tgt.object_id IS NULL OR ISNULL(tgt.base_object_name{#COLLATION#},N'''')<>ISNULL(sn.base_object_name{#COLLATION#},N''''))
    AND (PARSENAME(sn.base_object_name,3) IS NULL OR PARSENAME(sn.base_object_name,3){#COLLATION#}=N''{#SOURCEDB#}''{#COLLATION#})
        AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard=1 AND (sn.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#}+N''.''{#COLLATION#}+sn.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard=0 AND (sn.name{#COLLATION#}=ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#}+N''.''{#COLLATION#}+sn.name{#COLLATION#})=ex.Pattern{#COLLATION#})))
        ) AS X(Phase, Command)
    WHERE X.Command IS NOT NULL AND LEN(X.Command)>0;';
    SET @DYNAMICSQL = REPLACE(REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);
    SET @DYNAMICSQL = REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDB, @SOURCEDATABASE), @PLACEHOLDERTARGETDB, @TARGETDATABASE);
    BEGIN TRY
        EXEC sys.sp_executesql @DYNAMICSQL;
    IF @DEBUG = 1 PRINT '[DEBUG] Synonym commands built successfully';
    END TRY
    BEGIN CATCH
        PRINT '[ERROR] Failed to build Synonym commands!';
        PRINT '[ERROR] Error Message: ' + ERROR_MESSAGE();
        PRINT '[ERROR] Dynamic SQL (first 8000 chars):';
        PRINT SUBSTRING(@DYNAMICSQL, 1, 8000);
        IF LEN(@DYNAMICSQL) > 8000 PRINT '[ERROR] (continued): ' + SUBSTRING(@DYNAMICSQL, 8001, 8000);
        THROW;
    END CATCH;
    END ELSE PRINT '[INFO] Skipping Synonyms (disabled by @pSYNCOBJECTS)';
    -----------------------------------------------------------------------------------------------------------------------------

        --=================================================-- Datenabgleich (optional, Tabellen mit PK) --================================================--
        IF @INCLUDEDATA = 1 BEGIN
                --===============================================-- Log result to table --=================================================--
                IF (UPPER(@LOGLEVEL) IN (N'INFO',N'WARN',N'ERROR')) BEGIN
                    INSERT INTO [TBDD_SYNC_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1],    [MESSAGE2],             [MESSAGE3],                    [ADDED_WHO],        [ADDED_WHEN])
                                                    VALUES ('INFO',  '04-Data', 'Data-Sync aktiviert', 'Hash-basierter Datenabgleich, DeleteMissing: '+CONVERT(NVARCHAR(1),@DELETEMISSING), @MY_PROCEDURE_NAME, GETDATE());
                END;

                -----------------------------------------------------------------------------------------------------------------------------
         SET @DELCLAUSE = CASE WHEN @DELETEMISSING=1 THEN N'
        WHEN NOT MATCHED BY SOURCE THEN DELETE' ELSE N'' END;
         SET @DYNAMICSQL = N'
    INSERT INTO #TBDD_SYNC_DATABASE_COMMANDS(Phase, Command)
    SELECT * FROM (
    SELECT N''20-Data'',
        N''USE {#TARGETDB_Q#};
    IF OBJECT_ID(N'''''' + QUOTENAME(sch.name)+N''.'' + QUOTENAME(t.name) + N'''''', N''''U'''') IS NULL RETURN;
    DECLARE @rcs BIGINT, @rct BIGINT, @chks INT, @chkt INT;
    SELECT @rcs=COUNT(*), @chks=CHECKSUM_AGG(BINARY_CHECKSUM(''+cols.ChecksumCols+'')) FROM {#SOURCEDB_Q#}.''+QUOTENAME(sch.name)+N''.''{#COLLATION#}+QUOTENAME(t.name)+'';
    SELECT @rct=COUNT(*), @chkt=CHECKSUM_AGG(BINARY_CHECKSUM(''+cols.ChecksumCols+'')) FROM {#TARGETDB_Q#}.''+QUOTENAME(sch.name)+N''.''{#COLLATION#}+QUOTENAME(t.name)+'';
    IF @rcs<>@rct OR ISNULL(@chks,-1)<>ISNULL(@chkt,-1)
    BEGIN
      '' + CASE WHEN idc.object_id IS NOT NULL THEN N''SET IDENTITY_INSERT ''+QUOTENAME(sch.name)+N''.''{#COLLATION#}+QUOTENAME(t.name)+'' ON; '' ELSE N'''' END +
      N''MERGE {#TARGETDB_Q#}.''+QUOTENAME(sch.name)+N''.''{#COLLATION#}+QUOTENAME(t.name)+'' AS tgt
        USING {#SOURCEDB_Q#}.''+QUOTENAME(sch.name)+N''.''{#COLLATION#}+QUOTENAME(t.name)+'' AS src
          ON ''+pkhash.JoinCondition+''
                '' + CASE WHEN cols.UpdateList IS NOT NULL AND cols.UpdateList<>'''' THEN N''WHEN MATCHED AND BINARY_CHECKSUM(''+pkhash.HashSrcCols+'') <> BINARY_CHECKSUM(''+pkhash.HashTgtCols+'') THEN UPDATE SET ''+cols.UpdateList+'' '' ELSE N'''' END +
        N''WHEN NOT MATCHED THEN INSERT(''+cols.InsertCols+'') VALUES(''+cols.InsertVals+'')'' + N''@DEL_CLAUSE@'' + N'';
      '' + CASE WHEN idc.object_id IS NOT NULL THEN N''SET IDENTITY_INSERT ''+QUOTENAME(sch.name)+N''.''{#COLLATION#}+QUOTENAME(t.name)+'' OFF;'' ELSE N'''' END + N''
    END;''
    FROM {#SOURCEDB_Q#}.sys.tables t
    JOIN {#SOURCEDB_Q#}.sys.schemas sch ON sch.schema_id=t.schema_id
    JOIN {#SOURCEDB_Q#}.sys.key_constraints pkc ON pkc.parent_object_id=t.object_id AND pkc.type{#COLLATION#}=''PK''{#COLLATION#}
    OUTER APPLY (
        SELECT STUFF((SELECT '' AND src.''+QUOTENAME(c.name)+'' = tgt.''+QUOTENAME(c.name)
                FROM {#SOURCEDB_Q#}.sys.index_columns ic
                JOIN {#SOURCEDB_Q#}.sys.columns c ON c.object_id=ic.object_id AND c.column_id=ic.column_id
                WHERE ic.object_id=pkc.parent_object_id AND ic.index_id=pkc.unique_index_id
                ORDER BY ic.key_ordinal
                FOR XML PATH(''''), TYPE).value(''.'',''nvarchar(max)''),1,5,N'''') AS JoinCondition,
            STUFF((SELECT '','' + ''src.''+QUOTENAME(c.name)
                FROM {#SOURCEDB_Q#}.sys.columns c
                JOIN {#SOURCEDB_Q#}.sys.types tyh ON tyh.user_type_id=c.user_type_id
                WHERE c.object_id=t.object_id AND c.is_computed=0 AND tyh.name{#COLLATION#} NOT IN (N''text'',N''ntext'',N''image'',N''xml'',N''geography'',N''geometry'',N''hierarchyid'',N''timestamp'',N''rowversion'')
                ORDER BY c.column_id
                FOR XML PATH(''''), TYPE).value(''.'',''nvarchar(max)''),1,1,N'''') AS HashSrcCols,
            STUFF((SELECT '','' + ''tgt.''+QUOTENAME(c.name)
                FROM {#SOURCEDB_Q#}.sys.columns c
                JOIN {#SOURCEDB_Q#}.sys.types tyh ON tyh.user_type_id=c.user_type_id
                WHERE c.object_id=t.object_id AND c.is_computed=0 AND tyh.name{#COLLATION#} NOT IN (N''text'',N''ntext'',N''image'',N''xml'',N''geography'',N''geometry'',N''hierarchyid'',N''timestamp'',N''rowversion'')
                ORDER BY c.column_id
                FOR XML PATH(''''), TYPE).value(''.'',''nvarchar(max)''),1,1,N'''') AS HashTgtCols
    ) pkhash
    CROSS APPLY (
        SELECT STUFF((SELECT '',''{#COLLATION#}+QUOTENAME(c.name)
                FROM {#SOURCEDB_Q#}.sys.columns c
                JOIN {#SOURCEDB_Q#}.sys.types ty2 ON ty2.user_type_id=c.user_type_id
                WHERE c.object_id=t.object_id AND c.is_computed=0 AND ty2.name{#COLLATION#} NOT IN (N''text'',N''ntext'',N''image'',N''xml'',N''geography'',N''geometry'',N''hierarchyid'',N''timestamp'',N''rowversion'')
                ORDER BY c.column_id
                FOR XML PATH(''''), TYPE).value(''.'',''nvarchar(max)''),1,1,N'''') AS InsertCols,
            STUFF((SELECT '','' + ''src.''+QUOTENAME(c.name)
                FROM {#SOURCEDB_Q#}.sys.columns c
                JOIN {#SOURCEDB_Q#}.sys.types ty2 ON ty2.user_type_id=c.user_type_id
                WHERE c.object_id=t.object_id AND c.is_computed=0 AND ty2.name{#COLLATION#} NOT IN (N''text'',N''ntext'',N''image'',N''xml'',N''geography'',N''geometry'',N''hierarchyid'',N''timestamp'',N''rowversion'')
                ORDER BY c.column_id
                FOR XML PATH(''''), TYPE).value(''.'',''nvarchar(max)''),1,1,N'''') AS InsertVals,
            STUFF((SELECT '',''{#COLLATION#}+QUOTENAME(c.name)+'' = src.''+QUOTENAME(c.name)
                FROM {#SOURCEDB_Q#}.sys.columns c
                JOIN {#SOURCEDB_Q#}.sys.types ty2 ON ty2.user_type_id=c.user_type_id
                LEFT JOIN {#SOURCEDB_Q#}.sys.index_columns ic ON ic.object_id=c.object_id AND ic.column_id=c.column_id AND ic.index_id=pkc.unique_index_id
                WHERE c.object_id=t.object_id AND c.is_computed=0 AND ISNULL(ic.key_ordinal,0)=0 AND ty2.name{#COLLATION#} NOT IN (N''text'',N''ntext'',N''image'',N''xml'',N''geography'',N''geometry'',N''hierarchyid'',N''timestamp'',N''rowversion'')
                ORDER BY c.column_id
                FOR XML PATH(''''), TYPE).value(''.'',''nvarchar(max)''),1,1,N'''') AS UpdateList,
            ISNULL(STUFF((SELECT '',''{#COLLATION#}+QUOTENAME(c.name)
                FROM {#SOURCEDB_Q#}.sys.columns c
                JOIN {#SOURCEDB_Q#}.sys.types ty2 ON ty2.user_type_id=c.user_type_id
                WHERE c.object_id=t.object_id AND c.is_computed=0 AND ty2.name{#COLLATION#} NOT IN (N''text'',N''ntext'',N''image'',N''xml'',N''geography'',N''geometry'',N''hierarchyid'',N''timestamp'',N''rowversion'')
                ORDER BY c.column_id
                FOR XML PATH(''''), TYPE).value(''.'',''nvarchar(max)''),1,1,N''''), N''0'') AS ChecksumCols
    ) cols
        LEFT JOIN {#SOURCEDB_Q#}.sys.identity_columns idc ON idc.object_id=t.object_id
        WHERE sch.name{#COLLATION#}<>N''sys''{#COLLATION#} AND t.is_ms_shipped=0
            AND pkhash.JoinCondition IS NOT NULL
                AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard=1 AND (t.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (sch.name{#COLLATION#}+N''.''{#COLLATION#}+t.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard=0 AND (t.name{#COLLATION#}=ex.Pattern{#COLLATION#} OR (sch.name{#COLLATION#}+N''.''{#COLLATION#}+t.name{#COLLATION#})=ex.Pattern{#COLLATION#})))
            ) AS X(Phase, Command)
            WHERE X.Command IS NOT NULL AND LEN(X.Command)>0;';
            SET @DYNAMICSQL = REPLACE(@DYNAMICSQL, N'@DEL_CLAUSE@', @DELCLAUSE);
            SET @DYNAMICSQL = REPLACE(REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);
            SET @DYNAMICSQL = REPLACE(REPLACE(@DYNAMICSQL, @PLACEHOLDERSOURCEDB, @SOURCEDATABASE), @PLACEHOLDERTARGETDB, @TARGETDATABASE);
            BEGIN TRY
                EXEC sys.sp_executesql @DYNAMICSQL;
    IF @DEBUG = 1 PRINT '[DEBUG] Data commands built successfully';
            END TRY
            BEGIN CATCH
                PRINT '[ERROR] Failed to build Data commands!';
                PRINT '[ERROR] Error Message: ' + ERROR_MESSAGE();
                PRINT '[ERROR] Dynamic SQL (first 8000 chars):';
                PRINT SUBSTRING(@DYNAMICSQL, 1, 8000);
                IF LEN(@DYNAMICSQL) > 8000 PRINT '[ERROR] (continued): ' + SUBSTRING(@DYNAMICSQL, 8001, 8000);
                THROW;
            END CATCH;
        END;
    -----------------------------------------------------------------------------------------------------------------------------

    --=================================================-- Platzhalter ersetzen --================================================--
    -- Performance: Ein UPDATE mit mehrfachem REPLACE statt vier separate UPDATEs
    UPDATE #TBDD_SYNC_DATABASE_COMMANDS
    SET    Command = REPLACE(
                        REPLACE(
                            REPLACE(
                                REPLACE(Command, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED),
                            @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED),
                        @PLACEHOLDERSOURCEDB, @SOURCEDATABASE),
                    @PLACEHOLDERTARGETDB, @TARGETDATABASE);

    --=================================================-- Befehle generiert --=================================================--
    DECLARE @COMMANDCOUNT INT = (SELECT COUNT(*) FROM #TBDD_SYNC_DATABASE_COMMANDS);
    --===============================================-- Log result to table --=================================================--
    IF (UPPER(@LOGLEVEL) IN (N'INFO',N'WARN',N'ERROR')) BEGIN
        INSERT INTO [TBDD_SYNC_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1],    [MESSAGE2],             [MESSAGE3],                    [ADDED_WHO],        [ADDED_WHEN])
                                        VALUES ('INFO',  '05-Planning', 'Befehle generiert', 'Anzahl: '+CONVERT(NVARCHAR(10),@COMMANDCOUNT)+', Execute-Modus: '+CONVERT(NVARCHAR(1),@EXECUTE), @MY_PROCEDURE_NAME, GETDATE());
    END;

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

    --=================================================-- SYNC SUMMARY --=================================================--
    -- Zusammenfassung der zu synchronisierenden Objekte (nur wenn Commands vorhanden)
    IF @COMMANDCOUNT > 0 BEGIN
        PRINT N'';
        PRINT N'====================================================================================================';
        PRINT N'                              SYNCHRONIZATION SUMMARY';
        PRINT N'====================================================================================================';
        PRINT N'Source: ' + @SOURCEDATABASE + N'  -->  Target: ' + @TARGETDATABASE;
        PRINT N'Execute Mode: ' + CASE WHEN @EXECUTE = 1 THEN N'LIVE (Changes will be applied)' ELSE N'DRY-RUN (Preview only)' END;
        PRINT N'----------------------------------------------------------------------------------------------------';

    -- Aggregierte Übersicht nach Objekttyp
    DECLARE @SummaryTable TABLE (
        Category NVARCHAR(50),
        ObjectType NVARCHAR(50),
        ObjectCount INT,
        SortOrder INT
    );

    INSERT INTO @SummaryTable (Category, ObjectType, ObjectCount, SortOrder)
    SELECT
        CASE
            WHEN Phase LIKE '01-Security%' THEN 'Security'
            WHEN Phase = '02-Schemas' THEN 'Schemas'
            WHEN Phase LIKE '03-Table%' THEN 'Tables'
            WHEN Phase = '04-FK-DropForPK' THEN 'FK Drops (for PK)'
            WHEN Phase = '05-PrimaryKeys' THEN 'Primary Keys'
            WHEN Phase = '06-ForeignKeys' THEN 'Foreign Keys'
            WHEN Phase = '07-Indexes' THEN 'Indexes'
            WHEN Phase LIKE '10%Triggers%' THEN 'Triggers'
            WHEN Phase LIKE '11%Views%' THEN 'Views'
            WHEN Phase LIKE '12%Functions%' THEN 'Functions'
            WHEN Phase LIKE '13%Procedures%' THEN 'Procedures'
            WHEN Phase = '14-Sequences' THEN 'Sequences'
            WHEN Phase = '15-Synonyms' THEN 'Synonyms'
            WHEN Phase = '20-Data' THEN 'Data Sync'
            ELSE 'Other'
        END,
        CASE
            WHEN Phase = '01-Security-Roles' THEN 'Roles'
            WHEN Phase = '01-Security-Users' THEN 'Users'
            WHEN Phase = '01-Security-RoleMembers' THEN 'Role Members'
            WHEN Phase = '01-Security-Grants' THEN 'Permissions'
            WHEN Phase = '02-Schemas' THEN 'Schemas'
            WHEN Phase = '03-Tables' THEN 'New Tables'
            WHEN Phase = '03-Table-AddColumns' THEN 'Add Columns'
            WHEN Phase = '03-Table-AlterColumns' THEN 'Alter Columns'
            WHEN Phase = '03-Table-Defaults' THEN 'Default Constraints'
            WHEN Phase = '03-Table-Checks' THEN 'Check Constraints'
            WHEN Phase = '03-Table-Unique' THEN 'Unique Constraints'
            WHEN Phase = '04-FK-DropForPK' THEN 'FK Drops'
            WHEN Phase = '05-PrimaryKeys' THEN 'Primary Keys'
            WHEN Phase = '06-ForeignKeys' THEN 'Foreign Keys'
            WHEN Phase = '07-Indexes' THEN 'Indexes'
            WHEN Phase LIKE '10%Triggers%' THEN 'Triggers'
            WHEN Phase LIKE '11%Views%' THEN 'Views'
            WHEN Phase LIKE '12%Functions%' THEN 'Functions'
            WHEN Phase LIKE '13%Procedures%' THEN 'Procedures'
            WHEN Phase = '14-Sequences' THEN 'Sequences'
            WHEN Phase = '15-Synonyms' THEN 'Synonyms'
            WHEN Phase = '20-Data' THEN 'Tables with Data'
            ELSE Phase
        END,
        COUNT(*),
        CASE
            WHEN Phase LIKE '01%' THEN 1
            WHEN Phase LIKE '02%' THEN 2
            WHEN Phase LIKE '03%' THEN 3
            WHEN Phase LIKE '04%' THEN 4
            WHEN Phase LIKE '05%' THEN 5
            WHEN Phase LIKE '06%' THEN 6
            WHEN Phase LIKE '07%' THEN 7
            WHEN Phase LIKE '10%' THEN 10
            WHEN Phase LIKE '11%' THEN 11
            WHEN Phase LIKE '12%' THEN 12
            WHEN Phase LIKE '13%' THEN 13
            WHEN Phase LIKE '14%' THEN 14
            WHEN Phase LIKE '15%' THEN 15
            WHEN Phase LIKE '20%' THEN 20
            ELSE 99
        END
    FROM #TBDD_SYNC_DATABASE_COMMANDS
    GROUP BY Phase;

    -- Print Summary Header
    PRINT N'';
    PRINT N'TOTAL COMMANDS TO ' + CASE WHEN @EXECUTE = 1 THEN N'EXECUTE' ELSE N'PREVIEW' END + N': ' + CONVERT(NVARCHAR(10), @COMMANDCOUNT);
    PRINT N'';

    -- Kategorisierte Ausgabe per Cursor
    DECLARE @Cat NVARCHAR(50), @ObjType NVARCHAR(50), @ObjCount INT, @LastCat NVARCHAR(50) = N'';
    DECLARE summary_cursor CURSOR LOCAL FAST_FORWARD FOR
        SELECT Category, ObjectType, ObjectCount
        FROM @SummaryTable
        ORDER BY SortOrder, ObjectType;

    OPEN summary_cursor;
    FETCH NEXT FROM summary_cursor INTO @Cat, @ObjType, @ObjCount;

    WHILE @@FETCH_STATUS = 0 BEGIN
        IF @Cat <> @LastCat BEGIN
            IF @LastCat <> N'' PRINT N'';
            PRINT N'[ ' + @Cat + N' ]';
            SET @LastCat = @Cat;
        END;
        PRINT N'    ' + @ObjType + N': ' + CONVERT(NVARCHAR(10), @ObjCount);
        FETCH NEXT FROM summary_cursor INTO @Cat, @ObjType, @ObjCount;
    END;
    CLOSE summary_cursor;
    DEALLOCATE summary_cursor;

    -- Detaillierte Objektliste nur bei DEBUG-Modus
    IF @DEBUG = 1 BEGIN
    PRINT N'';
    PRINT N'====================================================================================================';
    PRINT N'                              OBJECT DETAILS BY CATEGORY';
    PRINT N'====================================================================================================';

    -- Detaillierte Objektliste per Phase mit extrahierten Objektnamen
    -- Vereinfachte, robuste Extraktion
    DECLARE @DetailPhase NVARCHAR(50), @DetailObjectName NVARCHAR(500), @LastPhase NVARCHAR(50) = N'';
    DECLARE detail_cursor CURSOR LOCAL FAST_FORWARD FOR
        SELECT
            Phase,
            -- Phase-basierte Objektnamen-Extraktion
            CASE
                -- 02-Schemas: Extrahiere aus "name = N'SchemaName'"
                WHEN Phase = N'02-Schemas' AND CHARINDEX('name = N''', Command) > 0 THEN
                    LEFT(SUBSTRING(Command, CHARINDEX('name = N''', Command) + 9, 100),
                         ISNULL(NULLIF(CHARINDEX('''', SUBSTRING(Command, CHARINDEX('name = N''', Command) + 9, 100)), 0), 50) - 1)
                -- 11-Views: N'CREATE VIEW ObjectName
                WHEN Phase = N'11B-Views' AND CHARINDEX('N''CREATE VIEW ', Command) > 0 THEN
                    LEFT(SUBSTRING(Command, CHARINDEX('N''CREATE VIEW ', Command) + 14, 200),
                         ISNULL(NULLIF(CHARINDEX(' ', SUBSTRING(Command, CHARINDEX('N''CREATE VIEW ', Command) + 14, 200)), 0), 100) - 1)
                -- 12-Functions: N'CREATE FUNCTION ObjectName
                WHEN Phase = N'12B-Functions' AND CHARINDEX('N''CREATE FUNCTION ', Command) > 0 THEN
                    LEFT(SUBSTRING(Command, CHARINDEX('N''CREATE FUNCTION ', Command) + 18, 200),
                         ISNULL(NULLIF(CHARINDEX('(', SUBSTRING(Command, CHARINDEX('N''CREATE FUNCTION ', Command) + 18, 200)), 0), 100) - 1)
                -- 13-Procedures: N'CREATE PROCEDURE ObjectName
                WHEN Phase = N'13B-Procedures' AND CHARINDEX('N''CREATE PROCEDURE ', Command) > 0 THEN
                    LEFT(SUBSTRING(Command, CHARINDEX('N''CREATE PROCEDURE ', Command) + 19, 200),
                         ISNULL(NULLIF(CHARINDEX(' ', SUBSTRING(Command, CHARINDEX('N''CREATE PROCEDURE ', Command) + 19, 200)), 0), 100) - 1)
                -- 10-Triggers: N'CREATE TRIGGER ObjectName
                WHEN Phase = N'10B-Triggers' AND CHARINDEX('N''CREATE TRIGGER ', Command) > 0 THEN
                    LEFT(SUBSTRING(Command, CHARINDEX('N''CREATE TRIGGER ', Command) + 17, 200),
                         ISNULL(NULLIF(CHARINDEX(' ', SUBSTRING(Command, CHARINDEX('N''CREATE TRIGGER ', Command) + 17, 200)), 0), 100) - 1)
                WHEN Command LIKE '%ALTER TABLE %' THEN
                    LEFT(SUBSTRING(Command, CHARINDEX('ALTER TABLE ', Command) + 12, 200),
                         ISNULL(NULLIF(CHARINDEX(' ', SUBSTRING(Command, CHARINDEX('ALTER TABLE ', Command) + 12, 200)), 0), 200) - 1)
                WHEN Command LIKE '%CREATE TABLE %' THEN
                    LEFT(SUBSTRING(Command, CHARINDEX('CREATE TABLE ', Command) + 13, 200),
                         ISNULL(NULLIF(CHARINDEX('(', SUBSTRING(Command, CHARINDEX('CREATE TABLE ', Command) + 13, 200)), 0), 200) - 1)

                -- Constraints: ADD CONSTRAINT [Name]
                WHEN Command LIKE '%ADD CONSTRAINT %' THEN
                    LEFT(SUBSTRING(Command, CHARINDEX('ADD CONSTRAINT ', Command) + 15, 200),
                         ISNULL(NULLIF(CHARINDEX(' ', SUBSTRING(Command, CHARINDEX('ADD CONSTRAINT ', Command) + 15, 200)), 0), 200) - 1)

                -- DROP CONSTRAINT
                WHEN Command LIKE '%DROP CONSTRAINT %' THEN
                    LEFT(SUBSTRING(Command, CHARINDEX('DROP CONSTRAINT ', Command) + 16, 200),
                         ISNULL(NULLIF(CHARINDEX(';', SUBSTRING(Command, CHARINDEX('DROP CONSTRAINT ', Command) + 16, 200)), 0), 200) - 1)

                -- Indexes: CREATE ... INDEX [Name] ON (robuster Check #4)
                WHEN Command LIKE '%INDEX %' AND Command LIKE '% ON %' AND CHARINDEX(' ON ', Command) > 4 THEN
                    REVERSE(LEFT(REVERSE(LEFT(Command, CHARINDEX(' ON ', Command) - 1)),
                         ISNULL(NULLIF(CHARINDEX(' ', REVERSE(LEFT(Command, CHARINDEX(' ON ', Command) - 1))), 0), 100) - 1))

                -- Triggers: CREATE OR ALTER TRIGGER [Name] ON
                WHEN Command LIKE '%CREATE OR ALTER TRIGGER %' AND Command LIKE '% ON %' THEN
                    LEFT(SUBSTRING(Command, CHARINDEX('CREATE OR ALTER TRIGGER ', Command) + 24, 200),
                         ISNULL(NULLIF(CHARINDEX(' ON ', SUBSTRING(Command, CHARINDEX('CREATE OR ALTER TRIGGER ', Command) + 24, 200)), 0), 200) - 1)

                -- Views: CREATE OR ALTER VIEW [Schema].[Name]
                WHEN Command LIKE '%CREATE OR ALTER VIEW %' THEN
                    LEFT(SUBSTRING(Command, PATINDEX('%CREATE OR ALTER VIEW %', Command) + 21, 200),
                         ISNULL(NULLIF(PATINDEX('%[^a-zA-Z0-9_.[]]]%', SUBSTRING(Command, PATINDEX('%CREATE OR ALTER VIEW %', Command) + 21, 200) + ' '), 0), 100) - 1)

                -- Functions: CREATE OR ALTER FUNCTION
                WHEN Command LIKE '%CREATE OR ALTER FUNCTION %' THEN
                    LEFT(SUBSTRING(Command, PATINDEX('%CREATE OR ALTER FUNCTION %', Command) + 25, 200),
                         ISNULL(NULLIF(CHARINDEX('(', SUBSTRING(Command, PATINDEX('%CREATE OR ALTER FUNCTION %', Command) + 25, 200)), 0), 100) - 1)

                -- Procedures: CREATE OR ALTER PROC[EDURE]
                WHEN Command LIKE '%CREATE OR ALTER PROCEDURE %' THEN
                    LEFT(SUBSTRING(Command, PATINDEX('%CREATE OR ALTER PROCEDURE %', Command) + 26, 200),
                         ISNULL(NULLIF(PATINDEX('%[^a-zA-Z0-9_.[]]]%', SUBSTRING(Command, PATINDEX('%CREATE OR ALTER PROCEDURE %', Command) + 26, 200) + ' '), 0), 100) - 1)
                WHEN Command LIKE '%CREATE OR ALTER PROC %' THEN
                    LEFT(SUBSTRING(Command, PATINDEX('%CREATE OR ALTER PROC %', Command) + 21, 200),
                         ISNULL(NULLIF(PATINDEX('%[^a-zA-Z0-9_.[]]]%', SUBSTRING(Command, PATINDEX('%CREATE OR ALTER PROC %', Command) + 21, 200) + ' '), 0), 100) - 1)

                -- Sequences
                WHEN Command LIKE '%CREATE SEQUENCE %' THEN
                    LEFT(SUBSTRING(Command, CHARINDEX('CREATE SEQUENCE ', Command) + 16, 200),
                         ISNULL(NULLIF(CHARINDEX(' ', SUBSTRING(Command, CHARINDEX('CREATE SEQUENCE ', Command) + 16, 200)), 0), 100) - 1)

                -- Synonyms
                WHEN Command LIKE '%CREATE SYNONYM %' THEN
                    LEFT(SUBSTRING(Command, CHARINDEX('CREATE SYNONYM ', Command) + 15, 200),
                         ISNULL(NULLIF(CHARINDEX(' FOR ', SUBSTRING(Command, CHARINDEX('CREATE SYNONYM ', Command) + 15, 200)), 0), 100) - 1)

                -- Data Sync: MERGE [DB].[Schema].[Table] AS tgt
                WHEN Command LIKE '%MERGE %' AND Command LIKE '% AS tgt%' THEN
                    LEFT(SUBSTRING(Command, CHARINDEX('MERGE ', Command) + 6, 200),
                         ISNULL(NULLIF(CHARINDEX(' AS tgt', SUBSTRING(Command, CHARINDEX('MERGE ', Command) + 6, 200)), 0), 100) - 1)

                -- Security: CREATE ROLE, CREATE USER, CREATE SCHEMA
                WHEN Command LIKE '%CREATE ROLE %' THEN
                    LEFT(SUBSTRING(Command, CHARINDEX('CREATE ROLE ', Command) + 12, 100),
                         ISNULL(NULLIF(CHARINDEX(';', SUBSTRING(Command, CHARINDEX('CREATE ROLE ', Command) + 12, 100)), 0), 50) - 1)
                WHEN Command LIKE '%CREATE USER %' THEN
                    LEFT(SUBSTRING(Command, CHARINDEX('CREATE USER ', Command) + 12, 100),
                         ISNULL(NULLIF(CHARINDEX(' ', SUBSTRING(Command, CHARINDEX('CREATE USER ', Command) + 12, 100)), 0), 50) - 1)
                WHEN Command LIKE '%CREATE SCHEMA %' THEN
                    LEFT(SUBSTRING(Command, CHARINDEX('CREATE SCHEMA ', Command) + 14, 100),
                         ISNULL(NULLIF(CHARINDEX(' ', SUBSTRING(Command, CHARINDEX('CREATE SCHEMA ', Command) + 14, 100)), 0), 50) - 1)

                -- Grants/RoleMembers: Zeige Anfang des Commands
                WHEN Phase LIKE '01-Security%' THEN LEFT(Command, 80)

                -- Fallback
                ELSE LEFT(Command, 80)
            END AS ObjectName
        FROM #TBDD_SYNC_DATABASE_COMMANDS
        ORDER BY Phase, Command;

    OPEN detail_cursor;
    FETCH NEXT FROM detail_cursor INTO @DetailPhase, @DetailObjectName;

    WHILE @@FETCH_STATUS = 0 BEGIN
        IF @DetailPhase <> @LastPhase BEGIN
            PRINT N'';
            PRINT N'--- ' + @DetailPhase + N' ---';
            SET @LastPhase = @DetailPhase;
        END;
        -- Objektname ausgeben (max 100 Zeichen, bereinigt)
        SET @DetailObjectName = REPLACE(REPLACE(LTRIM(RTRIM(@DetailObjectName)), CHAR(13), ''), CHAR(10), '');
        IF LEN(@DetailObjectName) > 100 SET @DetailObjectName = LEFT(@DetailObjectName, 97) + N'...';
        PRINT N'    ' + @DetailObjectName;
        FETCH NEXT FROM detail_cursor INTO @DetailPhase, @DetailObjectName;
    END;
    CLOSE detail_cursor;
    DEALLOCATE detail_cursor;

    PRINT N'';
    PRINT N'====================================================================================================';
    END -- Ende IF @DEBUG = 1

    END -- Ende von IF @COMMANDCOUNT > 0
    ELSE BEGIN
        -- Keine Änderungen erforderlich
        PRINT N'';
        PRINT N'====================================================================================================';
        PRINT N'                              SYNCHRONIZATION SUMMARY';
        PRINT N'====================================================================================================';
        PRINT N'Source: ' + @SOURCEDATABASE + N'  -->  Target: ' + @TARGETDATABASE;
        PRINT N'';
        PRINT N'NO CHANGES REQUIRED - Databases are already in sync!';
        PRINT N'====================================================================================================';
    END;

    --=================================================-- Ausgabe oder Ausführung --================================================--
    IF @EXECUTE = 0 BEGIN
        -- Detailliste der Commands ausgeben
        PRINT N'';
        PRINT N'                              COMMAND DETAILS (DRY-RUN)';
        PRINT N'====================================================================================================';

        SELECT Step, Phase, Command
        FROM   #TBDD_SYNC_DATABASE_COMMANDS
        ORDER  BY Step;

        -- Nach DRY-RUN wird Validation durchgefuehrt (falls aktiviert)
    END
    ELSE BEGIN
        --=================================================-- Cursor-Ausfuehrung startet --=================================================--
        --===============================================-- Log result to table --=================================================--
        IF (UPPER(@LOGLEVEL) IN (N'INFO',N'WARN',N'ERROR')) BEGIN
            INSERT INTO [TBDD_SYNC_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1],    [MESSAGE2],             [MESSAGE3],                    [ADDED_WHO],        [ADDED_WHEN])
                                            VALUES ('INFO',  '06-Execution', 'Cursor-Ausfuehrung startet', 'Anzahl Commands: '+CONVERT(NVARCHAR(10),@COMMANDCOUNT), @MY_PROCEDURE_NAME, GETDATE());
        END;

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

        SET @STEP      = NULL;
        SET @PHASE     = NULL;
        SET @COMMAND   = NULL;

        -- Fehler-Counter für Gesamt-Statistik am Ende
        DECLARE @EXECUTION_ERRORS INT = 0;
        DECLARE @EXECUTION_SUCCESS INT = 0;

        -- Transaction-Modus: Bei @USETRANSACTION=1 wird alles in einer Transaction ausgefuehrt
        IF @USETRANSACTION = 1 BEGIN
            PRINT N'[INFO] Transaction mode enabled - starting transaction...';
            BEGIN TRANSACTION SYNC_TRANSACTION;
        END;

        -- Äußeres TRY/CATCH für Cursor-Block
        BEGIN TRY
            DECLARE cmd_cur CURSOR LOCAL FAST_FORWARD FOR
                SELECT Step, Phase, Command
                FROM   #TBDD_SYNC_DATABASE_COMMANDS
                ORDER  BY Step;

            OPEN cmd_cur;
            FETCH NEXT FROM cmd_cur INTO @STEP, @PHASE, @COMMAND;

        WHILE @@FETCH_STATUS = 0 BEGIN
            PRINT 'Executing Step ' + CONVERT(NVARCHAR(10), @STEP) + ' (' + @PHASE + ')';
            SET @SEVERITY = 'INFO';
            SET @ERRMSG   = NULL;

            BEGIN TRY
                -- Sicherheit #19: sp_executesql statt EXEC für bessere Parameterisierung
                EXEC sys.sp_executesql @COMMAND;
                SET @EXECUTION_SUCCESS = @EXECUTION_SUCCESS + 1;  -- Erfolg zählen
            END TRY
        BEGIN CATCH
                SET @SEVERITY = 'ERROR';
                SET @ERRMSG   = ERROR_MESSAGE();
                SET @RETURN_STATUS = 1; -- Status bei Fehler setzen
                SET @EXECUTION_ERRORS = @EXECUTION_ERRORS + 1;  -- Fehler zählen
                PRINT '[ERROR] Step ' + CONVERT(NVARCHAR(10), @STEP) + ' (' + @PHASE + '): ' + @ERRMSG;
                -- Always output failed command for debugging (chunked for large statements)
                BEGIN
                    SET @ERRPOS = 1;  -- Reset for each error
                    PRINT '[ERROR] Failed SQL (length: ' + CONVERT(NVARCHAR(10), LEN(@COMMAND)) + '):';
                    WHILE @ERRPOS <= LEN(@COMMAND) BEGIN
                        PRINT SUBSTRING(@COMMAND, @ERRPOS, @ERRCHUNK);
                        SET @ERRPOS = @ERRPOS + @ERRCHUNK;
                    END;
                    PRINT '[ERROR] --- End of failed SQL ---';
                END;
                -- Abort bei kritischen Phasen
                IF @PHASE IN ('02-Schemas', '03-Tables', '05-PrimaryKeys') BEGIN
                    PRINT 'CRITICAL ERROR in ' + @PHASE + ' - Aborting execution!';
                    -- Transaction-Rollback bei kritischem Fehler
                    IF @USETRANSACTION = 1 AND @@TRANCOUNT > 0 BEGIN
                        PRINT N'[INFO] Rolling back transaction due to critical error...';
                        ROLLBACK TRANSACTION SYNC_TRANSACTION;
                    END;
                    CLOSE cmd_cur;
                    DEALLOCATE cmd_cur;
                    SET @RETURN_STATUS = 1;
                    GOTO CLEANUP_AND_EXIT;
                END;
            END CATCH;

            --================================================-- Log execution result to table --=================================================--
            IF (UPPER(@LOGLEVEL) IN (N'INFO',N'WARN',N'ERROR')) BEGIN
                INSERT INTO [TBDD_SYNC_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1],     [MESSAGE2],                 [MESSAGE3],                      [ADDED_WHO],        [ADDED_WHEN])
                                                 VALUES (@SEVERITY,  @PHASE,      CONVERT(NVARCHAR(256),@STEP),  ISNULL(@ERRMSG, N'OK'),        @MY_PROCEDURE_NAME, GETDATE());
            END;
            -----------------------------------------------------------------------------------------------------------------------

            FETCH NEXT FROM cmd_cur INTO @STEP, @PHASE, @COMMAND;
        END

        CLOSE cmd_cur;
        DEALLOCATE cmd_cur;

        -- Transaction-COMMIT nach erfolgreicher Cursor-Ausfuehrung (ohne kritische Fehler)
        IF @USETRANSACTION = 1 AND @@TRANCOUNT > 0 BEGIN
            IF @EXECUTION_ERRORS = 0 BEGIN
                PRINT N'[INFO] All commands executed successfully - committing transaction...';
                COMMIT TRANSACTION SYNC_TRANSACTION;
            END ELSE BEGIN
                PRINT N'[WARN] ' + CONVERT(NVARCHAR(10), @EXECUTION_ERRORS) + N' errors occurred - rolling back transaction...';
                ROLLBACK TRANSACTION SYNC_TRANSACTION;
                SET @RETURN_STATUS = 1;
            END;
        END;

        END TRY
        BEGIN CATCH  -- Äußeres CATCH für Cursor-Fehler (z.B. DEALLOCATE bei Abbruch)
            -- Transaction-Rollback bei unbehandeltem Fehler
            IF @USETRANSACTION = 1 AND @@TRANCOUNT > 0 BEGIN
                PRINT N'[ERROR] Rolling back transaction due to unhandled error...';
                ROLLBACK TRANSACTION SYNC_TRANSACTION;
            END;
            -- Cursor sicher schließen falls offen
            IF CURSOR_STATUS('local', 'cmd_cur') >= 0 BEGIN
                CLOSE cmd_cur;
                DEALLOCATE cmd_cur;
            END;
            PRINT '[ERROR] Cursor execution failed: ' + ERROR_MESSAGE();
            SET @RETURN_STATUS = 1;
        END CATCH;

        -- Ausführungs-Zusammenfassung ausgeben
        PRINT N'';
        PRINT N'Execution Summary: ' + CONVERT(NVARCHAR(10), @EXECUTION_SUCCESS) + N' successful, ' +
              CONVERT(NVARCHAR(10), @EXECUTION_ERRORS) + N' errors of ' + CONVERT(NVARCHAR(10), @COMMANDCOUNT) + N' total commands';

        --===============================================-- Log result to table --=================================================--
        IF (UPPER(@LOGLEVEL) IN (N'INFO',N'WARN',N'ERROR')) BEGIN
            INSERT INTO [TBDD_SYNC_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1],    [MESSAGE2],             [MESSAGE3],                    [ADDED_WHO],        [ADDED_WHEN])
                                            VALUES ('INFO',  '07-Complete', 'Cursor-Ausfuehrung abgeschlossen',
                                                    'Success: ' + CONVERT(NVARCHAR(10), @EXECUTION_SUCCESS) + ', Errors: ' + CONVERT(NVARCHAR(10), @EXECUTION_ERRORS) + ' of ' + CONVERT(NVARCHAR(10), @COMMANDCOUNT),
                                                    @MY_PROCEDURE_NAME, GETDATE());
        END;
        -----------------------------------------------------------------------------------------------------------------------------

        END; -- End of ELSE block (EXECUTE = 1)

    --=================================================-- CLEANUP AND EXIT --=================================================--
        --=================================================-- Validation Phase --==============================================--
    -- @VALIDATION_MODE: 'NONE', 'SCHEMA', 'DATA', 'ALL' (definiert in Hauptblock)

    IF UPPER(@VALIDATION_MODE) <> N'NONE' BEGIN  -- Validation auch bei @EXECUTE=0 (Preview)
        PRINT N'';
        PRINT N'====================================================================================================';
        PRINT N'Starting validation (' + @VALIDATION_MODE + ')...';
        PRINT N'====================================================================================================';

        -- Reset validation counters for this run
        SET @VALIDATION_PASSED = 1;
        SET @VALIDATION_ERRORS = 0;
        SET @VALIDATION_WARNINGS = 0;

        -- Create validation results table
        CREATE TABLE #ValidationResults (
            ValidationType   NVARCHAR(20)  NOT NULL,  -- 'SCHEMA' or 'DATA'
            ObjectType       NVARCHAR(50)  NOT NULL,
            ObjectName       NVARCHAR(256) NULL,
            SourceCount      BIGINT        NOT NULL,
            TargetCount      BIGINT        NOT NULL,
            Status           NVARCHAR(10)  NOT NULL,  -- 'OK', 'WARN', 'ERROR'
            Message          NVARCHAR(500) NULL
        );

        --===================== SCHEMA VALIDATION =====================--
        IF UPPER(@VALIDATION_MODE) IN (N'ALL', N'SCHEMA') BEGIN
            PRINT N'';
            PRINT N'--- Schema Validation ---';

            -- Validate Tables
            SET @VALIDATION_SQL = N'
            SELECT @src = COUNT(*) FROM {#SOURCEDB_Q#}.sys.tables t
            JOIN {#SOURCEDB_Q#}.sys.schemas s ON s.schema_id = t.schema_id
            WHERE s.name{#COLLATION#}<>N''sys''{#COLLATION#} AND t.is_ms_shipped = 0
              AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard = 1 AND (t.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#} + N''.'' + t.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard = 0 AND (t.name{#COLLATION#} = ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#} + N''.'' + t.name{#COLLATION#}) = ex.Pattern{#COLLATION#})));';
            SET @VALIDATION_SQL = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@VALIDATION_SQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERSOURCEDB, @SOURCEDATABASE), @PLACEHOLDERTARGETDB, @TARGETDATABASE), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);

            EXEC sys.sp_executesql @VALIDATION_SQL, N'@src INT OUTPUT', @src = @VALIDATION_SOURCE_COUNT OUTPUT;

            -- Target-Validation berücksichtigt auch Excludes für korrekten Vergleich
            SET @VALIDATION_SQL = N'
            SELECT @tgt = COUNT(*) FROM {#TARGETDB_Q#}.sys.tables t
            JOIN {#TARGETDB_Q#}.sys.schemas s ON s.schema_id = t.schema_id
            WHERE s.name{#COLLATION#}<>N''sys''{#COLLATION#} AND t.is_ms_shipped = 0
              AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard = 1 AND (t.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#} + N''.'' + t.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard = 0 AND (t.name{#COLLATION#} = ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#} + N''.'' + t.name{#COLLATION#}) = ex.Pattern{#COLLATION#})));';
            SET @VALIDATION_SQL = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@VALIDATION_SQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERSOURCEDB, @SOURCEDATABASE), @PLACEHOLDERTARGETDB, @TARGETDATABASE), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);

            EXEC sys.sp_executesql @VALIDATION_SQL, N'@tgt INT OUTPUT', @tgt = @VALIDATION_TARGET_COUNT OUTPUT;

            INSERT INTO #ValidationResults VALUES ('SCHEMA', 'Tables', NULL, @VALIDATION_SOURCE_COUNT, @VALIDATION_TARGET_COUNT,
                CASE WHEN @VALIDATION_SOURCE_COUNT = @VALIDATION_TARGET_COUNT THEN 'OK' ELSE 'ERROR' END,
                CASE WHEN @VALIDATION_SOURCE_COUNT = @VALIDATION_TARGET_COUNT THEN NULL ELSE 'Mismatch: Source=' + CONVERT(NVARCHAR(10), @VALIDATION_SOURCE_COUNT) + ', Target=' + CONVERT(NVARCHAR(10), @VALIDATION_TARGET_COUNT) END);
            IF @VALIDATION_SOURCE_COUNT <> @VALIDATION_TARGET_COUNT BEGIN SET @VALIDATION_PASSED = 0; SET @VALIDATION_ERRORS = @VALIDATION_ERRORS + 1; END;
            PRINT 'Tables: Source=' + CONVERT(NVARCHAR(10), @VALIDATION_SOURCE_COUNT) + ', Target=' + CONVERT(NVARCHAR(10), @VALIDATION_TARGET_COUNT) + CASE WHEN @VALIDATION_SOURCE_COUNT = @VALIDATION_TARGET_COUNT THEN ' [OK]' ELSE ' [ERROR]' END;

            -- Validate Views
                        SET @VALIDATION_SQL = N'
                        SELECT @src = COUNT(*) FROM {#SOURCEDB_Q#}.sys.views v
                        JOIN {#SOURCEDB_Q#}.sys.schemas s ON s.schema_id = v.schema_id
                        WHERE s.name{#COLLATION#}<>N''sys''{#COLLATION#}
                            AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard = 1 AND (v.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#} + N''.'' + v.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard = 0 AND (v.name{#COLLATION#} = ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#} + N''.'' + v.name{#COLLATION#}) = ex.Pattern{#COLLATION#})));';
            SET @VALIDATION_SQL = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@VALIDATION_SQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERSOURCEDB, @SOURCEDATABASE), @PLACEHOLDERTARGETDB, @TARGETDATABASE), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);

            EXEC sys.sp_executesql @VALIDATION_SQL, N'@src INT OUTPUT', @src = @VALIDATION_SOURCE_COUNT OUTPUT;

            -- Target-Validation berücksichtigt auch Excludes für korrekten Vergleich
            SET @VALIDATION_SQL = N'
            SELECT @tgt = COUNT(*) FROM {#TARGETDB_Q#}.sys.views v
            JOIN {#TARGETDB_Q#}.sys.schemas s ON s.schema_id = v.schema_id
            WHERE s.name{#COLLATION#}<>N''sys''{#COLLATION#}
              AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard = 1 AND (v.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#} + N''.'' + v.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard = 0 AND (v.name{#COLLATION#} = ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#} + N''.'' + v.name{#COLLATION#}) = ex.Pattern{#COLLATION#})));';
            SET @VALIDATION_SQL = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@VALIDATION_SQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERSOURCEDB, @SOURCEDATABASE), @PLACEHOLDERTARGETDB, @TARGETDATABASE), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);

            EXEC sys.sp_executesql @VALIDATION_SQL, N'@tgt INT OUTPUT', @tgt = @VALIDATION_TARGET_COUNT OUTPUT;

            INSERT INTO #ValidationResults VALUES ('SCHEMA', 'Views', NULL, @VALIDATION_SOURCE_COUNT, @VALIDATION_TARGET_COUNT,
                CASE WHEN @VALIDATION_SOURCE_COUNT = @VALIDATION_TARGET_COUNT THEN 'OK' ELSE 'ERROR' END, NULL);
            IF @VALIDATION_SOURCE_COUNT <> @VALIDATION_TARGET_COUNT BEGIN SET @VALIDATION_PASSED = 0; SET @VALIDATION_ERRORS = @VALIDATION_ERRORS + 1; END;
            PRINT 'Views: Source=' + CONVERT(NVARCHAR(10), @VALIDATION_SOURCE_COUNT) + ', Target=' + CONVERT(NVARCHAR(10), @VALIDATION_TARGET_COUNT) + CASE WHEN @VALIDATION_SOURCE_COUNT = @VALIDATION_TARGET_COUNT THEN ' [OK]' ELSE ' [ERROR]' END;

            -- Validate Stored Procedures
                        SET @VALIDATION_SQL = N'
                        SELECT @src = COUNT(*) FROM {#SOURCEDB_Q#}.sys.procedures p
                        JOIN {#SOURCEDB_Q#}.sys.schemas s ON s.schema_id = p.schema_id
                        WHERE s.name{#COLLATION#}<>N''sys''{#COLLATION#}
                            AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard = 1 AND (p.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#} + N''.'' + p.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard = 0 AND (p.name{#COLLATION#} = ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#} + N''.'' + p.name{#COLLATION#}) = ex.Pattern{#COLLATION#})));';
            SET @VALIDATION_SQL = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@VALIDATION_SQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERSOURCEDB, @SOURCEDATABASE), @PLACEHOLDERTARGETDB, @TARGETDATABASE), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);

            EXEC sys.sp_executesql @VALIDATION_SQL, N'@src INT OUTPUT', @src = @VALIDATION_SOURCE_COUNT OUTPUT;

            -- Target-Validation berücksichtigt auch Excludes für korrekten Vergleich
            SET @VALIDATION_SQL = N'
            SELECT @tgt = COUNT(*) FROM {#TARGETDB_Q#}.sys.procedures p
            JOIN {#TARGETDB_Q#}.sys.schemas s ON s.schema_id = p.schema_id
            WHERE s.name{#COLLATION#}<>N''sys''{#COLLATION#}
              AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard = 1 AND (p.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#} + N''.'' + p.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard = 0 AND (p.name{#COLLATION#} = ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#} + N''.'' + p.name{#COLLATION#}) = ex.Pattern{#COLLATION#})));';
            SET @VALIDATION_SQL = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@VALIDATION_SQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERSOURCEDB, @SOURCEDATABASE), @PLACEHOLDERTARGETDB, @TARGETDATABASE), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);

            EXEC sys.sp_executesql @VALIDATION_SQL, N'@tgt INT OUTPUT', @tgt = @VALIDATION_TARGET_COUNT OUTPUT;

            INSERT INTO #ValidationResults VALUES ('SCHEMA', 'Procedures', NULL, @VALIDATION_SOURCE_COUNT, @VALIDATION_TARGET_COUNT,
                CASE WHEN @VALIDATION_SOURCE_COUNT = @VALIDATION_TARGET_COUNT THEN 'OK' ELSE 'ERROR' END, NULL);
            IF @VALIDATION_SOURCE_COUNT <> @VALIDATION_TARGET_COUNT BEGIN SET @VALIDATION_PASSED = 0; SET @VALIDATION_ERRORS = @VALIDATION_ERRORS + 1; END;
            PRINT 'Procedures: Source=' + CONVERT(NVARCHAR(10), @VALIDATION_SOURCE_COUNT) + ', Target=' + CONVERT(NVARCHAR(10), @VALIDATION_TARGET_COUNT) + CASE WHEN @VALIDATION_SOURCE_COUNT = @VALIDATION_TARGET_COUNT THEN ' [OK]' ELSE ' [ERROR]' END;

            -- Validate Functions
                        SET @VALIDATION_SQL = N'
                        SELECT @src = COUNT(*) FROM {#SOURCEDB_Q#}.sys.objects o
                        JOIN {#SOURCEDB_Q#}.sys.schemas s ON s.schema_id = o.schema_id
                        WHERE s.name{#COLLATION#}<>N''sys''{#COLLATION#} AND o.[type] IN (N''FN'',N''TF'',N''IF'',N''FS'',N''FT'')
                            AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard = 1 AND (o.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#} + N''.'' + o.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard = 0 AND (o.name{#COLLATION#} = ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#} + N''.'' + o.name{#COLLATION#}) = ex.Pattern{#COLLATION#})));';
            SET @VALIDATION_SQL = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@VALIDATION_SQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERSOURCEDB, @SOURCEDATABASE), @PLACEHOLDERTARGETDB, @TARGETDATABASE), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);

            EXEC sys.sp_executesql @VALIDATION_SQL, N'@src INT OUTPUT', @src = @VALIDATION_SOURCE_COUNT OUTPUT;

            -- Target-Validation berücksichtigt auch Excludes für korrekten Vergleich
            SET @VALIDATION_SQL = N'
            SELECT @tgt = COUNT(*) FROM {#TARGETDB_Q#}.sys.objects o
            JOIN {#TARGETDB_Q#}.sys.schemas s ON s.schema_id = o.schema_id
            WHERE s.name{#COLLATION#}<>N''sys''{#COLLATION#} AND o.[type] IN (N''FN'',N''TF'',N''IF'',N''FS'',N''FT'')
              AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard = 1 AND (o.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#} + N''.'' + o.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard = 0 AND (o.name{#COLLATION#} = ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#} + N''.'' + o.name{#COLLATION#}) = ex.Pattern{#COLLATION#})));';
            SET @VALIDATION_SQL = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@VALIDATION_SQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERSOURCEDB, @SOURCEDATABASE), @PLACEHOLDERTARGETDB, @TARGETDATABASE), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);

            EXEC sys.sp_executesql @VALIDATION_SQL, N'@tgt INT OUTPUT', @tgt = @VALIDATION_TARGET_COUNT OUTPUT;

            INSERT INTO #ValidationResults VALUES ('SCHEMA', 'Functions', NULL, @VALIDATION_SOURCE_COUNT, @VALIDATION_TARGET_COUNT,
                CASE WHEN @VALIDATION_SOURCE_COUNT = @VALIDATION_TARGET_COUNT THEN 'OK' ELSE 'ERROR' END, NULL);
            IF @VALIDATION_SOURCE_COUNT <> @VALIDATION_TARGET_COUNT BEGIN SET @VALIDATION_PASSED = 0; SET @VALIDATION_ERRORS = @VALIDATION_ERRORS + 1; END;
            PRINT 'Functions: Source=' + CONVERT(NVARCHAR(10), @VALIDATION_SOURCE_COUNT) + ', Target=' + CONVERT(NVARCHAR(10), @VALIDATION_TARGET_COUNT) + CASE WHEN @VALIDATION_SOURCE_COUNT = @VALIDATION_TARGET_COUNT THEN ' [OK]' ELSE ' [ERROR]' END;

            -- Validate Triggers
                        SET @VALIDATION_SQL = N'
                        SELECT @src = COUNT(*) FROM {#SOURCEDB_Q#}.sys.triggers tr
                        JOIN {#SOURCEDB_Q#}.sys.objects o ON o.object_id = tr.object_id
                        JOIN {#SOURCEDB_Q#}.sys.schemas s ON s.schema_id = o.schema_id
                        JOIN {#SOURCEDB_Q#}.sys.tables pt ON pt.object_id = tr.parent_id
                        JOIN {#SOURCEDB_Q#}.sys.schemas ps ON ps.schema_id = pt.schema_id
                        WHERE s.name{#COLLATION#}<>N''sys''{#COLLATION#} AND tr.parent_id IS NOT NULL
                            AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard = 1 AND (pt.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (ps.name{#COLLATION#} + N''.'' + pt.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard = 0 AND (pt.name{#COLLATION#} = ex.Pattern{#COLLATION#} OR (ps.name{#COLLATION#} + N''.'' + pt.name{#COLLATION#}) = ex.Pattern{#COLLATION#})))
                            AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard = 1 AND (tr.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#} + N''.'' + tr.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard = 0 AND (tr.name{#COLLATION#} = ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#} + N''.'' + tr.name{#COLLATION#}) = ex.Pattern{#COLLATION#})));';
            SET @VALIDATION_SQL = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@VALIDATION_SQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERSOURCEDB, @SOURCEDATABASE), @PLACEHOLDERTARGETDB, @TARGETDATABASE), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);

            EXEC sys.sp_executesql @VALIDATION_SQL, N'@src INT OUTPUT', @src = @VALIDATION_SOURCE_COUNT OUTPUT;

            -- Target-Validation berücksichtigt auch Excludes für korrekten Vergleich
            SET @VALIDATION_SQL = N'
            SELECT @tgt = COUNT(*) FROM {#TARGETDB_Q#}.sys.triggers tr
            JOIN {#TARGETDB_Q#}.sys.objects o ON o.object_id = tr.object_id
            JOIN {#TARGETDB_Q#}.sys.schemas s ON s.schema_id = o.schema_id
            JOIN {#TARGETDB_Q#}.sys.tables pt ON pt.object_id = tr.parent_id
            JOIN {#TARGETDB_Q#}.sys.schemas ps ON ps.schema_id = pt.schema_id
            WHERE s.name{#COLLATION#}<>N''sys''{#COLLATION#} AND tr.parent_id IS NOT NULL
              AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard = 1 AND (pt.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (ps.name{#COLLATION#} + N''.'' + pt.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard = 0 AND (pt.name{#COLLATION#} = ex.Pattern{#COLLATION#} OR (ps.name{#COLLATION#} + N''.'' + pt.name{#COLLATION#}) = ex.Pattern{#COLLATION#})))
              AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard = 1 AND (tr.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#} + N''.'' + tr.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard = 0 AND (tr.name{#COLLATION#} = ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#} + N''.'' + tr.name{#COLLATION#}) = ex.Pattern{#COLLATION#})));';
            SET @VALIDATION_SQL = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@VALIDATION_SQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERSOURCEDB, @SOURCEDATABASE), @PLACEHOLDERTARGETDB, @TARGETDATABASE), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);

            EXEC sys.sp_executesql @VALIDATION_SQL, N'@tgt INT OUTPUT', @tgt = @VALIDATION_TARGET_COUNT OUTPUT;

            INSERT INTO #ValidationResults VALUES ('SCHEMA', 'Triggers', NULL, @VALIDATION_SOURCE_COUNT, @VALIDATION_TARGET_COUNT,
                CASE WHEN @VALIDATION_SOURCE_COUNT = @VALIDATION_TARGET_COUNT THEN 'OK' ELSE 'ERROR' END, NULL);
            IF @VALIDATION_SOURCE_COUNT <> @VALIDATION_TARGET_COUNT BEGIN SET @VALIDATION_PASSED = 0; SET @VALIDATION_ERRORS = @VALIDATION_ERRORS + 1; END;
            PRINT 'Triggers: Source=' + CONVERT(NVARCHAR(10), @VALIDATION_SOURCE_COUNT) + ', Target=' + CONVERT(NVARCHAR(10), @VALIDATION_TARGET_COUNT) + CASE WHEN @VALIDATION_SOURCE_COUNT = @VALIDATION_TARGET_COUNT THEN ' [OK]' ELSE ' [ERROR]' END;

            -- Validate Indexes
                        SET @VALIDATION_SQL = N'
                        SELECT @src = COUNT(*) FROM {#SOURCEDB_Q#}.sys.indexes i
                        JOIN {#SOURCEDB_Q#}.sys.tables t ON t.object_id = i.object_id
                        JOIN {#SOURCEDB_Q#}.sys.schemas s ON s.schema_id = t.schema_id
                        WHERE s.name{#COLLATION#}<>N''sys''{#COLLATION#} AND i.index_id > 0
                            AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard = 1 AND (t.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#} + N''.'' + t.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard = 0 AND (t.name{#COLLATION#} = ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#} + N''.'' + t.name{#COLLATION#}) = ex.Pattern{#COLLATION#})));';
            SET @VALIDATION_SQL = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@VALIDATION_SQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERSOURCEDB, @SOURCEDATABASE), @PLACEHOLDERTARGETDB, @TARGETDATABASE), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);

            EXEC sys.sp_executesql @VALIDATION_SQL, N'@src INT OUTPUT', @src = @VALIDATION_SOURCE_COUNT OUTPUT;

            -- Target-Validation berücksichtigt auch Excludes für korrekten Vergleich
            SET @VALIDATION_SQL = N'
            SELECT @tgt = COUNT(*) FROM {#TARGETDB_Q#}.sys.indexes i
            JOIN {#TARGETDB_Q#}.sys.tables t ON t.object_id = i.object_id
            JOIN {#TARGETDB_Q#}.sys.schemas s ON s.schema_id = t.schema_id
            WHERE s.name{#COLLATION#}<>N''sys''{#COLLATION#} AND i.index_id > 0
              AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard = 1 AND (t.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#} + N''.'' + t.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard = 0 AND (t.name{#COLLATION#} = ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#} + N''.'' + t.name{#COLLATION#}) = ex.Pattern{#COLLATION#})));';
            SET @VALIDATION_SQL = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@VALIDATION_SQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERSOURCEDB, @SOURCEDATABASE), @PLACEHOLDERTARGETDB, @TARGETDATABASE), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);

            EXEC sys.sp_executesql @VALIDATION_SQL, N'@tgt INT OUTPUT', @tgt = @VALIDATION_TARGET_COUNT OUTPUT;

            INSERT INTO #ValidationResults VALUES ('SCHEMA', 'Indexes', NULL, @VALIDATION_SOURCE_COUNT, @VALIDATION_TARGET_COUNT,
                CASE WHEN @VALIDATION_SOURCE_COUNT = @VALIDATION_TARGET_COUNT THEN 'OK' ELSE 'WARN' END, NULL);
            IF @VALIDATION_SOURCE_COUNT <> @VALIDATION_TARGET_COUNT BEGIN SET @VALIDATION_WARNINGS = @VALIDATION_WARNINGS + 1; END;
            PRINT 'Indexes: Source=' + CONVERT(NVARCHAR(10), @VALIDATION_SOURCE_COUNT) + ', Target=' + CONVERT(NVARCHAR(10), @VALIDATION_TARGET_COUNT) + CASE WHEN @VALIDATION_SOURCE_COUNT = @VALIDATION_TARGET_COUNT THEN ' [OK]' ELSE ' [WARN]' END;

            -- Validate Foreign Keys
                        SET @VALIDATION_SQL = N'
                        SELECT @src = COUNT(*) FROM {#SOURCEDB_Q#}.sys.foreign_keys fk
                        JOIN {#SOURCEDB_Q#}.sys.tables pt ON pt.object_id = fk.parent_object_id
                        JOIN {#SOURCEDB_Q#}.sys.tables rt ON rt.object_id = fk.referenced_object_id
                        JOIN {#SOURCEDB_Q#}.sys.schemas spt ON spt.schema_id = pt.schema_id
                        JOIN {#SOURCEDB_Q#}.sys.schemas srt ON srt.schema_id = rt.schema_id
                        WHERE spt.name{#COLLATION#}<>N''sys''{#COLLATION#}
                            AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard = 1 AND (pt.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (spt.name{#COLLATION#} + N''.'' + pt.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard = 0 AND (pt.name{#COLLATION#} = ex.Pattern{#COLLATION#} OR (spt.name{#COLLATION#} + N''.'' + pt.name{#COLLATION#}) = ex.Pattern{#COLLATION#})))
                            AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard = 1 AND (rt.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (srt.name{#COLLATION#} + N''.'' + rt.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard = 0 AND (rt.name{#COLLATION#} = ex.Pattern{#COLLATION#} OR (srt.name{#COLLATION#} + N''.'' + rt.name{#COLLATION#}) = ex.Pattern{#COLLATION#})));';
            SET @VALIDATION_SQL = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@VALIDATION_SQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERSOURCEDB, @SOURCEDATABASE), @PLACEHOLDERTARGETDB, @TARGETDATABASE), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);

            EXEC sys.sp_executesql @VALIDATION_SQL, N'@src INT OUTPUT', @src = @VALIDATION_SOURCE_COUNT OUTPUT;

            -- Target-Validation berücksichtigt auch Excludes für korrekten Vergleich
            SET @VALIDATION_SQL = N'
            SELECT @tgt = COUNT(*) FROM {#TARGETDB_Q#}.sys.foreign_keys fk
            JOIN {#TARGETDB_Q#}.sys.tables pt ON pt.object_id = fk.parent_object_id
            JOIN {#TARGETDB_Q#}.sys.tables rt ON rt.object_id = fk.referenced_object_id
            JOIN {#TARGETDB_Q#}.sys.schemas spt ON spt.schema_id = pt.schema_id
            JOIN {#TARGETDB_Q#}.sys.schemas srt ON srt.schema_id = rt.schema_id
            WHERE spt.name{#COLLATION#}<>N''sys''{#COLLATION#}
              AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard = 1 AND (pt.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (spt.name{#COLLATION#} + N''.'' + pt.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard = 0 AND (pt.name{#COLLATION#} = ex.Pattern{#COLLATION#} OR (spt.name{#COLLATION#} + N''.'' + pt.name{#COLLATION#}) = ex.Pattern{#COLLATION#})))
              AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex WHERE (ex.IsWildcard = 1 AND (rt.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (srt.name{#COLLATION#} + N''.'' + rt.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#})) OR (ex.IsWildcard = 0 AND (rt.name{#COLLATION#} = ex.Pattern{#COLLATION#} OR (srt.name{#COLLATION#} + N''.'' + rt.name{#COLLATION#}) = ex.Pattern{#COLLATION#})));';
            SET @VALIDATION_SQL = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@VALIDATION_SQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERSOURCEDB, @SOURCEDATABASE), @PLACEHOLDERTARGETDB, @TARGETDATABASE), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);

            EXEC sys.sp_executesql @VALIDATION_SQL, N'@tgt INT OUTPUT', @tgt = @VALIDATION_TARGET_COUNT OUTPUT;

            INSERT INTO #ValidationResults VALUES ('SCHEMA', 'ForeignKeys', NULL, @VALIDATION_SOURCE_COUNT, @VALIDATION_TARGET_COUNT,
                CASE WHEN @VALIDATION_SOURCE_COUNT = @VALIDATION_TARGET_COUNT THEN 'OK' ELSE 'ERROR' END, NULL);
            IF @VALIDATION_SOURCE_COUNT <> @VALIDATION_TARGET_COUNT BEGIN SET @VALIDATION_PASSED = 0; SET @VALIDATION_ERRORS = @VALIDATION_ERRORS + 1; END;
            PRINT 'ForeignKeys: Source=' + CONVERT(NVARCHAR(10), @VALIDATION_SOURCE_COUNT) + ', Target=' + CONVERT(NVARCHAR(10), @VALIDATION_TARGET_COUNT) + CASE WHEN @VALIDATION_SOURCE_COUNT = @VALIDATION_TARGET_COUNT THEN ' [OK]' ELSE ' [ERROR]' END;
        END;

        --===================== DATA VALIDATION =====================--
        IF UPPER(@VALIDATION_MODE) IN (N'ALL', N'DATA') AND @EXECUTE = 1 BEGIN
            PRINT N'';
            PRINT N'--- Data Validation (Row Counts) ---';

            -- Create temp table to hold table list for validation
            CREATE TABLE #TBDD_SYNC_DATABASE_DATA_VALIDATION (
                SchemaName NVARCHAR(128) NOT NULL,
                TableName NVARCHAR(128) NOT NULL
            );
    IF @DEBUG = 1 PRINT '[DEBUG] TBDD_SYNC_DATABASE_DATA_VALIDATION temp table created';

            -- Populate table list dynamically
            -- Korrigiertes Collation-Handling: Beide Seiten der Verkettung mit COLLATE
            SET @VALIDATION_CURSOR_SQL = N'
            INSERT INTO #TBDD_SYNC_DATABASE_DATA_VALIDATION (SchemaName, TableName)
            SELECT s.name AS SchemaName, t.name AS TableName
            FROM {#SOURCEDB_Q#}.sys.tables t
            JOIN {#SOURCEDB_Q#}.sys.schemas s ON s.schema_id = t.schema_id
            WHERE s.name{#COLLATION#}<>N''sys''{#COLLATION#} AND t.is_ms_shipped = 0
              AND NOT EXISTS (SELECT 1 FROM #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS ex
                  WHERE (ex.IsWildcard = 1 AND (t.name{#COLLATION#} LIKE ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#} + N''.'' + t.name{#COLLATION#}) LIKE ex.Pattern{#COLLATION#}))
                     OR (ex.IsWildcard = 0 AND (t.name{#COLLATION#} = ex.Pattern{#COLLATION#} OR (s.name{#COLLATION#} + N''.'' + t.name{#COLLATION#}) = ex.Pattern{#COLLATION#})))
            ORDER BY s.name, t.name;';
            SET @VALIDATION_CURSOR_SQL = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@VALIDATION_CURSOR_SQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERSOURCEDB, @SOURCEDATABASE), @PLACEHOLDERTARGETDB, @TARGETDATABASE), @PLACEHOLDERCOLLATION, @COMPARECOLLATION);
            EXEC sys.sp_executesql @VALIDATION_CURSOR_SQL;

            -- Declare cursor on temp table (LOCAL für Session-Isolation)
            DECLARE data_val_cur CURSOR LOCAL FAST_FORWARD FOR
                SELECT SchemaName, TableName FROM #TBDD_SYNC_DATABASE_DATA_VALIDATION ORDER BY SchemaName, TableName;

            OPEN data_val_cur;
            FETCH NEXT FROM data_val_cur INTO @VALIDATION_SCHEMA_NAME, @VALIDATION_TABLE_NAME;
            WHILE @@FETCH_STATUS = 0 BEGIN
                -- Get source row count
                BEGIN TRY
                    SET @VALIDATION_SQL = N'SELECT @cnt = COUNT_BIG(*) FROM {#SOURCEDB_Q#}.' + QUOTENAME(@VALIDATION_SCHEMA_NAME) + N'.' + QUOTENAME(@VALIDATION_TABLE_NAME) + N';';
                    SET @VALIDATION_SQL = REPLACE(REPLACE(REPLACE(REPLACE(@VALIDATION_SQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERSOURCEDB, @SOURCEDATABASE), @PLACEHOLDERTARGETDB, @TARGETDATABASE);

                    EXEC sys.sp_executesql @VALIDATION_SQL, N'@cnt BIGINT OUTPUT', @cnt = @VALIDATION_SOURCE_ROWS OUTPUT;
                END TRY
        BEGIN CATCH
                    SET @VALIDATION_SOURCE_ROWS = -1;  -- Error reading source table
                    PRINT N'[WARN] Could not read source table ' + @VALIDATION_SCHEMA_NAME + N'.' + @VALIDATION_TABLE_NAME + N': ' + ERROR_MESSAGE();
                END CATCH;

                -- Get target row count
                BEGIN TRY
                    SET @VALIDATION_SQL = N'SELECT @cnt = COUNT_BIG(*) FROM {#TARGETDB_Q#}.' + QUOTENAME(@VALIDATION_SCHEMA_NAME) + N'.' + QUOTENAME(@VALIDATION_TABLE_NAME) + N';';
                    SET @VALIDATION_SQL = REPLACE(REPLACE(REPLACE(REPLACE(@VALIDATION_SQL, @PLACEHOLDERSOURCEDBQUOTED, @SOURCEDATABASEQUOTED), @PLACEHOLDERTARGETDBQUOTED, @TARGETDATABASEQUOTED), @PLACEHOLDERSOURCEDB, @SOURCEDATABASE), @PLACEHOLDERTARGETDB, @TARGETDATABASE);

                    EXEC sys.sp_executesql @VALIDATION_SQL, N'@cnt BIGINT OUTPUT', @cnt = @VALIDATION_TARGET_ROWS OUTPUT;
                END TRY
        BEGIN CATCH
                    SET @VALIDATION_TARGET_ROWS = -1;  -- Table doesn't exist
                END CATCH;

                INSERT INTO #ValidationResults VALUES ('DATA', 'TableRows', @VALIDATION_SCHEMA_NAME + N'.' + @VALIDATION_TABLE_NAME, @VALIDATION_SOURCE_ROWS, @VALIDATION_TARGET_ROWS,
                    CASE WHEN @VALIDATION_SOURCE_ROWS = @VALIDATION_TARGET_ROWS THEN 'OK' WHEN @VALIDATION_TARGET_ROWS = -1 THEN 'ERROR' ELSE 'ERROR' END,
                    CASE WHEN @VALIDATION_SOURCE_ROWS = @VALIDATION_TARGET_ROWS THEN NULL WHEN @VALIDATION_TARGET_ROWS = -1 THEN 'Table missing in target' ELSE 'Row mismatch' END);

                IF @VALIDATION_SOURCE_ROWS <> @VALIDATION_TARGET_ROWS BEGIN
                    SET @VALIDATION_PASSED = 0;
                    SET @VALIDATION_ERRORS = @VALIDATION_ERRORS + 1;
                    PRINT @VALIDATION_SCHEMA_NAME + N'.' + @VALIDATION_TABLE_NAME + N': Source=' + CONVERT(NVARCHAR(20), @VALIDATION_SOURCE_ROWS) + N', Target=' + CONVERT(NVARCHAR(20), @VALIDATION_TARGET_ROWS) + N' [ERROR]';
                END
                ELSE BEGIN
                    PRINT @VALIDATION_SCHEMA_NAME + N'.' + @VALIDATION_TABLE_NAME + N': ' + CONVERT(NVARCHAR(20), @VALIDATION_SOURCE_ROWS) + N' rows [OK]';
                END;

                FETCH NEXT FROM data_val_cur INTO @VALIDATION_SCHEMA_NAME, @VALIDATION_TABLE_NAME;
            END;
            CLOSE data_val_cur;
            DEALLOCATE data_val_cur;

            -- Cleanup temp table
            IF OBJECT_ID('tempdb..#TBDD_SYNC_DATABASE_DATA_VALIDATION') IS NOT NULL DROP TABLE #TBDD_SYNC_DATABASE_DATA_VALIDATION;
        END;

        -- Log validation results
        IF (UPPER(@LOGLEVEL) IN (N'INFO')) BEGIN
            INSERT INTO [TBDD_SYNC_DATABASE_LOG]
                ([LOG_LEVEL], [MESSAGE1], [MESSAGE2], [MESSAGE3], [ADDED_WHO], [ADDED_WHEN])
            SELECT
                CASE WHEN Status = 'ERROR' THEN 'ERROR' WHEN Status = 'WARN' THEN 'WARN' ELSE 'INFO' END,
                'Validation-' + ValidationType,
                ObjectType + ISNULL(' (' + ObjectName + ')', ''),
                'Source=' + CONVERT(NVARCHAR(20), SourceCount) + ', Target=' + CONVERT(NVARCHAR(20), TargetCount) + ' [' + Status + ']',
                @MY_PROCEDURE_NAME,
                GETDATE()
            FROM #ValidationResults;
        END;

        -- Output validation summary
        PRINT N'';
        PRINT N'====================================================================================================';
        IF @VALIDATION_PASSED = 1 BEGIN
            PRINT N'VALIDATION PASSED! All objects migrated successfully.';
            IF @VALIDATION_WARNINGS > 0 BEGIN
                PRINT N'Warnings: ' + CONVERT(NVARCHAR(10), @VALIDATION_WARNINGS);
            END;

            IF (UPPER(@LOGLEVEL) IN (N'INFO')) BEGIN
                INSERT INTO [TBDD_SYNC_DATABASE_LOG]
                    ([LOG_LEVEL], [MESSAGE1], [MESSAGE2], [MESSAGE3], [ADDED_WHO], [ADDED_WHEN])
                VALUES ('INFO', '08-Validation', 'PASSED', 'All checks completed successfully', @MY_PROCEDURE_NAME, GETDATE());
            END;
        END
        ELSE BEGIN
            PRINT N'VALIDATION FAILED! Errors: ' + CONVERT(NVARCHAR(10), @VALIDATION_ERRORS) + N', Warnings: ' + CONVERT(NVARCHAR(10), @VALIDATION_WARNINGS);
            SET @RETURN_STATUS = 1; -- Status bei Validation-Fehler setzen

            IF (UPPER(@LOGLEVEL) IN (N'INFO', N'WARN', N'ERROR')) BEGIN
                INSERT INTO [TBDD_SYNC_DATABASE_LOG]
                    ([LOG_LEVEL], [MESSAGE1], [MESSAGE2], [MESSAGE3], [ADDED_WHO], [ADDED_WHEN])
                VALUES ('ERROR', '08-Validation', 'FAILED', 'Errors: ' + CONVERT(NVARCHAR(10), @VALIDATION_ERRORS) + ', Warnings: ' + CONVERT(NVARCHAR(10), @VALIDATION_WARNINGS), @MY_PROCEDURE_NAME, GETDATE());
            END;
        END;
        PRINT N'====================================================================================================';

        -- Output validation details table (print each row for visibility)
        PRINT N'';
        PRINT N'--- Validation Details ---';
        PRINT N'';

        DECLARE @vr_Type NVARCHAR(20), @vr_ObjType NVARCHAR(50), @vr_ObjName NVARCHAR(256),
                @vr_SrcCnt BIGINT, @vr_TgtCnt BIGINT, @vr_Status NVARCHAR(10), @vr_Msg NVARCHAR(500);
        DECLARE @vr_HasRows BIT = 0;

        DECLARE validation_cursor CURSOR LOCAL FAST_FORWARD FOR
            SELECT ValidationType, ObjectType, ObjectName, SourceCount, TargetCount, Status, Message
            FROM #ValidationResults
            ORDER BY CASE Status WHEN 'ERROR' THEN 1 WHEN 'WARN' THEN 2 ELSE 3 END, ValidationType, ObjectType;

        OPEN validation_cursor;
        FETCH NEXT FROM validation_cursor INTO @vr_Type, @vr_ObjType, @vr_ObjName, @vr_SrcCnt, @vr_TgtCnt, @vr_Status, @vr_Msg;

        WHILE @@FETCH_STATUS = 0 BEGIN
            SET @vr_HasRows = 1;
            PRINT N'[' + @vr_Status + N'] ' + @vr_Type + N' - ' + @vr_ObjType +
                  ISNULL(N' (' + @vr_ObjName + N')', N'') +
                  N': Source=' + CONVERT(NVARCHAR(20), @vr_SrcCnt) +
                  N', Target=' + CONVERT(NVARCHAR(20), @vr_TgtCnt) +
                  ISNULL(N' - ' + @vr_Msg, N'');
            FETCH NEXT FROM validation_cursor INTO @vr_Type, @vr_ObjType, @vr_ObjName, @vr_SrcCnt, @vr_TgtCnt, @vr_Status, @vr_Msg;
        END;

        CLOSE validation_cursor;
        DEALLOCATE validation_cursor;

        IF @vr_HasRows = 0 PRINT N'(No validation results to display)';
        PRINT N'';

        -- ===================== DETAILED MISMATCH REPORT (Bidirectional) =====================
        -- Show specific objects that differ between Source and Target
        IF @VALIDATION_PASSED = 0 AND UPPER(@VALIDATION_MODE) IN (N'ALL', N'SCHEMA') BEGIN
            PRINT N'';
            PRINT N'--- Detailed Mismatch Report ---';
            PRINT N'';

            DECLARE @MismatchSQL NVARCHAR(MAX);
            DECLARE @MismatchName NVARCHAR(500);
            DECLARE @MismatchFound BIT = 0;

            CREATE TABLE #TempMismatch (ObjectName NVARCHAR(500));

            -- Tables: Missing in Target (exist in Source)
            SET @MismatchSQL = N'
            SELECT s.name + N''.'' + t.name
            FROM ' + @SOURCEDATABASEQUOTED + N'.sys.tables t
            JOIN ' + @SOURCEDATABASEQUOTED + N'.sys.schemas s ON s.schema_id = t.schema_id
            WHERE s.name' + @COMPARECOLLATION + N'<>N''sys''' + @COMPARECOLLATION + N' AND t.is_ms_shipped = 0
              AND NOT EXISTS (SELECT 1 FROM ' + @TARGETDATABASEQUOTED + N'.sys.tables tt
                              JOIN ' + @TARGETDATABASEQUOTED + N'.sys.schemas ts ON ts.schema_id = tt.schema_id
                              WHERE ts.name' + @COMPARECOLLATION + N'=s.name' + @COMPARECOLLATION + N'
                                AND tt.name' + @COMPARECOLLATION + N'=t.name' + @COMPARECOLLATION + N')';
            INSERT INTO #TempMismatch EXEC sys.sp_executesql @MismatchSQL;
            IF EXISTS (SELECT 1 FROM #TempMismatch) BEGIN
                SET @MismatchFound = 1;
                PRINT N'[Tables] Missing in Target (exist in Source):';
                DECLARE mismatch_cur CURSOR LOCAL FAST_FORWARD FOR SELECT ObjectName FROM #TempMismatch;
                OPEN mismatch_cur; FETCH NEXT FROM mismatch_cur INTO @MismatchName;
                WHILE @@FETCH_STATUS = 0 BEGIN PRINT N'  - ' + @MismatchName; FETCH NEXT FROM mismatch_cur INTO @MismatchName; END;
                CLOSE mismatch_cur; DEALLOCATE mismatch_cur;
            END;
            TRUNCATE TABLE #TempMismatch;

            -- Tables: Extra in Target (not in Source)
            SET @MismatchSQL = N'
            SELECT s.name + N''.'' + t.name
            FROM ' + @TARGETDATABASEQUOTED + N'.sys.tables t
            JOIN ' + @TARGETDATABASEQUOTED + N'.sys.schemas s ON s.schema_id = t.schema_id
            WHERE s.name' + @COMPARECOLLATION + N'<>N''sys''' + @COMPARECOLLATION + N' AND t.is_ms_shipped = 0
              AND NOT EXISTS (SELECT 1 FROM ' + @SOURCEDATABASEQUOTED + N'.sys.tables st
                              JOIN ' + @SOURCEDATABASEQUOTED + N'.sys.schemas ss ON ss.schema_id = st.schema_id
                              WHERE ss.name' + @COMPARECOLLATION + N'=s.name' + @COMPARECOLLATION + N'
                                AND st.name' + @COMPARECOLLATION + N'=t.name' + @COMPARECOLLATION + N')';
            INSERT INTO #TempMismatch EXEC sys.sp_executesql @MismatchSQL;
            IF EXISTS (SELECT 1 FROM #TempMismatch) BEGIN
                SET @MismatchFound = 1;
                PRINT N'[Tables] Extra in Target (not in Source):';
                DECLARE mismatch_cur CURSOR LOCAL FAST_FORWARD FOR SELECT ObjectName FROM #TempMismatch;
                OPEN mismatch_cur; FETCH NEXT FROM mismatch_cur INTO @MismatchName;
                WHILE @@FETCH_STATUS = 0 BEGIN PRINT N'  - ' + @MismatchName; FETCH NEXT FROM mismatch_cur INTO @MismatchName; END;
                CLOSE mismatch_cur; DEALLOCATE mismatch_cur;
            END;
            TRUNCATE TABLE #TempMismatch;

            -- Views: Missing in Target
            SET @MismatchSQL = N'
            SELECT s.name + N''.'' + v.name FROM ' + @SOURCEDATABASEQUOTED + N'.sys.views v
            JOIN ' + @SOURCEDATABASEQUOTED + N'.sys.schemas s ON s.schema_id = v.schema_id
            WHERE s.name' + @COMPARECOLLATION + N'<>N''sys''' + @COMPARECOLLATION + N'
              AND NOT EXISTS (SELECT 1 FROM ' + @TARGETDATABASEQUOTED + N'.sys.views tv
                              JOIN ' + @TARGETDATABASEQUOTED + N'.sys.schemas ts ON ts.schema_id = tv.schema_id
                              WHERE ts.name' + @COMPARECOLLATION + N'=s.name' + @COMPARECOLLATION + N' AND tv.name' + @COMPARECOLLATION + N'=v.name' + @COMPARECOLLATION + N')';
            INSERT INTO #TempMismatch EXEC sys.sp_executesql @MismatchSQL;
            IF EXISTS (SELECT 1 FROM #TempMismatch) BEGIN
                SET @MismatchFound = 1;
                PRINT N'[Views] Missing in Target:';
                DECLARE mismatch_cur CURSOR LOCAL FAST_FORWARD FOR SELECT ObjectName FROM #TempMismatch;
                OPEN mismatch_cur; FETCH NEXT FROM mismatch_cur INTO @MismatchName;
                WHILE @@FETCH_STATUS = 0 BEGIN PRINT N'  - ' + @MismatchName; FETCH NEXT FROM mismatch_cur INTO @MismatchName; END;
                CLOSE mismatch_cur; DEALLOCATE mismatch_cur;
            END;
            TRUNCATE TABLE #TempMismatch;

            -- Views: Extra in Target
            SET @MismatchSQL = N'
            SELECT s.name + N''.'' + v.name FROM ' + @TARGETDATABASEQUOTED + N'.sys.views v
            JOIN ' + @TARGETDATABASEQUOTED + N'.sys.schemas s ON s.schema_id = v.schema_id
            WHERE s.name' + @COMPARECOLLATION + N'<>N''sys''' + @COMPARECOLLATION + N'
              AND NOT EXISTS (SELECT 1 FROM ' + @SOURCEDATABASEQUOTED + N'.sys.views sv
                              JOIN ' + @SOURCEDATABASEQUOTED + N'.sys.schemas ss ON ss.schema_id = sv.schema_id
                              WHERE ss.name' + @COMPARECOLLATION + N'=s.name' + @COMPARECOLLATION + N' AND sv.name' + @COMPARECOLLATION + N'=v.name' + @COMPARECOLLATION + N')';
            INSERT INTO #TempMismatch EXEC sys.sp_executesql @MismatchSQL;
            IF EXISTS (SELECT 1 FROM #TempMismatch) BEGIN
                SET @MismatchFound = 1;
                PRINT N'[Views] Extra in Target:';
                DECLARE mismatch_cur CURSOR LOCAL FAST_FORWARD FOR SELECT ObjectName FROM #TempMismatch;
                OPEN mismatch_cur; FETCH NEXT FROM mismatch_cur INTO @MismatchName;
                WHILE @@FETCH_STATUS = 0 BEGIN PRINT N'  - ' + @MismatchName; FETCH NEXT FROM mismatch_cur INTO @MismatchName; END;
                CLOSE mismatch_cur; DEALLOCATE mismatch_cur;
            END;
            TRUNCATE TABLE #TempMismatch;

            -- Procedures: Missing in Target
            SET @MismatchSQL = N'
            SELECT s.name + N''.'' + p.name FROM ' + @SOURCEDATABASEQUOTED + N'.sys.procedures p
            JOIN ' + @SOURCEDATABASEQUOTED + N'.sys.schemas s ON s.schema_id = p.schema_id
            WHERE s.name' + @COMPARECOLLATION + N'<>N''sys''' + @COMPARECOLLATION + N'
              AND NOT EXISTS (SELECT 1 FROM ' + @TARGETDATABASEQUOTED + N'.sys.procedures tp
                              JOIN ' + @TARGETDATABASEQUOTED + N'.sys.schemas ts ON ts.schema_id = tp.schema_id
                              WHERE ts.name' + @COMPARECOLLATION + N'=s.name' + @COMPARECOLLATION + N' AND tp.name' + @COMPARECOLLATION + N'=p.name' + @COMPARECOLLATION + N')';
            INSERT INTO #TempMismatch EXEC sys.sp_executesql @MismatchSQL;
            IF EXISTS (SELECT 1 FROM #TempMismatch) BEGIN
                SET @MismatchFound = 1;
                PRINT N'[Procedures] Missing in Target:';
                DECLARE mismatch_cur CURSOR LOCAL FAST_FORWARD FOR SELECT ObjectName FROM #TempMismatch;
                OPEN mismatch_cur; FETCH NEXT FROM mismatch_cur INTO @MismatchName;
                WHILE @@FETCH_STATUS = 0 BEGIN PRINT N'  - ' + @MismatchName; FETCH NEXT FROM mismatch_cur INTO @MismatchName; END;
                CLOSE mismatch_cur; DEALLOCATE mismatch_cur;
            END;
            TRUNCATE TABLE #TempMismatch;

            -- Procedures: Extra in Target
            SET @MismatchSQL = N'
            SELECT s.name + N''.'' + p.name FROM ' + @TARGETDATABASEQUOTED + N'.sys.procedures p
            JOIN ' + @TARGETDATABASEQUOTED + N'.sys.schemas s ON s.schema_id = p.schema_id
            WHERE s.name' + @COMPARECOLLATION + N'<>N''sys''' + @COMPARECOLLATION + N'
              AND NOT EXISTS (SELECT 1 FROM ' + @SOURCEDATABASEQUOTED + N'.sys.procedures sp
                              JOIN ' + @SOURCEDATABASEQUOTED + N'.sys.schemas ss ON ss.schema_id = sp.schema_id
                              WHERE ss.name' + @COMPARECOLLATION + N'=s.name' + @COMPARECOLLATION + N' AND sp.name' + @COMPARECOLLATION + N'=p.name' + @COMPARECOLLATION + N')';
            INSERT INTO #TempMismatch EXEC sys.sp_executesql @MismatchSQL;
            IF EXISTS (SELECT 1 FROM #TempMismatch) BEGIN
                SET @MismatchFound = 1;
                PRINT N'[Procedures] Extra in Target:';
                DECLARE mismatch_cur CURSOR LOCAL FAST_FORWARD FOR SELECT ObjectName FROM #TempMismatch;
                OPEN mismatch_cur; FETCH NEXT FROM mismatch_cur INTO @MismatchName;
                WHILE @@FETCH_STATUS = 0 BEGIN PRINT N'  - ' + @MismatchName; FETCH NEXT FROM mismatch_cur INTO @MismatchName; END;
                CLOSE mismatch_cur; DEALLOCATE mismatch_cur;
            END;
            TRUNCATE TABLE #TempMismatch;

            -- Functions: Missing in Target
            SET @MismatchSQL = N'
            SELECT s.name + N''.'' + o.name FROM ' + @SOURCEDATABASEQUOTED + N'.sys.objects o
            JOIN ' + @SOURCEDATABASEQUOTED + N'.sys.schemas s ON s.schema_id = o.schema_id
            WHERE o.type IN (N''FN'',N''TF'',N''IF'') AND s.name' + @COMPARECOLLATION + N'<>N''sys''' + @COMPARECOLLATION + N'
              AND NOT EXISTS (SELECT 1 FROM ' + @TARGETDATABASEQUOTED + N'.sys.objects tobj
                              JOIN ' + @TARGETDATABASEQUOTED + N'.sys.schemas ts ON ts.schema_id = tobj.schema_id
                              WHERE ts.name' + @COMPARECOLLATION + N'=s.name' + @COMPARECOLLATION + N' AND tobj.name' + @COMPARECOLLATION + N'=o.name' + @COMPARECOLLATION + N' AND tobj.type IN (N''FN'',N''TF'',N''IF''))';
            INSERT INTO #TempMismatch EXEC sys.sp_executesql @MismatchSQL;
            IF EXISTS (SELECT 1 FROM #TempMismatch) BEGIN
                SET @MismatchFound = 1;
                PRINT N'[Functions] Missing in Target:';
                DECLARE mismatch_cur CURSOR LOCAL FAST_FORWARD FOR SELECT ObjectName FROM #TempMismatch;
                OPEN mismatch_cur; FETCH NEXT FROM mismatch_cur INTO @MismatchName;
                WHILE @@FETCH_STATUS = 0 BEGIN PRINT N'  - ' + @MismatchName; FETCH NEXT FROM mismatch_cur INTO @MismatchName; END;
                CLOSE mismatch_cur; DEALLOCATE mismatch_cur;
            END;
            TRUNCATE TABLE #TempMismatch;

            -- Functions: Extra in Target
            SET @MismatchSQL = N'
            SELECT s.name + N''.'' + o.name FROM ' + @TARGETDATABASEQUOTED + N'.sys.objects o
            JOIN ' + @TARGETDATABASEQUOTED + N'.sys.schemas s ON s.schema_id = o.schema_id
            WHERE o.type IN (N''FN'',N''TF'',N''IF'') AND s.name' + @COMPARECOLLATION + N'<>N''sys''' + @COMPARECOLLATION + N'
              AND NOT EXISTS (SELECT 1 FROM ' + @SOURCEDATABASEQUOTED + N'.sys.objects sobj
                              JOIN ' + @SOURCEDATABASEQUOTED + N'.sys.schemas ss ON ss.schema_id = sobj.schema_id
                              WHERE ss.name' + @COMPARECOLLATION + N'=s.name' + @COMPARECOLLATION + N' AND sobj.name' + @COMPARECOLLATION + N'=o.name' + @COMPARECOLLATION + N' AND sobj.type IN (N''FN'',N''TF'',N''IF''))';
            INSERT INTO #TempMismatch EXEC sys.sp_executesql @MismatchSQL;
            IF EXISTS (SELECT 1 FROM #TempMismatch) BEGIN
                SET @MismatchFound = 1;
                PRINT N'[Functions] Extra in Target:';
                DECLARE mismatch_cur CURSOR LOCAL FAST_FORWARD FOR SELECT ObjectName FROM #TempMismatch;
                OPEN mismatch_cur; FETCH NEXT FROM mismatch_cur INTO @MismatchName;
                WHILE @@FETCH_STATUS = 0 BEGIN PRINT N'  - ' + @MismatchName; FETCH NEXT FROM mismatch_cur INTO @MismatchName; END;
                CLOSE mismatch_cur; DEALLOCATE mismatch_cur;
            END;

            DROP TABLE #TempMismatch;

            IF @MismatchFound = 0 PRINT N'(Count mismatch but no specific objects found - may be due to exclusions)';
            PRINT N'';
        END; -- End of Detailed Mismatch Report

        IF OBJECT_ID('tempdb..#ValidationResults') IS NOT NULL DROP TABLE #ValidationResults;
    END; -- End of validation

    --=================================================-- Statistics Update --=================================================--
    -- Stats-Block VOR CLEANUP, da #TBDD_SYNC_DATABASE_COMMANDS noch benötigt wird
    -- Statistiken nur für geänderte Tabellen aktualisieren (statt sp_updatestats für alle)
    IF @EXECUTE = 1 AND @INCLUDEDATA = 1 AND OBJECT_ID('tempdb..#TBDD_SYNC_DATABASE_COMMANDS') IS NOT NULL BEGIN
        BEGIN TRY
            DECLARE @UpdateStatsSQL NVARCHAR(MAX);
            DECLARE @StatTableName NVARCHAR(256);
            DECLARE @StatsUpdated INT = 0;

            -- Nur für Tabellen, die im Data-Sync waren, Statistiken aktualisieren
            DECLARE stats_cursor CURSOR LOCAL FAST_FORWARD FOR
                SELECT DISTINCT
                    SUBSTRING(Command, CHARINDEX('MERGE ', Command) + 6,
                        CHARINDEX(' AS tgt', Command) - CHARINDEX('MERGE ', Command) - 6)
                FROM #TBDD_SYNC_DATABASE_COMMANDS
                WHERE Phase = '20-Data' AND Command LIKE '%MERGE %';

            OPEN stats_cursor;
            FETCH NEXT FROM stats_cursor INTO @StatTableName;

            WHILE @@FETCH_STATUS = 0 BEGIN
                BEGIN TRY
                    SET @UpdateStatsSQL = N'USE ' + QUOTENAME(@TARGETDATABASE) + N'; UPDATE STATISTICS ' + @StatTableName + N';';
                    EXEC sys.sp_executesql @UpdateStatsSQL;
                    SET @StatsUpdated = @StatsUpdated + 1;
                END TRY
        BEGIN CATCH
                    PRINT '[WARN] Could not update stats for ' + @StatTableName + ': ' + ERROR_MESSAGE();
                END CATCH;
                FETCH NEXT FROM stats_cursor INTO @StatTableName;
            END;

            CLOSE stats_cursor;
            DEALLOCATE stats_cursor;

            PRINT '[INFO] Statistics updated for ' + CONVERT(NVARCHAR(10), @StatsUpdated) + ' tables';
        END TRY
        BEGIN CATCH
            PRINT '[WARN] Statistics update failed: ' + ERROR_MESSAGE();
        END CATCH;
    END;

    --=================================================-- Cleanup and Exit --=================================================--
    CLEANUP_AND_EXIT:

    -- Transaction Safety Net: Falls noch eine offene Transaction existiert, rollback
    -- Dies kann vorkommen bei GOTO-Sprung nach kritischem Fehler (falls Rollback nicht bereits erfolgt ist)
    IF @@TRANCOUNT > 0 BEGIN
        PRINT N'[WARN] Open transaction detected at cleanup - rolling back...';
        ROLLBACK TRANSACTION;
    END;

    -- Cleanup: Temp-Tables explizit droppen
    IF OBJECT_ID('tempdb..#TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS') IS NOT NULL DROP TABLE #TBDD_SYNC_DATABASE_EXCLUDEDOBJECTS;
    IF OBJECT_ID('tempdb..#TBDD_SYNC_DATABASE_COMMANDS') IS NOT NULL DROP TABLE #TBDD_SYNC_DATABASE_COMMANDS;
    -- DATA_VALIDATION-Tabelle wird bereits in der Validation-Phase gedroppt

    RETURN @RETURN_STATUS;

END;
GO
