नल स्तंभों के साथ अद्वितीय बाधा बनाएँ


252

मेरे पास इस लेआउट वाली एक तालिका है:

CREATE TABLE Favorites
(
  FavoriteId uuid NOT NULL PRIMARY KEY,
  UserId uuid NOT NULL,
  RecipeId uuid NOT NULL,
  MenuId uuid
)

मैं इसके समान एक अद्वितीय अवरोध बनाना चाहता हूं:

ALTER TABLE Favorites
ADD CONSTRAINT Favorites_UniqueFavorite UNIQUE(UserId, MenuId, RecipeId);

हालांकि, यह एक ही के साथ कई पंक्तियों को अनुमति देगा (UserId, RecipeId), यदि MenuId IS NULL। मैं अनुमति देना चाहते हैं NULLमें MenuIdएक पसंदीदा कोई संबंधित किया जाता है कि मेनू स्टोर करने के लिए है, लेकिन मैं केवल प्रति उपयोगकर्ता / नुस्खा जोड़ी इन पंक्तियों की अधिकतम एक बार चाहता हूँ।

मेरे पास अब तक के विचार हैं:

  1. अशक्त के बजाय कुछ हार्ड-कोडेड यूयूआईडी (जैसे सभी शून्य) का उपयोग करें।
    हालांकि, MenuIdप्रत्येक उपयोगकर्ता के मेनू पर एक FK बाधा है, इसलिए मुझे हर उपयोगकर्ता के लिए एक विशेष "अशक्त" मेनू बनाना होगा जो एक परेशानी है।

  2. इसके बजाय ट्रिगर का उपयोग करके एक अशक्त प्रविष्टि के अस्तित्व की जांच करें।
    मुझे लगता है कि यह एक परेशानी है और मुझे जहां भी संभव हो ट्रिगर से बचना पसंद है। इसके अलावा, मुझे भरोसा नहीं है कि मेरे डेटा की गारंटी देने की स्थिति कभी खराब नहीं होगी।

  3. बस इसके बारे में भूल जाओ और मध्य-बर्तन में या प्रविष्टि फ़ंक्शन में एक अशक्त प्रविष्टि के पिछले अस्तित्व की जांच करें, और यह बाधा नहीं है।

मैं का उपयोग कर रहा हूँ Postgres 9.0।

क्या कोई तरीका है जो मैं देख रहा हूँ?


ऐसा क्यों है जो एक ही ( UserId, RecipeId), के साथ कई पंक्तियों की अनुमति देगा , यदि MenuId IS NULL?
Drux

जवाबों:


382

दो आंशिक सूचकांक बनाएँ :

CREATE UNIQUE INDEX favo_3col_uni_idx ON favorites (user_id, menu_id, recipe_id)
WHERE menu_id IS NOT NULL;

CREATE UNIQUE INDEX favo_2col_uni_idx ON favorites (user_id, recipe_id)
WHERE menu_id IS NULL;

इस तरह, वहाँ केवल में से एक संयोजन हो सकता है (user_id, recipe_id)जहां menu_id IS NULL, प्रभावी रूप से वांछित बाधा को लागू करने।

संभावित कमियां: आपके पास एक विदेशी कुंजी संदर्भ (user_id, menu_id, recipe_id)नहीं हो सकता है, आप CLUSTERएक आंशिक सूचकांक पर आधारित नहीं हो सकते हैं , और मिलान WHEREस्थिति के बिना क्वेरी आंशिक सूचकांक का उपयोग नहीं कर सकते हैं। (ऐसा लगता है कि आप एक FK संदर्भ तीन कॉलम चौड़ा चाहते हैं - इसके बजाय PK स्तंभ का उपयोग करें)।

यदि आपको पूर्ण सूचकांक की आवश्यकता है , तो आप वैकल्पिक रूप से WHEREस्थिति को छोड़ सकते हैं favo_3col_uni_idxऔर आपकी आवश्यकताओं को अभी भी लागू किया जा सकता है।
सूचकांक, जिसमें अब पूरी तालिका शामिल है, दूसरे के साथ ओवरलैप हो जाता है और बड़ा हो जाता है। विशिष्ट प्रश्नों और NULLमूल्यों के प्रतिशत के आधार पर , यह उपयोगी हो सकता है या नहीं भी हो सकता है। चरम स्थितियों में यह सभी तीन अनुक्रमित (दो आंशिक वाले और शीर्ष पर कुल) को बनाए रखने में भी मदद कर सकता है।

एक तरफ: मैं PostgreSQL में मिश्रित मामले पहचानकर्ताओं का उपयोग नहीं करने की सलाह देता हूं ।


1
@ इरविन ब्रांड्ससेट्टर: " मिश्रित केस आइडेंटीजर्स " टिप्पणी के बारे में: जब तक कोई दोहरे उद्धरण चिह्नों का उपयोग नहीं किया जाता है, मिश्रित कैसिड आइडेंटर्स का उपयोग करना बिल्कुल ठीक है। सभी लोअरकेस पहचानकर्ताओं का उपयोग करने में कोई अंतर नहीं है (फिर से: केवल अगर कोई उद्धरण उपयोग नहीं किया जाता है)
a_horse_with_no_name

