NULLs

2010-11-02 16 views
26

के साथ अद्वितीय कुंजी इस प्रश्न को कुछ काल्पनिक पृष्ठभूमि की आवश्यकता है। आइए employee तालिका पर कॉल करें जिसमें कॉलम name, date_of_birth, title, salary, MySQL का उपयोग RDBMS के रूप में करते हैं। चूंकि किसी दिए गए व्यक्ति के पास एक ही व्यक्ति और जन्म तिथि किसी अन्य व्यक्ति के रूप में होती है, तो वे परिभाषा के अनुसार, एक ही व्यक्ति (अद्भुत संयोग छोड़कर जहां हमारे पास अब्राहम लिंकन नामक दो लोग 12 फरवरी, 180 9 को पैदा हुए हैं), हम एक डाल देंगे name और date_of_birth पर अद्वितीय कुंजी का अर्थ है "एक ही व्यक्ति को दो बार स्टोर न करें।"NULLs

id name  date_of_birth title   salary 
1 John Smith 1960-10-02 President  500,000 
2 Jane Doe 1982-05-05 Accountant  80,000 
3 Jim Johnson NULL   Office Manager 40,000 
4 Tim Smith 1899-04-11 Janitor   95,000 

अब मैं निम्नलिखित बयान चलाने का प्रयास करते हैं, तो यह चाहिए और असफल हो जायेगी:

INSERT INTO employee (name, date_of_birth, title, salary) 
VALUES ('Tim Smith', '1899-04-11', 'Janitor', '95,000') 

अगर मैं इस एक कोशिश है, यह सफल होगा:

INSERT INTO employee (name, title, salary) 
VALUES ('Jim Johnson', 'Office Manager', '40,000') 
अब इस डेटा पर विचार

और अब अपने डेटा इस तरह दिखेगा:

id name  date_of_birth title   salary 
1 John Smith 1960-10-02 President  500,000 
2 Jane Doe 1982-05-05 Accountant  80,000 
3 Jim Johnson NULL   Office Manager 40,000 
4 Tim Smith 1899-04-11 Janitor   95,000 
5 Jim Johnson NULL   Office Manager 40,000 

यह वही नहीं है जो मैं चाहता हूं लेकिन मैं नहीं कह सकता कि मैं जो हुआ उससे पूरी तरह से असहमत हूं। अगर हम गणितीय सेट के संदर्भ में बात करते हैं,

{'Tim Smith', '1899-04-11'} = {'Tim Smith', '1899-04-11'} <-- TRUE 
{'Tim Smith', '1899-04-11'} = {'Jane Doe', '1982-05-05'} <-- FALSE 
{'Tim Smith', '1899-04-11'} = {'Jim Johnson', NULL} <-- UNKNOWN 
{'Jim Johnson', NULL} = {'Jim Johnson', NULL} <-- UNKNOWN 

मेरा अनुमान है कि MySQL कहते हैं, "जब से मैं पता नहीं है कि एक NULL जन्म तिथि के साथ पहले से ही इस तालिका में नहीं है जिम जॉनसन, मैं उसे जोड़ देंगे। "

मेरा प्रश्न है: मैं डुप्लिकेट को कैसे रोक सकता हूं भले ही date_of_birth हमेशा ज्ञात नहीं है? सबसे अच्छा मैं अब तक आया हूं date_of_birth को एक अलग तालिका में ले जाना है। हालांकि, इसके साथ समस्या यह है कि मैं एक ही नाम, शीर्षक और वेतन, अलग जन्म तिथियों के साथ दो कैशियर, डुप्लिकेट किए बिना दोनों को स्टोर करने का कोई तरीका नहीं कह सकता हूं।

+7

जन्म का नाम और तारीख बहुत अनोखी नहीं है। –

+3

एक सेंटीनेल जन्म तिथि का उपयोग करें, उदा। '0000-00-00'। – smilingthax

+2

