2009-06-04 63 views
1

考慮下面的表格,它的約束有點笨拙,但足以證明我的觀點。請注意,爲了使事情變得非常簡單,約束條件只涉及文字值。列ID僅存在,因爲表必須至少有一列(!!),但該列未包含在約束中。雖然有點愚蠢(因此名稱),這是完全合法的語法,類似於將WHERE 0 = 1添加到SELECT查詢以確保它返回零行。ACE/Jet的IN運算符或CHECK約束是否「損壞」?

(標準SQL DDL代碼,將在ACE/Jet的ANSI-92查詢模式執行)

CREATE TABLE Test1 
(
    ID INTEGER NOT NULL, 
    CONSTRAINT daft_1 CHECK (5 = NULL) 
); 

以下INSERT成功:

INSERT INTO Test1 (ID) VALUES (1); 

這是正常現象。謂詞5 = NULL應評估爲UNKNOWNINSERT是'給予懷疑的好處'並且成功。那裏沒問題。

使用IN運營商考慮這個類似的例子:

CREATE TABLE Test2 
(
    ID INTEGER NOT NULL, 
    CONSTRAINT daft_2 CHECK (5 IN (0, 1, NULL)) 
); 

以下INSERT因爲約束咬傷失敗:

INSERT INTO Test2 (ID) VALUES (1); 

這是出乎意料的behviour,通過我至少。我希望5 IN (0, 1, NULL)再次被評估爲UNKNOWNINSERT成功的原因與第一個例子相同。

我期望在第二示例中的邏輯是相同的如下面的第三示例:

CREATE TABLE Test3 
(
    ID INTEGER NOT NULL, 
    CONSTRAINT daft_3 CHECK((5 = 0) OR (5 = 1) OR (5 = NULL)) 
); 

以下INSERT成功:

INSERT INTO Test3 (ID) VALUES (1); 

這是預期行爲。

我已經測試了SQL Server上的所有三個示例,並且所有工作都如我所料,即所有三個INSERT語句都成功。事實上,檢查信息模式顯示,在第二個例子中的SQL Server作爲「有益」(哎呀)改寫約束的條款與

((5)=NULL OR (5)=(1) OR (5)=(0)) 

所以更換IN運營商,爲ACE /噴氣,什麼是「破'這裏:IN運營商或CHECK約束?

下面是一些使用NULLable列重現問題的VBA代碼;也證明了下降的約束使得INSERT成功:

Sub TestJetInCheck() 

    On Error Resume Next 
    Kill Environ$("temp") & "\DropMe.mdb" 
    On Error GoTo 0 

    Dim cat 
    Set cat = CreateObject("ADOX.Catalog") 

    With cat 
    .Create _ 
     "Provider=Microsoft.Jet.OLEDB.4.0;" & _ 
     "Data Source=" & _ 
     Environ$("temp") & "\DropMe.mdb" 

    With .ActiveConnection 

     Dim Sql As String 
     Sql = _ 
      "CREATE TABLE Test" & vbCr & _ 
      "(" & vbCr & _ 
      " ID INTEGER, " & vbCr & _ 
      " CONSTRAINT daft_constraint " & vbCr & _ 
      "  CHECK (5 IN (0, 1, NULL))" & vbCr & _ 
      ");" 
     .Execute Sql 

     Sql = "INSERT INTO Test (ID) VALUES (1);" 

     On Error Resume Next 
     .Execute Sql 
     If Err.Number <> 0 Then 
     MsgBox Err.Description 
     Else 
     MsgBox "{{no error}}" 
     End If 
     On Error GoTo 0 

     .Execute "ALTER TABLE Test DROP CONSTRAINT daft_constraint;" 

     On Error Resume Next 
     .Execute Sql 
     If Err.Number <> 0 Then 
     MsgBox Err.Description 
     Else 
     MsgBox "{{no error}}" 
     End If 
     On Error GoTo 0 

    End With 

    Set .ActiveConnection = Nothing 
    End With 
End Sub 

編輯:我只是想試試這個:

SELECT NULL(1); - 返回NULL

SELECT 1 IN(NULL) - 返回零即FALSE

+0

爲什麼不嘗試使用DAO而不是DDL? – 2009-06-06 04:21:51

回答

0

我可以通過創建專門消除CHECK約束驗證規則(@大衛W.芬頓:對不起,我發現SQL DDL和ADO,更容易比DAO編寫但感謝靈感):

Sub TestJetInValidationRule() 

    On Error Resume Next 
    Kill Environ$("temp") & "\DropMe.mdb" 
    On Error GoTo 0 

    Dim cat 
    Set cat = CreateObject("ADOX.Catalog") 

    With cat 
    .Create _ 
     "Provider=Microsoft.Jet.OLEDB.4.0;" & _ 
     "Data Source=" & _ 
     Environ$("temp") & "\DropMe.mdb" 

    With .ActiveConnection 

     Dim Sql As String 
     Sql = _ 
      "CREATE TABLE Test" & vbCr & _ 
      "(" & vbCr & _ 
      " ID INTEGER" & vbCr & _ 
      ");" 
     .Execute Sql 
    End With 

    ' Create Validation Rules 
    Dim jeng 
    Set jeng = CreateObject("JRO.JetEngine") 
    jeng.RefreshCache .ActiveConnection 

    .Tables("Test").Columns("ID") _ 
    .Properties("Jet OLEDB:Column Validation Rule").value = _ 
    "5 IN (0, 1, NULL)" 

    jeng.RefreshCache .ActiveConnection 

    With .ActiveConnection 

     Sql = "INSERT INTO Test (ID) VALUES (1);" 

     On Error Resume Next 
     .Execute Sql 
     If Err.Number <> 0 Then 
     MsgBox Err.Description 
     Else 
     MsgBox "{{no error}}" 
     End If 
     On Error GoTo 0 

    End With 

    Set .ActiveConnection = Nothing 
    End With 
End Sub 

驗證規則咬合和INSERT失敗。因此,我懷疑IN子句意外行爲。我將在未來使用嵌套OR子句!