Sql-Server

SQL Server 是否支持自定義域?

  • June 2, 2018

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])?)*$' );

SQL Server 是否支持觸發器系統之外的任何此類功能?

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

由於上面的程式碼沒有簽名,你應該只在可以啟用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]

然後可以像這樣使用 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

上面的程式碼返回:

+------------------+
| (無列名) |
+------------------+
| 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

您會看到一個錯誤:

消息 6522,級別 16,狀態 2,第 27

行在執行使用者定義的常式或聚合“EmailType”期間發生 .NET Framework 錯誤:

System.Exception:us@asdfasdfasdfasdfasdfasdfasdfasdf90097809878907098908908908908.com 不是來自可解析的域。

System.Exception:

在 SQLCLREmailType.EmailType.IsValidDomain()

在 SQLCLREmailType.EmailType.ValidateEmailAddress()

在 SQLCLREmailType.EmailType.Parse(SqlString s)

引用自:https://dba.stackexchange.com/questions/165980