Skip to content

FileIndexDB erstellen

Ab der Version 26.8.10 benötigt WWS MaX eine FileIndexDB, um die semantische Suche und die KI-Funktionen nutzen zu können. Diese Datenbank wird automatisch durch den SQL Update Assistent von WWS MaX beim Start erstellt, sofern die angegebene Datenbankverbindung über CREATE DATABASE Berechtigung auf dem Server verfügt.

Sollte die Datenbankverbindung nicht über die Berechtigung CREATE DATABASE verfügen, muss die Datenbank vor einem Update der WWS manuell erstellt werden.

  1. Öffnen Sie auf dem Datenbankserver das SQL Server Management Studio (SSMS) und verbinden Sie sich mit dem Server, auf dem die WWS MaX Datenbank liegt.

  2. Wählen Sie die WWS MaX Datenbank auf der linken Seite aus und klicken Sie mit der rechten Maustaste darauf. Wählen Sie Neue Abfrage aus.

    Die Bezeichnung der WWS MaX Datenbank kann je nach Installation variieren. In der Regel ist dies der Name des Unternehmens, z.B. SProssGmbH.

  3. Kopieren Sie das nachfolgende Script in den Abfrage-Editor und klicken Sie auf Ausführen.

    SET NOCOUNT ON;
    SET IMPLICIT_TRANSACTIONS OFF;
    /* ============================================================
    WWS FileIndex - Bootstrap / Update
    Das Script wird IMMER auf der WWS-Hauptdatenbank ausgeführt.
    Beispiel:
    Aktuelle Datenbank:
    SProssGmbH
    FileIndex-Datenbank:
    SProssGmbH_FileIndex
    Verhalten:
    - Verhindert *_FileIndex_FileIndex
    - Legt FileIndex-DB an, wenn sie fehlt
    - Verwendet vorhandene FileIndex-DB weiter
    - Konfiguriert die FileIndex-DB
    - Übernimmt direkte db_owner-Benutzer
    - Unterstützt Benutzer WITH LOGIN
    - Unterstützt Benutzer WITHOUT LOGIN
    - Nicht reproduzierbare Benutzer werden nur gemeldet
    - Kann beliebig oft ausgeführt werden
    ============================================================ */
    DECLARE @Suffix nvarchar(20) = N'_FileIndex';
    DECLARE @SourceDatabase sysname = DB_NAME();
    DECLARE @TargetDatabase sysname;
    DECLARE @TargetDatabaseLong nvarchar(260);
    DECLARE @Sql nvarchar(max);
    DECLARE @ErrorMessage nvarchar(2048);
    DECLARE @DatabaseCreated bit = 0;
    /* ============================================================
    1. Grundlegende Sicherheitsprüfungen
    ============================================================ */
    /* ------------------------------------------------------------
    CREATE/ALTER DATABASE darf nicht innerhalb einer
    bestehenden Transaktion ausgeführt werden.
    ------------------------------------------------------------ */
    IF @@TRANCOUNT <> 0
    BEGIN
    ;THROW 51000,
    'Das FileIndex-Update darf nicht innerhalb einer SQL-Transaktion ausgeführt werden.',
    1;
    END;
    /* ------------------------------------------------------------
    Aktuelle Datenbank muss feststellbar sein.
    ------------------------------------------------------------ */
    IF @SourceDatabase IS NULL
    BEGIN
    ;THROW 51001,
    'Die aktuelle WWS-Datenbank konnte nicht ermittelt werden.',
    1;
    END;
    /* ------------------------------------------------------------
    Systemdatenbanken ausschließen.
    ------------------------------------------------------------ */
    IF DB_ID(@SourceDatabase) <= 4
    BEGIN
    ;THROW 51002,
    'Das FileIndex-Update darf nicht auf einer SQL-Server-Systemdatenbank ausgeführt werden.',
    1;
    END;
    /* ------------------------------------------------------------
    Unbedingt verhindern:
    SProssGmbH_FileIndex
    ->
    SProssGmbH_FileIndex_FileIndex
    ------------------------------------------------------------ */
    IF LOWER(RIGHT(@SourceDatabase, LEN(@Suffix))) = LOWER(@Suffix)
    BEGIN
    ;THROW 51003,
    'Die aktuelle Datenbank ist bereits eine FileIndex-Datenbank. Das Update wurde abgebrochen.',
    1;
    END;
    /* ============================================================
    2. Ziel-Datenbanknamen erzeugen
    ============================================================ */
    SET @TargetDatabaseLong =
    CONVERT(nvarchar(128), @SourceDatabase) +
    @Suffix;
    IF LEN(@TargetDatabaseLong) > 128
    BEGIN
    ;THROW 51004,
    'Der abgeleitete Name der FileIndex-Datenbank ist länger als 128 Zeichen.',
    1;
    END;
    SET @TargetDatabase =
    CONVERT(sysname, @TargetDatabaseLong);
    /* ============================================================
    3. FileIndex-Datenbank bei Bedarf anlegen
    ============================================================ */
    IF DB_ID(@TargetDatabase) IS NULL
    BEGIN
    BEGIN TRY
    /* ----------------------------------------------------
    CREATE DATABASE wird als eigener dynamischer
    SQL-Batch ausgeführt.
    ---------------------------------------------------- */
    SET @Sql =
    N'CREATE DATABASE ' +
    QUOTENAME(@TargetDatabase) +
    N';';
    EXEC sys.sp_executesql @Sql;
    SET @DatabaseCreated = 1;
    PRINT
    N'FileIndex-Datenbank wurde angelegt: ' +
    QUOTENAME(@TargetDatabase);
    END TRY
    BEGIN CATCH
    SET @ErrorMessage =
    N'Die FileIndex-Datenbank "' +
    @TargetDatabase +
    N'" konnte nicht angelegt werden. ' +
    N'SQL Server: ' +
    ERROR_MESSAGE();
    ;THROW 51010, @ErrorMessage, 1;
    END CATCH;
    END
    ELSE
    BEGIN
    PRINT
    N'FileIndex-Datenbank ist bereits vorhanden: ' +
    QUOTENAME(@TargetDatabase);
    END;
    /* ------------------------------------------------------------
    Sicherheitsprüfung
    ------------------------------------------------------------ */
    IF DB_ID(@TargetDatabase) IS NULL
    BEGIN
    ;THROW 51011,
    'Die erwartete FileIndex-Datenbank wurde nicht gefunden.',
    1;
    END;
    /* ============================================================
    4. FileIndex-Datenbank konfigurieren
    ============================================================ */
    BEGIN TRY
    /* --------------------------------------------------------
    Recovery Model
    -------------------------------------------------------- */
    SET @Sql =
    N'ALTER DATABASE ' +
    QUOTENAME(@TargetDatabase) +
    N' SET RECOVERY SIMPLE;';
    EXEC sys.sp_executesql @Sql;
    /* --------------------------------------------------------
    AUTO_CLOSE
    -------------------------------------------------------- */
    SET @Sql =
    N'ALTER DATABASE ' +
    QUOTENAME(@TargetDatabase) +
    N' SET AUTO_CLOSE OFF;';
    EXEC sys.sp_executesql @Sql;
    /* --------------------------------------------------------
    AUTO_SHRINK
    -------------------------------------------------------- */
    SET @Sql =
    N'ALTER DATABASE ' +
    QUOTENAME(@TargetDatabase) +
    N' SET AUTO_SHRINK OFF;';
    EXEC sys.sp_executesql @Sql;
    /* --------------------------------------------------------
    Statistiken
    -------------------------------------------------------- */
    SET @Sql =
    N'ALTER DATABASE ' +
    QUOTENAME(@TargetDatabase) +
    N' SET AUTO_CREATE_STATISTICS ON;';
    EXEC sys.sp_executesql @Sql;
    SET @Sql =
    N'ALTER DATABASE ' +
    QUOTENAME(@TargetDatabase) +
    N' SET AUTO_UPDATE_STATISTICS ON;';
    EXEC sys.sp_executesql @Sql;
    SET @Sql =
    N'ALTER DATABASE ' +
    QUOTENAME(@TargetDatabase) +
    N' SET AUTO_UPDATE_STATISTICS_ASYNC ON;';
    EXEC sys.sp_executesql @Sql;
    /* --------------------------------------------------------
    PAGE_VERIFY
    -------------------------------------------------------- */
    SET @Sql =
    N'ALTER DATABASE ' +
    QUOTENAME(@TargetDatabase) +
    N' SET PAGE_VERIFY CHECKSUM;';
    EXEC sys.sp_executesql @Sql;
    /* --------------------------------------------------------
    READ_COMMITTED_SNAPSHOT nur ändern, wenn noch OFF.
    Das vermeidet unnötige ALTER DATABASE Aufrufe.
    -------------------------------------------------------- */
    IF EXISTS
    (
    SELECT 1
    FROM sys.databases
    WHERE name = @TargetDatabase
    AND is_read_committed_snapshot_on = 0
    )
    BEGIN
    SET @Sql =
    N'ALTER DATABASE ' +
    QUOTENAME(@TargetDatabase) +
    N' SET READ_COMMITTED_SNAPSHOT ON;';
    EXEC sys.sp_executesql @Sql;
    END;
    /* --------------------------------------------------------
    SQL Server 2022 / Version 16 oder neuer
    ASYNC_STATS_UPDATE_WAIT_AT_LOW_PRIORITY
    -------------------------------------------------------- */
    IF TRY_CONVERT(
    int,
    SERVERPROPERTY('ProductMajorVersion')
    ) >= 16
    BEGIN
    SET @Sql =
    N'USE ' +
    QUOTENAME(@TargetDatabase) +
    N';
    ALTER DATABASE SCOPED CONFIGURATION
    SET ASYNC_STATS_UPDATE_WAIT_AT_LOW_PRIORITY = ON;';
    EXEC sys.sp_executesql @Sql;
    END;
    PRINT
    N'FileIndex-Datenbank wurde konfiguriert.';
    END TRY
    BEGIN CATCH
    SET @ErrorMessage =
    N'Die FileIndex-Datenbank "' +
    @TargetDatabase +
    N'" konnte nicht vollständig konfiguriert werden. ' +
    N'SQL Server: ' +
    ERROR_MESSAGE();
    ;THROW 51020, @ErrorMessage, 1;
    END CATCH;
    /* ============================================================
    5. db_owner-Mitglieder der Hauptdatenbank übernehmen
    ============================================================ */
    /* ------------------------------------------------------------
    Es werden bewusst nur Benutzer / Windows-Benutzer /
    Windows-Gruppen berücksichtigt.
    S = SQL User
    U = Windows User
    G = Windows Group
    Benutzerdefinierte Datenbankrollen werden nicht kopiert.
    ------------------------------------------------------------ */
    DECLARE
    @UserName sysname,
    @LoginName sysname,
    @UserType char(1),
    @AuthenticationType nvarchar(60),
    @UserSid varbinary(85);
    DECLARE @DbOwnerCount int = 0;
    DECLARE @CopiedCount int = 0;
    DECLARE @SkippedCount int = 0;
    DECLARE DbOwnerCursor CURSOR LOCAL FAST_FORWARD
    FOR
    SELECT DISTINCT
    MemberPrincipal.name,
    ServerPrincipal.name,
    MemberPrincipal.type,
    MemberPrincipal.authentication_type_desc,
    MemberPrincipal.sid
    FROM sys.database_role_members AS RoleMember
    INNER JOIN sys.database_principals AS RolePrincipal
    ON RolePrincipal.principal_id =
    RoleMember.role_principal_id
    INNER JOIN sys.database_principals AS MemberPrincipal
    ON MemberPrincipal.principal_id =
    RoleMember.member_principal_id
    LEFT JOIN sys.server_principals AS ServerPrincipal
    ON ServerPrincipal.sid =
    MemberPrincipal.sid
    AND ServerPrincipal.type IN
    (
    'S',
    'U',
    'G'
    )
    WHERE
    RolePrincipal.name = N'db_owner'
    AND MemberPrincipal.type IN
    (
    'S',
    'U',
    'G'
    )
    AND MemberPrincipal.name NOT IN
    (
    N'dbo',
    N'guest',
    N'INFORMATION_SCHEMA',
    N'sys'
    )
    ORDER BY
    MemberPrincipal.name;
    OPEN DbOwnerCursor;
    FETCH NEXT FROM DbOwnerCursor
    INTO
    @UserName,
    @LoginName,
    @UserType,
    @AuthenticationType,
    @UserSid;
    WHILE @@FETCH_STATUS = 0
    BEGIN
    SET @DbOwnerCount += 1;
    BEGIN TRY
    /* ====================================================
    5.1 Benutzer besitzt einen Server-Login
    ==================================================== */
    IF @LoginName IS NOT NULL
    BEGIN
    SET @Sql =
    N'USE ' +
    QUOTENAME(@TargetDatabase) +
    N';
    DECLARE @ExistingPrincipal sysname;
    /* ---------------------------------------------------------
    Existiert der gleiche Login über seine SID bereits?
    Das kann insbesondere "dbo" sein, wenn dieser Login
    Besitzer der Datenbank ist.
    --------------------------------------------------------- */
    SELECT TOP (1)
    @ExistingPrincipal = name
    FROM sys.database_principals
    WHERE sid = @UserSid;
    /* ---------------------------------------------------------
    Noch kein Benutzer für diesen Login vorhanden.
    --------------------------------------------------------- */
    IF @ExistingPrincipal IS NULL
    BEGIN
    /* -----------------------------------------------------
    Namenskollision verhindern.
    Gleicher Benutzername aber andere SID wäre gefährlich.
    ----------------------------------------------------- */
    IF EXISTS
    (
    SELECT 1
    FROM sys.database_principals
    WHERE name = @UserName
    AND sid <> @UserSid
    )
    BEGIN
    DECLARE @CollisionMessage nvarchar(2048);
    SET @CollisionMessage =
    N''Der Benutzername "'' +
    @UserName +
    N''" existiert in der FileIndex-Datenbank bereits mit einer anderen SID.'';
    ;THROW 51031, @CollisionMessage, 1;
    END;
    CREATE USER ' +
    QUOTENAME(@UserName) +
    N'
    FOR LOGIN ' +
    QUOTENAME(@LoginName) +
    N';
    SET @ExistingPrincipal = @UserName;
    END;
    /* ---------------------------------------------------------
    dbo besitzt ohnehin vollständige Datenbankrechte.
    --------------------------------------------------------- */
    IF @ExistingPrincipal <> N''dbo''
    BEGIN
    IF NOT EXISTS
    (
    SELECT 1
    FROM sys.database_role_members AS RM
    INNER JOIN sys.database_principals AS R
    ON R.principal_id =
    RM.role_principal_id
    INNER JOIN sys.database_principals AS M
    ON M.principal_id =
    RM.member_principal_id
    WHERE
    R.name = N''db_owner''
    AND M.name = @ExistingPrincipal
    )
    BEGIN
    DECLARE @RoleSql nvarchar(max);
    SET @RoleSql =
    N''ALTER ROLE [db_owner] ADD MEMBER '' +
    QUOTENAME(@ExistingPrincipal) +
    N'';'';
    EXEC sys.sp_executesql @RoleSql;
    END;
    END;
    ';
    EXEC sys.sp_executesql
    @Sql,
    N'
    @UserName sysname,
    @UserSid varbinary(85)
    ',
    @UserName = @UserName,
    @UserSid = @UserSid;
    PRINT
    N'db_owner sichergestellt: ' +
    QUOTENAME(@UserName) +
    N' -> Login ' +
    QUOTENAME(@LoginName);
    SET @CopiedCount += 1;
    END
    /* ====================================================
    5.2 Benutzer WITHOUT LOGIN
    Beispiel:
    CREATE USER [Auftragsverwaltung]
    WITHOUT LOGIN
    ==================================================== */
    ELSE IF
    @UserType = 'S'
    AND @AuthenticationType = N'NONE'
    BEGIN
    SET @Sql =
    N'USE ' +
    QUOTENAME(@TargetDatabase) +
    N';
    DECLARE @ExistingAuthentication nvarchar(60);
    SELECT
    @ExistingAuthentication =
    authentication_type_desc
    FROM sys.database_principals
    WHERE name = @UserName;
    /* ---------------------------------------------------------
    Benutzer existiert noch nicht.
    --------------------------------------------------------- */
    IF @ExistingAuthentication IS NULL
    BEGIN
    CREATE USER ' +
    QUOTENAME(@UserName) +
    N'
    WITHOUT LOGIN;
    END
    ELSE IF @ExistingAuthentication <> N''NONE''
    BEGIN
    DECLARE @CollisionMessage nvarchar(2048);
    SET @CollisionMessage =
    N''Der Benutzer "'' +
    @UserName +
    N''" existiert bereits, ist aber kein Benutzer WITHOUT LOGIN.'';
    ;THROW 51032, @CollisionMessage, 1;
    END;
    /* ---------------------------------------------------------
    db_owner sicherstellen.
    --------------------------------------------------------- */
    IF NOT EXISTS
    (
    SELECT 1
    FROM sys.database_role_members AS RM
    INNER JOIN sys.database_principals AS R
    ON R.principal_id =
    RM.role_principal_id
    INNER JOIN sys.database_principals AS M
    ON M.principal_id =
    RM.member_principal_id
    WHERE
    R.name = N''db_owner''
    AND M.name = @UserName
    )
    BEGIN
    DECLARE @RoleSql nvarchar(max);
    SET @RoleSql =
    N''ALTER ROLE [db_owner] ADD MEMBER '' +
    QUOTENAME(@UserName) +
    N'';'';
    EXEC sys.sp_executesql @RoleSql;
    END;
    ';
    EXEC sys.sp_executesql
    @Sql,
    N'@UserName sysname',
    @UserName = @UserName;
    PRINT
    N'db_owner sichergestellt: ' +
    QUOTENAME(@UserName) +
    N' (WITHOUT LOGIN)';
    SET @CopiedCount += 1;
    END
    /* ====================================================
    5.3 Nicht automatisch reproduzierbarer Benutzer
    Beispiele:
    - verwaister SQL-Benutzer
    - contained database user
    - unbekannter Authentifizierungstyp
    Kein Abbruch des gesamten Updates.
    ==================================================== */
    ELSE
    BEGIN
    PRINT
    N'WARNUNG: db_owner-Benutzer ' +
    QUOTENAME(@UserName) +
    N' wurde nicht übernommen. Typ=' +
    ISNULL(@UserType, N'?') +
    N', Authentifizierung=' +
    ISNULL(@AuthenticationType, N'?') +
    N', kein zugehöriger Server-Login gefunden.';
    SET @SkippedCount += 1;
    END;
    END TRY
    BEGIN CATCH
    /* ----------------------------------------------------
    Ein einzelner problematischer Benutzer darf das
    gesamte FileIndex-Update nicht abbrechen.
    ---------------------------------------------------- */
    PRINT
    N'WARNUNG: db_owner-Benutzer ' +
    QUOTENAME(@UserName) +
    N' konnte nicht übernommen werden. SQL Server: ' +
    ERROR_MESSAGE();
    SET @SkippedCount += 1;
    END CATCH;
    FETCH NEXT FROM DbOwnerCursor
    INTO
    @UserName,
    @LoginName,
    @UserType,
    @AuthenticationType,
    @UserSid;
    END;
    CLOSE DbOwnerCursor;
    DEALLOCATE DbOwnerCursor;
    /* ============================================================
    6. Abschluss
    ============================================================ */
    PRINT N'';
    PRINT N'============================================================';
    PRINT N'WWS FileIndex Update abgeschlossen';
    PRINT N'============================================================';
    PRINT
    N'Hauptdatenbank: ' +
    QUOTENAME(@SourceDatabase);
    PRINT
    N'FileIndex: ' +
    QUOTENAME(@TargetDatabase);
    PRINT
    N'Neu angelegt: ' +
    CASE
    WHEN @DatabaseCreated = 1
    THEN N'Ja'
    ELSE
    N'Nein'
    END;
    PRINT
    N'db_owner gefunden: ' +
    CONVERT(nvarchar(20), @DbOwnerCount);
    PRINT
    N'db_owner sichergestellt:' +
    CONVERT(nvarchar(20), @CopiedCount);
    PRINT
    N'db_owner übersprungen: ' +
    CONVERT(nvarchar(20), @SkippedCount);
    PRINT N'============================================================';
  4. Nach erfolgreichem Ausführen des Scripts und aktualisieren der Anzeige mit F5 sollte eine zusätzliche Datenbank mit der Endung _FileIndex vorhanden sein.

  5. Starten Sie WWS MaX und führen Sie das Update durch. Die FileIndexDB wird nun automatisch genutzt.