@ पॉल टॉम्बलिन: मुझे पता है कि वे नहीं हैं। क्या आप नहीं देख सकते कि सवाल यह नहीं है कि यद्यपि सवाल क्या है? –

उत्तर

18

की एक मूलभूत संपत्ति अद्वितीय कुंजी यह है कि यह अद्वितीय होना चाहिए। उस कुंजी का हिस्सा बनाना Nullable इस संपत्ति को नष्ट कर देता है।

आपकी समस्या का दो संभव समाधान कर रहे हैं:

  • एक तरह से, गलत तरीके से, कुछ जादू तारीख उपयोग करने के लिए अज्ञात प्रतिनिधित्व करने के लिए किया जाएगा। यह आपको डीबीएमएस "समस्या" से पहले प्राप्त करता है लेकिन समस्या को तार्किक अर्थ में हल नहीं करता है। अज्ञात दिनांक जन्म के दो "जॉन स्मिथ" प्रविष्टियों के साथ समस्याओं की अपेक्षा करें। क्या ये लोग एक हैं और वही हैं या वे अद्वितीय व्यक्ति हैं? यदि आप जानते हैं कि वे अलग हैं तो आप एक ही पुरानी समस्या पर वापस आ गए हैं - आपकी अनोखी कुंजी बस अद्वितीय नहीं है। "अज्ञात" का प्रतिनिधित्व करने के लिए जादू की तारीखों की पूरी श्रृंखला असाइन करने के बारे में भी सोचें - यह वास्तव में नरक की राह है।

  • एक बेहतर तरीका एक कर्मचारी कुंजी विशेषता को सरोगेट कुंजी के रूप में बनाना है। यह सिर्फ मनमाने ढंग से पहचानकर्ता है जिसे आप व्यक्तियों को असाइन करते हैं कि अद्वितीय हैं। यह पहचानकर्ता अक्सर एक पूर्णांक मान होता है। फिर कर्मचारी आईडी (अद्वितीय, गैर-शून्यणीय कुंजी) से संबंधित एक कर्मचारी तालिका बनाएं जो आप मानते हैं कि आश्रित गुणक हैं, इस मामले में जन्म और जन्म तिथि (जिनमें से कोई भी शून्य हो सकता है)। आप कहीं भी नाम का नाम/दिनांक-जन्म का उपयोग करते हुए कर्मचारी नौकरी सरोगेट कुंजी का उपयोग करें। यह आपके सिस्टम में एक नई तालिका जोड़ता है लेकिन अज्ञात मानों की समस्या को मजबूत तरीके से हल करता है।

+0

"यह आपको डीबीएमएस 'समस्या' से पहले ले जाता है लेकिन समस्या को तार्किक अर्थ में हल नहीं करता है।" ठीक ठीक! यह कहने के लिए धन्यवाद। मैं डिजाइन की समस्या को हल करना चाहता हूं, भौतिक कार्यान्वयन समस्या नहीं। –

+0

"अज्ञात 'का प्रतिनिधित्व करने के लिए जादू तिथियों की पूरी श्रृंखला को असाइन करने के बारे में भी मत सोचो - यह वास्तव में नरक की राह है।" यह भी कहने के लिए धन्यवाद। इस बुरे विचार को इतने सारे अपवित्र होने के लिए निराशाजनक था। –

6

मुझे लगता है कि MySQL इसे यहां करता है। कुछ अन्य डेटाबेस (उदाहरण के लिए माइक्रोसॉफ्ट एसक्यूएल सर्वर) एनयूएलएल को एक मान के रूप में मानते हैं जिसे केवल एक बार कॉलम में डाला जा सकता है, लेकिन व्यक्तिगत रूप से मुझे यह अजीब और अप्रत्याशित व्यवहार लगता है।

लेकिन जब से यह तुम क्या चाहते है, तो आप कुछ "जादू" मूल्य के बजाय शून्य की, इस तरह के एक तारीख के रूप में एक लंबे समय के अतीत

