SQL-Registry-Prozeduren #
Die SQL-Registry wird über eine Reihe von Stored Procedures verwaltet. Alle Prozeduren tragen das Präfix cn_Reg. Sie ermöglichen das Erstellen, Lesen, Schreiben und Löschen von Schlüsseln und Werten in der hierarchischen Registry-Struktur.
Eine Übersicht der Registry-Struktur findet sich unter SQL-Registry.
Schlüsselverwaltung #
cn_RegCreateKey #
Erstellt einen neuen Schlüssel unter einem vorhandenen Elternschlüssel.
EXEC cn_RegCreateKey
@basekeyid = int, -- ID des Elternschlüssels
@name = nvarchar(200), -- Name des neuen Schlüssels
@newkeyid = int OUT -- ID des erzeugten Schlüssels
cn_RegOpenPath #
Öffnet einen Schlüssel über seinen Pfad. Optional wird der Pfad angelegt, falls er nicht existiert.
EXEC cn_RegOpenPath
@basekeyid = int, -- Basis-Schlüssel-ID (0 = ROOT)
@path = nvarchar(max), -- Pfad (z.B. '\MODULES\Datev')
@keyid = int OUT, -- ID des gefundenen/erzeugten Schlüssels
@CanCreate = int, -- 0 = nur öffnen, 1 = bei Bedarf anlegen
@rootkeyid = int -- Optionaler Root-Key (Standard: NULL)
Beispiel: Schlüssel öffnen oder anlegen #
DECLARE @keyid int
-- Nur öffnen (Fehler wenn nicht vorhanden)
EXEC cn_RegOpenPath 0, '\MODULES\Grundwerte\Xfacture', @keyid OUT, 0
-- Öffnen oder anlegen
EXEC cn_RegOpenPath 0, '\VENDOR\MeinPlugin\Settings', @keyid OUT, 1
cn_RegDelKey #
Löscht einen Schlüssel und rekursiv alle Unterschlüssel und Werte.
EXEC cn_RegDelKey
@keyid = int -- ID des zu löschenden Schlüssels
Rückgabewert: 0 = Erfolg, 1 = Schlüssel existiert nicht
cn_RegKeyExists #
Prüft, ob ein Schlüssel unter einem Elternschlüssel existiert.
EXEC cn_RegKeyExists
@BaseKeyId = int, -- ID des Elternschlüssels
@KeyName = nvarchar(200), -- Gesuchter Schlüsselname
@KeyId = int OUT -- ID des gefundenen Schlüssels (NULL wenn nicht vorhanden)
Rückgabewert: 0 = existiert, 1 = nicht gefunden
cn_RegKeyRename #
Benennt einen Schlüssel um.
EXEC cn_RegKeyRename
@basekeyid = int, -- ID des Elternschlüssels
@oldname = nvarchar(200), -- Alter Name
@newname = nvarchar(200) -- Neuer Name
Rückgabewert: 0 = Erfolg, 1 = alter Name nicht gefunden, 2 = neuer Name existiert bereits
cn_RegCopyKey #
Kopiert einen Schlüssel mit allen Unterschlüsseln und Werten an eine neue Position.
EXEC cn_RegCopyKey
@SourceKeyId = int, -- Quell-Schlüssel-ID
@TargetKeyId = int -- Ziel-Elternschlüssel-ID
cn_RegKeyInfo #
Liefert Metadaten zu einem Schlüssel.
EXEC cn_RegKeyInfo
@BaseKeyId = int, -- Elternschlüssel-ID
@KeyName = nvarchar(200), -- Schlüsselname
@KeyId = int OUT, -- Schlüssel-ID
@HasSubKeys = tinyint OUT, -- Hat Unterschlüssel?
@HasValues = tinyint OUT, -- Hat Werte?
@Timestamp = timestamp OUT -- Zeitstempel
Rückgabewert: 0 = gefunden, 1 = nicht gefunden
Werte lesen #
cn_RegReadString #
Liest einen Zeichenketten-Wert über den Pfad. Dies ist die am häufigsten verwendete Lese-Prozedur.
EXEC cn_RegReadString
@path = nvarchar(max), -- Registry-Pfad
@valuename = nvarchar(200), -- Name des Werts
@value = nvarchar(max) OUT -- Gelesener Wert
Beispiel: Konfiguration lesen #
-- Datev-Ausgabeordner lesen
DECLARE @folder nvarchar(max)
EXEC cn_RegReadString '\MODULES\Datev', 'AusgabeOrdner', @folder OUT
SELECT @folder
-- E-Mail-Provider ermitteln
DECLARE @provider nvarchar(max)
EXEC cn_RegReadString '\SYSTEM\Devices\EMail', 'Provider', @provider OUT
SELECT @provider
cn_RegReadInt #
Liest einen Ganzzahl-Wert.
EXEC cn_RegReadInt
@path = nvarchar(max), -- Registry-Pfad
@valuename = nvarchar(200), -- Name des Werts
@value = int OUT -- Gelesener Wert
Rückgabewert: 0 = Erfolg, -1 = Pfad nicht gefunden, -2 = Wert nicht gefunden, -3 = Werttyp ist nicht INT
cn_RegReadBlobById #
Liest Binärdaten über die Registry-ID (z.B. eingebettete Dateien, verschlüsselte Skripte).
EXEC cn_RegReadBlobById
@id = int, -- Registry-Eintrags-ID
@stamp = binary(8) OUT, -- Zeitstempel
@UseUtf16 = bit -- 0 = ANSI (Standard), 1 = UTF-16
Rückgabe: Ein Resultset mit einer Zeile und einer Spalte, die die Binärdaten als varbinary(max) enthält. Bei grossen BLOBs liefert ADO das Ergebnis ggf. in einem zweiten Recordset – Client-seitig NextRecordset aufrufen, bis eines mit gefüllter Fields.Count kommt.
Rückgabewert: 0 = Erfolg, -1 = Eintrag nicht gefunden
cn_RegBulkRead #
Liest in einem Aufruf alle Werte eines Schlüssels und seiner Unterschlüssel rekursiv. Die effizienteste Art, viele Registry-Werte aus einem Teilbaum zu laden – ein einziger Datenbank-Roundtrip statt N Einzelaufrufe. Über @options kann getrennt angefordert werden, ob Schlüssel-Struktur, Wert-Daten oder beides zurückgegeben werden sollen.
EXEC cn_RegBulkRead
@basekeyid = int, -- Basis-Schlüssel-ID (Wurzel des Teilbaums)
@options = int, -- Bitmaske (siehe unten); Standard: 3
@UseUtf16 = bit -- 0 = VARCHAR (Standard), 1 = NVARCHAR
Optionen (@options als Bitmaske):
| Bit | Wert | Wirkung |
|---|---|---|
| 1 | 1 | Erstes Resultset: Schlüssel-Struktur (alle Subkeys rekursiv) |
| 2 | 2 | Zweites Resultset: Werte-Daten (aller Values in allen Subkeys) |
| 3 | 4 | Grosse TEXT/BLOB-Werte (>8000 Bytes) als ntext/image zusätzlich ausgeben. Ohne dieses Bit werden grosse Werte auf NULL gesetzt (Metadaten bleiben) |
Häufige Kombinationen: @options=3 (Schlüssel + Werte, Standard), @options=2 (nur Werte, schlankstes Ergebnis für Pivot/Lookup in PowerShell/Delphi), @options=7 (alles inkl. grosser BLOBs).
Resultset 1 – Schlüssel-Struktur (wenn Bit 1 gesetzt):
| Spalte | Typ | Bedeutung |
|---|---|---|
KEY_ID | int | ID des Unterschlüssels |
PARENT_KEY_ID | int | ID des Elternschlüssels |
KEY_NAME | nvarchar / varchar | Name des Schlüssels |
Resultset 2 – Werte-Daten (wenn Bit 2 gesetzt):
| Spalte | Typ | Bedeutung |
|---|---|---|
KEY_ID | int | Eltern-Schlüssel-ID dieses Werts (Join-Schlüssel zum ersten Resultset) |
VALUE_ID | int | ID des Wert-Eintrags |
VALUE_NAME | nvarchar / varchar | Name des Werts |
VALUE_TYPE | int | Effektiver Typ (siehe unten) |
VALUE_TYPE_NAME | varchar | Typ-Name: STRING, TEXT, BINARY, INT, UNKNOWN |
DATA_SIZE | int | Länge in Bytes |
VALUE_INT | int | Gefüllt bei Typ 3 (INT) |
VALUE_STRING | varchar(8000) / nvarchar(max) | Gefüllt bei Typ 2 (STRING) oder Typ 4 (TEXT) bis 8000 Bytes |
VALUE_BINARY | varbinary(8000) | Gefüllt bei Typ 6 (BINARY) bis 8000 Bytes |
VALUE_TEXT | ntext | Nur mit Bit 3 und bei TEXT >8000 Bytes |
VALUE_BLOB | image | Nur mit Bit 3 und bei BINARY >8000 Bytes |
VALUE_TIMESTAMP | timestamp | Zeilen-Zeitstempel |
Werttypen (VALUE_TYPE): 2 = STRING, 3 = INT, 4 = TEXT, 6 = BINARY. Die Prozedur normalisiert den Typ: TEXT bis 8000 Bytes wird als STRING gemeldet, BINARY bis 8000 Bytes bleibt BINARY – so lassen sich die kleinen Werte direkt aus VALUE_STRING bzw. VALUE_BINARY lesen.
Berechtigung: Für eul_User freigegeben – auch aus Plugin-Kontext aufrufbar.
Beispiel: Firmenstamm kompakt laden #
-- 1. Wurzel-KeyID holen
DECLARE @kRoot int
EXEC cn_RegOpenPath NULL, '\MODULES\Grundwerte', @kRoot OUT, 0, 0
-- 2. Alle Werte unter Grundwerte in einem Aufruf
EXEC cn_RegBulkRead @basekeyid = @kRoot, @options = 2, @UseUtf16 = 1
Das Ergebnis enthält in einem einzigen Resultset alle Werte aus Grundwerte\Firmenstamm, Grundwerte\Xfacture, Grundwerte\Bank\1..5, Grundwerte\PrintFlags, etc. – jeweils mit ihrer Eltern-KEY_ID. Im Client werden die Zeilen per KEY_ID gruppiert, anschliessend per VALUE_NAME nachgeschlagen.
Vergleich: klassisch vs. Bulk #
| Variante | Datenbank-Roundtrips | Interne Pfad-Auflösungen | VPN-Verhalten |
|---|---|---|---|
30 × cn_RegReadString (Einzelaufrufe) | 30 | 30 | 30 × Netz-Latenz |
30 × cnf_RegPathReadString in einem SELECT | 1 | 30 | gut |
cn_RegBulkRead mit @options=2 | 1 | 1 | optimal |
Der Unterschied zwischen Variante 2 und 3 wird besonders bei vielen verschiedenen Pfaden unter demselben Teilbaum sichtbar – cn_RegBulkRead liest den Teilbaum einmal komplett, während cnf_RegPathReadString pro Aufruf den Pfad neu auflöst.
Werte schreiben #
cn_RegWriteString #
Schreibt einen Zeichenketten-Wert. Der Pfad wird bei Bedarf angelegt.
EXEC cn_RegWriteString
@path = nvarchar(max), -- Registry-Pfad
@valuename = nvarchar(200), -- Name des Werts
@value = nvarchar(max) -- Zu schreibender Wert
Rückgabewert: -1 wenn der Pfad nicht geöffnet werden konnte
Beispiel: Konfiguration schreiben #
-- Plugin-Version speichern
EXEC cn_RegWriteString '\VENDOR\MeinPlugin', 'Version', '2.0'
-- Modul-Einstellung setzen
EXEC cn_RegWriteString '\MODULES\Grundwerte\MeinModul', 'Aktiv', '1'
cn_RegWriteTextValue #
Schreibt einen Text-Wert über die Schlüssel-ID (für mehrzeilige Texte).
EXEC cn_RegWriteTextValue
@basekeyid = int, -- Schlüssel-ID
@valuename = nvarchar(200), -- Name des Werts
@value = nvarchar(max), -- Text-Wert
@options = int -- Optionen
cn_RegWriteValue #
Allgemeine Schreib-Prozedur mit Typangabe. Wird intern von den spezialisierten Prozeduren verwendet.
EXEC cn_RegWriteValue
@basekeyid = int, -- Schlüssel-ID
@valuename = nvarchar(200), -- Name des Werts
@ValueType = tinyint, -- Werttyp (2=String, 3=Int, 4=Text, 5=Float, 6=Binary)
@data = varbinary(max), -- Binäre Darstellung des Werts
@Options = int -- Optionen (Standard: NULL)
cn_RegWriteBlobValue #
Schreibt Binärdaten (z.B. eingebettete Dateien, Bilder).
EXEC cn_RegWriteBlobValue
@basekeyid = int, -- Schlüssel-ID
@valuename = nvarchar(200), -- Name des Werts
@value = varbinary(max), -- Binärdaten
@options = int -- Optionen
Werte verwalten #
cn_RegValueExists #
Prüft, ob ein Wert unter einem Schlüssel existiert.
EXEC cn_RegValueExists
@BaseKeyId = int, -- Schlüssel-ID
@ValueName = nvarchar(200), -- Gesuchter Wertname
@ValueId = int OUT -- ID des gefundenen Werts
Rückgabewert: 0 = existiert, 1 = nicht gefunden
cn_RegValueDelete #
Löscht einen einzelnen Wert.
EXEC cn_RegValueDelete
@basekeyid = int, -- Schlüssel-ID
@valuename = nvarchar(200) -- Name des zu löschenden Werts
cn_RegValueRename #
Benennt einen Wert um.
EXEC cn_RegValueRename
@basekeyid = int, -- Schlüssel-ID
@oldname = nvarchar(200), -- Alter Name
@newname = nvarchar(200) -- Neuer Name
Rückgabewert: 0 = Erfolg, 1 = alter Name nicht gefunden, 2 = neuer Name existiert bereits
cn_RegValueInfo #
Liefert Metadaten und Daten eines Werts.
EXEC cn_RegValueInfo
@BaseKeyId = int, -- Schlüssel-ID
@ValueName = nvarchar(200), -- Wertname
@Options = int, -- Optionen
@ValueId = int OUT, -- Wert-ID
@ValueType = int OUT, -- Werttyp
@DataSize = int OUT, -- Datengröße in Bytes
@Data = varbinary(8000) OUT, -- Daten (max. 8000 Bytes)
@UseUtf16 = bit -- 0 = ANSI, 1 = UTF-16
cn_RegCopyValues #
Kopiert alle Werte von einem Schlüssel zu einem anderen.
EXEC cn_RegCopyValues
@SourceKeyId = int, -- Quell-Schlüssel-ID
@TargetKeyId = int -- Ziel-Schlüssel-ID
Navigation und Abfrage #
cn_RegSubKeys #
Listet rekursiv alle Unterschlüssel in einer temporären Tabelle #subkeys auf.
-- Temporäre Tabelle muss vorher existieren
DECLARE @level int
EXEC cn_RegSubKeys
@rootid = int, -- Startschlüssel-ID
@level = int OUT -- Verschachtelungstiefe
cn_RegKeyNames #
Liefert die Namen aller direkten Unterschlüssel.
EXEC cn_RegKeyNames
@baseKeyId = int, -- Elternschlüssel-ID
@Options = int, -- 0 = Resultset, andere = konkateniert
@Delimiter = nvarchar(100), -- Trennzeichen bei Konkatenierung
@ValueNames = nvarchar(max) OUT, -- Konkatenierte Namen (bei @Options<>0)
@ValueCount = int OUT -- Anzahl der Schlüssel
Rückgabe-Varianten:
@Options = 0: liefert ein Resultset mit den SpaltenID(int) undName(nvarchar(200)). Geeignet fürTADOQuery.Openmit anschliessender Zeilen-Iteration.@Options <> 0: liefert die Namen als konkatenierten String über den OUTPUT-Parameter@ValueNames, getrennt durch@Delimiter. Kein Resultset – geeignet fürEXEC ... WITH OUTPUTodercmd.Parameters-Lesen.
Achtung: Die beiden Varianten lassen sich nicht mischen. Wer beides braucht, macht zwei Aufrufe oder verwendet cn_RegBulkRead @options=1.
cn_RegValueNames #
Liefert die Namen aller Werte eines Schlüssels.
EXEC cn_RegValueNames
@baseKeyId = int, -- Schlüssel-ID
@Options = int, -- 0 = Resultset, andere = konkateniert
@Delimiter = nvarchar(100), -- Trennzeichen bei Konkatenierung
@ValueNames = nvarchar(max) OUT, -- Konkatenierte Namen (bei @Options<>0)
@ValueCount = int OUT -- Anzahl der Werte
Rückgabe-Varianten: analog zu cn_RegKeyNames – @Options = 0 liefert ein Resultset mit ID/Name, andernfalls werden die Namen als konkatenierter String ausgegeben.
cn_RegShowPath #
Gibt den vollständigen Pfad eines Registry-Eintrags über PRINT aus. Nützlich zum Debuggen.
EXEC cn_RegShowPath
@id = int -- Registry-Eintrags-ID
cn_RegList #
Listet die gesamte Registry-Baumstruktur mit Einrückung auf.
EXEC cn_RegList
@maxindent = int -- Maximale Tiefe (NULL = unbegrenzt)
Beispiel: Registry-Baum anzeigen #
-- Erste drei Ebenen anzeigen
EXEC cn_RegList @maxindent = 3
cn_RegGetVersion #
Liefert die Version des Registry-Subsystems.
EXEC cn_RegGetVersion
@VersionMajor = tinyint OUT, -- Hauptversion (aktuell: 2)
@VersionMinor = tinyint OUT -- Nebenversion (aktuell: 0)
Die Version 2.0 unterstützt NVARCHAR für Namen und Werte (Unicode).
cn_RegSpecialKey #
Öffnet einen vordefinierten Spezialschlüssel über seinen Typ oder Namen.
EXEC cn_RegSpecialKey
@KeyType = int, -- Numerischer Schlüsseltyp
@KeyTypeName = varchar(50), -- Oder: Name (z.B. 'DEFAULT_USER')
@AccessID = int, -- Optionale Zugriffs-ID
@Qualifier1 = nvarchar(200), -- Optionaler Qualifier
@Qualifier2 = nvarchar(200), -- Optionaler Qualifier
@KeyId = int OUT, -- Gefundene Schlüssel-ID
@CanCreate = int -- 0 = nur öffnen, 1 = anlegen
Praxisbeispiel: Plugin-Konfiguration #
Das folgende Beispiel zeigt den typischen Ablauf beim Zugriff auf die SQL-Registry – hier am Beispiel des Xfacture-Plugins für elektronische Rechnungen:
-- 1. Prüfen ob das Plugin installiert ist
DECLARE @keyid int
EXEC cn_RegOpenPath NULL, '\VENDOR\ESOL\MODULES\DMS', @keyid OUT, 0, 0
IF @keyid IS NULL
PRINT 'DMS-Modul nicht installiert'
-- 2. Konfiguration lesen
DECLARE @format nvarchar(max), @email nvarchar(max)
EXEC cn_RegReadString '\MODULES\Grundwerte\Xfacture', 'Format', @format OUT
EXEC cn_RegReadString '\MODULES\Grundwerte\Xfacture', 'EMail', @email OUT
SELECT @format AS Format, @email AS EMail
-- 3. E-Mail-Provider ermitteln
DECLARE @provider nvarchar(max)
EXEC cn_RegReadString '\SYSTEM\Devices\EMail', 'Provider', @provider OUT
-- 4. Eigene Einstellung speichern
EXEC cn_RegWriteString '\VENDOR\MeinPlugin', 'LetzterExport', CONVERT(varchar, GETDATE(), 126)
Performance-Muster #
Mehrere Werte aus einem Teilbaum laden #
Bei Plugins und Reports müssen häufig viele Werte aus demselben Registry-Teilbaum gelesen werden – etwa der komplette Firmenstamm, alle Druck-Flags oder die Bank-Verbindungen. Hier lohnt sich die Wahl der richtigen API, besonders über VPN-Verbindungen mit nicht-vernachlässigbarer Netz-Latenz.
Faustregel:
- Ein einzelner Wert:
cn_RegReadString/cn_RegReadInt– einfach, direkt per Pfad. - Mehrere Werte, derselbe Pfad, festes Ergebnis-Schema:
DECLARE @k = dbo.cnf_RegKeyIdFromPath(...)+ N ×dbo.cnf_RegReadString(@k, N'Name') AS [Name]in einem SELECT. Gibt eine feste Spalten-Row zurück, die vom Client unverändert weiterverarbeitet werden kann. - Mehrere Werte aus einem Teilbaum (mit verschiedenen Unterschlüsseln):
cn_RegBulkReadmit@options=2. Gibt alle Werte rekursiv zurück; der Client gruppiert perKEY_ID. - Kompletten Teilbaum mit Struktur-Information benötigt:
cn_RegBulkReadmit@options=3. Liefert Schlüssel-Hierarchie und Werte in zwei Resultsets.
Anti-Muster: N einzelne Reads auf denselben Pfad #
-- Schlecht: 10 separate Prozedur-Aufrufe, jeder mit eigener Pfad-Auflösung
EXEC cn_RegReadString '\MODULES\Grundwerte\Firmenstamm', 'Firma', @firma OUT
EXEC cn_RegReadString '\MODULES\Grundwerte\Firmenstamm', 'Strasse', @strasse OUT
EXEC cn_RegReadString '\MODULES\Grundwerte\Firmenstamm', 'Plz', @plz OUT
-- ... 7 weitere Aufrufe
Auf lokalem Netz kaum spürbar, auf VPN bei 25 ms Roundtrip-Latenz: 250 ms nur für diesen Block. Der Batch wird oft gecachet, die einzelnen Aufrufe aber nicht.
Besser:
-- Variante A: Ein SELECT mit gecached KeyID
DECLARE @k int = dbo.cnf_RegKeyIdFromPath(N'\MODULES\Grundwerte\Firmenstamm', 0, 0)
SELECT
dbo.cnf_RegReadString(@k, N'Firma') AS Firma,
dbo.cnf_RegReadString(@k, N'Strasse') AS Strasse,
dbo.cnf_RegReadString(@k, N'Plz') AS Plz
-- ... weitere Spalten
-- Variante B: BulkRead auf den Teilbaum, Client gruppiert
DECLARE @kGW int
EXEC cn_RegOpenPath NULL, '\MODULES\Grundwerte', @kGW OUT, 0, 0
EXEC cn_RegBulkRead @basekeyid = @kGW, @options = 2, @UseUtf16 = 1
Variante A ist einfacher wenn das Client-Code-Modell ein flaches Resultset erwartet. Variante B ist leistungsfähiger wenn mehrere Unterschlüssel gleichzeitig gebraucht werden (Firmenstamm + Xfacture + Bank\1-5 etc.) – ein Aufruf für den ganzen Teilbaum.
Werttyp-Beschränkungen beachten #
cn_RegReadStringgibtnvarchar(max)zurück – geeignet für beliebig lange Werte.- Einige OUTPUT-Parameter in SP-Aufrufen sind auf kleinere Grössen deklariert (z.B.
nvarchar(1024)), was bei langen HTML-Templates oder Mail-Bodies zum Abschneiden führen kann. In solchen Fällen stattdessen die Scalar-Funktiondbo.cnf_RegReadString(@k, N'Name')im SELECT verwenden – sie liefert garantiertnvarchar(max). VALUE_STRINGim Resultset voncn_RegBulkReadist bei@UseUtf16=0aufvarchar(8000)begrenzt, bei@UseUtf16=1auf die ersten 8000 Bytes (also ca. 4000 Unicode-Zeichen). Für längere Werte zusätzlich@options | 4setzen undVALUE_TEXTauslesen.
Siehe auch #
- SQL-Registry – Aufbau und Struktur der Registry
- DATAOBJECTS / Actions – Menübefehle über die Registry definieren
- Plugins – Plugin-Registrierung