เหตุใด Oracle 9i จึงถือว่าสตริงว่างเป็น NULL


216

ฉันรู้ว่ามันจะพิจารณา '' เป็นNULLแต่ไม่ได้ทำอะไรมากที่จะบอกฉันว่าทำไมถึงเป็นเช่นนี้ ตามที่ฉันเข้าใจข้อมูลจำเพาะของ SQL '' จะไม่เหมือนกับNULL- อันหนึ่งเป็นข้อมูลที่ถูกต้องและอีกรายการหนึ่งระบุว่าไม่มีข้อมูลเดียวกันนั้น

อย่าลังเลที่จะคาดเดา แต่โปรดระบุว่าเป็นอย่างนั้นหรือไม่ หากมีใครจาก Oracle ที่สามารถแสดงความคิดเห็นได้นั่นก็ยอดเยี่ยมมาก!


9
รู้สึกฟรีเพื่อเก็งกำไร? อย่างใดฉันไม่คิดว่าจะให้คำตอบที่ยิ่งใหญ่ที่สุดแก่คุณ ..
SCdF

1
ฉันไม่คิด แต่ฉันไม่แน่ใจว่ามีความมั่นใจในเรื่องดังนั้นฉันคิดว่าฉันจะโยนเปิดประตู ดูเหมือนว่าจะได้ผลออกมาโอเคจนถึงตอนนี้
Chris R


ที่เกี่ยวข้อง: dba.stackexchange.com/q/49744/56961
AlikElzin-kilaka

คำตอบ:


216

ฉันเชื่อว่าคำตอบคือ Oracle มีอายุเก่าแก่มาก

ย้อนกลับไปในสมัยก่อนก่อนที่จะมีมาตรฐาน SQL, Oracle ทำการตัดสินใจออกแบบที่สตริงว่างในVARCHAR/ VARCHAR2คอลัมน์เป็นNULLและมีเพียงหนึ่งความรู้สึกของ NULL (มีทฤษฎีเชิงสัมพันธ์ที่จะแยกความแตกต่างระหว่างข้อมูลที่ไม่เคยได้รับ ข้อมูลที่มีคำตอบอยู่ แต่ไม่เป็นที่รู้จักของผู้ใช้ข้อมูลที่ไม่มีคำตอบ ฯลฯ ซึ่งทั้งหมดเป็นความรู้สึกNULL)

เมื่อถึงเวลาที่มาตรฐาน SQL เข้ามาและตกลงNULLและสตริงว่างนั้นเป็นเอนทิตีที่แตกต่างกันมีผู้ใช้ Oracle ที่มีรหัสที่คิดว่าทั้งสองนั้นเทียบเท่ากันแล้ว ดังนั้น Oracle จึงเหลือตัวเลือกในการทำลายรหัสที่มีอยู่ละเมิดมาตรฐาน SQL หรือแนะนำพารามิเตอร์การเริ่มต้นบางประเภทที่จะเปลี่ยนการทำงานของแบบสอบถามจำนวนมากที่อาจเกิดขึ้น การละเมิดมาตรฐาน SQL (IMHO) เป็นตัวเลือกที่รบกวนน้อยที่สุดของตัวเลือกทั้งสามนี้

ออราเคิลเปิดโอกาสให้VARCHARประเภทข้อมูลจะเปลี่ยนไปในอนาคตเพื่อให้เป็นไปตามมาตรฐาน SQL (ซึ่งเป็นสาเหตุที่ทุกคนใช้VARCHAR2ใน Oracle เนื่องจากพฤติกรรมของประเภทข้อมูลนั้นรับประกันว่าจะยังคงเหมือนเดิม)


60

Tom Kyte VP ของ Oracle:

varchar ความยาวศูนย์จะถือว่าเป็น NULL

'' ไม่ถือว่าเป็น NULL

'' เมื่อกำหนดให้กับถ่าน (1) กลายเป็น '' (ประเภทถ่านเป็นสตริงที่มีเบาะรอง)

