MySQL JOIN सबसे हाल ही में पंक्ति?


103

मेरे पास एक टेबल ग्राहक है जो एक customer_id, ईमेल और संदर्भ संग्रहीत करता है। एक अतिरिक्त तालिका customer_data है जो ग्राहक को किए गए परिवर्तनों का एक ऐतिहासिक रिकॉर्ड संग्रहीत करता है, अर्थात जब कोई परिवर्तन किया जाता है तो एक नई पंक्ति डाली जाती है।

किसी तालिका में ग्राहक की जानकारी प्रदर्शित करने के लिए, दो तालिकाओं को जोड़ने की आवश्यकता है, हालांकि ग्राहक_डेटा से केवल सबसे हाल की पंक्ति को ग्राहक तालिका में शामिल किया जाना चाहिए।

यह थोड़ा और अधिक जटिल हो जाता है कि क्वेरी पृष्ठबद्ध है, इसलिए इसकी एक सीमा और एक ऑफसेट है।

मैं MySQL के साथ यह कैसे कर सकता हूं? मुझे लगता है कि मैं वहां कहीं DISTINCT लगाना चाहता हूं ...

मिनट पर क्वेरी इस तरह है-

SELECT *, CONCAT(title,' ',forename,' ',surname) AS name
FROM customer c
INNER JOIN customer_data d on c.customer_id=d.customer_id
WHERE name LIKE '%Smith%' LIMIT 10, 20

अतिरिक्त, क्या मैं यह सोचकर सही हूं कि मैं CONCAT का उपयोग इस तरह से कर सकता हूं?

(मैं सराहना करता हूं कि INNER JOIN गलत प्रकार का JOIN हो सकता है। मेरे पास वास्तव में इस बात का कोई सुराग नहीं है कि विभिन्न JOINs में क्या अंतर है। मैं अब उस पर गौर करने जा रहा हूं!)


ग्राहक इतिहास तालिका कैसे दिखती है? सबसे हाल की पंक्ति कैसे निर्धारित की जाती है? क्या कोई टाइमस्टैम्प क्षेत्र है?
डैनियल वेसलो

सबसे हाल ही में डाली गई अंतिम पंक्ति है - इसलिए इसकी प्राथमिक कुंजी उच्चतम संख्या है।
bcmcfc

ट्रिगर क्यों नहीं? इस उत्तर पर एक नज़र डालें: stackoverflow.com/questions/26661314/…
रॉड्रिगो पोलो

अधिकांश / सभी उत्तर लाखों पंक्तियों के साथ बहुत लंबे समय तक ले रहे थे। बेहतर प्रदर्शन के साथ कुछ समाधान भी हैं ।
हालिल Hal ज़ुगर

जवाबों:


142

आप निम्नलिखित कोशिश करना चाहते हो सकता है:

SELECT    CONCAT(title, ' ', forename, ' ', surname) AS name
FROM      customer c
JOIN      (
              SELECT    MAX(id) max_id, customer_id 
              FROM      customer_data 
              GROUP BY  customer_id
          ) c_max ON (c_max.customer_id = c.customer_id)
JOIN      customer_data cd ON (cd.id = c_max.max_id)
WHERE     CONCAT(title, ' ', forename, ' ', surname) LIKE '%Smith%' 
LIMIT     10, 20;

ध्यान दें कि a JOINकेवल एक पर्यायवाची है INNER JOIN

परीक्षण का मामला:

CREATE TABLE customer (customer_id int);
CREATE TABLE customer_data (
   id int, 
   customer_id int, 
   title varchar(10),
   forename varchar(10),
   surname varchar(10)
);

INSERT INTO customer VALUES (1);
INSERT INTO customer VALUES (2);
INSERT INTO customer VALUES (3);

INSERT INTO customer_data VALUES (1, 1, 'Mr', 'Bobby', 'Smith');
INSERT INTO customer_data VALUES (2, 1, 'Mr', 'Bob', 'Smith');
INSERT INTO customer_data VALUES (3, 2, 'Mr', 'Jane', 'Green');
INSERT INTO customer_data VALUES (4, 2, 'Miss', 'Jane', 'Green');
INSERT INTO customer_data VALUES (5, 3, 'Dr', 'Jack', 'Black');

परिणाम ( LIMITऔर बिना क्वेरी WHERE):

