किसी रिलेशनल डेटाबेस में ऑर्डर की गई जानकारी को कैसे स्टोर करें


20

मैं यह समझने की कोशिश कर रहा हूं कि एक रिलेशनल डेटाबेस में ऑर्डर की गई जानकारी को कैसे ठीक से स्टोर किया जाए।

एक उदाहरण:

बोलो मेरे पास एक प्लेलिस्ट है, जिसमें गाने हैं। मेरे रिलेशनल डेटाबेस के अंदर, मेरे पास एक टेबल है Playlists, जिसमें कुछ मेटाडेटा (नाम, निर्माता, आदि) हैं। मेरे पास एक तालिका भी है Songs, जिसमें एक playlist_id, साथ ही गीत-विशिष्ट जानकारी (नाम, कलाकार, अवधि, आदि) है।

डिफ़ॉल्ट रूप से, जब एक नया सॉन्ग एक प्लेलिस्ट में जोड़ा जाता है, तो इसे अंत में जोड़ा जाता है। जब सॉन्ग-आईडी (आरोही) पर ऑर्डर किया जाता है, तो ऑर्डर जोड़ के आदेश होगा। लेकिन क्या होगा अगर कोई उपयोगकर्ता प्लेलिस्ट में गाने को फिर से ऑर्डर करने में सक्षम हो?

मैं कुछ विचारों के साथ आया, प्रत्येक उनके फायदे और नुकसान के साथ:

  1. एक स्तंभ order, जिसे पूर्णांक कहा जाता है । जब एक गीत को स्थानांतरित किया जाता है, तो परिवर्तन को प्रतिबिंबित करने के लिए, इसकी पुरानी और नई स्थिति के बीच सभी गीतों का क्रम बदल दिया जाता है। इसका दोष यह है कि एक गीत को स्थानांतरित करने के लिए प्रत्येक बार बहुत सारे प्रश्नों की आवश्यकता होती है, और चलती एल्गोरिथ्म अन्य विकल्पों के साथ तुच्छ नहीं है।
  2. एक स्तंभ order, जिसे एक दशमलव ( NUMERIC) कहा जाता है । जब एक गीत को स्थानांतरित किया जाता है, तो इसे दो आसन्न संख्याओं के बीच फ्लोटिंग पॉइंट मान सौंपा जाता है। दोष: दशमलव क्षेत्र अधिक स्थान लेते हैं, और यह संभव है कि सटीकता से बाहर चला जाए, जब तक कि हर कुछ परिवर्तनों के बाद सीमा को फिर से वितरित करने के लिए देखभाल न की जाए।
  3. एक और तरीका होगा एक previousऔर एक nextक्षेत्र है जो अन्य गीतों का संदर्भ देता है। (या पहले के मामले में NULL हैं, अभी प्लेलिस्ट में अंतिम गीत का सम्मान करें; मूल रूप से आप लिंक्ड-लिस्ट बनाते हैं )। ड्राबैक: 'सूची में Xth सॉन्ग को ढूंढें' जैसी क्वेरीज़ अब निरंतर-समय नहीं हैं, बल्कि रैखिक-समय पर हैं।

इनमें से कौन सी प्रक्रिया सबसे अधिक बार अभ्यास में उपयोग की जाती है? मध्यम से बड़े डेटाबेस में इनमें से कौन सी प्रक्रिया सबसे तेज है? क्या इसे संग्रहीत करने के लिए कोई अन्य तरीके हैं?

संपादित करें: सरलता के लिए, उदाहरण में एक गीत केवल एक प्लेलिस्ट (कई-से-एक संबंध) के अंतर्गत आता है। बेशक, कोई भी एक जंक्शन टेबल का उपयोग कर सकता है, इसलिए गीत-सूची कई-से-कई संबंध है (और उस तालिका पर उपरोक्त रणनीतियों में से एक को लागू करें)।


1
आप 100-चरणों के साथ विकल्प एक (इंटेगर के रूप में ऑर्डर) का उपयोग कर सकते हैं। यदि आप एक गीत को स्थानांतरित करते हैं, तो आपको फिर से आदेश देने की आवश्यकता नहीं है, बस 100 के बीच का मान लें। समय-समय पर आपको गीतों के बीच फिर से अंतराल प्राप्त करने के लिए एक नए सिरे से काम करने की आवश्यकता हो सकती है।
22