+2

मैं अनिवार्य रूप से असहमत नहीं हूं कि MySQL इसे सही तरीके से संभालता है। अंतिम परिणाम वह नहीं है जो मैं चाहता हूं, हालांकि: मैं डुप्लिकेट के साथ समाप्त होता हूं, जो स्वीकार्य नहीं है। और मेरे लिए, एक "जादू" मूल्य सिर्फ एक "नकली नल" है। कोई अपराध नहीं, लेकिन मुझे पेट में थोड़ा मुश्किल लगता है कि यह करने का सही तरीका है। –

+0

इसके अलावा, यह बिल्कुल नहीं है कि मुझे वहां दो बार होने की परवाह है। यह "जिम जॉनसन" है। –

+0

जादू मूल्यों का कभी भी उपयोग नहीं करें। – Rafa

3

में उपयोग कर सकते हैं एक एक और तरीका यह करने के लिए नहीं है। Date_of_birth कॉलम के स्ट्रिंग मान का प्रतिनिधित्व करने के लिए कॉलम (गैर-शून्य) जोड़ना। नया कॉलम मान "(खाली स्ट्रिंग) होगा यदि date_of_birth शून्य है।

हम कॉलम को date_of_birth_str के रूप में नामित करते हैं और एक अद्वितीय बाधा कर्मचारी (नाम, date_of_birth_str) बनाते हैं। तो जब दो रिकॉर्ड्स एक ही नाम और शून्य date_of_birth मान के साथ आते हैं, तो अद्वितीय बाधा अभी भी काम करती है।

लेकिन दो समान अर्थ कॉलम के लिए रखरखाव के प्रयास, और, नए कॉलम के प्रदर्शन नुकसान को ध्यान से माना जाना चाहिए।

+0

दिलचस्प विचार । मुझे इसके बारे में बहुत अच्छा नहीं लगता क्योंकि इसमें दो बार एक ही सटीक डेटा संग्रह करना शामिल है, लेकिन मैं मानता हूं कि यह काम करेगा। –

+0

यदि आप वहां जाना चाहते हैं, तो मैं इसके बजाय पूर्णांक 1 या 0 के साथ कॉलम date_of_birth_is_known जोड़ दूंगा ... और फिर भी आपको इसके साथ काम करने के लिए "IF" जोड़ना होगा। मुझे अतीत में इस तरह के डिजाइन से निपटना पड़ा, और यह एक स्ट्रिंग या एक इंट फ्लैग के साथ --- किसी भी तरह से भयानक है। कभी-कभी लोग एक चीज़ को अपडेट करते हैं, कभी-कभी दोनों - एक क्रम में, कभी-कभी दूसरे। कभी-कभी लेनदेन के बाहर कुछ विफल रहता है ... फिर आप स्थिरता को सत्यापित करने के लिए स्क्रिप्ट लिखते हैं ... समय की भयानक अपशिष्ट। – osa

+0

फ़ील्ड को सिंक में रखने के लिए एक डीबी ट्रिगर जोड़ें। –

5

नाम के आधार पर डुप्लीकेट नहीं होने की आपकी समस्या हल करने योग्य नहीं है क्योंकि आपके पास प्राकृतिक कुंजी नहीं है। उन लोगों के लिए नकली तारीख डालना जिनकी जन्म तिथि अज्ञात है, आपकी समस्या का समाधान नहीं करेंगे। 1 9 00/01/01 को जन्मे जॉन स्मिथ 1 9 60/09/09 को जॉन स्मिथ के जन्म से एक अलग व्यक्ति होने जा रहे हैं।

