मुझे एक चयन में वर्तमान और अगला अधिक मूल्य कैसे मिलेगा?


18

मेरे पास कॉलमों के साथ एक InnoDB टेबल 'आइडीटम्स' (MySQL 5.0.22-log) है

`id` int(11) NOT NULL,
`time` int(20) NOT NULL, [...]

एक यौगिक अद्वितीय कुंजी के साथ

UNIQUE KEY `id_time` (`id`,`time`)

इसलिए प्रति आईडी में कई टाइमस्टैम्प हो सकते हैं और टाइमस्टैम्प पर कई आईडी हो सकते हैं।

मैं एक क्वेरी सेट करने की कोशिश कर रहा हूं, जहां मुझे सभी प्रविष्टियां मिलती हैं और प्रत्येक प्रविष्टि के लिए अगली अधिक से अधिक समय, यदि यह मौजूद है, तो इसे वापस लौटना चाहिए:

+-----+------------+------------+
| id  | time       | nexttime   |
+-----+------------+------------+
| 155 | 1300000000 | 1311111111 |
| 155 | 1311111111 | 1322222222 |
| 155 | 1322222222 |       NULL |
| 156 | 1312345678 | 1318765432 |
| 156 | 1318765432 |       NULL |
+-----+------------+------------+

अभी मैं बहुत दूर हूँ:

SELECT l.id, l.time, r.time FROM 
    idtimes AS l LEFT JOIN idtimes AS r ON l.id = r.id
    WHERE l.time < r.time ORDER BY l.id ASC, l.time ASC;

लेकिन निश्चित रूप से यह r.time> l.time के साथ सभी पंक्तियों को लौटाता है और न केवल पहला ...

मुझे लगता है कि मुझे एक सबसिनेट की आवश्यकता होगी

SELECT outer.id, outer.time, 
    (SELECT time FROM idtimes WHERE id = outer.id AND time > outer.time 
        ORDER BY time ASC LIMIT 1)
    FROM idtimes AS outer ORDER BY outer.id ASC, outer.time ASC;

लेकिन मुझे नहीं पता कि वर्तमान समय का संदर्भ कैसे दिया जाए (मुझे पता है कि उपरोक्त मान्य SQL नहीं है)।

मैं एक ही प्रश्न के साथ ऐसा कैसे कर सकता हूं (और मैं @variables का उपयोग नहीं करना चाहूंगा जो कि एक समय में तालिका एक पंक्ति को छोड़ने पर निर्भर करता है और अंतिम मूल्य याद है)?

जवाबों:


20

एक JOIN करना एक ऐसी चीज है जिसकी आपको आवश्यकता हो सकती है।

SELECT l.id, l.time, r.time FROM 
    idtimes AS l LEFT JOIN idtimes AS r ON l.id = r.id

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

WHERE l.time < r.time ORDER BY l.id ASC, l.time ASC;

आप केवल आर चाहते हैं। पंक्ति जिसमें सबसे कम (MIN) समय होता है जो l.time से अधिक होता है। यह वह जगह है जहाँ आपको सबक्वेरी की जरूरत होती है।

WHERE r.time = (SELECT MIN(time) FROM idtimes r2 where r2.id = l.id AND r2.time > l.time)

अब नल को। यदि "कोई अगला-उच्चतर समय नहीं है", तो SELECT MIN () शून्य (या अधिक खराब) का मूल्यांकन करेगा, और वह स्वयं कभी भी किसी चीज़ की तुलना नहीं करेगा, इसलिए आपका WHERE क्लॉज़ कभी संतुष्ट नहीं होगा, और "उच्चतम समय" प्रत्येक आईडी के लिए, परिणाम सेट में कभी भी प्रकट नहीं हो सकता है।

आप इसे अपने JOIN को हटाकर, और सेलेर सबक्वेरी को सेलेक्ट लिस्ट में ले जाकर हल करें:

