মাইএসকিউএল কেবলমাত্র সাম্প্রতিক সারিতে যোগ দেবেন?


106

আমার কাছে একটি টেবিল গ্রাহক রয়েছে যা গ্রাহক_আইডি, ইমেল এবং রেফারেন্স সঞ্চয় করে। গ্রাহক-ডেটাতে একটি অতিরিক্ত টেবিল রয়েছে যা গ্রাহকের পরিবর্তনের historicalতিহাসিক রেকর্ড সংরক্ষণ করে, অর্থাত্‍ যখন পরিবর্তন আসে তখন একটি নতুন সারি .োকানো হয়।

কোনও টেবিলে গ্রাহকের তথ্য প্রদর্শন করতে, দুটি টেবিলের সাথে যুক্ত হওয়া দরকার, তবে কেবলমাত্র গ্রাহক_ডাটা থেকে সাম্প্রতিক সারিটি গ্রাহক টেবিলে যুক্ত হতে হবে।

এটি আরও কিছুটা জটিল হয়ে ওঠে যে ক্যোয়ারী পৃষ্ঠাবদ্ধ রয়েছে, সুতরাং এর একটি সীমা এবং অফসেট রয়েছে।

আমি কীভাবে মাইএসকিউএল দিয়ে এটি করতে পারি? আমি মনে করি আমি সেখানে কোথাও একটি 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

সংযোজনী, আমি কি এই ভেবে সঠিকভাবে কনক্যাটটি লাইক দিয়ে ব্যবহার করতে পারি?

(আমি প্রশংসা করি যে অন্তর্ভুক্ত হতে পারে ব্যবহারের জন্য জিনের ভুল প্রকার হতে পারে the বিভিন্ন জিনদের মধ্যে পার্থক্য কী তা আসলে আমার কোনও ধারণা নেই I'm আমি এখন এটি সন্ধান করব!)


গ্রাহকের ইতিহাসের টেবিলটি কেমন দেখাচ্ছে? সাম্প্রতিক সারিটি কীভাবে নির্ধারণ করা হয়? একটি টাইমস্ট্যাম্প ক্ষেত্র আছে?
ড্যানিয়েল ভ্যাসালো 14

সর্বাধিক সাম্প্রতিকতমটি কেবল সর্বশেষ সারিটি sertedোকানো হয়েছে - সুতরাং এটির প্রাথমিক কীটি সর্বাধিক সংখ্যা।
বিসিএমসিএফসি

ট্রিগার না কেন? এই উত্তরটি একবার দেখুন: stackoverflow.com/questions/26661314/…
রদ্রিগো পোলো

বেশিরভাগ / সমস্ত উত্তর লক্ষ লক্ষ সারি নিয়ে খুব বেশি সময় নিচ্ছিল। আরও ভাল পারফরম্যান্স সহ কয়েকটি সমাধান রয়েছে।
হালিল Özgür

উত্তর:


150

আপনি নিম্নলিখিত চেষ্টা করতে পারেন:

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;

মনে রাখবেন যে একটি 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)

4
আপনি যে স্তরে গেছেন সেই স্তরের জন্য ধন্যবাদ আমি আশা করি এটি আমার পাশাপাশি অন্যদেরও সহায়তা করে!
বিসিএমসিএফসি

21
দীর্ঘমেয়াদে এই পদ্ধতির কার্য সম্পাদনের সমস্যা তৈরি হতে পারে কারণ এটি একটি অস্থায়ী সারণী তৈরি করতে হবে। সুতরাং আর একটি সমাধান (সম্ভব হলে) হ'ল গ্রাহক_ডাটাতে একটি নতুন বুলিয়ান ক্ষেত্র (is_last) যুক্ত করা যা আপনাকে প্রতিবার একটি নতুন এন্ট্রি যুক্ত হওয়ার পরে আপডেট করতে হবে। সর্বশেষ এন্ট্রিটি হ'ল_স্টাস্ট = 1, এই গ্রাহকের জন্য সমস্ত অন্যান্য - is_last = 0।
সেফুও