मैं हर दिन बड़े और छोटे संगठनों से नाम डेटा के साथ काम करता हूं और मैं आपको आश्वस्त कर सकता हूं कि उनके पास हर समय एक ही नाम के साथ दो अलग-अलग लोग हैं। कभी-कभी एक ही नौकरी के शीर्षक के साथ। जन्मदिन को विशिष्टता की कोई गारंटी नहीं है, उसी दिन जॉन स्मिथ का जन्म हुआ। हेक जब हम चिकित्सकों के कार्यालय डेटा के साथ काम करते हैं तो हमारे पास अक्सर दो नाम होते हैं, जिनमें एक ही नाम, पता और फोन नंबर (पिता और बेटे संयोजन)

आपकी सर्वश्रेष्ठ शर्त एक कर्मचारी आईडी है यदि आप पहचानने के लिए कर्मचारी डेटा डाल रहे हैं प्रत्येक कर्मचारी विशिष्ट रूप से। फिर उपयोगकर्ता इंटरफ़ेस में यूनिकनाम का जांच करें और यदि एक या अधिक मैचों हैं, तो उपयोगकर्ता से पूछें कि क्या उनका मतलब है और यदि वह नहीं कहता है, तो रिकॉर्ड डालें। फिर किसी को दुर्घटना से दो आईडी सौंपने पर समस्याओं को ठीक करने के लिए एक अपरिपक्व प्रक्रिया का निर्माण करें।

0

सही समाधान आधारित ब्रिटेन के समारोह के लिए समर्थन किया जाएगा, लेकिन यह अधिक जटिल हो जाता के रूप में mySQL भी तो समारोह आधारित अनुक्रमित समर्थन करने के लिए की आवश्यकता होगी। यह न्यूल के स्थान पर "नकली" मानों का उपयोग करने की आवश्यकता को रोक देगा, जबकि डेवलपर्स को यह तय करने की क्षमता भी होगी कि ब्रिटेन में नल मूल्यों का इलाज कैसे किया जाए। दुर्भाग्यवश, mySQL वर्तमान में ऐसी कार्यक्षमता का समर्थन नहीं करता है जिसे मैं जानता हूं, इसलिए हमें कामकाज के साथ छोड़ दिया गया है।

CREATE TABLE employee( 
name CHAR(50) NOT NULL, 
date_of_birth DATE, 
title CHAR(50), 
UNIQUE KEY idx_name_dob (name, IFNULL(date_of_birth,'0000-00-00 00:00:00')) 
); 

(नोट अद्वितीय कुंजी परिभाषा IFNULL() समारोह का उपयोग)

+2

यह 'ERROR 1064 (42000) उत्पन्न करता है: आपको अपने SQL वाक्यविन्यास में कोई त्रुटि है; 'date_of_birth,' 0000-00-00 00:00:00 ') के उपयोग के लिए सही वाक्यविन्यास के लिए आपके MySQL सर्वर संस्करण से मेल खाने वाले मैन्युअल की जांच करें))' MySQL 5.5 – CrackerJack9

0

सरल शब्दों में, अद्वितीय बाधा की भूमिका क्षेत्र या स्तंभ बनाने के लिए है।

रूप प्राथमिक कुंजी

+3

में लाइन 1' पर जहां तक ​​मैं कह सकता हूं , प्राथमिक कुंजी का कॉलम हिस्सा वास्तव में एक मूल्य के रूप में NULL को स्वीकार करने की अपनी क्षमता को हटा देता है। – ACJ

0

मैं था अद्वितीय कुंजी बनाओ: अशक्त डेटाबेस के रूप में व्यवहार करता है अशक्त के रूप में अज्ञात

Inorder डुप्लिकेट से बचने और अशक्त अनुमति देने के लिए इस संपत्ति को नष्ट कर देता इस के लिए एक समान समस्या है, लेकिन एक मोड़ के साथ। आपके मामले में, प्रत्येक कर्मचारी का जन्मदिन होता है, हालांकि यह अज्ञात हो सकता है। उस स्थिति में, यह अज्ञात जन्मदिन वाले कर्मचारियों के लिए दो मूल्यों को आवंटित करने के लिए सिस्टम के लिए तार्किक अर्थ बनाता है लेकिन अन्यथा समान जानकारी। नीलबी का स्वीकार्य उत्तर बहुत सटीक है।

