SQL Server 是否支持自定义域?

Eva*_*oll 8 sql-server datatypes sql-clr sql-standard domain

PostgreSQL 支持DOMAIN规范,来自 SQL 2011 工作草案规范,

域是命名的用户定义对象,可以在某些可以指定数据类型的地方将其指定为数据类型的替代。域由数据类型、可能是默认选项和零个或多个(域)约束组成。

这使我们能够做一些非常酷的事情,例如通过不区分大小写的文本类型为电子邮件实现HTML5 规范的它确保所有访问数据库的客户端都对插入的数据进行完整性检查。

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])?)*$' );
Run Code Online (Sandbox Code Playgroud)

SQL Server 是否支持触发器系统之外的任何此类功能?

Han*_*non 10

SQLCLR 为创建完全自定义的数据类型提供了一个表面区域。事实上,这就是 SQL Server 支持几何和层次结构数据类型的方式。

由于 SQLCLR 基于 Microsoft.Net 公共中间语言,因此 SQLCLR 数据类型可能存在多种约束。例如,您可以通过查询 DNS 以获取 MX 记录作为数据验证代码的一部分,轻松确保电子邮件地址来自有效域。

只要IsByteOrdered:=True设置了 SQL Server 就可以索引 CLR UDT 。有大量类似的属性,您可以更改这些属性以影响 SQL Server 使用 UDT 的方式。对于索引所需的等式匹配,SQL Server 只查看存储在页面中的二进制值,即它根本不需要查看 UDT 代码。

作为检查域空间有效性的 SQLCLR 用户定义类型的示例,我编写了以下糟糕的概念验证 VB.Net 代码:

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
Run Code Online (Sandbox Code Playgroud)

由于上面的代码没有签名,你应该只在可以启用TRUSTWORTHY数据库设置的开发机器上测试它。代码编译完成后,通过以下方式将其导入 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]
Run Code Online (Sandbox Code Playgroud)

然后可以像这样使用 UDT:

DECLARE @t TABLE (
    col EmailType NOT NULL
    );

INSERT INTO @t (col)
VALUES ('mvernon@mvct.com')
    , ('us@them.com');

SELECT CONVERT(varchar(50), t.col)
FROM @t t
GO
Run Code Online (Sandbox Code Playgroud)

上面的代码返回:

+-----------------+
| (无列名) |
+-----------------+
| mvernon@mvct.com |
| us@them.com |
+-----------------+

但是,当尝试插入属于不存在的电子邮件域的地址时,例如:

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
Run Code Online (Sandbox Code Playgroud)

你看到一个错误:

消息 6522,级别 16,状态 2,第 27
行在执行用户定义的例程或聚合“EmailType”期间发生 .NET Framework 错误:
System.Exception:us@asdfasdfasdfasdfasdf900978098789070989089088908 is not a resolvable domain
System.Exception:
在 SQLCLREmailType.EmailType.IsValidDomain()
在 SQLCLREmailType.EmailType.ValidateEmailAddress()
在 SQLCLREmailType.EmailType.Parse(SqlString s)