Поддерживаются ли значения по умолчанию в Oracle для столбцов с семантикой символов? - PullRequest
5 голосов
/ 26 марта 2012

Я пытался добавить столбец в таблицу, но с предложением DEFAULT получил неожиданный эффект. В таблицу с существующими строками я добавил новый столбец, например:

alter table t add c char(1 char) default 'N' not null;

Когда я впоследствии добавил проверочное ограничение в таблицу, это не удалось:

alter table t add constraint chk check(c in ('N', 'Y'));

Что привело к

ERROR at line 1:
ORA-02293: cannot validate (T.CHK) - check constraint violated.

Другая информация:

  1. Поскольку я устанавливаю единицы явно (то есть char (1 char), а не просто char (1)), я не ожидаю, что значение nls_length_semanatics будет релевантным.
  2. После добавления столбца как char (1 символ), недавно добавленные "N" на самом деле являются "N", и я не уверен, что такое дополнительный пробел.
  3. Добавление столбца как char (1 байт) работает как положено;
  4. Добавление столбца без «по умолчанию» N «not null» с последующим обновлением всех существующих строк на «N» с последующим изменением столбца на «not null» также работает как ожидалось.
  5. NLS_CHARACTERSET - это AL32UTF8, но я не ожидаю, что это также будет актуально.
  6. База данных Oracle 11g; 11.2.0.1.0.

Спасибо.

Ответы [ 3 ]

5 голосов
/ 26 марта 2012

Я полагаю, что то, что вы видите, является ошибкой, основанной на взаимодействии нескольких разных вещей

  • Во-первых, набор символов базы данных должен быть набором символов переменной ширины (т. Е. AL32UTF8), чтобы для одного символа требовалось до четырех байтов памяти.
  • Во-вторых, столбец должен быть объявлен с семантикой длины символа
  • В-третьих, начиная с версии 11.1, Oracle добавила оптимизацию, так что если вы добавите в таблицу столбец, объявленный NOT NULL и имеющий DEFAULT, то Oracle сможет сделать это, просто обновив словарь данных, а не фактически хранение значения по умолчанию в каждой строке таблицы.

Когда обе эти вещи верны, оказывается, что возвращаемое значение имеет длину 4 и дополняется символом CHR(0).

SQL> select * from v$version;

BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
PL/SQL Release 11.2.0.1.0 - Production
CORE    11.2.0.1.0      Production
TNS for 64-bit Windows: Version 11.2.0.1.0 - Production
NLSRTL Version 11.2.0.1.0 - Production

SQL> create table foo( col1 number );

Table created.

SQL> insert into foo values( 1 );

1 row created.

SQL> commit;

Commit complete.

SQL> alter table foo add c char(1 char) default 'N' not null;

Table altered.

SQL> alter table foo add constraint chk_foo check( c in ('Y', 'N') );
alter table foo add constraint chk_foo check( c in ('Y', 'N') )
                               *
ERROR at line 1:
ORA-02293: cannot validate (SCOTT.CHK_FOO) - check constraint violated

SQL> select c, dump(c) from foo;

C    DUMP(C)
---- ------------------------------
N    Typ=1 Len=4: 78,0,0,0

Если вы на самом деле принудительно сохраните значение в таблице, вы получите ожидаемое поведение при отсутствии заполнения CHR(0). Поэтому, если я вставлю новую строку в таблицу, она пройдет.

SQL> insert into foo(col1) values (2);

1 row created.

SQL> select c, dump(c) from foo;

C    DUMP(C)
---- ------------------------------
N    Typ=1 Len=4: 78,0,0,0
N    Typ=1 Len=1: 78

Вы также можете ввести UPDATE для обновления строк, которые на самом деле не хранят значение в строках таблицы

SQL> update foo
  2     set c = 'N'
  3   where c != 'N';

1 row updated.

SQL> select c, dump(c) from foo;

C    DUMP(C)
---- ------------------------------
N    Typ=1 Len=1: 78
N    Typ=1 Len=1: 78
2 голосов
/ 26 марта 2012

Причина ошибки ORA-02293, как вы уже упоминали, заключается в том, что она вставляет «N» (с дополненным пробелом), а не «N».Итак, ваше ограничение нарушено.

Более интересный вопрос: почему он добавляет это пространство?Ну, по определению, CHAR - это фиксированная ширина, а VARCHAR - нет.CHAR всегда будет заполнять пробел, чтобы заполнить все пространство памяти, выделенное для столбца.Поскольку вы выбрали ширину 1 CHAR, а AL32UTF8 - это набор символов с переменной шириной, это может противоречить природе CHAR с фиксированной шириной.Похоже, что он дополняется, чтобы заполнить дополнительные байты, которые не используются символом «N».Или, по крайней мере, я предполагаю, что это то, что происходит.

2 голосов
/ 26 марта 2012

Вы пометили oracle11g, но не указали версию.

Это работает для меня на 11.2.0.2 в Linux x86-64.

SQL*Plus: Release 11.2.0.2.0 Production on Mon Mar 26 13:13:52 2012

Copyright (c) 1982, 2010, Oracle.  All rights reserved.

Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 - 64bit Production
With the Partitioning, Real Application Clusters, Automatic Storage Management and OLAP options

SQL> create table tt(a number);

Table created.

SQL> insert into tt values (1);

1 row created.

SQL> commit;

Commit complete.

SQL> alter table tt add c char(1 char) default 'N' not null;

Table altered.

SQL> alter table tt add constraint chk check(c in('N','Y'));

Table altered.

SQL> select * from tt;

     A C
---------- -
     1 N

SQL> column dump(c) format a30
SQL> select c, length(c),dump(c) from tt;

C  LENGTH(C) DUMP(C)
- ---------- ------------------------------
N      1 Typ=96 Len=1: 78

Так что ....возможно, у вас есть ошибка в вашей версии?

Надеюсь, это поможет.

...