简体   繁体   中英

mysql LEFT JOIN not acting like left join

Here's my problem. I have to get a list of questions and their answers in the same query. If the answer is older than 30 days, I want to get an empty reply, but I still want the question:

Im trying to do a left join, but if there is no match the question is still not showing up. You can see the schema and my select in this sqlfiddle and also down here:

http://sqlfiddle.com/#!9/a88184/1

SELECT p.*
     , r.nota
     , r.replied 
  FROM preguntas p 
  LEFT 
  JOIN respuestas r 
    ON p.id = r.id_pregunta 
   AND r.uuid_user ="f6912e4bb23130b9" 
 WHERE r.replied > DATE_SUB(NOW(),INTERVAL 30 DAY) 
   AND p.id_formulario = "1" 
   AND activo ="1" 
 ORDER 
    BY orden ASC

Schema

CREATE TABLE `preguntas` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`id_formulario` INT(11) NOT NULL,
`pregunta` TEXT NULL COLLATE 'utf8_spanish_ci',
`opcional` TINYINT(1) NOT NULL DEFAULT '0',
`activo` TINYINT(1) NOT NULL DEFAULT '0',
`orden` INT(11) NOT NULL,
PRIMARY KEY (`id`))

COLLATE='utf8_spanish_ci'
ENGINE=InnoDB
ROW_FORMAT=DYNAMIC
AUTO_INCREMENT=302
;

CREATE TABLE `respuestas` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`intento` INT(11) NOT NULL DEFAULT '1',
`id_formulario` INT(11) NOT NULL,
`id_pregunta` INT(11) NULL DEFAULT NULL,
`uuid_user` VARCHAR(50) NULL DEFAULT NULL COLLATE 'utf8_spanish_ci',
`nota` INT(11) NULL DEFAULT NULL,
`replied` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`))

COLLATE='utf8_spanish_ci'
ENGINE=InnoDB
ROW_FORMAT=DYNAMIC
AUTO_INCREMENT=1259;

INSERT INTO `preguntas` (`id`, `id_formulario`, `pregunta`, `opcional`, `activo`, `orden`) VALUES (126, 1, 'INICIATIVA PERSONAL', 0, 1, 1);
INSERT INTO `preguntas` (`id`, `id_formulario`, `pregunta`, `opcional`, `activo`, `orden`) VALUES (139, 1, 'TENER RAPIDEZ Y AGILIDAD', 0, 1, 5);

INSERT INTO `respuestas` (`id`, `intento`, `id_formulario`, `id_pregunta`, `uuid_user`, `nota`, `replied`) VALUES (174, 1, 1, 126, 'f6912e4bb23130b9', 4, '2019-05-23 18:08:15');
INSERT INTO `respuestas` (`id`, `intento`, `id_formulario`, `id_pregunta`, `uuid_user`, `nota`, `replied`) VALUES (175, 1, 1, 139, 'f6912e4bb23130b9', 4, '2019-04-03 18:08:15');

Current result:

id  id_formulario   pregunta    opcional    activo  orden   nota    replied
126 1   INICIATIVA PERSONAL false   true    1   4   2019-05-23T18:08:15Z

Expected result:

id  id_formulario   pregunta    opcional    activo  orden   nota    replied
126 1   INICIATIVA PERSONAL false   true    1   4   2019-05-23T18:08:15Z
139 1   TENER RAPIDEZ Y AGILIDAD    false   true    5   (empty) (empty)

Putting the left table's columns in where clause effectively turns the left join into an inner join .

To prevent that, Move the condition to join:

SELECT p.*, r.nota, r.replied FROM preguntas p 
LEFT JOIN respuestas r ON p.id = r.id_pregunta 
    AND r.uuid_user ="f6912e4bb23130b9" and r.replied > DATE_SUB(NOW(),INTERVAL 30 DAY) 
where p.id_formulario = "1" AND activo ="1" ORDER BY orden ASC

sqlfiddle

The reason you don't get the results you want is the WHERE clause.
Put the conditions in the ON clause:

SELECT p.*, r.nota, r.replied FROM preguntas p 
LEFT JOIN respuestas r 
ON p.id = r.id_pregunta AND r.uuid_user ="f6912e4bb23130b9" 
AND r.replied > DATE_SUB(NOW(),INTERVAL 30 DAY) 
AND p.id_formulario = "1" AND activo ="1" 
ORDER BY orden ASC

This condition:

r.replied > DATE_SUB(NOW(),INTERVAL 30 DAY)

removes the row from the results when it is placed in the WHERE clause, this is why you don't see it. By placing the condition in the ON clause of the join it is still there although there is no match in the other table.
For the other 2 conditions:

p.id_formulario = "1" AND activo ="1"

I'm not sure if you want them to reduce the results, so keep them in WHERE.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM