Unterstützt SQL Server benutzerdefinierte DOMAINs?

8

PostgreSQL unterstützt die DOMAINSpezifikation aus dem SQL 2011-Arbeitsentwurf.

Eine Domäne ist ein benanntes benutzerdefiniertes Objekt, das an bestimmten Stellen, an denen ein Datentyp angegeben werden kann, als Alternative zu einem Datentyp angegeben werden kann. Eine Domäne besteht aus einem Datentyp, möglicherweise einer Standardoption, und null oder mehr (Domänen-) Einschränkungen.

Dies ermöglicht uns , wirklich coole Sachen zu tun , wie eine Umsetzung Domäne für die für E - Mail über einen Groß- und Kleinschreibung Text HTML5-Spezifikation Typen . Es stellt sicher, dass alle Clients, die auf die Datenbank zugreifen, eine Integritätsprüfung der eingefügten Daten erhalten.

CREATE DOMAIN email AS citext
  CHECK ( value ~ '^[a-zA-Z0-9.!#$%&''*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$' );

Unterstützt SQL Server solche Funktionen außerhalb des Triggersystems?

Evan Carroll
quelle
1
Das Nächste sind Regeln, aber sie sind veraltet
a_horse_with_no_name

Antworten:

10

SQLCLR bietet eine Oberfläche zum Erstellen vollständig benutzerdefinierter Datentypen. Auf diese Weise unterstützt SQL Server geometrische Datentypen und Hierarchiedatentypen.

Da SQLCLR auf der Common-Intermediate-Sprache von Microsoft.Net basiert, sind für einen SQLCLR-Datentyp eine Vielzahl von Einschränkungen möglich. Sie können beispielsweise leicht sicherstellen, dass eine E-Mail-Adresse von einer gültigen Domain stammt, indem Sie DNS als Teil des Datenüberprüfungscodes nach dem MX-Eintrag abfragen.

SQL Server kann eine CLR-UDT indizieren, solange sie festgelegt IsByteOrdered:=Trueist. Es gibt eine Menge solcher Eigenschaften, die Sie ändern können, um die Verwendung von UDT durch SQL Server zu beeinflussen. Für eine Gleichheitsübereinstimmung, wie sie für einen Index erforderlich ist, überprüft SQL Server einfach den auf der Seite gespeicherten Binärwert, dh er muss den UDT-Code überhaupt nicht anzeigen.

Als Beispiel für einen benutzerdefinierten SQLCLR-Typ, der einen Domänenbereich auf Gültigkeit überprüft, habe ich den folgenden schrecklichen Proof-of-Concept-VB.Net-Code geschrieben:

Imports System
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.SqlTypes
Imports Microsoft.SqlServer.Server

<Serializable()> _
<Microsoft.SqlServer.Server.SqlUserDefinedType(Format.UserDefined, IsByteOrdered:=True, MaxByteSize:=320, ValidationMethodName:="ValidateEmailAddress")> _
Public Structure EmailType
    Implements INullable
    Implements IBinarySerialize

    Private m_Null As Boolean
    Private m_EmailAddress As String

    Public Function ValidateEmailAddress() As Boolean
        'is the email address valid?
        If Me.IsValidDomain Then
            Return True
        Else
            Return False
        End If
    End Function

    Public Overrides Function ToString() As String
        Return Me.m_EmailAddress
    End Function

    Public ReadOnly Property IsNull() As Boolean Implements INullable.IsNull
        Get
            ' Put your code here
            If Me.m_EmailAddress Is Nothing Then
                Me.m_Null = True
            Else
                Me.m_Null = False
            End If
            Return m_Null
        End Get
    End Property

    Public Shared ReadOnly Property Null As EmailType
        Get
            Dim h As New EmailType
            h.m_Null = True
            Return h
        End Get
    End Property

    'called when SQL Server passes in a SqlString value to the UDT
    Public Shared Function Parse(ByVal s As SqlString) As EmailType
        If s.IsNull Then
            Return Null
        End If

        Dim u As New EmailType

        u.m_EmailAddress = CType(s, String)
        If u.ValidateEmailAddress = False Then
            Throw New Exception("Invalid Email Address")
        End If
        Return u
    End Function

    Public Function IsValidDomain() As Boolean
        Dim iAtSign As Int32 = Microsoft.VisualBasic.Strings.InStr(Me.m_EmailAddress, "@")
        Dim iDomainLength As Int32 = Microsoft.VisualBasic.Strings.Len(Me.m_EmailAddress) - iAtSign
        Dim sDomain As String = Microsoft.VisualBasic.Strings.Right(Me.m_EmailAddress, iDomainLength)
        Dim bResolvable As Boolean = False
        Try
            Dim ip As System.Net.IPHostEntry = System.Net.Dns.GetHostEntry(sDomain)
            bResolvable = True
        Catch ex As Exception
            Throw New Exception(Me.m_EmailAddress & " is not from a resolvable domain.")
        End Try
        Return bResolvable
    End Function

    ' save the value to the database
    Public Sub Write(w As System.IO.BinaryWriter) Implements IBinarySerialize.Write
        w.Write(Me.m_EmailAddress)
    End Sub

    ' retrieve the value from the database
    Public Sub Read(r As System.IO.BinaryReader) Implements IBinarySerialize.Read
        Dim sTemp As String = r.ReadString
        Dim sTemp1 As String = ""
        For Each n As Char In sTemp.ToCharArray
            sTemp1 = sTemp1 & n.ToString
        Next
        Me.m_EmailAddress = sTemp
    End Sub

