Pojawia się i znika....



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ę.

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.

 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