4
"इसका दोष यह है कि हर बार एक गीत को स्थानांतरित करने के लिए बहुत सारे प्रश्नों की आवश्यकता होती है?" - update songorder set order = order - 1 where order >= 12 & order <= 42; update songorder set order = 42 where id = 123;- यह दो अपडेट हैं - तीस नहीं। तीन यदि आप ऑर्डर पर एक अद्वितीय बाधा डालना चाहते हैं।

2
विकल्प एक का उपयोग करें जब तक आप एक तथ्य के लिए नहीं जानते हैं कि आपको कुछ और चाहिए। डेटाबेस एनकाउंटर के लिए एक समस्या प्रोग्रामर समझ में नहीं आ रहा है कि डेटाबेस इस तरह के बहुत अच्छे हैं। अपने डीबी को काम करने से डरो मत।
ग्रैंडमास्टरबी

1
Queries like 'find the Xth Song in the list' are no longer constant-timeविकल्प 2 के लिए भी सही है।
डॉक्टर ब्राउन

2
@ माइकाइकिस: यह महंगा लगता है, लेकिन सारा काम सर्वर पर किया जा रहा है, जो (आमतौर पर) इस तरह के काम के लिए अनुकूलित है। मैं लाखों पंक्तियों वाली तालिका में इस तकनीक का उपयोग नहीं करूंगा, लेकिन मैं इसे केवल एक-दो हजार वाली तालिका के लिए छूट नहीं दूंगा।
TMN

जवाबों:


29

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

विचार करें:

order song
1     Happy Birthday
2     Beat It
3     Never Gonna Give You Up
4     Safety Dance
5     Imperial March

और आप Beat Itअंत तक जाना चाहते हैं, आपके पास दो प्रश्न होंगे:

update table 
  set order = order - 1
  where order >= 2 and order <= 5;

update table
  set order = 5
  where song = 'Beat It'

और बस। यह बहुत बड़ी संख्या के साथ बहुत अच्छी तरह से बढ़ता है। अपने डेटाबेस में एक काल्पनिक प्लेलिस्ट में कुछ हज़ार गाने डालने की कोशिश करें और देखें कि एक गीत को एक स्थान से दूसरे स्थान पर ले जाने में कितना समय लगता है। चूंकि ये बहुत मानकीकृत रूप हैं:

update table 
  set order = order - 1
  where order >= ? and order <= ?;

update table
  set order = ?
  where song = ?

आपके पास दो तैयार किए गए कथन हैं जो आप बहुत कुशलता से पुन: उपयोग कर सकते हैं।

यह कुछ महत्वपूर्ण लाभ प्रदान करता है - तालिका का क्रम कुछ ऐसा है जिसके बारे में आप तर्क कर सकते हैं। तीसरे गाने में order3 का हमेशा है। इसकी गारंटी देने का एकमात्र तरीका क्रम के रूप में लगातार पूर्णांक का उपयोग करना है। छद्म से जुड़ी सूचियों या अंतराल के साथ दशमलव संख्या या पूर्णांक का उपयोग करना आपको इस संपत्ति की गारंटी नहीं देगा; इन मामलों में nth गीत प्राप्त करने का एकमात्र तरीका पूरी तालिका को सॉर्ट करना और nth रिकॉर्ड प्राप्त करना है।

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


2
मैं इस दृष्टिकोण को पसंद करने लगा हूं।
माइक नाकिस

2
@ मायकेनीस यह अच्छी तरह से काम करता है। एक बाइनरी ट्री भी है जो एक समान विचार पर आधारित है - संशोधित प्रीऑर्डर ट्री । अपने सिर को चारों ओर ले जाने में थोड़ा अधिक लगता है, लेकिन यह आपको पदानुक्रमित डेटा के लिए कुछ बहुत अच्छे प्रश्न करने देता है। मुझे इसके साथ प्रदर्शन की समस्या कभी नहीं हुई, यहां तक ​​कि बड़े पेड़ों में भी। कोड के बारे में तर्क करने में सक्षम होने के कारण मैं कुछ जोर देता हूं जब तक कि यह नहीं दिखाया जाता है कि सरल कोड में आवश्यक प्रदर्शन की कमी है (और यह केवल चरम स्थितियों में रहा है)।

क्या कोई महत्वपूर्ण शब्द है जिसके प्रयोग orderसे कोई समस्या होगी order by?
kojow7

@ kojow7, यदि आपके फ़ील्ड में कीवर्ड के साथ परस्पर विरोधी नाम हैं, तो आपको उन्हें "` "टिकमार्क में लपेटना चाहिए।
एंड्री

