2010-09-01 11 views
45

Tengo un cliente de mesa que almacena un customer_id, correo electrónico y referencia. Hay una tabla adicional customer_data que almacena un registro histórico de los cambios realizados al cliente, es decir, cuando se realiza un cambio, se inserta una nueva fila.MySQL ÚNASE a la fila más reciente solamente?

Para mostrar la información del cliente en una tabla, las dos tablas deben unirse, sin embargo, solo la última fila de customer_data debe unirse a la tabla del cliente.

Se vuelve un poco más complicado porque la consulta está paginada, por lo que tiene un límite y un desplazamiento.

¿Cómo puedo hacer esto con MySQL? Creo que estoy con ganas de poner un DISTINCT en alguna parte ...

La consulta en el minuto es así-

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 

Adicionalmente, estoy en lo cierto al pensar que puedo utilizar CONCAT con LIKE en ¿de esta manera?

(I apreciar que INNER JOIN podría ser el tipo incorrecto de JOIN para su uso. En realidad no tengo idea de cuál es la diferencia entre los diferentes uniones. Voy a mirar en eso ahora!)

+0

¿Cómo se ve la tabla de historial como cliente? ¿Cómo se determina la fila más reciente? ¿Hay un campo de marca de tiempo? –

+0

La más reciente es simplemente la última fila insertada, por lo que su clave principal es el número más alto. – bcmcfc

+0

¿Por qué no un gatillo? eche un vistazo a esta respuesta: http://stackoverflow.com/questions/26661314/best-and-optimal-way-to-join-max-value-from-other-table/26664982#26664982 –

Respuesta

76

Es posible que desee probar el siguiente:

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; 

Tenga en cuenta que una JOIN es sólo un sinónimo de INNER JOIN. caso

prueba:

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

Resultado (consulta sin la LIMIT y 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) 
+1

Gracias por el nivel de detalle que has estado allí. ¡Espero que ayude a otros tan solo como a mí! – bcmcfc

+6

A largo plazo, este enfoque podría crear problemas de rendimiento, ya que necesitaría crear una tabla temporal. Entonces, otra solución (si es posible) es agregar un nuevo campo booleano (is_last) en customer_data, que deberá actualizar cada vez que se agregue una nueva entrada. La última entrada tendrá is_last = 1, todas las demás para este cliente - is_last = 0. – cephuo

+0

La gente debería (por favor) también leer la siguiente respuesta (de Danny Coulombe), porque esta respuesta (lo siento Daniel) es terriblemente lenta con consultas más largas/más datos. Hice que mi página "esperara" 12 segundos para cargar; Por lo tanto, también compruebe https://stackoverflow.com/a/35965649/2776747. No me di cuenta hasta después de muchos otros cambios, así que tardé mucho en descubrirlo. – Art

0
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%' 

Creo que se necesita para cambiar c.customer_id a c.IDENTIFICACION

otra estructura de la tabla de actualización

+0

He votado en contra porque leí mal tu respuesta y al principio pensé que estaba mal. La celeridad es un mal consejero :-) – Wirone

0

es una buena idea de que el registro de datos reales en la tabla "customer_data". Con esta información puede seleccionar todos los datos de la tabla "customer_data" como desee.

10

Suponiendo que la columna de incremento automático en customer_data se llama Id, que puede hacer:

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 
7

Para cualquier persona que deba trabajar con un versión anterior de MySQL (pre-5.0 ish) no puede hacer subconsultas para este tipo de consulta. Aquí está la solución que pude hacer y me pareció que funcionaba muy bien.

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; 

Básicamente, esto es encontrar el id máximo de la tabla de datos de unirse al cliente, entonces unirse a la tabla de datos para la identificación del máximo encontrado. La razón de esto es porque seleccionar el máximo de un grupo no garantiza que el resto de los datos coincida con el ID a menos que lo vuelva a unir.

No he probado esto en versiones más nuevas de MySQL, pero funciona en 4.0.30.

+0

Esto es exquisito en su simplicidad. ¿Por qué es esta la primera vez que veo este enfoque? Tenga en cuenta que 'EXPLAIN' indica que esto utiliza una tabla temporal y filesort. Agregar 'ORDER BY NULL' al final elimina el filess. – Timo

+0

A mi pesar, mi propia y no tan hermosa solución es 3.5 veces más rápida para mis datos. Utilicé una subconsulta para seleccionar la tabla principal más los ID más recientes de las tablas combinadas, y luego una consulta externa que selecciona la subconsulta y lee los datos reales de las tablas combinadas. Estoy uniendo 5 tablas a la tabla principal y probando con una condición where que selecciona 1000 registros. Los índices son óptimos. – Timo

+0

Estaba usando su solución con 'SELECT *, MAX (firstData.id), MAX (secondData.id) [...]'. Lógicamente, al cambiar a 'SELECT main. *, FirstData2. *, SecondData2. *, MAX (firstData.id), MAX (secondData.id), [...]' Pude hacerlo mucho más rápido. Esto permite que las primeras combinaciones solo se lean desde el índice, en lugar de tener que leer también todos los datos del índice principal. Ahora la solución bonita solo requiere 1.9 veces más que la solución basada en subconsultas. – Timo

31

Si está trabajando con consultas pesadas, es mejor mover la solicitud de la última fila en la cláusula where. Es mucho más rápido y se ve más limpio.

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 
    ) 
+3

Wow Casi no estoy seguro de cuánta diferencia de rendimiento es esta. No estoy seguro de por qué eso era tan drástico aún, pero hasta ahora era mucho más rápido que se siente como si hubiera metido la pata en otro lado ... –

+1

Realmente me gustaría poder hacer +1 más de una vez para que se vea más. He probado esto bastante y de alguna manera hace que mis consultas sean prácticamente instantáneas (WorkBench literalmente dice 0.000 segundos, incluso con 'sql_no_cache set'), mientras que hacer la búsqueda en la unión tomó varios segundos para completarse. Todavía estoy desconcertado, pero quiero decir que no se puede discutir con resultados como ese. –

+0

No estoy seguro de por qué es más rápido, pero escuché que MySQL siempre comienza desde abajo. Por lo tanto, es probable que la consulta "SELECCIONAR MAX" simplemente continúe donde estaba y solo ejecute unas pocas líneas debido al índice de ID en lugar de repetirse cada vez que se coloca en un JOIN. –

0

También puede hacer esto

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; 
Cuestiones relacionadas