हालांकि, मुझे जिस समस्या का सामना करना पड़ा वह वह था जिसमें डेटा फ़ील्ड के पास कोई मूल्य नहीं था। उदाहरण के लिए, यदि आपने अपनी तालिका में 'name_of_spouse' फ़ील्ड जोड़ा है, तो तालिका की प्रत्येक पंक्ति के लिए आवश्यक नहीं होगा। उस स्थिति में, नीलबी का पहला बुलेट प्वाइंट ('गलत तरीका') वास्तव में समझ में आता है। इस मामले में, प्रत्येक पंक्ति के लिए कॉलम name_of_spouse में एक स्ट्रिंग 'कोई नहीं' डाला जाना चाहिए जिसमें कोई ज्ञात पति/पत्नी नहीं था।

जिस स्थिति में मैं इस समस्या में भाग गया था, वह आईपी यातायात को वर्गीकृत करने के लिए डेटाबेस के साथ एक प्रोग्राम लिखने में था। लक्ष्य एक निजी नेटवर्क पर आईपी यातायात का ग्राफ बनाना था। प्रत्येक पैकेट को अपने आईपी स्रोत और dest, पोर्ट स्रोत और dest, ट्रांसपोर्ट प्रोटोकॉल, और अनुप्रयोग प्रोटोकॉल के आधार पर एक अद्वितीय कनेक्शन इंडेक्स के साथ डेटाबेस तालिका में रखा गया था। हालांकि, कई पैकेट में केवल एक एप्लिकेशन प्रोटोकॉल नहीं है। उदाहरण के लिए, एप्लिकेशन प्रोटोकॉल के बिना सभी टीसीपी पैकेट को एक साथ वर्गीकृत किया जाना चाहिए, और कनेक्शन इंडेक्स में एक अनन्य प्रविष्टि पर कब्जा कर लेना चाहिए। ऐसा इसलिए है क्योंकि मैं उन पैकेट को अपने ग्राफ के एक किनारे बनाने के लिए चाहता हूं। इस स्थिति में, मैंने ऊपर से अपनी सलाह ली और एप्लिकेशन प्रोटोकॉल फ़ील्ड में एक स्ट्रिंग 'कोई नहीं' संग्रहित किया ताकि यह सुनिश्चित किया जा सके कि इन पैकेटों ने एक अद्वितीय समूह बनाया है।

0

मैं अतिरिक्त तालिका कॉलम checksum बनाने की अनुशंसा करता हूं जिसमें name और date_of_birth का md5 हैश होगा। अद्वितीय कुंजी (name, date_of_birth) ड्रॉप करें क्योंकि यह समस्या का समाधान नहीं करता है। चेकसम पर एक अनूठी कुंजी बनाएं।

ALTER TABLE employee 
    ADD COLUMN checksum CHAR(32) NOT NULL; 

UPDATE employee 
SET checksum = MD5(CONCAT(name, IFNULL(date_of_birth, ''))); 

ALTER TABLE employee 
    ADD UNIQUE (checksum); 

यह समाधान छोटे तकनीकी भूमि के ऊपर, हर डाला जोड़े आप हैश (सभी खोज क्वेरी के लिए एक ही बात) उत्पन्न करने के लिए की जरूरत के लिए कारण पैदा करता है। आगे के सुधारों के लिए आप ट्रिगर जोड़ सकते हैं जो प्रत्येक सम्मिलन में आपके लिए हैश उत्पन्न करेगा:

CREATE TRIGGER before_insert_employee 
BEFORE INSERT ON employee 
FOR EACH ROW 
    IF new.checksum IS NULL THEN 
     SET new.checksum = MD5(CONCAT(new.name, IFNULL(new.date_of_birth, ''))); 
    END IF; 
संबंधित मुद्दे