Sql-Server

如何使用基於策略的管理為對象名稱應用 Title-Case RegEx 條件

  • June 2, 2016

我在 SQL Server 2014 實例上實現 PBM,並希望強制執行一個條件,即所有對像都在dbo架構之外創建,並且所有對象名稱都以標題大小寫(即 OrderDetail)創建。我起草了正則表達式[A-Z]{1}[a-z]+[A-Z]?[a-z]*,但無法用它創建令人滿意的對象名稱。範例輸出在這裡。

Policy condition: '@Schema != 'dbo' AND @Name LIKE '[A-Z]{1}[a-z]+[A-Z]?[a-z]*'' Policy description: '' Additional help: '' : '' Statement: 'CREATE TABLE Test.OrderDetail ( Col1 int)

PBM 不支持這個特定的表達式,還是我應該在條件中使用替代語法?

SQL Server 對Like運算符的實現僅支持一種真正縮減的正則表達式,其中包括基本字元匹配和萬用字元,但不包括重複次數 like{1}或其他類型的正則表達式錨點,如\bor \s。一般來說,它也是不區分大小寫的。

也就是說,您有時可以使用Like. 假設您想找到 3 個字母字元和兩個數字,您可以執行以下操作:

[a-z][a-z][a-z][0-9][0-9]

您還可以通過使用區分大小寫的排序規則來強制區分大小寫匹配,例如

DECLARE @s VARCHAR(10) = 'orderDetail'

-- Check case; 
SELECT
   CASE
       WHEN UPPER( LEFT( @s, 1 ) ) = LEFT( @s, 1 ) COLLATE SQL_Latin1_General_CP1_CS_AS THEN 1 
       ELSE 0
   END

SET @s = 'OrderDetail'

-- Check case; 
SELECT
   CASE
       WHEN UPPER( LEFT( @s, 1 ) ) = LEFT( @s, 1 ) COLLATE SQL_Latin1_General_CP1_CS_AS THEN 1 
       ELSE 0
   END

然而,TitleCase 與其說是一種政策,不如說是一種哲學立場?您將如何確定TitleCaseTitlecase是否正確?事實上,他們倆都是。

您的特定正則表達式模式也將匹配“orderDetails”和“orderDetails”的“Details”部分。你可以做的是添加開始和結束詞錨,例如

^[A-Z]{1}[a-z]+[A-Z]?[a-z]*$

但正如所描述的,這無論如何都不起作用,Like我不相信這是值得的。所有你真正想做的(除了dbo模式檢查,這很容易而且很有價值),就是檢查第一個字元是大寫的。所以理論上你可以用極其強大的 PBM ExecuteSql函式來做這樣的事情:

ExecuteSql('Bool', 'SELECT CASE WHEN UPPER( LEFT( @@ObjectName, 1 ) ) = LEFT( @@ObjectName, 1 ) COLLATE SQL_Latin1_General_CP1_CS_AS THEN 1 ELSE 0 END')

我花了很短的時間就完成了這項工作,此外,您可能應該使用 camelCase :)

另一個有趣的例子: https ://www.simple-talk.com/blogs/2013/09/16/sql-server-policy-based-management-creating-a-custom-condition/

高溫高壓

政策評估結果

我認為您的意思是PascalCase(不是camelCase),因為Title Case僅適用於單詞之間有空格的情況。

我同意@wBob 的觀點,即您可以通過 T-SQL 做的最好的事情是確保第一個字母是大寫的。儘管您可能會更進一步,擴展@wBob 建議的條件,並確保第二個字母(假設它不會被接受為具有單個字母表名)遵循相同的模式(並且您也可以使用二進制排序規則,因為您只是將字元與自身進行比較):

ExecuteSql('Bool',
          'SELECT CASE
                    WHEN  UPPER( LEFT( @@ObjectName, 1 ) ) =
                          LEFT( @@ObjectName, 1 ) COLLATE Latin1_General_100_BIN2
                      AND LOWER( SUBSTRING( @@ObjectName, 2, 1 ) ) =
                          SUBSTRING( @@ObjectName, 2, 1 ) COLLATE Latin1_General_100_BIN2
                      THEN 1 ELSE 0 END')

但是,只要您這樣做ExecuteSql,您可能會朝著目標再邁出一步並應用實際的正則表達式。RegEx 功能只能通過 SQLCLR 獲得,在這種情況下,您可以編寫自己的程式碼或下載免費版本的SQL#(我創建的,但 RegEx 函式是免費的),並使用以下模式:\b(?:\p{Lu}\p{Ll}+)+\b. 這個模式正在做的是:

  • \b意思是“單詞邊界”。它不會在單詞中間匹配,但會在相鄰字元為空格、標點符號等時匹配單詞字元之前或之後的字元。
  • (?: ... )+指被找到 1 次或多次的非擷取組。
  • \p{Lu}表示與“大寫字母”的 Unicode 類別匹配的任何單個字元。\p{...}表示“Unicode 類別”,而具體表示Lu“大寫字母”類別(L其本身表示任何字母,無論大小寫)。這允許此條件接受不屬於該A-Z範圍的其他語言的大寫字母。無需指定{1}表示單個實例,因為無論如何這是預設值。
  • \p{Ll}+表示與“小寫字母”的 Unicode 類別匹配的 1 個或多個字元。

雖然我相信此模式擷取了您嘗試的模式的意圖,但正如您從下面的結果中看到的那樣,它仍然無法處理名稱中間應該使用大寫字母但目前是小寫的情況:

DECLARE @Tests TABLE (Name NVARCHAR(50), ExpectedResult BIT);
INSERT INTO @Tests (Name, ExpectedResult) VALUES (N'OrderDetail', 1);
INSERT INTO @Tests (Name, ExpectedResult) VALUES (N'orderDetail', 0);
INSERT INTO @Tests (Name, ExpectedResult) VALUES (N'Orderdetail', 0);
INSERT INTO @Tests (Name, ExpectedResult) VALUES (N'OrderDEtail', 0);
INSERT INTO @Tests (Name, ExpectedResult) VALUES (N'ORderDetail', 0);
INSERT INTO @Tests (Name, ExpectedResult) VALUES (N'OrderDetaiL', 0);

SELECT t.[Name],
      t.ExpectedResult,
      SQL#.RegEx_IsMatch(t.[Name], N'\b(?:\p{Lu}\p{Ll}+)+\b', 1, NULL) AS [ActualResult]
FROM   @Tests t

回報:

Name           ExpectedResult    ActualResult
OrderDetail    1                 1
orderDetail    0                 0
Orderdetail    0                 1
OrderDEtail    0                 0
ORderDetail    0                 0
OrderDetaiL    0                 0 

Orderdetail應該只有人類才能確定OrderDetail。這就是為什麼,無論您如何實施條件,政策的“評估模式”都不應該是“改變:防止”。這種類型的標準/政策確實應該留給程式碼審查,所以最好將“評估模式”保留為“按需”,並可能在每個開發週期結束時執行它(雖然之前仍有機會修復它任何違規行為都會進入生產階段)。

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