यह दृष्टिकोण समझ में आता है, लेकिन orderकिसी नए गीत को प्लेलिस्ट में जोड़ते समय मूल्य प्राप्त करने का सबसे अच्छा तरीका क्या है । यह कहें कि यह 9 वां गीत है, orderक्या COUNTरिकॉर्ड जोड़ने से पहले 9 करने से बेहतर कोई और तरीका है ?
प्रलय

3

सबसे पहले, यह आपके विवरण से स्पष्ट नहीं है कि आपने क्या किया है, लेकिन आपको एक PlaylistSongsतालिका की आवश्यकता है जिसमें एक PlaylistIdहै SongId, जिसमें वर्णन है कि कौन से गीत किस प्लेलिस्ट के हैं।

यह इस तालिका में है कि आपको आदेश देने वाली जानकारी को जोड़ना होगा।

मेरा पसंदीदा तंत्र वास्तविक संख्याओं के साथ है। मैंने इसे हाल ही में लागू किया, और इसने एक आकर्षण की तरह काम किया। जब आप किसी गीत को एक विशिष्ट स्थिति में ले जाना चाहते हैं, तो आप पिछले गीत और अगले गीत Orderingके Orderingमूल्यों के औसत के रूप में उसके नए मूल्य की गणना करते हैं। यदि आप 64-बिट वास्तविक संख्या का उपयोग करते हैं, तो आप सटीक रूप से उसी समय बाहर निकलेंगे, जब नरक समाप्त हो जाएगा, लेकिन यदि आप वास्तव में अपने सॉफ़्टवेयर को पोस्टरिटी के लिए लिख रहे हैं, तो Orderingप्रत्येक गीत के सभी गीतों के लिए अच्छे गोल पूर्णांक मानों को फिर से असाइन करें। हर एक समय में एक बार प्लेलिस्ट।

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

कक्षा है ParameterTemplate(जो भी हो, मत पूछो!) विधि को पैरामीटर टेम्पलेट की सूची मिलती है जिससे यह टेम्पलेट अपने माता-पिता से संबंधित है ActivityTemplate। (जो भी हो, पूछिए मत!) कोड में सटीक बाहर चलाने के खिलाफ कुछ गार्ड हैं। विभाजक का उपयोग परीक्षण के लिए किया जाता है: इकाई परीक्षण एक बड़े भाजक का उपयोग करता है ताकि जल्दी से बाहर चला जाए, और इस प्रकार सटीक संरक्षक कोड को ट्रिगर किया जा सके। दूसरी विधि सार्वजनिक है और "केवल आंतरिक उपयोग के लिए; आह्वान न करें" ताकि परीक्षण कोड इसे लागू कर सके। (यह पैकेज-निजी नहीं हो सकता है क्योंकि मेरा परीक्षण कोड उसी पैकेज में नहीं है जितना कि यह परीक्षण करता है।) वह क्षेत्र जो आदेश को नियंत्रित करता है उसे कॉल किया जाता है Ordering, getOrdering()और के माध्यम से एक्सेस किया जाता है setOrdering()। आपको कोई SQL दिखाई नहीं देता क्योंकि मैं हाइबरनेट के माध्यम से ऑब्जेक्ट-रिलेशनल मैपिंग का उपयोग कर रहा हूं।

/**
 * Moves this {@link ParameterTemplate} to the given index in the list of {@link ParameterTemplate}s of the parent {@link ActivityTemplate}.
 *
 * The index must be greater than or equal to zero, and less than or equal to the number of entries in the list.  Specifying an index of zero will move this item to the top of
 * the list. Specifying an index which is equal to the number of entries will move this item to the end of the list.  Any other index will move this item to the position
 * specified, also moving other items in the list as necessary. The given index cannot be equal to the current index of the item, nor can it be equal to the current index plus
 * one.  If the given index is below the current index of the item, then the item will be moved so that its new index will be equal to the given index.  If the given index is
 * above the current index, then the new index of the item will be the given index minus one.
 *
 * NOTE: this method flushes the persistor and refreshes the parent node so as to guarantee that the changes will be immediately visible in the list of {@link
 * ParameterTemplate}s of the parent {@link ActivityTemplate}.
 *
 * @param toIndex the desired new index of this {@link ParameterTemplate} in the list of {@link ParameterTemplate}s of the parent {@link ActivityTemplate}.
 */
public void moveAt( int toIndex )
{
    moveAt( toIndex, 2.0 );
}

/**
 * For internal use only; do not invoke.
 */