14
@a_horse_with_no_name: मुझे लगता है कि आप जानते हैं कि मुझे पता है। यह वास्तव में एक कारण है कि मैं इसके उपयोग के खिलाफ सलाह देता हूं । जो लोग बारीकियों को नहीं जानते हैं वे अच्छी तरह से भ्रमित हो जाते हैं, क्योंकि अन्य RDBMS पहचानकर्ता (आंशिक रूप से) मामले संवेदनशील हैं। कभी-कभी लोग खुद को भ्रमित करते हैं। या वे डायनामिक एसक्यूएल का निर्माण करते हैं और बोली_साइड () का उपयोग करते हैं, जैसा कि उन्हें पहचानना चाहिए और कम केस स्ट्रिंग्स के रूप में पहचानकर्ताओं को पास करना चाहिए! यदि आप इससे बच सकते हैं, तो PostgreSQL में मिश्रित केस आइडेंटिफ़ायर का उपयोग न करें। मैंने यहाँ इस मूर्खता से उपजी कई हताश अनुरोधों को देखा है।
इरविन ब्रान्डसेट्टर

3
@a_horse_with_no_name: हाँ, यह बिल्कुल सच है। लेकिन अगर आप उनसे बच सकते हैं: आप मिश्रित मामले के पहचानकर्ता नहीं चाहते हैं । वे बिना किसी उद्देश्य के सेवा करते हैं। यदि आप उनसे बच सकते हैं: उनका उपयोग न करें। इसके अलावा: वे सीधे सादे बदसूरत हैं। उद्धृत पहचान बदसूरत हैं, भी। SQL92 उन स्थानों के साथ पहचानकर्ता एक समिति द्वारा बनाई गई एक गलत छवि है। उनका उपयोग न करें।
वाइल्डप्लासर

2
@ माइक: मुझे लगता है कि आपको उस बारे में एसक्यूएल मानकों समिति से बात करनी होगी, सौभाग्य :)
म्यू

1
@ बफ़र: रखरखाव लागत और कुल भंडारण मूल रूप से एक ही है (प्रति इंडेक्स के लिए एक मामूली तय ओवरहेड को छोड़कर)। प्रत्येक पंक्ति को केवल एक सूचकांक में दर्शाया गया है। प्रदर्शन: यदि आपके परिणाम दोनों मामलों में हैं, तो एक अतिरिक्त कुल सादा सूचकांक भुगतान कर सकता है। यदि नहीं, तो एक आंशिक सूचकांक आम तौर पर एक पूर्ण सूचकांक से तेज होता है, मुख्यतः छोटे आकार के कारण। अनुक्रमणिका स्थिति को क्वेरी (अतिरेक) में जोड़ें यदि Postgres यह पता नहीं लगाता है कि यह अपने आप आंशिक अनुक्रमणिका का उपयोग कर सकता है। उदाहरण।
इरविन ब्रान्डेसटेटर

75

आप MenuId पर एक मोटे सूचकांक के साथ एक अद्वितीय सूचकांक बना सकते हैं:

CREATE UNIQUE INDEX
Favorites_UniqueFavorite ON Favorites
(UserId, COALESCE(MenuId, '00000000-0000-0000-0000-000000000000'), RecipeId);

आपको केवल उस COALESCE के लिए UUID चुनना होगा जो "वास्तविक जीवन" में कभी नहीं होगा। आप शायद वास्तविक जीवन में कभी भी शून्य यूयूआईडी नहीं देख पाएंगे, लेकिन यदि आप पागल हैं (और जब से वे वास्तव में आपको पाने के लिए बाहर हैं ...):

alter table Favorites
add constraint check
(MenuId <> '00000000-0000-0000-0000-000000000000')

1
यह (सैद्धांतिक) दोष वहन करता है, कि menu_id = '00000000-0000-0000-0000-0000-000000000000' के साथ प्रविष्टियाँ झूठी अनोखी उल्लंघनों को ट्रिगर कर सकती हैं - लेकिन आपने पहले ही अपनी टिप्पणी में इसे संबोधित किया था।
एरविन ब्रान्डस्टेट्टर

2
@ मुइस्टोओशॉर्ट: हाँ, यह एक उचित समाधान है। (MenuId <> '00000000-0000-0000-0000-000000000000')हालांकि सरल । NULLडिफ़ॉल्ट रूप से अनुमति है। Btw, तीन तरह के लोग हैं। लकवाग्रस्त, और जो लोग डेटाबेस नहीं करते हैं। तीसरे प्रकार कभी-कभी घबराहट में एसओ पर सवाल पोस्ट करता है। ;)
इरविन ब्रांडस्टैटर

2
@Erwin: क्या आपका मतलब "टूटे हुए डेटाबेस वाले अपंगों और अपनों" से नहीं है?
एमयू

2
यह उत्कृष्ट समाधान एक सरल बाधा में एक सरल प्रकार के एक अशक्त स्तंभ को शामिल करना बहुत आसान बनाता है, एक अद्वितीय बाधा में।
मार्कस Pscheidt