End Structure

Da der obige Code nicht signiert ist, sollten Sie ihn nur auf einem Entwicklungscomputer testen, auf dem Sie die TRUSTWORTHYDatenbankeinstellung aktivieren können . Sobald der Code kompiliert ist, importieren Sie ihn wie folgt in SQL Server:

CREATE ASSEMBLY SQLCLREmailType 
AUTHORIZATION dbo 
FROM 'C:\Path\Goes\Here\SQLCLREmailType.dll' 
WITH PERMISSION_SET = UNSAFE;

CREATE TYPE EmailType
EXTERNAL NAME SQLCLREmailType.[SQLCLREmailType.EmailType]

Das UDT kann dann folgendermaßen verwendet werden:

DECLARE @t TABLE (
    col EmailType NOT NULL
    );

INSERT INTO @t (col)
VALUES ('[email protected]')
    , ('[email protected]');

SELECT CONVERT(varchar(50), t.col)
FROM @t t
GO

Der obige Code gibt Folgendes zurück:

+ ------------------ +
| (Kein Spaltenname) |
+ ------------------ +
| [email protected] |
| [email protected] |
+ ------------------ +

Wenn Sie jedoch versuchen, eine Adresse einzufügen, die zu einer nicht vorhandenen E-Mail-Domäne gehört, wie in:

DECLARE @t TABLE (
    col EmailType NOT NULL
    );

INSERT INTO @t (col)
VALUES , ('us@asdfasdfasdfasdfasdfasdfasdfasdf90097809878907098908908908908.com');

SELECT CONVERT(varchar(50), t.col)
FROM @t t
GO

Sie sehen einen Fehler:

Meldung 6522, Ebene 16,
Status 2, Zeile 27 Während der Ausführung der benutzerdefinierten Routine oder des Aggregats "EmailType" ist ein .NET Framework-Fehler aufgetreten:
System.Exception: us@asdfasdfasdfasdfasdfasdfasdfasdf90097809878907098908908908908.com stammt nicht aus einer auflösbaren Domäne.
System.Exception:
bei SQLCLREmailType.EmailType.IsValidDomain ()
bei SQLCLREmailType.EmailType.ValidateEmailAddress ()
bei SQLCLREmailType.EmailType.Parse (SqlString s)

Max Vernon
quelle
1
Das Abfragen von DNS nach dem MX-Eintrag ist jedoch außerhalb von SQL Server viel besser.
Martin Smith
@ MartinSmith - auf jeden Fall; In der Praxis würde ich das eigentlich nie so machen. Dies ist nur ein Proof-of-Concept.
Max Vernon
3

Ich werde dies noch nicht als ausgewählt markieren, da es möglicherweise eine Problemumgehung gibt, aber technisch werden sie nicht unterstützt. Sie können das Problem in Microsoft Connect sehen.

Hallo Erland,

Nachdem wir alle Vorschläge in unserer Pipeline sorgfältig geprüft haben, schließen wir Elemente, die wir in naher Zukunft aufgrund der aktuellen Elemente mit höherer Priorität nicht implementieren werden. Wir werden die geschlossenen Vorschläge in Zukunft auf der Grundlage der Produkt-Roadmap erneut bewerten.

Nochmals vielen Dank für den Produktvorschlag und die fortgesetzte Unterstützung für unser Produkt.

- Umachandar, SQL Programmability Team

Die Ausgabe wurde 2012 eröffnet.

Evan Carroll
quelle
Das Connect-Element, auf das verwiesen wird, gibt an, dass Microsoft CREATE DOMAINin der jeweils aktuellen Version von SQL Server (2012) keine Implementierung durchführen wird . Dies bedeutet nicht, dass die Funktion nicht unterstützt wird, wie meine Antwort zeigt.
Max Vernon