public boolean moveAt( int toIndex, double divisor )
{
    MutableList<ParameterTemplate<?>> parameterTemplates = getLogicDomain().getMutableCollections().newArrayList();
    parameterTemplates.addAll( getParentActivityTemplate().getParameterTemplates() );
    assert parameterTemplates.getLength() >= 1; //guaranteed since at the very least, this parameter template must be in the list.
    int fromIndex = parameterTemplates.indexOf( this );
    assert 0 <= toIndex;
    assert toIndex <= parameterTemplates.getLength();
    assert 0 <= fromIndex;
    assert fromIndex < parameterTemplates.getLength();
    assert fromIndex != toIndex;
    assert fromIndex != toIndex - 1;

    double order;
    if( toIndex == 0 )
    {
        order = parameterTemplates.fetchFirstElement().getOrdering() - 1.0;
    }
    else if( toIndex == parameterTemplates.getLength() )
    {
        order = parameterTemplates.fetchLastElement().getOrdering() + 1.0;
    }
    else
    {
        double prevOrder = parameterTemplates.get( toIndex - 1 ).getOrdering();
        parameterTemplates.moveAt( fromIndex, toIndex );
        double nextOrder = parameterTemplates.get( toIndex + (toIndex > fromIndex ? 0 : 1) ).getOrdering();
        assert prevOrder <= nextOrder;
        order = (prevOrder + nextOrder) / divisor;
        if( order <= prevOrder || order >= nextOrder ) //if the accuracy of the double has been exceeded
        {
            parameterTemplates.clear();
            parameterTemplates.addAll( getParentActivityTemplate().getParameterTemplates() );
            for( int i = 0; i < parameterTemplates.getLength(); i++ )
                parameterTemplates.get( i ).setOrdering( i * 1.0 );
            rocs3dDomain.getPersistor().flush();
            rocs3dDomain.getPersistor().refresh( getParentActivityTemplate() );
            moveAt( toIndex );
            return true;
        }
    }
    setOrdering( order );
    rocs3dDomain.getPersistor().flush();
    rocs3dDomain.getPersistor().refresh( getParentActivityTemplate() );
    assert getParentActivityTemplate().getParameterTemplates().indexOf( this ) == (toIndex > fromIndex ? toIndex - 1 : toIndex);
    return false;
}

मैं एक पूर्णांक आदेश का उपयोग करूंगा और अगर मुझे लगा कि पुन: व्यवस्थित करना बहुत महंगा है, तो मैं बस सीमाओं की संख्या को कम कर दूंगा, प्रत्येक X द्वारा कूदने से, जहां X वह राशि है जिसके द्वारा मुझे पुन: क्रमबद्धता को कम करने की आवश्यकता है, 20 का कहना है, जो स्टार्टर के रूप में ठीक होना चाहिए।
वारेन P

1
@WarrenP हाँ, मुझे पता है, यह इस तरह से भी किया जा सकता है, इसीलिए मैंने "बेस्ट" या "द वन" अप्रोच के बजाय इसे "मेरा पसंदीदा" दृष्टिकोण कहा है।
माइक नाकिस

0

मेरे लिए क्या काम किया, 100 वस्तुओं के आदेश पर एक छोटी सूची के लिए एक संकर दृष्टिकोण लेना था:

  1. दशमलव सॉर्टऑर्डर स्तंभ, लेकिन केवल 0.5 अंतर (यानी दशमलव (8,2) या कुछ और) स्टोर करने के लिए पर्याप्त सटीकता के साथ।
  2. छँटाई करते समय, ऊपर और नीचे की पंक्ति के पीके को पकड़ें जहाँ वर्तमान पंक्ति को स्थानांतरित किया गया था, यदि वे मौजूद हैं। (यदि आप आइटम को पहले स्थान पर ले जाते हैं, तो उदाहरण के लिए ऊपर एक पंक्ति नहीं है)
  3. सॉर्ट करने के लिए सर्वर के वर्तमान, पिछली और अगली पंक्ति के पीके पोस्ट करें।
  4. यदि आपके पास एक प्रचलित पंक्ति है, तो मौजूदा पंक्ति की स्थिति को प्रचलित + 0.5 पर सेट करें। यदि आपके पास केवल एक अगला है, तो वर्तमान पंक्ति की स्थिति को अगले - 0.5 पर सेट करें।
  5. अगला मेरे पास एक संग्रहित खरीद है जो SQL सर्वर रो_नंबर फ़ंक्शन का उपयोग करके सभी पदों को अपडेट करता है, नए प्रकार के क्रम से ऑर्डर करता है। यह ऑर्डर को 1,1.5,2,3,4,6 से 1,2,3,4,5,6 में बदल देगा, क्योंकि row_number फ़ंक्शन आपको पूर्णांक अध्यादेश देता है।

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.