SELECT    CONCAT(title, ' ', forename, ' ', surname) AS name
FROM      customer c
JOIN      (
              SELECT    MAX(id) max_id, customer_id 
              FROM      customer_data 
              GROUP BY  customer_id
          ) c_max ON (c_max.customer_id = c.customer_id)
JOIN      customer_data cd ON (cd.id = c_max.max_id);

+-----------------+
| name            |
+-----------------+
| Mr Bob Smith    |
| Miss Jane Green |
| Dr Jack Black   |
+-----------------+
3 rows in set (0.00 sec)

2
आपके द्वारा वहां गए विस्तार के स्तर के लिए धन्यवाद। मुझे आशा है कि यह दूसरों के साथ-साथ मुझे भी मदद करता है!
bcmcfc

21
लंबे समय में यह दृष्टिकोण प्रदर्शन की समस्याएं पैदा कर सकता है क्योंकि इसे एक अस्थायी तालिका बनाने की आवश्यकता होगी। तो एक और समाधान (यदि संभव हो तो) customer_data में एक नया बूलियन फ़ील्ड (is_last) जोड़ना है जिसे आपको हर बार एक नई प्रविष्टि जोड़ने के लिए अद्यतन करना होगा। अंतिम प्रविष्टि में is_last = 1 होगा, इस ग्राहक के लिए अन्य सभी - is_last = 0।
सेफू

5
लोगों को चाहिए (कृपया) निम्नलिखित उत्तर (डैनी कूलोम्बे से) पढ़ें, क्योंकि यह उत्तर (क्षमा करें डैनियल) लंबे प्रश्नों / अधिक डेटा के साथ बहुत धीमा है। लोड करने के लिए 12 सेकंड के लिए मेरा पृष्ठ "प्रतीक्षा करें"; तो कृपया stackoverflow.com/a/35965649/2776747 भी देखें । मैंने कई अन्य परिवर्तनों के बाद तक इसे नोटिस नहीं किया था इसलिए मुझे यह पता लगाने में बहुत लंबा समय लगा।
आर्ट

आपको पता नहीं है कि इसने मेरी कितनी मदद की है :)
थैंक्यू

104

यदि आप भारी प्रश्नों के साथ काम कर रहे हैं, तो आप बेहतर स्थिति में नवीनतम पंक्ति के लिए अनुरोध को स्थानांतरित करते हैं। यह बहुत तेज है और साफ दिखता है।

SELECT c.*,
FROM client AS c
LEFT JOIN client_calling_history AS cch ON cch.client_id = c.client_id
WHERE
   cch.cchid = (
      SELECT MAX(cchid)
      FROM client_calling_history
      WHERE client_id = c.client_id AND cal_event_id = c.cal_event_id
   )

4
वाह मैं लगभग अविश्वास में हूँ कि यह कितना अंतर है। यकीन नहीं होता कि यह इतना कठोर क्यों था, लेकिन अभी तक यह इतना तेज था कि ऐसा लगता है जैसे मैंने कहीं और गड़बड़ कर दी ...
ब्रायन लीशमैन

2
मैं वास्तव में चाहता हूं कि मैं इसे एक से अधिक बार +1 कर सकूं ताकि यह और अधिक दिखाई दे। मैंने इसे काफी परीक्षण किया है और किसी तरह यह मेरे प्रश्नों को लगभग तात्कालिक बना देता है (वर्कबैंक शाब्दिक 0.000 सेकंड कहता है, यहां तक ​​कि sql_no_cache set), जबकि जॉइन में खोज को पूरा करने में कई सेकंड लगे। अभी भी चकित है, लेकिन मेरा मतलब है कि आप इस तरह के परिणामों के साथ बहस नहीं कर सकते।
ब्रायन लीशमैन

1
आप सीधे पहले 2 तालिकाओं में शामिल हो रहे हैं और फिर WHERE के साथ फ़िल्टर कर रहे हैं। मुझे लगता है कि अगर आपके पास एक लाख ग्राहक हैं और लाखों कॉलिंग इतिहास हैं। क्योंकि SQL पहले 2 टेबल से जुड़ने की कोशिश करेगा और फिर सिंगल क्लाइंट को फ़िल्टर करेगा। मैं पहले उप-क्वेरी में पहले टेबल से क्लाइंट्स और संबंधित कॉलिंग इतिहास को फ़िल्टर करूंगा और फिर टेबल से जुड़ूंगा।
तारिक