2
यह सच है कि एक UUID अभ्यस्त उस विशेष स्ट्रिंग के साथ आता है, न केवल इसमें शामिल संभावनाओं के कारण, बल्कि इसलिए भी कि यह एक मान्य UUID नहीं है । एक यूयूआईडी जनरेटर किसी भी स्थिति में किसी भी हेक्स अंक का उपयोग करने के लिए स्वतंत्र नहीं है, उदाहरण के लिए एक स्थिति यूयूआईडी के संस्करण संख्या के लिए आरक्षित है।
टॉबी 1 केनोबी

1

आप किसी अलग तालिका में कोई संबद्ध मेनू के साथ पसंदीदा स्टोर कर सकते हैं:

CREATE TABLE FavoriteWithoutMenu
(
  FavoriteWithoutMenuId uuid NOT NULL, --Primary key
  UserId uuid NOT NULL,
  RecipeId uuid NOT NULL,
  UNIQUE KEY (UserId, RecipeId)
)

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

मुझे नहीं लगता कि और अधिक जटिल डालने के लिए। यदि आप के साथ एक रिकॉर्ड सम्मिलित करना चाहते हैं NULL MenuId, तो आप इस तालिका में सम्मिलित करते हैं। यदि नहीं, तो Favoritesमेज पर। लेकिन क्वेरी करना, हाँ, यह अधिक जटिल होगा।
ypercube y

वास्तव में खरोंच है कि, सभी पसंदीदा का चयन करने के लिए मेनू पाने के लिए सिर्फ एक ही LEFT जॉइन होगा। हम्म ये जाने का रास्ता हो सकता है ..
माइक क्रिस्टेंसन

यदि आप एक ही रेसिपी को एक से अधिक मेनू में जोड़ना चाहते हैं, तो INSERT अधिक जटिल हो जाता है, क्योंकि आपके पास पसंदीदाविमेनटाउन पर UserId / RecipeId पर UNIQUE बाधा है। यदि यह पहले से मौजूद नहीं था, तो मुझे यह पंक्ति बनाने की आवश्यकता होगी।
माइक क्रिस्टीनसेन

1
धन्यवाद! यह उत्तर एक +1 के योग्य है क्योंकि यह एक क्रॉस-डेटाबेस शुद्ध एसक्यूएल चीज़ से अधिक है .. हालांकि, इस मामले में मैं आंशिक सूचकांक मार्ग पर जाने वाला हूं क्योंकि इसे मेरे स्कीमा में कोई बदलाव नहीं करना है और मुझे यह पसंद है :)
माइक क्रिस्टेंसन

-1

मुझे लगता है कि यहाँ एक शब्दार्थ समस्या है। मेरे विचार में, एक उपयोगकर्ता के पास एक विशिष्ट मेनू तैयार करने के लिए एक (लेकिन केवल एक ) पसंदीदा नुस्खा हो सकता है। (ओपी में मेन्यू और रेसिपी मिलाई गई है; अगर मैं गलत हूं: कृपया नीचे मीनू आई और रेसिपी इंटरचेंज करें) इसका तात्पर्य है कि इस तालिका में {उपयोगकर्ता, मेनू} एक अद्वितीय कुंजी होनी चाहिए। और यह बिल्कुल एक नुस्खा की ओर इशारा करना चाहिए । यदि उपयोगकर्ता के पास इस विशिष्ट मेनू के लिए कोई पसंदीदा नुस्खा नहीं है , तो इस {उपयोगकर्ता, मेनू} कुंजी जोड़ी के लिए कोई पंक्ति मौजूद नहीं होनी चाहिए । इसके अलावा: सरोगेट कुंजी (FaVouRiteId) सतही है: समग्र प्राथमिक कुंजी रिलेशनल-मैपिंग टेबल के लिए पूरी तरह से मान्य हैं।

इससे तालिका की परिभाषा कम हो जाएगी:

CREATE TABLE Favorites
( UserId uuid NOT NULL REFERENCES users(id)
, MenuId uuid NOT NULL REFERENCES menus(id)
, RecipeId uuid NOT NULL REFERENCES recipes(id)
, PRIMARY KEY (UserId, MenuId)
);

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

1
जैसा कि मैंने कहा: यह सभी शब्दार्थों के बारे में है। (मैं भोजन के बारे में सोच रहा था, जाहिर है) एक पसंदीदा "जो किसी भी मेनू से संबंधित नहीं है" होने से मुझे कोई मतलब नहीं है। आप ऐसी किसी चीज़ का पक्ष नहीं ले सकते जो मौजूद नहीं है, IMHO।
Wildplasser

कुछ डीबी सामान्यीकरण की तरह लगता है मदद कर सकता है। एक दूसरी तालिका बनाएं जो व्यंजनों को मेनू (या नहीं) से संबंधित करती है। हालांकि यह समस्या को सामान्य करता है और एक से अधिक मेनू के लिए अनुमति देता है जो एक नुस्खा का हिस्सा हो सकता है। भले ही, सवाल PostgreSQL में अद्वितीय अनुक्रमितों के बारे में था। धन्यवाद।
क्रिस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.