Niedawno, w działającej od kilku lat aplikacji pojawił się dziwny błąd, podczas wykonywania jednego z zapytań SQLowych: ORA-00932: inconsistent datatypes: expected - got CLOB. Jak widać po kodzie błędu pod spodem jest baza danych Oracle (dokładnie w wersji: 10.2.0.3.0). Wyjątek był o tyle dziwny, że w schemacie bazy, w żadnej tabeli nie było kolumny o typie CLOB :/

Zapytanie było dosyć skomplikowane – wyciągało dane z kilku tabel i konkatenowało ze sobą wszystkie wyniki. Służyło do przygotowania danych do eksportu do pliku CSV za jednym razem. Wyglądało mniej więcej tak:

    select
      'KolumnaPierwsza' || ',' || 'KolumnaDruga' || ',' || 'KolumnaTrzecia'
    from dual
  union
    select distinct
      a.pierwszaWartosc || ',' || a.drugaWartosc || ',' || a.trzeciaWartosc
    from tabela_testowa a 

Było o wiele bardziej rozbudowane, ale idea ta sama.

Błąd wyglądał tak, jakby jedna z konkatenowanych kolumn była typu CLOB lub jej wartość była rzutowana na CLOB-a. Pierwsze założenie obalone, bo w schemacie bazy nie było kolumny tego typu. Przeszukałem internet pod kątem założenia drugiego, ale nie znalazłem nic, co mogło by świadczyć o tym, że zwykły VARCHAR2 jest w jakichś szczególnych przypadkach rzutowany na CLOB. Nie znalazłem też nic o tym, żeby podczas konkatenacji następowało takie rzutowanie. To, czego udało mi się dowiedzieć, to kilka informacji o CLOB-ie:

  • CLOB-y nie zachowują się jak zwykłe stringi. Nie możesz na przykład zapytać o ich długość lub inne cechy dostępne przy stringach
  • CLOB-y (a także i BLOB-y) nie są dozwolone w sekcjach: GROUP BY, ORDER BY, SELECT DISTINCT, agregatach i JOIN

W problematycznym zapytaniu był użyty SELECT DISTINCT na konkatenowanych kolumnach. To tylko potwierdziło moje przypuszczenia, że tam się czai CLOB. Nie miałem niestety bezpośredniego dostępu do bazy, ani do danych, żeby sprawdzić dlaczego się tam czai. Działałem więc trochę na ślepo. Przeszukałem internet pod tym kątem i znalazłem rozwiązanie – PL/SQL-ową funkcję substr z biblioteki dbms_lob. Biblioteka ta jest od jakiegoś czasu domyślnie dostarczana razem z bazą Oracle i w przypadku wersji 10.2.0.3.0 również była dostępna.

Poprawione zapytanie wyglądało analogicznie do tego:

    select
      dbms_lob.substr('KolumnaPierwsza' || ',' || 'KolumnaDruga' || ',' || 'KolumnaTrzecia')
    from dual
  union
    select distinct
      dbms_lob.substr(a.pierwszaWartosc || ',' || a.drugaWartosc || ',' || a.trzeciaWartosc)
    from tabela_testowa a 

Funkcja dbms_lob.substr posłużyła w tym miejscu do „rzutowania” CLOB-a na VARCHAR2. Więcej na temat samej funkcji, jak i całej biblioteki można znaleźć na stronach:

Rozwiązanie znalazłem. Niestety cały czas nie znam przyczyny pojawienia się tajemniczego CLOB-a. Niedosyt więc pozostaje… 😉