'' เมื่อกำหนดให้กับ varchar2 (1) จะกลายเป็น '' ซึ่งเป็นสตริงที่มีความยาวเป็นศูนย์และสตริงที่มีความยาวเป็นศูนย์จะเป็น NULL ใน Oracle (ไม่ยาว '')


17
ว้าวคำพูดที่น่ากลัวของทอม เนื่องจากคำถามนั้นเกี่ยวกับความแตกต่างอย่างมากจาก SQL92 คุณอาจคิดว่าเขาจะกัดเซาะน้อยกว่า ... แม้ว่าเขาอาจจะเบื่อที่จะตอบคำถามก็ตาม
Chris R

8
สิ่งที่ดีที่สุดเกี่ยวกับทอมที่คุณได้รับคำตอบที่ชัดเจนซึ่งระบุว่าสิ่งที่เขาคิด ค้นหาความคิดเห็นบางส่วนที่ผู้คนเคยใช้ข้อความพูดคุยกับ Ask Tom
Chris Gill

9
แต่มันอาจจะมีความแม่นยำมากขึ้นถ้าบรรทัดที่สองได้เปลี่ยนไปเป็น'' ไม่ได้มักจะถือว่าเป็นโมฆะ
ypercubeᵀᴹ

2
@ypercube ข้อความไม่ได้แม่นยำมากขึ้นโดยการเปลี่ยนคำที่ทอมใช้จริง ถ้าคุณคิดว่าทอมพูดอย่างสับสน อาจจะ. ผมคิดว่าเขาจุดที่ สถานการณ์ที่สับสนมากที่สุดเกิดขึ้นเมื่อ''กำลังถูกแปลงเป็น VARCHAR2 โดยปริยายเช่นcast('' as char(1)) is nullซึ่ง ... น่าแปลกใจที่
แท้จริง

1
@sehe บิตที่ทำให้เกิดความสับสนสำหรับฉันคือเลือก 1 จากคู่ที่ ('' เป็นโมฆะ)
matt freake

20

ฉันสงสัยว่านี่เหมาะสมกว่าถ้าคุณคิดว่า Oracle เป็นวิธีที่นักพัฒนาก่อนหน้านี้ทำ - ในฐานะแบ็กเอนด์ที่ยอดเยี่ยมสำหรับระบบป้อนข้อมูล ทุกเขตข้อมูลในฐานข้อมูลสอดคล้องกับเขตข้อมูลในรูปแบบที่ผู้ดำเนินการป้อนข้อมูลเห็นบนหน้าจอของเขา หากผู้ประกอบการไม่ได้พิมพ์อะไรลงในเขตข้อมูลไม่ว่าจะเป็น "วันเกิด" หรือ "ที่อยู่" ดังนั้นข้อมูลสำหรับเขตข้อมูลนั้นคือ "ไม่ทราบ" ไม่มีวิธีใดที่โอเปอเรเตอร์จะระบุว่าที่อยู่ของใครบางคนเป็นสตริงที่ว่างเปล่าจริง ๆ และนั่นก็ไม่สมเหตุสมผลนัก


5
ซึ่งจะสมเหตุสมผลถ้าคุณสมมติว่าทุกฟิลด์ในระบบป้อนข้อมูลเป็นสิ่งจำเป็น ไม่ใช่คำตอบไปยังเขตข้อมูลที่ไม่บังคับ (เช่น "ชื่อสุนัข") ถูกต้องดังนั้นสตริงที่ว่างเปล่ายังคงมีวัตถุประสงค์ที่แตกต่างจาก NULL แม้จะมีข้อสันนิษฐานนั้นฉันก็ยังสงสัยว่านักพัฒนายุคแรก ๆ คิดว่า Oracle เป็น "แบ็กเอนด์ที่ยอดเยี่ยมสำหรับระบบป้อนข้อมูล" ดังนั้นฉันจึงไม่แน่ใจว่าคำตอบนี้สมเหตุสมผล
Jared

19