4
লোকেরাও (দয়া করে) নিম্নলিখিত উত্তরগুলি পড়তে হবে (ড্যানি কলোম্বের কাছ থেকে), কারণ এই উত্তরটি (দুঃখিত ড্যানিয়েল) দীর্ঘ প্রশ্নগুলি / আরও ডেটা সহ ভয়ঙ্কর ধীর। আমার পৃষ্ঠাটি লোড হওয়ার জন্য 12 সেকেন্ডের জন্য "অপেক্ষা" করুন; সুতরাং দয়া করে স্ট্যাকওভারফ্লো . com/a/35965649/2776747 দেখুন । অন্যান্য অনেক পরিবর্তন হওয়ার পরেও আমি এটি লক্ষ্য করি নি তাই এটি জানতে আমার খুব বেশি সময় লেগেছে।
আর্ট

এটি আমাকে কতটা সাহায্য করেছে আপনার কোনও ধারণা নেই :) আপনাকে ধন্যবাদ মাস্টার
নোড_ম্যান

109

আপনি যদি ভারী ক্যোয়ারী নিয়ে কাজ করছেন, তবে আপনি ক্লজটির সর্বশেষতম সারির জন্য অনুরোধটি আরও ভালভাবে সরিয়ে ফেলুন। এটি অনেক দ্রুত এবং পরিষ্কার দেখাচ্ছে।

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
বাহ আমি পারফরম্যান্সের পার্থক্য কতটা তা প্রায় অবিশ্বাসের মধ্যে। এটি এখনও এত কঠোর কেন তা নিশ্চিত নয় তবে এতদূর এত দ্রুত হয়েছিল যে মনে হচ্ছে আমি অন্য কোথাও গোলযোগ পেয়েছি ...
ব্রায়ান লেশম্যান

4
আমি সত্যিই চাই আমি আরও একবার এটি +1 করতে পারতাম যাতে এটি আরও দেখা যায়। আমি এটি বেশ খানিকটা পরীক্ষা করেছি এবং একরকমভাবে এটি আমার জিজ্ঞাসাগুলিকে কার্যত তাত্ক্ষণিক করে তোলে (ওয়ার্কবেঞ্চ আক্ষরিক অর্থে 0.000 সেকেন্ড বলে, এমনকি sql_no_cache set), যোগটিতে অনুসন্ধান শেষ করতে একাধিক সেকেন্ড সময় নিয়েছে। তবুও হতবাক, তবে আমি বলতে চাইছি আপনি এর মতো ফলাফল নিয়ে তর্ক করতে পারবেন না।
ব্রায়ান

4
আপনি প্রথমে সরাসরি 2 টেবিলগুলিতে যোগদান করছেন এবং তারপরে WHERE দিয়ে ফিল্টার করছেন। আমি মনে করি যদি আপনার কাছে এক মিলিয়ন ক্লায়েন্ট এবং কয়েক মিলিয়ন কলিং ইতিহাস রয়েছে it কারণ এসকিউএল প্রথমে 2 টি টেবিলগুলিতে যোগদান করার চেষ্টা করবে এবং তারপরে একক ক্লায়েন্টে ফিল্টার করবে। আমি বরং সাব-কোয়েরিতে প্রথমে টেবিলগুলি থেকে ক্লায়েন্ট এবং সম্পর্কিত কলিংয়ের ইতিহাসগুলি ফিল্টার করব এবং তারপরে টেবিলগুলিতে যোগদান করব।
তারিক

4
আমি মনে করি "ca.client_id" এবং "ca.cal_event_id" উভয়ের জন্য অবশ্যই "সি" হওয়া উচিত।
হারবার্ট ভ্যান-ভিলেট

