MySQL присоединяется только к самой последней строке?


У меня есть таблица customer, которая хранит customer_id, электронную почту и ссылку. Существует дополнительная таблица customer_data, в которой хранится историческая запись изменений, внесенных в клиента, т. е. когда есть изменение, вставляется новая строка.

для отображения информации о клиенте в таблице необходимо объединить две таблицы, однако к таблице customer следует присоединить только самую последнюю строку из customer_data.

он получает немного больше сложность в том, что запрос разбивается на страницы, поэтому имеет ограничение и смещение.

Как я могу сделать это с MySQL? Я думаю, что я хочу поставить отличного там где-то...

запрос в данный момент выглядит так -

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 с подобным образом?

(Я понимаю, что внутреннее соединение может быть неправильным типом соединения для использования. Я на самом деле понятия не имею, в чем разница между различными присоединяется. Я собираюсь разобраться в этом сейчас!)

9 61

9 ответов:

вы можете попробовать следующее:

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)

Если вы работаете с тяжелыми запросами, вам лучше переместить запрос на последнюю строку в предложении where. Это намного быстрее и выглядит чище.

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
   )

предполагая колонку автоинкремента в 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

для тех, кто должен работать с более старой версией MySQL (pre-5.0 ish), вы не можете выполнять подзапросы для этого типа запроса. Вот решение, которое я смог сделать, и оно, казалось, отлично работает.

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;

по существу это поиск максимального идентификатора вашей таблицы данных, соединяющей его с клиентом, а затем присоединение таблицы данных к максимальному идентификатору найдено. Причина этого заключается в том, что выбор max группы не гарантирует, что остальные данные совпадают с идентификатором, если только вы присоединяете его обратно к себе.

Я не тестировал это на более новых версиях MySQL, но он работает на 4.0.30.

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 to c.id

еще обновить структуру таблицы

я знаю, что этот вопрос стар, но он получил много внимания на протяжении многих лет, и я думаю, что ему не хватает концепции, которая может помочь кому-то в подобном случае. Я добавляю его здесь для полноты картины.

если вы не можете изменить исходную схему базы данных, то много хороших ответов были предоставлены и решить эту проблему просто отлично.

если вы можете, однако, изменить вашу схему, я бы посоветовал добавить поле в 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, это может быть очень легко управлять. 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;
    }
}

Это хорошая идея, что регистрация фактических данных в "customer_data" таблица. С помощью этих данных вы можете выбрать все данные из таблицы "customer_data", как вы хотите.

Это может помочь

как выбрать самый последний набор датированных записей из таблицы mysql

вы можете использовать подзапрос, чтобы получить самые последние записи, а затем присоединиться к своему клиенту.

вы также можете сделать это

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;