2012-03-26 4 views
5

Próbowałem dodać kolumnę do tabeli, ale uzyskałem zaskakujący efekt z klauzulą ​​DEFAULT. W tabeli z istniejących wierszy, dodałem nową kolumnę tak:Czy wartości domyślne są obsługiwane w Oracle dla kolumn z semantyką char?

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

Kiedy następnie dodaje ograniczenie wyboru do stołu, to nie powiodło się:

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

co spowodowało

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

Inne info:

  1. Bo Ustawiam jednostki jawnie (tj. Char (1 znak) w przeciwieństwie do znaku char (1)), nie oczekuję, że wartość nls_length_semanatics będzie istotna.
  2. Po dodaniu kolumny jako char (1 znak) nowo dodane litery "N" są w rzeczywistości "N" i nie jestem pewien, co to jest dodatkowy odstęp.
  3. Dodanie kolumny jako char (1 bajt) działa zgodnie z oczekiwaniami;
  4. Dodanie kolumny bez "domyślnego" N "nie zerowego", po której następuje aktualizacja wszystkich istniejących wierszy do "N", a następnie zmiana kolumny na "niezerowa" również działa zgodnie z oczekiwaniami.
  5. NLS_CHARACTERSET to AL32UTF8, ale nie spodziewam się, że będzie to istotne.
  6. Baza danych to Oracle 11g; 11.2.0.1.0.

Dzięki.

+0

jeśli tylko wartości mają być „N” lub „Y” , dlaczego nie zrobić (1 bajt)? – tbone

+2

Spróbuj przepisać ograniczenie sprawdzające jako 'CHECK (C IN (TO_NCHAR (" N '), TO_NCHAR ("Y')))". Nie jestem pewien, czy wywołania funkcji są dozwolone w ograniczeniach sprawdzania, ale może to być przynajmniej warte strzału. –

Odpowiedz

2

Oznaczyłeś tag oracle11g, ale nie określiłeś wersji.

To działa dla mnie 11.2.0.2 na 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 

Więc .... może masz błąd w swojej wersji?

Nadzieję, że pomaga.

+0

Interesujące. Próbowałem go zarówno w systemie Windows, jak i Linux, Oracle 11.2.0.1.0. Więc może to pomniejszy błąd wersji. Spróbuję z aktualizacją. Dzięki. –

+3

@ Mark J. Bobak - Jaki jest twój zestaw znaków bazy danych? Zakładam, że dzieje się tak tylko w bazach danych, które mają zestawy znaków o zmiennej szerokości (np. "AL32UTF8"). –

+0

@JustinCave FTW! Zgadzam się, myślę, że to zestaw znaków o zmiennej szerokości. Oracle musiałby przydzielić maksymalną szerokość dozwoloną przez zestaw znaków o zmiennej szerokości, a resztę pozostawić dla znaków, które nie używają maksymalnych bajtów. –

2

Powodem błędu ORA-02293, jak już wspomniano, jest to, że wstawia "N" (z wyściełaną białą spacją) zamiast "N". Więc twoje ograniczenie jest naruszone.

Bardziej interesujące pytanie brzmi: dlaczego dodaje przestrzeń? Z definicji, CHAR jest stałą szerokością, gdzie VARCHAR nie jest. CHAR zawsze będzie padał biały obszar, aby wypełnić całą przestrzeń pamięci przeznaczoną dla kolumny. Ponieważ wybrałeś szerokość 1 CHAR, a AL32UTF8 jest zestawem znaków o różnej szerokości, wydaje się, że jest to sprzeczne z naturą CHAR o stałej szerokości. Wygląda na to, że jest dopełniany, aby wypełnić dodatkowy bajt (y) nieużywany przez "N". Albo przynajmniej zakładam, że to się dzieje.

5

Wierzę, że to, co widzisz jest to błąd, który opiera się na kilku różnych rzeczy oddziałujących

  • Po pierwsze, zestaw znaków bazy danych musi być zestaw zmiennej szerokości znaków (tj AL32UTF8), tak aby pojedynczy znak może wymagać do czterech bajtów pamięci.
  • Po drugie, kolumna musi być zadeklarowana z semantyką długości znaków
  • Po trzecie, począwszy od 11.1, Oracle dodał optymalizację, więc jeśli dodasz kolumnę do tabeli, która jest zadeklarowana jako NOT NULL i która ma DEFAULT, Oracle może to zrobić po prostu przez aktualizację słownika danych zamiast przechowywania rzeczywistej wartości w każdym wierszu tabeli.

Kiedy obie te rzeczy są prawdziwe, wygląda na to, że zwrócona wartość ma długość 4 i jest dopełniona znakiem 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 

Jeśli faktycznie wymusić wartość mają być przechowywane w tabeli, dostaniesz oczekiwane zachowanie, gdzie nie ma CHR(0) wyściółka. Więc jeśli wstawię nowy wiersz do tabeli, przejdzie.

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 

Można też wystawić UPDATE zaktualizować wiersze, które nie są faktycznie przechowywanie wartości w wierszach tabeli

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 
+3

Zobacz także mój przykład i komentarz powyżej. Jest to najwyraźniej błąd w 11.2.0.1.0 i naprawiony w 11.2.0.2.0. –