Oracle veritabanında NULL

Basitçe değer yok anlamında bir ifade : NULL . Nümerik ya da karakter formatında değil; bir tipi ya da uzunluğu yok. Değer yok anlamında ama gene de bir değer.

Özellikle bazı işlemlerde dikkat edilmediğinde sürpriz hatalara sebep olabilir. Mesela iki tabloyu birbiri ile bitiştiriyorsanız ve kolonların NULL olma olasılığı varsa , sorgularınızda NOT EXIST , NOT IN gibi ifadeler kullanıyorsanız, karşılaştırma operatörleri ile bir koşulun sağlanıp sağlanmadığını kontrol edecekseniz , NULL değer barındıran kolonlarla sayısal işlemler yapıyorsanız … NULL değerler sesebiyle hatalı sonuç üretme riskiniz doğar

SQL dilinde NULL tipsiz bir değerdir. 0, 4 gibi bir sayı ya da bir metin değildir.Genel olarak NULL ile yapılan her işlem NULL değer döndürür. NULL bir değer ile toplama çıkarma vs işlemleri yapılamaz ve çoğu fonksiyon ile birlikte kullanılamaz. NULL ın bir tipi olmadığından herhangi bir değer ile hatta kendisi ile dahi karşılaştırılamaz. Bu sebeple NULL ile karşılaştırma işlemlerinde =, <> operatörleri değil IS -IS NOT kullanılmalıdır. NULL ın hayatı zorlaştıran bu özelliklerine binaen oracle da NVL, NVL2, NULLIF, NNVL gibi sırf NULL ile başetmek için fonksiyonlar yaratılmıştır.

Matematiksel ifadelerdeki etkisinden başlayalım. 0 ve NULL ve ” ne kadar benzer. Öncelikle;

SQL>SELECT ''
     , NULL
     , ' '
     , NULL || NULL
  FROM DUAL;

Eğer kullandığınız editörü özellikle NULL değerleri NULL ile doldur diye ayarlamadıysanız bu sorgudan size bomboş bir satır dönecek. Biraz görünür yapalım;

SQL>SELECT DUMP ('')
     , DUMP (NULL)
     , DUMP (' ')
     , DUMP (NULL || NULL)
  FROM DUAL;

DUMP('')            DUMP(NULL)          DUMP(' ')	                DUMP(NULL||NULL)
NULL	            NULL	        Typ=96 Len=1: 32                   NULL

Burada dikkat etmeniz gerken şey iki tırnak arasına hiçbir şey yazdaman sorgu yazdığınızda da ( ” ) NULL sonuç döndürmüş oluyoruz!

Ascii kodları;

