====== 3.3 ENIVERSPIMS — Sicherheitsgespräche ======
//Stand: 2026-03-05//
Übergeordnet: [[de:int:wvdsshell:notes:03-pims:start|PIMS-Architektur — Gesamtübersicht]]
Verwandt: [[de:int:wvdsshell:notes:03-pims:inspection|3.1 Prüfwesen]] | [[de:int:wvdsshell:notes:03-pims:work-orders|3.2 Arbeitsaufträge]]
===== Ausgangslage: Zwei parallele Systeme =====
Sicherheitsgespräche / SIL-Bewertungen werden in **zwei getrennten** Legacy-Systemen geführt:
^ Merkmal ^ AMED EZASichGesp ^ ENIVERS tblSicherheitGespraech ^
| Datensätze | 5.862 Zeilen | 60 Zeilen |
| Spaltenanzahl | 73 | 59 |
| Primärschlüssel | IDEZA (FK → EZAMASTER) | idSicherheitGespraech |
| Verknüpfung | pro EZA-Objekt (Instrument/Sensor) | pro Anlage/Schutzziel (fiSigBetrieb) |
| Risikodimensionen | SCHADENAUSMASS, AUFENTHALTSDAUER, GEFAHRENABWEICHUNG, EINTRITTSWAHRSCHEINL | intSigAusmassSchaden, intSigAufenthaltGefahrBereich, intSigVermeidungGefahr, intSigEintrittWahrscheinlichkeit |
| SIL-Feld | SIL (tinyint) | strSbWertSIL (via tblSicherheitBaum) |
| Klassifizierung | Klassif_A/B/C/X (bits), Risikoklasse | strSbKennbuchstaben, Klassifizierung, Status |
| Rechtl. Anforderungen | 10 bit-Flags (Dampfk_V, VAWS, VBF, ...) | bln*-Felder |
| Aktor/Sensor-Daten | 8×(Schalt_Klassi, Kontakt, Grenzwert) — denormalisiert | tblSicherheitGespraechAktorSensor (normalisiert) |
| Unterschriften | — (nicht vorhanden) | tblSicherheitUnterschrift |
| Alarmschwellen | — (nicht vorhanden) | strSigMinVoralarm ... strSigMaxAbschaltung2 |
| Schutzziel-Freitext | — (nicht vorhanden) | memSigSchutzziel (nvarchar MAX) |
==== Kernunterschiede ====
* **AMED EZASichGesp** = Sicherheitsgespräch pro **EZA-Instrument/Aktor** (Mikroebene).
Fokus: Klassifikation (A/B/C/X), gesetzliche Anforderungen, Risikoklasse, SIL-Ergebnis.
* **ENIVERS tblSicherheitGespraech** = SIL-Bewertung pro **Schutzziel** (Makroebene).
Fokus: Schutzziel-Freitext, Alarmschwellen, strukturierte AktorSensor-Liste, Unterschriften.
Beide basieren auf derselben **4-dimensionalen Risikomatrix** (Schadensausmaß × Aufenthaltsdauer × Vermeidbarkeit × Eintrittswahrscheinlichkeit → SIL).
===== Merge-Strategie: "Must Have" ohne Datenverlust =====
==== Prinzip: gemeinsame Tabelle + conversation_type-Unterscheidung ====
Ein Merge ist möglich, weil der **strukturelle Kern identisch** ist.
Quell-spezifische Felder werden erhalten — entweder als echte Spalten oder als JSON.
AMED EZASichGesp ──► safety.conversation (type='EZA')
│
conversation_actor_sensor
(aus 8×denorm. Spalten normalisiert)
ENIVERS tblSgEspr ──► safety.conversation (type='SCHUTZZIEL')
│
conversation_actor_sensor
(aus tblSicherheitGespraechAktorSensor)
│
conversation_justification
(aus tblSicherheitGespraechBegruendung)
│
conversation_signature
(aus tblSicherheitUnterschrift)
==== Migration ohne Datenverlust ====
* Alle ''legacy_id'' + ''legacy_source'' bleiben erhalten → bidirektionale Traceability
* Denormalisierte Spalten (EZASichGesp.Schalt_Klassi1–8 × 3 = 24 Spalten) → normalisiert in ''conversation_actor_sensor'' (max. 8 Zeilen pro Gespräch)
* Quell-spezifische Felder ohne Entsprechung auf der anderen Seite → ''extended_data NVARCHAR(MAX)'' als JSON
===== safety.conversation =====
CREATE TABLE safety.conversation (
id INT NOT NULL IDENTITY PRIMARY KEY,
conversation_type VARCHAR(20) NOT NULL, -- 'EZA' | 'SCHUTZZIEL'
-- Verknüpfung
item_id INT NULL, -- FK → ENIVERSCAFM.asset.item (für EZA)
branch_id INT NULL, -- FK → ENIVERSCAFM.org.branch (für SCHUTZZIEL)
-- Identifikation
bau_nummer NVARCHAR(20) NULL, -- Bau-/RI-Nummer
ri_fliessbild_nr NVARCHAR(20) NULL, -- R&I-Fließbild Nummer (tblSgEspr: strSigRiFliessbild)
blatt_nummer INT NULL, -- Blattnummer
anlage_teil NVARCHAR(50) NULL,
bezeichnung NVARCHAR(200) NULL,
schutzziel NVARCHAR(MAX) NULL, -- Schutzziel-Freitext (nur tblSgEspr)
-- Risikomatrix (beide Systeme, 4 Dimensionen)
risk_damage SMALLINT NULL, -- Schadensausmaß
risk_presence SMALLINT NULL, -- Aufenthaltsdauer
risk_avoidance SMALLINT NULL, -- Gefahrenvermeidung/-abweichung
risk_probability SMALLINT NULL, -- Eintrittswahrscheinlichkeit
-- Ergebnis
sil_required TINYINT NULL, -- geforderter SIL (aus Risikomatrix)
sil_achieved TINYINT NULL, -- erreichter SIL (aus MSR-Maßnahmen)
risk_class NVARCHAR(5) NULL, -- Risikoklasse
classification NVARCHAR(5) NULL, -- A / B / C / X
-- Klassif.-bits (EZA): als JSON gespeichert
klassif_a BIT NULL,
klassif_b BIT NULL,
klassif_c BIT NULL,
klassif_x BIT NULL,
-- Gesetzliche Anforderungen (EZA: 10 bit-Flags → JSON)
legal_flags NVARCHAR(MAX) NULL,
-- Technische Ausführung MSR
techn_msr_notes NVARCHAR(MAX) NULL,
-- Alarmschwellen (tblSgEspr: 4 Werte → JSON)
alarm_thresholds NVARCHAR(MAX) NULL,
-- Funktionsbeschreibung (EZASichGesp.Funktion)
function_desc NVARCHAR(MAX) NULL,
-- Abschluss
done_at DATETIME2 NULL,
-- Erweiterungs-JSON (quell-spezifische Felder ohne allg. Entsprechung)
extended_data NVARCHAR(MAX) NULL,
-- Migration
legacy_id INT NULL,
legacy_source VARCHAR(20) NULL, -- 'AMED_EZA' | 'ENIVERS_LD'
-- Audit
created_at DATETIME2 NOT NULL DEFAULT SYSUTCDATETIME(),
updated_at DATETIME2 NOT NULL DEFAULT SYSUTCDATETIME(),
created_by INT NULL,
updated_by INT NULL
)
===== safety.conversation_actor_sensor =====
Normalisiert die denormalisierten EZASichGesp-Spalten (Schalt_Klassi1–8, Kontakt1–8, Grenzwert1–8)
**und** tblSicherheitGespraechAktorSensor in eine gemeinsame Tabelle:
CREATE TABLE safety.conversation_actor_sensor (
id INT NOT NULL IDENTITY PRIMARY KEY,
conversation_id INT NOT NULL, -- FK → safety.conversation
sort_order SMALLINT NOT NULL DEFAULT 0,
-- Identifikation
plt_stelle NVARCHAR(100) NULL, -- PLT-Stelle / Funktionsbezeichnung
typ NVARCHAR(50) NULL, -- Aktor- oder Sensortyp
nummer NVARCHAR(50) NULL, -- Geräte-/KKS-Nummer
-- Aktor-Wirkung (aus tblSgEspr)
aktor_wirkung NVARCHAR(100) NULL,
aktor_stellung NVARCHAR(100) NULL,
-- Sensor-Messbereich (aus tblSgEspr)
messbereich_von DECIMAL(10,4) NULL,
messbereich_bis DECIMAL(10,4) NULL,
einheit NVARCHAR(20) NULL,
-- Klassifizierung (aus EZASichGesp Schalt_Klassi1–8)
klassifizierung NVARCHAR(10) NULL,
kontakt NVARCHAR(10) NULL,
grenzwert NVARCHAR(20) NULL,
grenzwert_einheit NVARCHAR(10) NULL,
-- Belegung (aus tblSgEspr.fiSgasBelegung)
belegung_id INT NULL,
-- Sonstiges
zusatz_info NVARCHAR(500) NULL
)
===== safety.conversation_signature =====
CREATE TABLE safety.conversation_signature (
id INT NOT NULL IDENTITY PRIMARY KEY,
conversation_id INT NOT NULL, -- FK → safety.conversation
sort_order INT NOT NULL,
name NVARCHAR(150) NULL,
title NVARCHAR(150) NULL,
signed_at DATETIME2 NULL
)
===== safety.conversation_justification =====
Begründungsstruktur aus ''tblSicherheitGespraechBegruendung'' (SIL-Stufenbegründung):
CREATE TABLE safety.conversation_justification (
id INT NOT NULL IDENTITY PRIMARY KEY,
conversation_id INT NOT NULL, -- FK → safety.conversation
level_code VARCHAR(5) NULL, -- Stufe (intSbStufe / strSbText)
text NVARCHAR(1024) NULL
)
===== tblSicherheitBaum — Risikomatrix-Lookup =====
Die LD-Tabelle ''tblSicherheitBaum'' ist ein Lookup für die 4-dimensionale Risikomatrix
(Schadensausmaß × Aufenthaltsdauer × Vermeidbarkeit × Eintrittswahrscheinlichkeit → SIL-Wert).
Sie wird als **seed data** in ''shared.list_item'' oder als separate kleine Tabelle migriert:
CREATE TABLE safety.risk_matrix (
id INT NOT NULL IDENTITY PRIMARY KEY,
damage_code VARCHAR(5) NOT NULL,
presence_code VARCHAR(5) NULL,
avoidance_code VARCHAR(5) NULL,
probability_code VARCHAR(5) NOT NULL,
sil_label VARCHAR(10) NULL, -- 'SIL 1', 'SIL 2', 'SIL 3', 'k'
ak_label VARCHAR(10) NULL, -- Anforderungsklasse
UNIQUE (damage_code, presence_code, avoidance_code, probability_code)
)
-- Migration: alle Zeilen aus tblSicherheitBaum
===== Migrations-SQL-Skizze =====
-- 1. AMED EZASichGesp → safety.conversation (type='EZA')
INSERT INTO safety.conversation (
conversation_type, item_id, risk_damage, risk_presence, risk_avoidance, risk_probability,
sil_required, risk_class, klassif_a, klassif_b, klassif_c, klassif_x,
techn_msr_notes, function_desc, done_at, legal_flags,
legacy_id, legacy_source, created_at, updated_at
)
SELECT
'EZA',
i.id, -- asset.item.id via IDEZA → EZAMASTER → APPMASTER → asset.item (legacy_id)
e.Schadenausmass_kurz, e.Aufenthaltsdauer_kurz, e.Gefahrenabweichung_kurz, e.Eintrittswahrscheinlichkeit_kurz,
e.SIL, e.Risikoklasse, e.Klassif_A, e.Klassif_B, e.Klassif_C, e.Klassif_X,
e.TECHN_AUSFUHR_MSR, e.Funktion, e.Done,
-- legal_flags als JSON aus den 10 bit-Spalten:
(SELECT e.Dampfk_V AS dampfk_v, e.VAWS AS vaws, e.VBF AS vbf, e.Gasbrenner AS gasbrenner,
e.Stoerf_V AS stoerf_v, e.Einleitb AS einleitb, e.UVV_VBG AS uvv_vbg,
e.Druckb_V AS druckb_v, e.Genehmigungsb AS genehmigungsb, e.Sonst_V AS sonst_v
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER),
e.IDEZA, 'AMED_EZA', e.dtmEsgCreated, e.dtmEsgUpdated
FROM [AMED].[dbo].[EZASichGesp] e
LEFT JOIN ENIVERSCAFM.asset.item i ON i.legacy_id = CAST(
-- IDEZA → EZAMASTER.erwAppNr → asset.item.legacy_id
(SELECT CAST(m.erwAppNr AS VARCHAR) FROM [AMED].[dbo].[EZAMASTER] m WHERE m.IDEZA = e.IDEZA)
AS VARCHAR) AND i.legacy_source = 'AMED';
-- 2. Denormalisierte Aktor-Spalten in conversation_actor_sensor auffalten
-- (Schalt_Klassi1..8 × Kontakt1..8 × Grenzwert1..8 → 8 Zeilen pro Gespräch)
INSERT INTO safety.conversation_actor_sensor (conversation_id, sort_order, klassifizierung, kontakt, grenzwert, grenzwert_einheit)
SELECT c.id, v.ord, v.klassi, v.kont, v.grenz, v.grenzeinh
FROM safety.conversation c
JOIN [AMED].[dbo].[EZASichGesp] e ON e.IDEZA = c.legacy_id AND c.legacy_source = 'AMED_EZA'
CROSS APPLY (VALUES
(1, e.Schalt_Klassi1, e.Kontakt1, e.Grenzwert1, e.Grenzwerteinh1),
(2, e.Schalt_Klassi2, e.Kontakt2, e.Grenzwert2, e.Grenzwerteinh2),
-- ... bis 8
(8, e.Schalt_Klassi8, e.Kontakt8, e.Grenzwert8, e.Grenzwerteinh8)
) AS v(ord, klassi, kont, grenz, grenzeinh)
WHERE COALESCE(v.klassi, v.kont, v.grenz) IS NOT NULL;