1
मुझे लगता है कि "ca.client_id" और "ca.cal_event_id" दोनों के लिए "c" होना चाहिए।
हर्बर्ट वान-व्लिएट

1
मैं @NickCoons से सहमत हूं। पूर्ण मान वापस नहीं किए जाएंगे क्योंकि वे उस खंड से बाहर किए जाते हैं। आप NULL मानों को शामिल करने के बारे में कैसे जाएंगे और अभी भी इस क्वेरी के उत्कृष्ट प्रदर्शन को बनाए रखेंगे?
ananders77

10

customer_dataनाम के स्वनिर्धारित कॉलम Idको मानते हुए, आप यह कर सकते हैं:

SELECT CONCAT(title,' ',forename,' ',surname) AS name *
FROM customer c
    INNER JOIN customer_data d 
        ON c.customer_id=d.customer_id
WHERE name LIKE '%Smith%'
    AND d.ID = (
                Select Max(D2.Id)
                From customer_data As D2
                Where D2.customer_id = D.customer_id
                )
LIMIT 10, 20

9

किसी ऐसे व्यक्ति के लिए, जिसे MySQL (पूर्व 5.0 ईश) के पुराने संस्करण के साथ काम करना होगा, आप इस प्रकार की क्वेरी के लिए उप-प्रश्न करने में असमर्थ हैं। यहां वह समाधान है जो मैं करने में सक्षम था और यह बहुत अच्छा काम कर रहा था।

SELECT MAX(d.id), d2.*, CONCAT(title,' ',forename,' ',surname) AS name
FROM customer AS c 
LEFT JOIN customer_data as d ON c.customer_id=d.customer_id 
LEFT JOIN customer_data as d2 ON d.id=d2.id
WHERE CONCAT(title, ' ', forename, ' ', surname) LIKE '%Smith%'
GROUP BY c.customer_id LIMIT 10, 20;

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

मैंने MySQL के नए संस्करणों पर इसका परीक्षण नहीं किया है, लेकिन यह 4.0.30 पर काम करता है।


यह अपनी सादगी में उत्तम है। मैंने पहली बार ऐसा तरीका क्यों देखा है? ध्यान दें कि यह EXPLAINइंगित करता है कि यह एक अस्थायी तालिका और फ़ाइलों का उपयोग करता है। ORDER BY NULLफाइल के अंत में खरपतवार को जोड़ना ।
टिमो

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

मैं आपके समाधान का उपयोग कर रहा था SELECT *, MAX(firstData.id), MAX(secondData.id) [...]। तार्किक रूप से, SELECT main.*, firstData2.*, secondData2.*, MAX(firstData.id), MAX(secondData.id), [...]मैं बदलकर इसे काफी तेज बनाने में सक्षम था। इससे पहला इंडेक्स केवल इंडेक्स से पढ़ने की अनुमति देता है, बल्कि प्राथमिक इंडेक्स से सभी डेटा को पढ़ने की भी। अब उप-आधारित समाधान के रूप में सुंदर समाधान केवल 1.9 बार लेता है।
टिमो

यह MySQL 5.7 में अब काम नहीं करता है। अब d2। * समूह में पहली पंक्ति के लिए डेटा लौटाएगा, अंतिम नहीं। MAX (R1.id), R2 का चयन करें। * इनवॉइस I LEFT JOIN प्रतिक्रियाओं में R1 ON Iid = R1.invoice_id LEFT JOIN प्रतिक्रियाओं R2 ON R1.id = R2.id Group by I.LIMIT 0,10
Marco Marsala

5

मुझे पता है कि यह प्रश्न पुराना है, लेकिन यह वर्षों से बहुत अधिक ध्यान आकर्षित करता है और मुझे लगता है कि यह एक अवधारणा को याद कर रहा है जो किसी ऐसे ही मामले में किसी की मदद कर सकता है। मैं इसे यहाँ पूर्णता के लिए जोड़ रहा हूँ।

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

यदि आप , हालांकि, अपने स्कीमा को संशोधित कर सकते हैं , तो मैं आपकी customerतालिका में एक फ़ील्ड जोड़ने की सलाह दूंगा जो इस idके लिए नवीनतम customer_dataरिकॉर्ड रखता है :

CREATE TABLE customer (
  id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  current_data_id INT UNSIGNED NULL DEFAULT NULL
);

CREATE TABLE customer_data (
   id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
   customer_id INT UNSIGNED NOT NULL, 
   title VARCHAR(10) NOT NULL,
   forename VARCHAR(10) NOT NULL,
   surname VARCHAR(10) NOT NULL
);