เอกสารของ Oracle แจ้งเตือนนักพัฒนาถึงปัญหานี้อย่างน้อยที่สุดกลับไปเป็นเวอร์ชัน 7

Oracle เลือกที่จะเป็นตัวแทนของ NULLS ด้วยเทคนิค "ค่าที่เป็นไปไม่ได้" ตัวอย่างเช่นค่า NULL ในตำแหน่งตัวเลขจะถูกเก็บเป็น "ลบศูนย์" ซึ่งเป็นค่าที่เป็นไปไม่ได้ ศูนย์ลบใด ๆ ที่เป็นผลมาจากการคำนวณจะถูกแปลงเป็นศูนย์บวกก่อนที่จะถูกเก็บไว้

นอกจากนี้ Oracle ยังเลือกอย่างผิดพลาดเพื่อพิจารณาสตริง VARCHAR ที่มีความยาวเป็นศูนย์ (สตริงว่าง) เป็นค่าที่เป็นไปไม่ได้และเป็นตัวเลือกที่เหมาะสมสำหรับการแทนค่า NULL ปรากฎว่าสตริงว่างอยู่ไกลจากค่าที่เป็นไปไม่ได้ แม้แต่ตัวตนภายใต้การทำงานของการต่อสตริง!

เอกสารของ Oracle เตือนนักออกแบบฐานข้อมูลและนักพัฒนาว่า Oracle เวอร์ชันในอนาคตบางรุ่นอาจแบ่งการเชื่อมโยงนี้ระหว่างสตริงว่างและ NULL และทำลายรหัสใด ๆ ที่ขึ้นอยู่กับความสัมพันธ์นั้น

มีเทคนิคในการตั้งค่าสถานะ NULLS นอกเหนือจากค่าที่เป็นไปไม่ได้ แต่ Oracle ไม่ได้ใช้

(ฉันใช้คำว่า "ตำแหน่ง" ด้านบนเพื่อหมายถึงจุดตัดของแถวและคอลัมน์)


เอกสารของ Oracle เตือนนักออกแบบฐานข้อมูลและนักพัฒนาว่า Oracle รุ่นในอนาคตบางรุ่นอาจแบ่งการเชื่อมโยงนี้ระหว่างสตริงว่างและ NULL และทำลายรหัสใด ๆ ที่ขึ้นอยู่กับการเชื่อมโยงนั้น - คุณช่วยอ้างอิงสำหรับคำสั่งนี้ได้หรือไม่?
Piotr Dobrogost


2

สตริงว่างเป็นเหมือนกับ NULL เพียงเพราะมันเป็น "ความชั่วร้ายที่น้อยกว่า" เมื่อเปรียบเทียบกับสถานการณ์เมื่อสอง (สตริงว่างและ null) ไม่เหมือนกัน

ในภาษาที่ NULL และสตริงว่างเปล่าไม่เหมือนกันจะต้องตรวจสอบทั้งสองเงื่อนไขเสมอ


เพียงตั้งnot nullข้อ จำกัด ในคอลัมน์ของคุณและตรวจสอบเฉพาะในสตริงที่ว่างเปล่า
Egor Skriptunoff

6
การตรวจสอบเงื่อนไขทั้งสองเป็นเรื่องเล็กน้อย: WHERE Field <> ''คืนค่าเป็นจริงเฉพาะเมื่อฟิลด์ไม่ใช่ NULL และไม่ว่างในฐานข้อมูลที่มีพฤติกรรม ANSI สำหรับสตริงว่าง

1

อ้างอิงจากเอกสาร 11g อย่างเป็นทางการ

ขณะนี้ฐานข้อมูล Oracle ปฏิบัติต่อค่าอักขระที่มีความยาวศูนย์เป็นโมฆะ อย่างไรก็ตามสิ่งนี้อาจไม่เป็นจริงในรุ่นอนาคตและ Oracle ขอแนะนำให้คุณไม่ใช้สตริงที่ว่างเปล่าเหมือนกับโมฆะ

