@Kekoc

Как организовать связь «родительской» таблицы с 2мя одинаковыми наборами данных в «дочерних»?

Здравствуйте.
Есть таблица А на неё ссылаются две записи из таблицы B.
Рис.bd159d435ffe44519187bf02d7154a16.jpg
Т.е. если по логике приложения , объект А состоит из 2х объектов В
И также происходит поиск, трудности когда необходимо в одном запросе искать по двум объектам, решил под запросом по первому объекту и доп. выборка по нему поиском по соответсвия условиям второму объекту.
Дело в том что mysql не знает, о том что они логически разделены на два РАЗНЫХ объекта для него это просто два одинаковых объекта с различными данными.

Т.к. данные полностью идентичные , то были размещены в одной таблицы в разных полях, на каждый из них ещё ссылаться дополнительная таблица с данными.

Возможно необходимо организовать хранение как-то иначе чтобы облегчить алгоритм поиска ?
  • Вопрос задан
  • 1197 просмотров
Пригласить эксперта
Ответы на вопрос 5
Decadal
@Decadal
Сделайте промежуточную таблицу с ключами id_a, id_b1, id_b2. У вас появится сущность, которая описывает сложную связь между А и B, только учтите симметричность id_b1 id_b2
Ответ написан
bitver
@bitver
Структура у вас нормальная, если учитывать, что в таблице В есть столбец оптсывающий тип.
Есть другой вариант это все-же сделать 2 разные таблицы, так как для разных сущностей одну таблицу делать это бред, хоть и типы данных у них совпадают.
Ответ написан
copist
@copist
Empower people to give
Не очень понятно слово "связь". Ты подразумеваешь понятие "отношение" реляционной модели данных? (wikipedia)

CREATE TABLE `A` (
  `ID` INT(11) NOT NULL AUTO_INCREMENT,
   PRIMARY KEY (`ID`)
);

CREATE TABLE `B` (
  `ID` INT(11) NOT NULL AUTO_INCREMENT,
  `A_ID` INT(11) NOT NULL,
  PRIMARY KEY (`ID`)
);

-- связь между дочерними записями и родительской записью
ALTER TABLE `B` ADD CONSTRAINT `B_A_ID` FOREIGN KEY `B` (`A_ID`) REFERENCES `A` (`ID`);


А то, что у тебя должно быть не больше двух записей B, ссылающихся на A.ID - это средствами MySQL не решается. Можно было бы триггерами решить, но нельзя в триггере обращаться к записям той же таблицы.
Поэтому это решают внешними средствами, в прикладном приложении.
Ответ написан
webinar
@webinar Куратор тега Yii
Учим yii: https://youtu.be/-WRMlGHLgRg
1. можно использовать join
2. Зачем Вам 2 одинаковые таблицы - ошибка именно тут. Перенесите все в 1 и в ней добавьте поле которое будет различать данные
Ответ написан
@Kekoc Автор вопроса
Привожу 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');

Скину заодно скрины исходных данных
Таблица А
af4cd7b449c84dd7b1502ed0fab559ec
Таблица B
fc3bbb8c4ba347fd8942fbebb13472d7

Задача: Наиболее оптимальными средствами получить объект 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);
Результат:
429c0640227543e6acd6bf78f1beb77d.PNG
Как видми мы получаем 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);
Получаем нужный результат
642ee9b134434d3e9fd6c00fb44eabcb

Т.к. данные весьма объёмные и в большом количестве но т.к. их в данный момент около милиона работает достаточно шустро с подзапросом порядка 1.8с, но вопрос об правильном решение сейчас на будущее, на сколько сложен подзапрос учитывая то, что в реальных данных более 3х left join в том числе через has_many

Т.е. может быть всётаки разнести дочерние элементы таблицы B по разным таблицам, чтобы избавиться от подзапроса.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы