Database
 sql >> Teknologi Basis Data >  >> RDS >> Database

50 Shades of NULL – Arti Berbeda dari NULL dalam SQL

Tony Hoare, yang sering disebut sebagai penemu referensi NULL, sekarang menyebutnya sebagai kesalahan miliaran dolar yang hampir semua bahasa sekarang "menderita", termasuk SQL.

Mengutip Tony (dari artikel Wikipedia-nya):

Saya menyebutnya kesalahan miliaran dolar saya. Itu adalah penemuan referensi nol pada tahun 1965. Saat itu, saya sedang merancang sistem tipe komprehensif pertama untuk referensi dalam bahasa berorientasi objek (ALGOL W). Tujuan saya adalah untuk memastikan bahwa semua penggunaan referensi harus benar-benar aman, dengan pemeriksaan dilakukan secara otomatis oleh kompiler. Tetapi saya tidak dapat menahan godaan untuk memasukkan referensi nol, hanya karena sangat mudah untuk diterapkan. Hal ini telah menyebabkan kesalahan yang tak terhitung banyaknya, kerentanan, dan sistem crash, yang mungkin telah menyebabkan satu miliar dolar rasa sakit dan kerusakan dalam empat puluh tahun terakhir.

Yang menarik di sini adalah Tony tergoda untuk menerapkan referensi itu karena mudah dilakukan. Tapi mengapa dia membutuhkan referensi seperti itu?

Arti yang berbeda dari NULL

Di dunia yang sempurna, kita tidak membutuhkan NULL. Setiap orang memiliki nama depan dan nama belakang. Setiap orang memiliki tanggal lahir, pekerjaan, dll. Atau apakah mereka?

Sayangnya, mereka tidak melakukannya.

Tidak semua negara menggunakan konsep nama depan dan belakang.

Tidak semua orang memiliki pekerjaan. Atau terkadang, kita tidak tahu pekerjaan mereka. Atau kami tidak peduli.

Di sinilah NULL sangat berguna. NULL dapat memodelkan semua status ini yang sebenarnya tidak ingin kita modelkan. NULL dapat berupa:

  • Nilai "tidak ditentukan" , yaitu, nilai yang belum ditentukan (mungkin karena alasan teknis) tetapi mungkin akan ditentukan nanti. Pikirkan tentang seseorang yang ingin kita tambahkan ke database untuk menggunakannya di tabel lain. Pada tahap selanjutnya, kami akan menambahkan pekerjaan orang itu.
  • Nilai “tidak diketahui” , yaitu, nilai yang kita tidak tahu (dan mungkin tidak pernah tahu). Mungkin kita tidak bisa lagi bertanya kepada orang ini atau kerabatnya tentang tanggal lahir mereka – infonya akan hilang selamanya. Tapi kami masih ingin memodelkan orangnya, jadi kami menggunakan NULL dalam arti UNKNOWN (yang merupakan arti sebenarnya dalam SQL, seperti yang akan kita lihat nanti).
  • Nilai “opsional” , yaitu, nilai yang tidak perlu didefinisikan. Perhatikan bahwa nilai "opsional" juga muncul dalam kasus OUTER JOIN, ketika outer join tidak menghasilkan nilai apa pun di satu sisi hubungan. Atau juga saat menggunakan GROUPING SETS, di mana kombinasi yang berbeda dari kolom GROUP BY digabungkan (atau dibiarkan kosong).
  • Nilai yang “dihapus” atau “dihindari” , yaitu, nilai yang tidak ingin kita tentukan. Mungkin kami biasanya mendaftarkan status perkawinan seseorang seperti yang dilakukan di beberapa yurisdiksi, tetapi tidak di yurisdiksi lain, di mana tidak sah untuk mendaftarkan data pribadi jenis ini. Oleh karena itu, kami tidak ingin mengetahui nilai ini dalam beberapa kasus.
  • Nilai “khusus” dalam konteks tertentu , yaitu, nilai yang tidak dapat kita modelkan sebaliknya dalam kisaran nilai yang mungkin. Ini sering dilakukan saat bekerja dengan rentang tanggal. Mari kita asumsikan pekerjaan seseorang dibatasi oleh dua tanggal, dan jika orang tersebut saat ini bekerja di posisi tersebut, kita akan menggunakan NULL untuk mengatakan bahwa periode tersebut tidak terbatas pada akhir rentang tanggal.
  • NULL "tidak disengaja" , yaitu nilai NULL yang hanya NULL karena pengembang tidak memperhatikan. Dengan tidak adanya batasan NOT NULL eksplisit, sebagian besar database menganggap kolom sebagai nullable. Dan begitu kolom dapat dibatalkan, pengembang mungkin “secara tidak sengaja” memasukkan nilai NULL di baris mereka, di tempat yang bahkan tidak mereka inginkan.

Seperti yang telah kita lihat di atas, ini hanya beberapa dari 50 Shades of NULL .

Contoh berikut menampilkan berbagai arti yang berbeda dari NULL dalam contoh SQL konkret:




CREATE TABLE company (
    id int NOT NULL,
    name text NOT NULL,
    CONSTRAINT company_pk PRIMARY KEY (id)
);
CREATE TABLE job (
    person_id int NOT NULL,
    start_date date NOT NULL,

    -- If end_date IS NULL, the “special value” of an unbounded
    -- interval is encoded
    end_date date NULL,
    description text NOT NULL,

    -- A job doesn’t have to be done at a company. It is “optional”.
    company_id int NULL,
    CONSTRAINT job_pk PRIMARY KEY (person_id,start_date),
    CONSTRAINT job_company FOREIGN KEY (company_id) 
        REFERENCES company (id) 
);
CREATE TABLE person (
    id int  NOT NULL,
    first_name text NOT NULL,

    -- Some people need to be created in the database before we
    -- know their last_names. It is “undefined”
    last_name text NULL,

    -- We may not know the date_of_birth. It is “unknown”
    date_of_birth date NULL,

    -- In some situations, we must not define any marital_status.
    -- It is “deleted”
    marital_status int NULL,
    CONSTRAINT person_pk PRIMARY KEY (id),
    CONSTRAINT job_person FOREIGN KEY (person_id)
        REFERENCES person (id)
); 

Orang selalu berdebat tentang tidak adanya nilai

Ketika NULL adalah nilai yang sangat berguna, mengapa orang terus mengkritiknya?

Semua kasus penggunaan sebelumnya untuk NULL (dan lainnya) ditampilkan dalam pembicaraan menarik terbaru oleh C.J. Date tentang “Masalah Informasi yang Hilang” (tonton video di YouTube).

SQL modern dapat melakukan banyak hal luar biasa yang tidak disadari oleh beberapa pengembang bahasa tujuan umum seperti Java, C#, PHP. Saya akan menunjukkan contoh lebih jauh ke bawah.

Di satu sisi, C.J. Date setuju dengan Tony Hoare bahwa (ab)menggunakan NULL untuk semua jenis "informasi yang hilang" ini adalah pilihan yang sangat buruk.

Misalnya, dalam elektronik, teknik serupa diterapkan untuk memodelkan hal-hal seperti 1, 0, "konflik", "tidak ditetapkan", "tidak diketahui", "tidak peduli", "impedansi tinggi". Namun perhatikan, bagaimana dalam elektronik, nilai khusus yang berbeda digunakan untuk hal-hal ini, daripada satu nilai NULL khusus . Apakah ini benar-benar lebih baik? Bagaimana perasaan programmer JavaScript tentang perbedaan antara nilai "palsu" yang berbeda, seperti "null", "undefined", "0", "NaN", string kosong ''? Apakah ini benar-benar lebih baik?

Berbicara tentang nol:Ketika kita meninggalkan ruang SQL sejenak dan masuk ke matematika, kita akan melihat bahwa budaya kuno seperti Romawi atau Yunani memiliki masalah yang sama dengan angka nol. Faktanya, mereka bahkan tidak memiliki cara untuk merepresentasikan nol tidak seperti budaya lain seperti yang dapat dilihat di artikel Wikipedia tentang angka nol. Mengutip dari artikel:

Catatan menunjukkan bahwa orang Yunani kuno tampaknya tidak yakin tentang status nol sebagai angka. Mereka bertanya pada diri sendiri, "Bagaimana tidak ada sesuatu?", yang mengarah ke filosofis dan, pada periode Abad Pertengahan, argumen agama tentang sifat dan keberadaan nol dan ruang hampa.

Seperti yang bisa kita lihat, “argumen agama” jelas meluas ke ilmu komputer dan perangkat lunak, di mana kita masih tidak tahu pasti apa yang harus dilakukan dengan tidak adanya nilai.

Kembali ke kenyataan:NULL di SQL

Sementara orang (termasuk akademisi) masih tidak setuju pada fakta apakah kita memerlukan pengkodean untuk "tidak terdefinisi", "tidak diketahui", "opsional", "dihapus", "khusus", mari kita kembali ke kenyataan dan bagian buruk tentang NULL SQL.

Satu hal yang sering dilupakan ketika berhadapan dengan NULL SQL adalah bahwa SQL secara formal mengimplementasikan kasus UNKNOWN, yang merupakan nilai khusus yang merupakan bagian dari apa yang disebut logika tiga nilai, dan ia melakukannya, secara tidak konsisten, mis. dalam kasus operasi UNION atau INTERSECT.

Jika kita kembali ke model kita:





Jika, misalnya, kami ingin menemukan semua orang yang tidak terdaftar sebagai menikah, secara intuitif, kami ingin menulis pernyataan berikut:

SELECT * FROM person WHERE marital_status != 'married'

Sayangnya, karena logika tiga nilai dan NULL SQL, kueri di atas tidak akan mengembalikan nilai yang tidak memiliki status_nikah eksplisit. Oleh karena itu, kita perlu menulis predikat tambahan yang eksplisit:

SELECT * FROM person 
WHERE marital_status != 'married'
OR marital_status IS NULL

Atau, kami memaksa nilai ke beberapa nilai NOT NULL sebelum membandingkannya

SELECT * FROM person
WHERE COALESCE(marital_status, 'null') != 'married'

Tiga logika bernilai sulit. Dan itu bukan satu-satunya masalah dengan NULL di SQL. Berikut adalah lebih banyak kerugian menggunakan NULL:

  • Hanya ada satu NULL, ketika kita benar-benar ingin mengkodekan beberapa nilai "absen" atau "khusus" yang berbeda. Kisaran nilai khusus yang berguna sangat tergantung pada domain dan tipe data yang digunakan. Namun, pengetahuan domain selalu diperlukan untuk menafsirkan dengan benar arti kolom yang dapat dibatalkan, dan kueri harus dirancang dengan hati-hati untuk mencegah hasil yang salah dikembalikan, seperti yang kita lihat di atas.
  • Sekali lagi, logika tiga nilai sangat sulit untuk diperbaiki. Meskipun contoh di atas masih cukup sederhana, menurut Anda apa yang akan dihasilkan oleh kueri berikut?
    SELECT * FROM person 
    WHERE marital_status NOT IN ('married', NULL)
    

    Tepat. Itu tidak akan menghasilkan apa-apa, seperti yang dijelaskan dalam artikel ini di sini. Singkatnya, kueri di atas sama dengan kueri di bawah ini:

    SELECT * FROM person 
    WHERE marital_status != 'married'
    AND marital_status != NULL -- This is always NULL / UNKNOWN
    
  • Database Oracle memperlakukan NULL dan string kosong '' sebagai hal yang sama. Ini sangat rumit karena Anda tidak akan segera menyadari mengapa kueri berikut selalu mengembalikan hasil kosong:

    SELECT * FROM person 
    WHERE marital_status NOT IN ('married', '')
    

  • Oracle (sekali lagi) tidak memasukkan nilai NULL dalam indeks. Ini adalah sumber dari banyak masalah kinerja yang buruk, misalnya, ketika Anda menggunakan kolom nullable dalam predikat NOT IN seperti:

    SELECT * FROM person 
    WHERE marital_status NOT IN (
      SELECT some_nullable_column
      FROM some_table
    )
    

    Dengan Oracle, anti-join di atas akan menghasilkan pemindaian tabel penuh, terlepas dari apakah Anda memiliki indeks pada some_nullable_column. Karena logika tiga nilai dan karena Oracle tidak menempatkan NULL dalam indeks, mesin perlu menekan tabel dan memeriksa setiap nilai hanya untuk memastikan tidak ada setidaknya satu nilai NULL di set, yang akan membuat seluruh predikat TIDAK DIKETAHUI.

Kesimpulan

Kami belum memecahkan masalah NULL di sebagian besar bahasa dan platform. Sementara saya mengklaim bahwa NULL BUKAN kesalahan miliaran dolar yang Tony Hoare coba minta maaf, NULL juga jauh dari sempurna.

Jika Anda ingin tetap aman dengan desain database Anda, hindari NULL dengan cara apa pun, kecuali jika Anda benar-benar membutuhkan salah satu dari nilai khusus tersebut untuk dikodekan menggunakan NULL. Ingat, nilai-nilai ini adalah:“tidak terdefinisi”, “tidak diketahui”, “opsional”, “dihapus”, dan “khusus”, dan banyak lagi:50 Nuansa NULL . Jika Anda tidak berada dalam situasi seperti itu, selalu default untuk menambahkan batasan NOT NULL ke setiap kolom di database Anda. Desain Anda akan jauh lebih bersih, dan kinerja Anda jauh lebih baik.

Jika saja NOT NULL adalah default di DDL, dan NULLABLE kata kunci yang perlu disetel secara eksplisit…

Apa pendapat dan pengalaman Anda dengan NULL? Bagaimana cara kerja SQL yang lebih baik menurut Anda?

Lukas Eder adalah pendiri dan CEO Data Geekery GmbH, yang berlokasi di Zurich, Swiss. Data Geekery telah menjual produk dan layanan database seputar Java dan SQL sejak 2013.

Sejak studi Masternya di EPFL pada tahun 2006, ia telah terpesona oleh interaksi Java dan SQL. Sebagian besar pengalaman ini diperolehnya di bidang Swiss E-Banking melalui berbagai varian (JDBC, Hibernate, sebagian besar dengan Oracle). Dia senang berbagi pengetahuan ini di berbagai konferensi, JUG, presentasi internal, dan blog perusahaannya.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Penyetelan:Tempat yang Baik untuk Memulai

  2. Memecahkan masalah Kinerja CPU pada VMware

  3. Dapatkan dinyalakan dengan Apache Spark – Bagian 1

  4. SQL Kurang Dari () Operator untuk Pemula

  5. Cara Menomori Baris dalam SQL