यह बहुत कुछ परिस्थितियों और सटीक आवश्यकताओं पर निर्भर करता है । प्रश्न के लिए मेरी टिप्पणी पर विचार करें ।
सरल उपाय
साथ DISTINCT ON
Postgres में:
SELECT DISTINCT ON (i.good, i.the_date)
i.the_date, p.the_date AS pricing_date, i.good, p.price
FROM inventory i
LEFT JOIN price p ON i.good = p.good AND i.the_date >= p.the_date
ORDER BY i.good, i.the_date, p.the_date DESC;
परिणाम का आदेश दिया।
या NOT EXISTS
मानक SQL में (हर RDBMS के साथ काम करता है जो मुझे पता है):
SELECT i.the_date, p.the_date AS pricing_date, i.good, i.quantity, p.price
FROM inventory i
LEFT JOIN price p ON p.good = i.good AND p.the_date <= i.the_date
WHERE NOT EXISTS (
SELECT 1 FROM price p1
WHERE p1.good = p.good
AND p1.the_date <= i.the_date
AND p1.the_date > p.the_date
);
समान परिणाम, लेकिन मनमाने ढंग से क्रम के साथ - जब तक आप नहीं जोड़ते ORDER BY
।
डेटा वितरण, सटीक आवश्यकताओं और सूचकांकों के आधार पर, इनमें से एक भी तेज हो सकता है।
आम तौर पर, DISTINCT ON
विजेता होता है और आपको इसके शीर्ष पर एक हल किया गया परिणाम मिलता है। लेकिन कुछ मामलों के लिए अन्य क्वेरी तकनीक (बहुत) तेज, फिर भी हैं। निचे देखो।
अधिकतम / मिनट मूल्यों की गणना करने के लिए उपश्रेणियों के साथ समाधान आम तौर पर धीमे होते हैं। सीटीई वाले वेरिएंट आमतौर पर धीमे होते हैं, फिर भी।
सादा विचार (जैसे एक अन्य उत्तर द्वारा प्रस्तावित) पोस्टग्रेज़ में प्रदर्शन में मदद नहीं करता है।
एसक्यूएल फिडल।
उचित समाधान
तार और टकराव
सबसे पहले, आप एक उप-इष्टतम तालिका लेआउट से पीड़ित हैं। यह तुच्छ लग सकता है, लेकिन अपने स्कीमा को सामान्य करने से बहुत लंबा रास्ता तय किया जा सकता है।
से अनुक्रमित चरित्र प्रकार ( text
, varchar
, ...) - स्थान के अनुसार किया जा सकता है COLLATION विशेष रूप से। सबसे अधिक संभावना है कि आपका DB नियमों के कुछ स्थानीय सेटों का उपयोग करता है (जैसे, मेरे मामले में:) de_AT.UTF-8
। इसके साथ पता करें:
SHOW lc_collate;
यह सॉर्टिंग और इंडेक्स लुक-अप को धीमा बनाता है । अब आपके तार (सामानों के नाम) बदतर हैं। यदि आप वास्तव में अपने आउटपुट में टकराव के नियमों (या सभी प्रकार के क्रम) की परवाह नहीं करते हैं, तो यह तेजी से हो सकता है यदि आप जोड़ते हैं COLLATE "C"
:
SELECT DISTINCT ON (i.good COLLATE "C", i.the_date)
i.the_date, p.the_date AS pricing_date, i.good, p.price
FROM inventory i
LEFT JOIN price p ON i.good = p.good AND i.the_date >= p.the_date
ORDER BY i.good COLLATE "C", i.the_date, p.the_date DESC;
ध्यान दें कि मैंने दो स्थानों पर टकराव को कैसे जोड़ा।
20k पंक्तियों के साथ मेरे परीक्षण में दो बार तेज और बहुत मूल नाम ('123 ')।
सूची
यदि आपकी क्वेरी किसी इंडेक्स का उपयोग करने वाली है, तो वर्ण डेटा वाले कॉलमों का मिलान मिलान ( good
उदाहरण में) करना होता है:
CREATE INDEX inventory_good_date_desc_collate_c_idx
ON price(good COLLATE "C", the_date DESC);
SO पर इस संबंधित उत्तर के अंतिम दो अध्यायों को अवश्य पढ़ें:
यहां तक कि एक ही कॉलम पर अलग-अलग कोलाज के साथ आपके पास कई इंडेक्स हो सकते हैं - यदि आपको अन्य प्रश्नों के अनुसार किसी अन्य (या डिफ़ॉल्ट) कोलेशन के अनुसार सॉर्ट किए गए सामान की भी आवश्यकता है।
सामान्य
निरर्थक तार (अच्छे का नाम) आपके टेबल और इंडेक्स को भी फूला देते हैं, जिससे सब कुछ धीमा हो जाता है। एक उचित तालिका लेआउट के साथ आप शुरू करने के लिए अधिकांश समस्या से बच सकते हैं। इस तरह देख सकते हैं:
CREATE TABLE good (
good_id serial PRIMARY KEY
, good text NOT NULL
);
CREATE TABLE inventory (
good_id int REFERENCES good (good_id)
, the_date date NOT NULL
, quantity int NOT NULL
, PRIMARY KEY(good_id, the_date)
);
CREATE TABLE price (
good_id int REFERENCES good (good_id)
, the_date date NOT NULL
, price numeric NOT NULL
, PRIMARY KEY(good_id, the_date));
प्राथमिक कुंजी स्वचालित रूप से हमारी आवश्यकता के सभी सूचकांकों को प्रदान करती है (लगभग)।
लापता विवरण के आधार पर, एक एकाधिक सूचकांक पर price
दूसरे स्तंभ पर अवरोही क्रम के प्रदर्शन में सुधार कर सकते हैं के साथ:
CREATE INDEX price_good_date_desc_idx ON price(good, the_date DESC);
फिर से, टकराव आपकी क्वेरी से मेल खाना चाहिए (ऊपर देखें)।
9.2 या बाद में इंडेक्स-ओनली स्कैन के लिए "कवरिंग इंडेक्स" में कुछ और मदद कर सकता है - खासकर अगर आपकी टेबल अतिरिक्त कॉलम रखती हैं, जिससे टेबल कवरिंग इंडेक्स से काफी बड़ी हो जाती है।
ये परिणामी प्रश्न बहुत तेज हैं:
अस्तित्व में नहीं है
SELECT i.the_date, p.the_date AS pricing_date, g.good, i.quantity, p.price
FROM inventory i
JOIN good g USING (good_id)
LEFT JOIN price p ON p.good_id = i.good_id AND p.the_date <= i.the_date
AND NOT EXISTS (
SELECT 1 FROM price p1
WHERE p1.good_id = p.good_id
AND p1.the_date <= i.the_date
AND p1.the_date > p.the_date
);
DISTINCT ON
SELECT DISTINCT ON (i.the_date)
i.the_date, p.the_date AS pricing_date, g.good, i.quantity, p.price
FROM inventory i
JOIN good g USING (good_id)
LEFT JOIN price p ON p.good_id = i.good_id AND p.the_date <= i.the_date
ORDER BY i.the_date, p.the_date DESC;
एसक्यूएल फिडल।
तेजी से समाधान
यदि वह अभी भी तेजी से पर्याप्त नहीं है, तो तेजी से समाधान हो सकते हैं।
पुनरावर्ती CTE / JOIN LATERAL
/ सहसंबद्ध उपश्रेणी
विशेष रूप से अच्छे मूल्य के साथ कई डेटा वितरण के लिए :
भौतिकवादी दृश्य
यदि आपको इसे अक्सर और तेज चलाने की आवश्यकता है, तो मेरा सुझाव है कि आप एक भौतिक दृष्टिकोण बनाएं। मुझे लगता है कि यह मानना सुरक्षित है कि पिछली तारीखों के लिए कीमतें और आविष्कार शायद ही कभी बदलते हैं। एक बार परिणाम की गणना करें और स्नैपशॉट को भौतिक रूप में देखें।
Postgres 9.3+ में भौतिक विचारों के लिए स्वचालित समर्थन है। आप पुराने संस्करणों में आसानी से एक मूल संस्करण लागू कर सकते हैं।