Pewnego razu przyszedł do mnie przygnębiony programista prosząc o pomoc w rozwiązaniu ważkiego problemu. Chłopak był mocno skołowany i mówił tylko w kółko, że raz mu zapytanie zwraca rekordy a raz nie i że on nie wie i żeby pomóc.
Przecież to NIEMOŻLIWE!
Od razu się zaciekawiłam i zabrałam się za analizę.
Od razu się zaciekawiłam i zabrałam się za analizę.
Zapytanie miało zwracać rekordy z tabeli TAB_A takie, gdzie wartość POLA1 znajduje się na liście wartości zwracanych przez podzapytanie do tabeli TAB_B. Czyli mniej więcej chodziło o taką konstrukcję zapytania:
SELECT * FROM TAB_A WHERE POLE1 IN (
SELECT POLE1 FROM TAB_B)
Faktycznie - zapytanie nie zwracało rekordów.
Jednak, gdy wykonywało się zapytanie z jawą listą wartości zwracanych z podzapytania:
SELECT * FROM TAB_A
WHERE POLE1 IN ('A', 'B', 'C');
Zapytanie zwracało poprawne wartości z tabeli.
Ot, zagadka... Niby rekordy w bazie są, ale gdy próbujemy je pobrać korzystając z podzapytania - Oracle udaje, że w zasadzie to takich to nie ma. I o, co mu zrobisz :o
Pierwsze co mi przyszło na myśl to sprawdzenie typów danych w obu tabelach].I to się okazał od razu dobry trop. W jednej tabeli POLE1 było typu CHAR a w drugiej - VARCHAR2. Niby podobne a jedna różne :)
CHAR to typ VARCHAR2 ale uzupełniony z prawej strony spacjami.
W jeden tabeli mieliśmy więc wartości 'A', 'B', 'C' ale już w drugiej 'A ', 'B ', 'C '.
Także w tym przypadku wartości kolumn POLE1 tabelach TAB_A i TAB_B były różne!
No tak, ale czemu w takim razie w przypadku zapytania z listą literałów Oracle znajdował odpowiednie rekordy? W takiej sytuacji Oracle po prostu robi niejawną konwersję danych. W sytuacji zaś, gdy w zapytaniu nie ma literałów tylko kolumny lub zmienne bindowane - Oracle takiej konwersji niejawnej nie wykona - przez co możemy nie znaleźć rekordów, których znalezienia oczekiwaliśmy.
Ale nie ma co wierzyć na słowo. Sprawdźmy na przykładzie.
Utwórzmy dwie tabele - jedną z kolumną typu CHAR a drugą z kolumną typu VARCHAR2.
Utwórzmy dwie tabele - jedną z kolumną typu CHAR a drugą z kolumną typu VARCHAR2.
create table tab_char (char_col char(10));
insert into tab_char values('10');
insert into tab_char values('20');
insert into tab_char values('30');
create table tab_vchar (vchar_col varchar(10));
insert into tab_vchar values('10');
insert into tab_vchar values('20');
insert into tab_vchar values('30');
I spróbujmy pobrać z tych tabel dane. Na początek - zapytanie z listą wartości:
select * from tab_char where char_col in ('10', '20');
Zapytanie zwraca poprawnie rekody: '10', '20'; czyli to, czego oczekiwaliśmy.
A zapytanie z podselectem?
select * from tab_char where char_col in (select vchar_col from tab_vchar);
Otrzymujemy:
no data found
Nie ma takich rekordów. Oracle nie zrobił w tym przypadku niejawnej konwersji a że dane w kolumnach są różne (różnica w spacjach) to nie znaleźliśmy żadnych rekordów.
Jest to dość częsty problem, jeśli w aplikacji używamy typu CHAR. Ponieważ nie jest to nic innego niż VARCHAR2 uzupełniony z prawej strony spacjami - lepiej używać zawsze tylko typu VARCHAR2.
Więcej o różnicach w typach CHAR i VARCHAR można przeczytać TUTAJ
Komentarze
Prześlij komentarz