Mengapa saya tidak bisa menggunakan nilai nol dalam gabungan?

13

Saya telah memecahkan masalah kueri dengan menggunakan ... row_number() over (partition by... ini adalah pertanyaan yang lebih umum tentang mengapa kita tidak dapat menggunakan kolom dengan nilai nol dalam gabungan. Mengapa nol tidak bisa sama dengan nol demi bergabung?

Mark Warner
sumber

Jawaban:

31

Mengapa nol tidak bisa sama dengan nol demi bergabung?

Katakan saja kepada Oracle untuk melakukan itu:

select *
from one t1 
  join two t2 on coalesce(t1.id, -1) = coalesce(t2.id, -1);

(Perhatikan bahwa dalam SQL standar yang dapat Anda gunakan t1.id is not distinct from t2.iduntuk mendapatkan operator kesetaraan nol-aman, tetapi Oracle tidak mendukung itu)

Tetapi ini hanya akan berfungsi jika nilai penggantian (-1 pada contoh di atas) tidak benar-benar muncul dalam tabel. Menemukan nilai "ajaib" untuk angka mungkin dimungkinkan, tetapi akan sangat sulit untuk nilai karakter (terutama karena Oracle juga memperlakukan string kosong null)

Plus: tidak ada indeks pada idkolom yang akan digunakan (Anda dapat menentukan indeks berdasarkan fungsi dengan coalesce()ekspresi).

Opsi lain yang berfungsi untuk semua jenis, tanpa nilai ajaib:

              on t1.id = t2.id or (t1.id is null and t2.id is null)

Tetapi pertanyaan sebenarnya adalah: apakah ini masuk akal?

Pertimbangkan contoh data berikut:

Tabel satu

id
----
1
2
(null)
(null)

Tabel dua

id
----
1
2
(null)
(null)
(null)

Kombinasi nilai nol mana yang harus dipilih dalam gabungan? Contoh saya di atas akan menghasilkan sesuatu seperti gabungan silang untuk semua nilai nol.

T1_ID  | T2_ID 
-------+-------
     1 |      1
     2 |      2
(null) | (null)
(null) | (null)
(null) | (null)
(null) | (null)
(null) | (null)
(null) | (null)
seekor kuda tanpa nama
sumber
6

Atau Anda dapat membuat dua nulls cocok satu sama lain menggunakan INTERSECTsebagai operator persamaan:

SELECT
  *
FROM
  t1
  INNER JOIN t2
    ON EXISTS (SELECT t1.ID FROM DUAL INTERSECT SELECT t2.ID FROM DUAL)
;

Lihat demo DBFiddle ini untuk ilustrasi.

Tentu saja, ini terlihat cukup seteguk, meskipun sebenarnya tidak lebih lama dari saran BriteSponge . Namun, ini tentu saja bukan pertandingan, jika Anda memaafkan permainan kata-kata, dengan keringkasan yang disebutkan sebelumnya dalam komentar cara standar, yang merupakan IS NOT DISTINCT FROMoperator, belum didukung di Oracle.

Andriy M
sumber
2

Hanya untuk kelengkapan saya akan menyebutkan bahwa fungsi SYS_OP_MAP_NONNULLsekarang dapat dengan aman digunakan untuk membandingkan nilai-nilai yang nol seperti yang sekarang didokumentasikan dalam dokumentasi 12c. Ini berarti Oracle tidak akan hanya secara acak menghapusnya dan merusak kode Anda.

SELECT *
FROM   one t1 
       JOIN two t2
         ON SYS_OP_MAP_NONNULL(t1.id) = SYS_OP_MAP_NONNULL(t2.id)

Keuntungannya adalah Anda tidak menemukan masalah angka 'ajaib'.

Referensi dalam dokumen Oracle ada di Basic Materialized Views - Memilih Indeks untuk Tampilan Materialized .

BriteSponge
sumber
Jadi sudah didokumentasikan sekarang? Karena AskTom (pada tahun 2003) menyatakan: " - tidak terdokumentasi, dan oleh karena itu berisiko pergi atau mengubah fungsi yang cukup dikatakan yang seharusnya membuat orang" berhenti membaca "pergi ke sana dan Anda mungkin benar-benar marah pada rilis berikutnya. satu-satunya cara yang BENAR adalah: where (a = b or (a is null and b is null)) titik. itu adalah pemikiran saya di atasnya. Saya tidak akan mempertimbangkan untuk menggunakan sys_op_map_nonnull, mengabaikan orang itu di balik tirai. "
ypercubeᵀᴹ
Jika Anda memiliki tautan, silakan tambahkan ke pertanyaan. Saya belum menemukan menyebutkan dalam Fungsi 12c tetapi mencari dokumentasi Oracle dan versi spesifik agak sulit.
ypercubeᵀᴹ
2

Anda dapat bergabung dengan nilai nol menggunakan decode:

on decode(t1.id, t2.id, 1, 0) = 1

decodememperlakukan nol sama dengan, jadi ini berfungsi tanpa angka "ajaib". Dua kolom harus memiliki tipe data yang sama.

Itu tidak akan membuat kode yang paling mudah dibaca, tetapi mungkin masih lebih baik daripada t1.id = t2.id or (t1.id is null and t2.id is null)

Tamás Bárász
sumber
1

Mengapa Anda tidak bisa menggunakan nilai nol dalam gabungan? Di Oracle, kedua hal berikut ini tidak bernilai true:

  • NULL = NULL
  • NULL <> NULL

Itu sebabnya kami harus IS NULL/ IS NOT NULLuntuk memeriksa nilai null.
Untuk menguji ini, Anda cukup melakukan:

SELECT * FROM table_name WHERE NULL = NULL

Bergabung sedang mengevaluasi kondisi boolean, dan mereka tidak memprogram mereka untuk beroperasi secara berbeda. Anda dapat memasukkan tanda lebih besar dari pada kondisi bergabung dan menambahkan kondisi lainnya; itu hanya mengevaluasinya sebagai ekspresi boolean.

Saya kira nol tidak bisa sama dengan nol dalam gabungan demi konsistensi. Itu akan menentang perilaku biasa operator perbandingan.

Andrew Puglionesi
sumber
NULL = anythinghasil NULLkarena standar SQL mengatakan demikian. Baris memuaskan kondisi gabungan hanya jika ekspresi itu benar.
Laurenz Albe
1
Di luar detail implementasi literal (yang tidak selalu terjadi: beberapa DB memiliki opsi untuk menyamakan NULL ke NULL untuk beberapa / semua tujuan) ada alasan logis: NULL tidak diketahui. Ketika Anda membandingkan NULL dengan NULL Anda bertanya "apakah hal yang tidak diketahui ini sama dengan hal yang tidak diketahui lainnya" yang satu-satunya jawaban yang masuk akal adalah "tidak diketahui" - NULL yang lain (yang dipetakan ke false dalam situasi perbandingan).
David Spillett
-4

Nilai nol di sebagian besar basis data relasional dianggap TIDAK DIKETAHUI. Jangan bingung dengan semua nol HEX. jika sesuatu mengandung null (tidak diketahui,) Anda tidak dapat membandingkannya.

Unknown = Known False
Unknown = Unknown False
Unknown >= Known False
Known >= Unknown False

Yang berarti, setiap kali Anda memiliki nol sebagai operan dalam ekspresi boolean, bagian lain akan selalu benar.

Bertentangan dengan kebencian umum terhadap null oleh pengembang, null memiliki tempatnya. Jika ada sesuatu yang tidak diketahui, gunakan null.

jujiro
sumber
6
Sebenarnya semua perbandingan contoh yang Anda miliki, hasilkan UNKNOWN, bukan FALSE;)
ypercubeᵀᴹ
Anda benar, namun tujuan dari ekspresi boolean adalah untuk menghasilkan benar atau salah saja, jadi, jangan menjadi gila di sini :).
jujiro