สาเหตุที่เป็นไปได้

  1. val IS NOT NULL สามารถอ่านได้มากกว่า val != ''
  2. ไม่จำเป็นต้องตรวจสอบทั้งสองเงื่อนไข val != '' and val IS NOT NULL

5
ในฐานข้อมูลที่สอดคล้องกับมาตรฐาน ANSI อย่างสมบูรณ์คุณไม่จำเป็นต้องตรวจสอบทั้งสองเงื่อนไข ไม่รวมอยู่แล้วval <> '' บางทีคุณอาจจะหมายถึงNULL val = '' OR val IS NULLแต่สตริงว่างที่ไม่เปรียบเทียบเป็นค่า NULL นั้นมีประโยชน์ !
ErikE

ฉันเห็นด้วยกับส่วนการเปรียบเทียบ
ตัวเรียงลำดับ

0

ตัวอย่างจากหนังสือ

   set serveroutput on;   
    DECLARE
    empty_varchar2 VARCHAR2(10) := '';
    empty_char CHAR(10) := '';
    BEGIN
    IF empty_varchar2 IS NULL THEN
    DBMS_OUTPUT.PUT_LINE('empty_varchar2 is NULL');
    END IF;


    IF '' IS NULL THEN
    DBMS_OUTPUT.PUT_LINE(''''' is NULL');
    END IF;

    IF empty_char IS NULL THEN
    DBMS_OUTPUT.PUT_LINE('empty_char is NULL');
    ELSIF empty_char IS NOT NULL THEN
    DBMS_OUTPUT.PUT_LINE('empty_char is NOT NULL');
    END IF;

    END;

-1

เพราะการไม่ถือมันเป็น NULL ก็ไม่ได้มีประโยชน์อะไรเป็นพิเศษ

หากคุณทำผิดในพื้นที่นี้บน Oracle คุณมักจะสังเกตเห็นได้ทันที ในเซิร์ฟเวอร์ SQL มันจะปรากฏขึ้นเพื่อทำงานและปัญหาจะปรากฏขึ้นเฉพาะเมื่อมีคนป้อนสตริงว่างแทน NULL (อาจมาจากไลบรารีไคลเอนต์. net ซึ่ง null จะแตกต่างจาก "" แต่คุณมักจะปฏิบัติต่อพวกเขาเหมือนกัน )

ฉันไม่ได้บอกว่า Oracle ถูกต้อง แต่สำหรับฉันแล้วทั้งสองวิธีนั้นไม่ดีเท่ากัน


2
ง่ายต่อการแก้ไขข้อบกพร่อง นอกจากนี้หากคุณเห็นเซลล์ว่างหรืออินพุตบนหน้าจอคุณรู้ว่าข้อมูลใน DB นั้นเป็นค่าว่าง ในฐานข้อมูลอื่นที่ '' <> NULL คุณไม่สามารถ "ดู" ว่าข้อมูลนั้นเป็นโมฆะหรือ '' สิ่งนี้จะนำไปสู่ข้อผิดพลาดที่ส่อเสียดมาก '' = null เป็นตัวเลือกที่ปลอดภัยแม้ว่าจะไม่ได้มาตรฐาน
Lucio M. Tato

2
“ ในฐานข้อมูลอื่นที่ '' <> NULL คุณไม่สามารถ "เห็น" ถ้าข้อมูลเป็นโมฆะหรือ ''” => โดยปกติเครื่องมือฐานข้อมูลจะแสดงค่า NULL ต่างจากสตริงว่าง จริงๆแล้วแม้แต่ Oracle SQL Developer ก็ยังแสดง NULL ว่าเป็น“ (null)” ฉันเดาว่านี่คือการแยกความแตกต่าง NULL จากช่องว่าง แต่มันไม่เกี่ยวข้องกับความแตกต่างระหว่าง NULL และสตริงว่าง
Didier L

-6

ที่จริงฉันไม่มีอะไรนอกจากความยากลำบากในการจัดการกับ Oracle รวมถึงค่าวันที่และเวลาที่ไม่ถูกต้อง (ไม่สามารถพิมพ์, แปลงหรืออะไรก็ได้เพียงแค่ดูด้วยฟังก์ชั่น DUMP ()) ที่ได้รับอนุญาตให้ใส่เข้าไปในฐานข้อมูล เวอร์ชันของไคลเอ็นต์เป็นคอลัมน์ไบนารี! มากสำหรับการปกป้องความสมบูรณ์ของฐานข้อมูล!

การจัดการลิงก์ NULL ของ Oracle:

http://digitalbush.com/2007/10/27/oracle-9i-null-behavior/

http://jeffkemponoracle.com/2006/02/empty-string-andor-null.html


1
ค่า datatime ที่ไม่ถูกต้อง? ไม่แน่ใจว่ามันหมายถึงอะไร คุณโพสต์คำถามนี้เป็นคำถามที่นี่หรือไม่

1
ปัญหา stackoverflow ก่อนวัน - ฉันไม่ได้รับข้อมูลที่เป็นประโยชน์จากฟอรั่ม Oracle และฉันสร้างวิธีแก้ปัญหา - ฉันจะติดตามบันทึกย่อของฉันและโพสต์ที่นี่
Cade Roux

โพสต์รายละเอียดเป็นคำถามได้ที่นี่
Cade Roux

-6

อย่างแรกสุดสตริง null และ null ไม่ได้รับการปฏิบัติเหมือนกันทุกครั้งโดย Oracle สตริง null คือตามนิยามสตริงที่ไม่มีอักขระ นี่ไม่ใช่สิ่งเดียวกันกับค่า null NULL คือการขาดข้อมูล

ห้าหรือหกปีก่อนดังนั้นสตริง null ได้รับการปฏิบัติแตกต่างจาก null โดย Oracle ในขณะที่เช่น null สตริง null มีค่าเท่ากับทุกอย่างและแตกต่างจากทุกอย่าง (ซึ่งฉันคิดว่าใช้ได้สำหรับ null แต่ผิดทั้งหมดสำหรับสตริง null) อย่างน้อยความยาว (สตริง null) จะส่งกลับ 0 เนื่องจากควรเป็นสตริง null สตริงที่มีความยาวเป็นศูนย์

ขณะนี้ใน Oracle ความยาว (null) จะคืนค่า null ซึ่งฉันเดาว่าตกลง แต่ความยาว (สตริง null) จะคืนค่า null ซึ่งผิดทั้งหมด

ฉันไม่เข้าใจว่าทำไมพวกเขาตัดสินใจที่จะเริ่มรักษา "ค่า" ที่แตกต่างกัน 2 แบบนี้เหมือนกัน พวกเขาหมายถึงสิ่งที่แตกต่างและโปรแกรมเมอร์ควรมีความสามารถในการดำเนินการในแต่ละวิธีที่แตกต่างกัน ความจริงที่ว่าพวกเขาได้เปลี่ยนวิธีการของพวกเขาบอกฉันว่าพวกเขาไม่มีเบาะแสเกี่ยวกับวิธีการปฏิบัติต่อค่าเหล่านี้


การอ้างอิงที่จำเป็นสำหรับการสร้างความแตกต่างระหว่าง "null string" และค่า NULL ในฐานข้อมูลใด ๆ ยกเว้น Oracle VARCHARเขตข้อมูลสามารถมีค่า (เป็นศูนย์หรือมากกว่าอักขระ) หรือไม่มีค่า (NULL), หยุดเต็ม

"ห้าหรือหกปีที่ผ่านมา" จาก 2011 จะตกอยู่ในกรอบเวลา 10g (10.1 ปล่อยตัว 2003, 10.2 ในปี 2005) 10g ไม่ได้แนะนำการเปลี่ยนแปลงระดับโลกใด ๆ ในการจัดการ nulls และไม่เคยมีความแตกต่างระหว่างNULLและสตริงที่มีค่า null และความแตกต่างดังกล่าวไม่สมเหตุสมผล ฉันกลัวคำตอบนี้เป็นจินตนาการที่สมบูรณ์
William Robertson
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.