Привожу sql дамп, чтобы показать на примере, предлагаю дальше обсуждение продолжать под данным коментом чтобы не прыгать по другим.
/*
MySQL Backup
Source Server Version: 5.5.41
Source Database: testtt
Date: 01.09.2016 23:02:55
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for `A`
-- ----------------------------
DROP TABLE IF EXISTS `A`;
CREATE TABLE `A` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`field1` int(11) DEFAULT NULL,
`field2` int(11) DEFAULT NULL,
`field3` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for `B`
-- ----------------------------
DROP TABLE IF EXISTS `B`;
CREATE TABLE `B` (
`id` int(11) NOT NULL,
`a_id` int(11) NOT NULL,
`price` int(11) DEFAULT NULL,
`type` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fk_a` (`a_id`),
CONSTRAINT `fk_a` FOREIGN KEY (`a_id`) REFERENCES `A` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records
-- ----------------------------
INSERT INTO `A` VALUES ('1','1','2','3'), ('2','1','2','3'), ('3','1','2','3'), ('4','1','2','3');
INSERT INTO `B` VALUES ('0','1','25','1'), ('1','1','20','0'), ('2','2','45','1'), ('3','2','55','0'), ('4','3','25','1'), ('5','3','59','0');
Скину заодно скрины исходных данных
Таблица А
Таблица B
Задача: Наиболее оптимальными средствами получить объект A, которому соответствует следующие условие, у объекта А дочерний элемент из B с типом 0 price=20, а с типом 1 price=25;
Запрос OR
SELECT `A`.* FROM A LEFT JOIN B ON `A`.`id` = `B`.`a_id` WHERE (B.price=25 AND type=1) OR (B.price=20 AND type=0);
Результат:
Как видми мы получаем 3 объекта первые повторяющиеся и если их сгрупировать они соотвектсвуют условию, но в выборку попал третий не подходящий нам т.к. условие сравнение OR
Запрос AND
SELECT `A`.* FROM A LEFT JOIN B ON `A`.`id` = `B`.`a_id` WHERE (B.price=25 AND type=1) AND (B.price=20 AND type=0);
Результата не даст, т.к. противоречие в условии
Запрос UNION аналогичен OR на сколько я помню, сейчас его не приведу
Моё решение:
Через подзапрос
SELECT `subquery`.* FROM (SELECT `A`.* FROM A LEFT JOIN B ON `A`.`id` = `B`.`a_id` WHERE (B.price=25 AND type=1)) subquery LEFT JOIN B ON `subquery`.`id` = `B`.`a_id` WHERE (B.price=20 AND type=0);
Получаем нужный результат
Т.к. данные весьма объёмные и в большом количестве но т.к. их в данный момент около милиона работает достаточно шустро с подзапросом порядка 1.8с, но вопрос об правильном решение сейчас на будущее, на сколько сложен подзапрос учитывая то, что в реальных данных более 3х left join в том числе через has_many
Т.е. может быть всётаки разнести дочерние элементы таблицы B по разным таблицам, чтобы избавиться от подзапроса.