4
আমি @ নিককুনসের সাথে একমত নাল মানগুলি ফেরত দেওয়া হবে না কারণ সেগুলি যেখানে ক্লজ দ্বারা বাদ দেওয়া হয়েছে। আপনি কীভাবে NULL মানগুলি অন্তর্ভুক্ত করবেন এবং এখনও এই কোয়েরির দুর্দান্ত পারফরম্যান্স রাখবেন?
aanders77

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

যে কাউকে অবশ্যই মাইএসকিউএল এর পূর্ববর্তী সংস্করণ (প্রাক-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;

মূলত এটি আপনার ডেটা টেবিলের গ্রাহকের সাথে যোগ দেওয়ার পরে সর্বাধিক আইডিতে ডেটা টেবিলটিতে যোগদানের সর্বাধিক আইডি সন্ধান করছে। এর কারণ হ'ল কোনও গ্রুপের সর্বাধিক নির্বাচন করা গ্যারান্টি দেয় না যে আপনি যদি নিজের সাথে আবার যোগ না করেন তবে বাকী ডেটা আইডির সাথে মেলে।

আমি এটি মাইএসকিউএল এর নতুন সংস্করণে পরীক্ষা করেছি না তবে এটি 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 এ আর কাজ করে না। এখন ডি 2। * গ্রুপের প্রথম সারির জন্য ডেটা ফিরিয়ে দেবে, শেষ নয়। । নির্বাচন MAX টি (R1.id), R2 হলো * চালানে থেকে আমি বাম JOIN প্রতিক্রিয়া R1 অন I.id = R1.invoice_id বাম JOIN প্রতিক্রিয়া R2 হলো অন R1.id = R2.id গ্রুপ I.id সীমাবদ্ধ করে 0,10
মার্কো Marsala

6

আমি জানি এই প্রশ্নটি পুরানো, তবে বছরের পর বছর ধরে এটি অনেক বেশি মনোযোগ পেয়েছে এবং আমি মনে করি এটির একটি ধারণা হারিয়েছে যা কাউকে অনুরূপ ক্ষেত্রে সহায়তা করতে পারে। আমি এটি এখানে সম্পূর্ণতার জন্য যুক্ত করছি।

আপনি যদি আপনার মূল ডাটাবেস স্কিমাটি পরিবর্তন করতে না পারেন, তবে অনেক ভাল উত্তর সরবরাহ করা হয়েছে এবং সমস্যাটি ঠিক ঠিক সমাধান করা হবে।

আপনি যদি নিজের স্কিমাটি পরিবর্তন করতে পারেন তবে আমি আপনার 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;

মোড়ক উম্মচন

গ্রাহক তৈরি / আপডেট করার অতিরিক্ত জটিলতা ভীতিজনক হতে পারে তবে এটি ট্রিগারগুলির সাথে সহজেই স্বয়ংক্রিয়ভাবে তৈরি করা যেতে পারে।

অবশেষে, আপনি যদি একটি ওআরএম ব্যবহার করেন তবে এটি পরিচালনা করা সত্যিই সহজ হতে পারে। ওআরএম আপনার জন্য মানগুলি সন্নিবেশ করা, আইডিকে আপডেট করতে এবং দুটি টেবিলে স্বয়ংক্রিয়ভাবে যোগদানের যত্ন নিতে পারে।

আপনার পরিবর্তনযোগ্য 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;
    }
}

আমি কোন উপকার ছাড়াই আমার কাঙ্ক্ষিত ফলাফল পেতে @ পেইন 8 এর সমাধান (উপরে) এর সাথে এই পদ্ধতির সমন্বয় করেছি।
আদা এবং ল্যাভেন্ডার

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.customer_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

" গ্রাহক_ডাটা " সারণিতে প্রকৃত ডেটা লগইন করা ভাল ধারণা । এই ডেটা দিয়ে আপনি "গ্রাহক_ডাটা" টেবিল থেকে আপনার ইচ্ছামত সমস্ত ডেটা নির্বাচন করতে পারেন।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.