SELECT id, time, 
    (SELECT MIN(time) FROM idtimes sub 
        WHERE sub.id = main.id AND sub.time > main.time) as nxttime
  FROM idtimes AS main 

4

मैं हमेशा या तो SELECTब्लॉक या ब्लॉक में उपश्रेणियों का उपयोग करने से बचता हूंFROM , क्योंकि यह कोड "डस्टियर" और कभी-कभी कम कुशल बनाता है।

मुझे लगता है कि यह करने के लिए एक और अधिक सुंदर तरीका है:

1. पता लगाएं बार की तुलना में अधिक समय पंक्ति के

आप इस एक साथ कर सकते हैं JOINके बीच idtimes ही में शामिल होने को बाधित करने के साथ ही मेज, आईडी और करने के लिए कई बार की तुलना में अधिक समय वर्तमान पंक्ति के।

आपको उन LEFT JOINपंक्तियों को छोड़कर उपयोग करना चाहिए जहाँ वर्तमान पंक्ति के एक से अधिक बार नहीं हैं ।

SELECT
    i1.id,
    i1.time AS time,
    i2.time AS greater_time
FROM
    idtimes AS i1
    LEFT JOIN idtimes AS i2 ON i1.id = i2.id AND i2.time > i1.time

जैसा कि आपने उल्लेख किया है, समस्या यह है कि आपके पास कई पंक्तियाँ हैं जहाँ next_time समय से अधिक है ।

+-----+------------+--------------+
| id  | time       | greater_time |
+-----+------------+--------------+
| 155 | 1300000000 | 1311111111   |
| 155 | 1300000000 | 1322222222   |
| 155 | 1311111111 | 1322222222   |
| 155 | 1322222222 |       NULL   |
| 156 | 1312345678 | 1318765432   |
| 156 | 1318765432 |       NULL   |
+-----+------------+--------------+

2. उन पंक्तियों का पता लगाएं जहां ज्यादा से ज्यादा न केवल आगे, बल्कि अगले समय पर

इन बेकार पंक्तियों के सभी फिल्टर करने के लिए सबसे अच्छा तरीका पता लगाने के लिए अगर वहाँ रहे हैं कई बार के बीच समय (से अधिक) और greater_time इस के लिए (की तुलना में कम) आईडी

SELECT
    i1.id,
    i1.time AS time,
    i2.time AS next_time,
    i3.time AS intrudor_time
FROM
    idtimes AS i1
    LEFT JOIN idtimes AS i2 ON i1.id = i2.id AND i2.time > i1.time
    LEFT JOIN idtimes AS i3 ON i2.id = i3.id AND i3.time > i1.time AND i3.time < i2.time

ऑप्स, हमारे पास अभी भी एक गलत अगली बार है !

+-----+------------+--------------+---------------+
| id  | time       | next_time    | intrudor_time |
+-----+------------+--------------+---------------+
| 155 | 1300000000 | 1311111111   |         NULL  |
| 155 | 1300000000 | 1322222222   |    1311111111 |
| 155 | 1311111111 | 1322222222   |         NULL  |
| 155 | 1322222222 |       NULL   |         NULL  |
| 156 | 1312345678 | 1318765432   |         NULL  |
| 156 | 1318765432 |       NULL   |         NULL  |
+-----+------------+--------------+---------------+

बस उन पंक्तियों को फ़िल्टर करें जहां यह घटना होती है, WHEREनीचे की बाधा को जोड़ते हुए

WHERE
    i3.time IS NULL

Voilà, हमारे पास वह है जो हमें चाहिए!

+-----+------------+--------------+---------------+
| id  | time       | next_time    | intrudor_time |
+-----+------------+--------------+---------------+
| 155 | 1300000000 | 1311111111   |         NULL  |
| 155 | 1311111111 | 1322222222   |         NULL  |
| 155 | 1322222222 |       NULL   |         NULL  |
| 156 | 1312345678 | 1318765432   |         NULL  |
| 156 | 1318765432 |       NULL   |         NULL  |
+-----+------------+--------------+---------------+

