web-dev-qa-db-fra.com

MySQL JOIN la dernière ligne seulement?

J'ai un client de table qui stocke un customer_id, email et référence. Il existe une table supplémentaire customer_data qui stocke un enregistrement historique des modifications apportées au client, c'est-à-dire qu'en cas de modification, une nouvelle ligne est insérée.

Afin d'afficher les informations sur le client dans une table, les deux tables doivent être jointes. Toutefois, seule la dernière ligne de customer_data doit être jointe à la table client.

Cela devient un peu plus compliqué en ce que la requête est paginée, donc a une limite et un décalage.

Comment puis-je faire cela avec MySQL? Je pense que je veux mettre un DISTINCT quelque part là-bas ...

La requête à la minute est comme ceci-

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

De plus, ai-je raison de penser que je peux utiliser CONCAT avec LIKE de cette manière?

(J'admets qu'INNER JOIN pourrait ne pas être le bon type de JOIN. Je n'ai aucune idée de la différence entre les différents JOIN. Je vais me renseigner maintenant!)

64
bcmcfc

Vous voudrez peut-être essayer ce qui suit:

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;

Notez qu'une JOIN est juste un synonyme de INNER JOIN.

Cas de test:

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');

Résultat (requête sans les LIMIT et 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)
105
Daniel Vassallo

Si vous travaillez avec des requêtes lourdes, il vaut mieux déplacer la demande de la dernière ligne de la clause where. C'est beaucoup plus rapide et semble plus propre.

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
   )
62
Danny Coulombe

En supposant que la colonne auto-incrémentation dans customer_data s'appelle Id, vous pouvez faire:

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
10
Thomas

Pour tous ceux qui doivent travailler avec une ancienne version de MySQL (version 5.0 antérieure), vous ne pouvez pas effectuer de sous-requêtes pour ce type de requête. Voici la solution que j'ai pu faire et cela a semblé bien fonctionner.

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;

Il s’agit essentiellement de trouver l’ID max de votre table de données en le joignant au client, puis en joignant la table de données à l’ID max trouvé. La raison en est que sélectionner le maximum d'un groupe ne garantit pas que le reste des données correspond à l'identifiant, à moins que vous ne le rejoigniez.

Je n'ai pas testé cela sur les versions les plus récentes de MySQL, mais cela fonctionne avec la version 4.0.30.

9
payne8

Je sais que cette question est ancienne, mais elle a attiré beaucoup d’attention au fil des ans et je pense qu’il manque un concept qui pourrait aider une personne dans un cas similaire. Je l'ajoute ici par souci d'exhaustivité.

Si vous ne pouvez pas modifier votre schéma de base de données d'origine, beaucoup de bonnes réponses ont été fournies et résolvent parfaitement le problème.

Si vous pouvez , cependant, modifier votre schéma, je vous conseillerais d'ajouter un champ dans votre table customer qui contient la id du dernier enregistrement customer_data de ce client:

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
);

Interroger les clients

L'interrogation est aussi simple et rapide que possible:

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

L'inconvénient est la complexité supplémentaire lors de la création ou de la mise à jour d'un client.

Mise à jour d'un client

Chaque fois que vous souhaitez mettre à jour un client, vous insérez un nouvel enregistrement dans la table customer_data et mettez à jour l'enregistrement 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;

Créer un client

La création d'un client consiste simplement à insérer l'entrée customer, puis à exécuter les mêmes instructions:

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;

Emballer

La complexité supplémentaire liée à la création/mise à jour d'un client peut être redoutable, mais elle peut facilement être automatisée à l'aide de déclencheurs.

Enfin, si vous utilisez un ORM, cela peut être très facile à gérer. L'ORM peut se charger d'insérer les valeurs, de mettre à jour les identifiants et de joindre les deux tables automatiquement pour vous.

Voici à quoi ressemblerait votre modèle mutable 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();
    }
}

Et votre modèle CustomerData immuable, qui ne contient que des accesseurs:

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;
    }
}
2
Benjamin
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%' 

je pense que vous devez changer c.customer_id à c.id

sinon mettre à jour la structure de la table

2
Pramendra Gupta

Vous pouvez aussi faire ça

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
Ajay Kumar

Il est judicieux de consigner les données réelles dans la table " customer_data ". Avec ces données, vous pouvez sélectionner toutes les données de la table "customer_data" à votre guise.

0
Burçin

Cela pourrait aider

Comment sélectionner le dernier jeu d'enregistrements datés d'une table mysql

Vous pouvez utiliser une sous-requête pour obtenir le ou les enregistrements les plus récents, puis les joindre à votre client.

0
Jaydee