Оператор IN ACE / Jet или ограничение CHECK 'нарушены'? - PullRequest
1 голос
/ 04 июня 2009

Рассмотрим следующую таблицу с ограничением, которое немного глупо, но достаточно просто, чтобы продемонстрировать мою точку зрения. Обратите внимание, что для простоты критерии ограничения включают только буквальные значения. Столбец ID существует только потому, что в таблице должен быть хотя бы один столбец (!!), но этот столбец не включен в ограничение. Несмотря на небольшую глупость (отсюда и название), это вполне допустимый синтаксис и похож на добавление WHERE 0 = 1 к запросу SELECT, чтобы гарантировать, что он возвращает ноль строк.

(стандартный код DDL SQL, будет выполняться в режиме запросов 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 должен быть оценен до UNKNOWN. INSERT «получает выгоду от сомнения» и добивается успеха. Там нет проблем.

Рассмотрим аналогичный пример, используя оператор IN:

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

Следующее INSERT завершается ошибкой из-за укусов ограничения:

INSERT INTO Test2 (ID) VALUES (1);

Это неожиданно, по крайней мере для меня. Я ожидаю, что 5 IN (0, 1, NULL) снова будет оцениваться как UNKNOWN, а INSERT будет успешным по тем же причинам, что и в первом примере.

Я ожидаю, что логика во втором примере будет такой же, как в следующем третьем примере:

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 как «услужливо» (grrr) переписать условие ограничения, чтобы заменить оператор IN на

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

Итак, что для ACE / Jet, что здесь «сломано»: оператор IN или ограничение CHECK?

Вот некоторый код VBA для воспроизведения проблемы с использованием столбца NULLable; также демонстрирует, что удаление ограничения позволяет 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

РЕДАКТИРОВАТЬ: Я просто подумал попробовать это:

ВЫБЕРИТЕ NULL IN (1); - возвращает NULL

ВЫБЕРИТЕ 1 IN (NULL) - возвращает ноль, т.е. FALSE

1 Ответ

0 голосов
/ 06 июня 2009

Я могу устранить ограничение CHECK, в частности, создав Правило валидации (@David W. Fenton: извините, я нахожу 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 в будущем!

...