SQL>SELECT ASCII ('')
     , ASCII ('''')
     , ASCII (NULL)
     , ASCII (' ')
     , ASCII (NULL || NULL)
  FROM DUAL;

ASCII('')	ASCII('''')	ASCII(NULL)	ASCII('')	ASCII(NULL||NULL)
34	            39		                  32	

Evet. Gördüğünüz gibi NULL bir karakter olmadığından ASCII karşılığı da yok.

Şimdi şu basit sorguyu ele alalım

SQL>  SELECT 0 * 5, NULL * 5,
             0 + 5, NULL + 5   
        FROM DUAL;

0*5 NULL*5 0+5 NULL+5
---- ------ ---- --------
0 NULL 5 NULL

0 ın toplamada etkisi yok. Ama NULL toplama işlemi sonucunu dahi NULL yapar. Sıfır ile tanımsız bir işlem yaptığınızda hata alırsınız ama NULL ile hata almazsınız. Sonuç hep NULL çıkar.

SQL>SELECT NULL / NULL , 5 / NULL ,
            ROUND (NULL / NULL + 1, 2) ,
            NULL * 0 , NULL * NULL ,
            ABS (NULL) , ADD_MONTHS (NULL, 5),
            LOG (5, NULL) , TRUNC (NULL)
       FROM DUAL;

NULL NULL NULL NULL NULL NULL NULL NULL NULL

Aritmetik operatörlerle kullanıldığında yanındakilere hiç acımıyor. Aynı sorguyu NULL yerine 0 ile yazmak isterseniz çoğu durumda hata alırsınız, Mesela 5/0 tanımsızdır ..

Bir değerin NULL olup olmadığı eşitlik operatörü ile yapılamaz. Bunun için IS -IS NOT kullanılır. Şu iki sorguya bakalım

SQL> SELECT
      CASE WHEN NULL = NULL THEN 'NULL eşittir NULL'
       ELSE 'NULL eşit değildir NULL'
      END FROM DUAL;

NULL eşit değildir NULL
SQL>SELECT CASE
            WHEN NULL IS NULL THEN 'NULL eşittir NULL'
            ELSE 'NULL eşit değildir NULL'
           END FROM DUAL;

NULL eşittir NULL

Burada NULL değer olarak içeriği boş bir değişken de olabilirdi. Zaten hata da genelde bu durumda olur

NULL olabilecek bir kolonda karşılaştırmada NVL gibi fonksiyonlar yardımıyla olası hatalardan rahatlıkla kaçınılabilir. Riskli olan şey herhangi bir kolonun NULL olup bu kolonun EXIST- NOT EXIST, IN- NOT gibi ifadelerde kullanılması.

Örnek bir tablo yaratalım ve hemen birkaç sorgu çekelim

SQL>CREATE TABLE null_test ( id NUMBER , a NUMBER , b NUMBER);

INSERT INTO null_test VALUES ( 1, 10, 10);
INSERT INTO null_test VALUES ( 2, 20, 20);
INSERT INTO null_test VALUES ( NULL, 30, 30);
INSERT INTO null_test VALUES ( 4, 50, NULL);
COMMIT;
SQL>SELECT * FROM null_test;

SQL>SELECT SUM (a) FROM null_test;

110
SQL>SELECT SUM (a) FROM null_test
     WHERE a NOT IN (SELECT NULL from DUAL);

NULL
SQL>SELECT SUM (a) FROM null_test t1
     WHERE NOT EXISTS (SELECT NULL FROM DUAL );
NULL

Burada SELECT NULL FROM DUAL yerine sonucu NULL değerdöndüren herhangi bir sorgu olabilir.

Dikkat. NOT IN ifadesi hiç değer döndürmüyorsa sorgu gene doğru sonuç verir. Hata , içteki sorguda herhangi bir değerin NULL olmasından kaynaklanır. Mesela aşağıdaki sorgu sonucu değer döndürmez fakat yukarıdaki gibi sonuç NULL dönmez.

SQL>SELECT SUM (a) FROM null_test
     WHERE a NOT IN (SELECT a FROM null_test WHERE 1 = 0);

110
SQL>SELECT SUM (a) FROM null_test t1
      WHERE NOT EXISTS (SELECT a FROM null_test WHERE 1 = 0);

110

Bu sorgu da SELECT a FROM null_test WHERE 1 = 0 ifadesi hiç değer döndürmez. NULL değer yoksa sorun yok. Ama SELECT NULL from DUAL ifadesi NULL değer döndürür.

Sorgu sonucunun NULL dönmesi için içteki sorgunun tammen NULL değer döndürmesi şart değil.Herhangi bir değerinin NULL olması yeterli.

Mesela

SQL>SELECT SUM (a) FROM null_test t1
      WHERE a NOT IN (SELECT 20 FROM DUAL);

90

a nın 20 olduğu değeri hariç bırakıp toplar. Sonuç 90

SQL>SELECT SUM (a) FROM null_test t1
      WHERE a NOT IN (SELECT b FROM null_test);

NULL

b kolonuda NULL bir değer olması bu sonuca sebep oldu. Bu sorgu yerine şunu da yazabilirdik

SQL>SELECT SUM (a) FROM null_test t1
      WHERE a NOT IN (
       SELECT 20 FROM DUAL&amp;lt;/pre&amp;gt;
UNION ALL
       SELECT NULL FROM DUAL);

NULL

Bu sorgu a ‘ nın 20 ve NULL olduğu değerleri hariç bırakıp toplar mı? Hayır. Sonuç NULL.

NULL olan değerleri filtrelersek bu sorunu çözebiliriz. Eğer NOT EXIST kullanıyorsanız join kolonlarının NULL değerlerini de dikkate almalısınız

SQL>SELECT SUM (a) FROM null_test t1
     WHERE a NOT IN (SELECT b FROM null_test
                       WHERE b IS NOT NULL);


şu sorguda b nin NULL gelen değerinden kurtulmak için sorguyu biraz çirkinleştirmemiz gerekiyor

SQL>SELECT SUM (a) FROM null_test t1
      WHERE NOT EXISTS (SELECT b FROM null_test t2
                         WHERE t1.id=t2.id);


SQL> SELECT SUM (a) FROM null_test t1
      WHERE NOT EXISTS (SELECT b FROM null_test t2
                         WHERE (t1.id = t2.id OR (t1 IS NULL AND t2 IS NULL))
);


&amp;lt;

Buradaki ifadede iki tabloya join işlemi uygularken kolonlarda NULL değer olabiliyorsa dikkatli olmamız gerektiğini de söylüyor. Tabii iki tabloyu bitiştirmek için kullanacağınız kolonda NULL olması beklenmemeli.Nadir de olsa karşılaşabilceğimiz bir durum. Mesela bir SCD yapısındaki tabloyu kaynağındaki farkları ayırarak yüklüyorsanız dikkat etmeniz gerekir

NULL içeren kolonların da join işlemine sokulacağını varsayıyorsanız sorgu yukarıdaki gibi

SQL> SELECT * FROM a, b
      WHERE (a.id = b.id OR (a.id IS NULL AND b.id IS NULL));


*Peki Dinamik SQL de NULL değeri parametre olarak kullanabilir miyiz?

Şu ifadede 5 değeri yerine NULL koyun

BEGIN
EXECUTE IMMEDIATE 'UPDATE null_test SET b=:var' USING 5;
END;

BEGIN
EXECUTE IMMEDIATE 'UPDATE null_test SET b=:var' USING NULL;
END;

hata verecektir. Peki NULL değer atamak istersek ne yapabiliriz? Bir değişkene NULL değer verebiliriz

DECLARE
  a NUMBER;
BEGIN
  a :=NULL;

  EXECUTE IMMEDIATE 'UPDATE null_test SET b=:var' USING a;
END;

Bu şekilde çalışır

NULL değer içeren kolonlar üzerinden join ise şu şekilde yapılmalıdır.( NOT exist için kullandığımız inner sqlde kullanmıştık )

SQL>SELECT * FROM a, b
      WHERE (a.id = b.id OR (a.id IS NULL AND b.id IS NULL));

Aksi takdirde sorgu sonucunda a ve b nin NULL olduğu kayıtlar gelmez. Bunun yerine NVL(a)=NVL(b) de kullanılabilir.

Bunların yanında sorgu sonucunu etkilemese de performansı olumsuz etkileyen bir örnek. NULL değer içeren kolonda index varsa

Mesela null_test tablosunda a kolonunda indeks olduğunu düşünün.

Bu tabloya çektiğiniz sorguda WHERE id IS NULL koşulu varsa indeks kullanılmaz.Perfromans kaybı yaşarsınız. İndeksi başka bir kolona koymalı ya da indeksi NVL(a, -99) gibi NULL için defult bir değer vererek oluşturmalısınız