मुझे उम्मीद है कि आपको अभी भी 4 साल बाद एक उत्तर की आवश्यकता है!


यह चालाकी है। मुझे यकीन नहीं है कि यह समझना आसान है। मुझे लगता है कि अगर हमने is nulli3 के साथ और जुड़ने की जगह ले ली where not exists (select 1 from itimes i3 where [same clause]), तो कोड अधिक बारीकी से प्रतिबिंबित करेगा कि हम क्या व्यक्त करना चाहते हैं।
एंड्रयू स्पेंसर

thx यार तुमने मेरा (अगला) दिन बचाया!
जैकब

2

समाधान पेश करने से पहले, मुझे ध्यान देना चाहिए कि यह सुंदर नहीं है। यह बहुत आसान होगा अगर AUTO_INCREMENTआपके टेबल पर कुछ कॉलम हो (क्या आप?)

SELECT 
  l.id, l.time, 
  SUBSTRING_INDEX(GROUP_CONCAT(r.time ORDER BY r.time), ',', 1)
FROM 
  idtimes AS l 
  LEFT JOIN idtimes AS r ON (l.id = r.id)
WHERE 
  l.time < r.time
GROUP BY
  l.id, l.time

स्पष्टीकरण:

  • उसी के रूप में आप के रूप में शामिल हों: दो तालिकाओं में शामिल हों, दाईं ओर केवल उच्च समय मिलता है
  • ग्रुप बाये टेबल से दोनों कॉलम: यह सुनिश्चित करता है कि हम सभी (id, time)संयोजनों को प्राप्त करें (जो कि अद्वितीय होने के लिए भी जाने जाते हैं)।
  • प्रत्येक के लिए (l.id, l.time), जो पहले r.time से अधिक है उसे प्राप्त करें l.time। यह पहली बार के r.timeमाध्यम से एस के आदेश के साथ होता है GROUP_CONCAT(r.time ORDER BY r.time), पहले टोकन के माध्यम से टुकड़ा करके SUBSTRING_INDEX

यदि यह तालिका बड़ी है तो सौभाग्य, और, अच्छे प्रदर्शन की उम्मीद न करें।


2

आप यह भी प्राप्त कर सकते हैं कि आप क्या चाहते हैं min()और GROUP BYकोई आंतरिक चयन नहीं है:

SELECT l.id, l.time, min(r.time) 
FROM idtimes l 
LEFT JOIN idtimes r on (r.id = l.id and r.time > l.time)
GROUP BY l.id, l.time;

मैं लगभग एक बड़ी राशि के लिए शर्त लगाता हूं कि आशावादी इसे उसी चीज़ में बदल देता है जैसा कि एरविन स्माउट के उत्तर में है, और यह बहस योग्य है कि क्या यह कोई स्पष्ट है, लेकिन यह पूर्णता के लिए है ...


1
इसके मूल्य के लिए, SSMS और SQLServer 2016 ने आपकी क्वेरी को Erwin's (2s रनटाइम बनाम 24s रनटाइम पर ~ 24k परिणाम सेट) की तुलना में बहुत अधिक पसंद किया
नाथन लॉफ़र्टी

एंड्रयू को ऐसा लगता है जैसे आपने बेट को खो दिया है :-)
इरविन स्मॉर्ट

दिलचस्प है, क्योंकि यह एक सामान्य मामला होना चाहिए कि एक सबक्वेरी जो पीके कॉलमों में से एक द्वारा बाहरी क्वेरी तालिका में वापस मिलती है, एक समूह द्वारा समान है। मुझे आश्चर्य है कि अगर कोई अन्य डेटाबेस इसे बेहतर तरीके से अनुकूलित करेगा। (मैं डेटाबेस ऑप्टिमाइज़र BTW के बारे में बहुत कम जानता हूँ; बस जिज्ञासु हो रहा हूँ।)
एंड्रयू स्पेंसर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.