ग्राहकों को छोड़कर

क्वेरी करना जितना आसान और तेज़ हो सकता है:

SELECT c.*, d.title, d.forename, d.surname
FROM customer c
INNER JOIN customer_data d on d.id = c.current_data_id
WHERE ...;

एक ग्राहक बनाते या अद्यतन करते समय दोष अतिरिक्त जटिलता है।

एक ग्राहक को अद्यतन करना

जब भी आप किसी ग्राहक को अपडेट करना चाहते हैं, तो आप customer_dataतालिका में एक नया रिकॉर्ड डालें , और customerरिकॉर्ड को अपडेट करें ।

INSERT INTO customer_data (customer_id, title, forename, surname) VALUES(2, 'Mr', 'John', 'Smith');
UPDATE customer SET current_data_id = LAST_INSERT_ID() WHERE id = 2;

ग्राहक बनाना

एक ग्राहक बनाना सिर्फ customerप्रविष्टि डालने की बात है , फिर वही बयान चलाना:

INSERT INTO customer () VALUES ();

SET @customer_id = LAST_INSERT_ID();
INSERT INTO customer_data (customer_id, title, forename, surname) VALUES(@customer_id, 'Mr', 'John', 'Smith');
UPDATE customer SET current_data_id = LAST_INSERT_ID() WHERE id = @customer_id;

समेट रहा हु

ग्राहक बनाने / अपडेट करने के लिए अतिरिक्त जटिलता शायद डरावनी हो, लेकिन इसे आसानी से ट्रिगर के साथ स्वचालित किया जा सकता है।

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

यहां बताया गया है कि आपका परिवर्तनशील Customerमॉडल कैसा दिखेगा:

class Customer
{
    private int id;
    private CustomerData currentData;

    public Customer(String title, String forename, String surname)
    {
        this.update(title, forename, surname);
    }

    public void update(String title, String forename, String surname)
    {
        this.currentData = new CustomerData(this, title, forename, surname);
    }

    public String getTitle()
    {
        return this.currentData.getTitle();
    }

    public String getForename()
    {
        return this.currentData.getForename();
    }

    public String getSurname()
    {
        return this.currentData.getSurname();
    }
}

और अपने अपरिवर्तनीय CustomerDataमॉडल, कि केवल टिककर खेल में शामिल हैं:

class CustomerData
{
    private int id;
    private Customer customer;
    private String title;
    private String forename;
    private String surname;

    public CustomerData(Customer customer, String title, String forename, String surname)
    {
        this.customer = customer;
        this.title    = title;
        this.forename = forename;
        this.surname  = surname;
    }

    public String getTitle()
    {
        return this.title;
    }

    public String getForename()
    {
        return this.forename;
    }

    public String getSurname()
    {
        return this.surname;
    }
}

मैंने बिना किसी उपश्रेणी के अपना वांछित परिणाम प्राप्त करने के लिए @ payne8 के समाधान (ऊपर) के साथ इस दृष्टिकोण को जोड़ा।
अदरक और लैवेंडर

2
SELECT CONCAT(title,' ',forename,' ',surname) AS name * FROM customer c 
INNER JOIN customer_data d on c.id=d.customer_id WHERE name LIKE '%Smith%' 

मुझे लगता है कि आपको c.ustomer_id को c.id में बदलना होगा

अपडेट तालिका संरचना


मैंने नीचा दिखाया क्योंकि मैंने आपके उत्तर को गलत बताया है और मैंने शुरू में सोचा कि यह गलत है। जल्दबाजी एक बुरा काउंसलर है :-)
Wirone

1

आप यह भी कर सकते हैं

SELECT    CONCAT(title, ' ', forename, ' ', surname) AS name
FROM      customer c
LEFT JOIN  (
              SELECT * FROM  customer_data ORDER BY id DESC
          ) customer_data ON (customer_data.customer_id = c.customer_id)
GROUP BY  c.customer_id          
WHERE     CONCAT(title, ' ', forename, ' ', surname) LIKE '%Smith%' 
LIMIT     10, 20;

0

यह एक अच्छा विचार है जो वास्तविक डेटा को " customer_data " तालिका में लॉग कर रहा है । इस डेटा के साथ आप अपनी इच्छानुसार "customer_data" तालिका से सभी डेटा का चयन कर सकते हैं।

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