Руководство по связи «многие ко многим»
Содержание:
- Типы соединения
- Что такое связи между таблицами?
- Создание и изменение отношений 1:N между сущностями
- Как определить связи между таблицами
- Отношение многие ко многим (Many to Many)
- Связь один ко многим
- LEFT JOIN
- INNER JOIN
- Элемент области навигации для основной сущности
- Один-ко-многим или многие-к-одному
- Извлечение данных для связи «многие ко многим» (SELECT)
- Тестирование связи «многие ко многим»
- Ассоциация ManyToOne / OneToMany.
Типы соединения
Существует три основных типа соединения: Вы можете увидеть их на следующем снимке экрана:
Вариант 1 определяет внутреннее соединение. Внутреннее соединение — это соединение, в котором записи из двух таблиц объединяются в результатах запроса только в том случае, если значения в объединенных полях соответствуют определенному состоянию. В запросе соединение по умолчанию — это внутреннее соединение, которое выбирает записи только в том случае, если значения в объединенных полях совпадают.
Вариант 2 определяет левое внешнее соединение. Левое внешнее соединение — это соединение, в котором все записи с левой стороны операции LEFT JOIN в оператора запроса SQL добавляются к результатам запроса, даже если нет соответствующих значений в объединенном поле из таблицы на правой стороне.
Вариант 3 определяет правое внешнее соединение. Правое внешнее соединение — это соединение, в котором все записи с правой стороны операции RIGHT JOIN в операторе запроса SQL добавляются к результатам запроса, даже если нет соответствующих значений в объединенном поле из таблицы на левой стороне.
Что такое связи между таблицами?
Лучшим решением является хранение информации издателя только один раз, в отдельной таблице, которую мы будем называть «Издатели». Затем вы поместите указатель в таблице «Названия», которая ссылается на запись в таблице «Издатели».
Чтобы убедиться, что данные остаются синхронизированными, можно обеспечить целостность данных между таблицами. Отношения целостности данных помогают убедиться, что информация в одной таблице соответствует информации в другой. Например, каждое название в таблице «Названия» должно быть связано с конкретным издателем в таблице «Издатели». Название не может быть добавлено в базу данных для издателя, которого не существует в базе данных.
Логические связи в базе данных позволяют эффективно запрашивать данные и создавать отчеты.
Создание и изменение отношений 1:N между сущностями
Откройте обозреватель решений.
В разделе Компоненты раскройте узел Сущности, затем раскройте сущность, с которой требуется работать.
Выберите Отношения 1:N.
Чтобы изменить отношение или просмотреть сведения для отношения, выберите отношение и нажмите на панели инструментов «Действия» кнопку Другие действия, затем выберите Изменить.
— ИЛИ —
Чтобы добавить новое отношение, выберите Создать отношение «один ко многим».
Важно!
Если кнопка Создать отношение «один ко многим» не отображается на панели инструментов «Действия», то создать отношение 1:N для этой сущности невозможно.
Для нового отношения в разделе Определение отношения выберите в списке Связанная сущность сущность для связывания.
Примечание
При указании связанной сущности задается значение по умолчанию в поле Имя. Если изменить связанную сущность перед ее сохранением, соответственно изменится и значение поля Имя.
Выберите, будет ли это поле доступно для поиска или нет.
В разделе Поле поиска укажите значение для поля в поле Отображаемое имя.
Важно!
При указании значения Отображаемое имя задается значение по умолчанию в поле Имя
Если изменить Отображаемое имя поля поиска перед сохранением данных, значение в поле Имя не изменится. Поэтому необходимо ввести в поле Имя информативное значение перед сохранением данных.
В списке Требование поля выберите вариант, чтобы указать требования к данным для поля перед сохранением записи.
В разделе Элемент области переходов для основной сущности в списке Параметры отображения выберите вариант отображения связанных представлений для пользовательской метки.
В разделе Поведение отношений выберите в списке Тип отношений один из следующих вариантов.
Родительское. В родительском отношении между двумя сущностями любое действие, выполняемое над записью основной (родительской) сущности, также выполняется над всеми связанными с ней записями дочерних сущностей.
Ссылочное. При ссылочном отношении между двумя сущностями можно переходить к любым связанным записям, но действия, выполняемые над одной записью, не применяются к другим.
Ссылочное с ограниченным удалением. В ссылоном отношении с ограничением удаления можно переходить к любым связанным записям. Действия, выполняемые над родительской записью, не будут выполняться над дочерней, но пока она существует, удалить родительскую запись будет невозможно. Учтите, что запись нельзя удалить, если имеются связанные с ней записи.
Настраиваемое каскадное. В настраиваемом каскадном отношении между двумя сущностями выбирается поведение, связанное с каждым из наборов возможных действий.
Важно!
Если выбрать поведения для действий, совпадающие с поведениями для действий, связанными с другим Типом поведения, то при сохранении отношения значение Тип поведения будет автоматически установлено равным такому совпадающему типу.
Дополнительные сведения:
-
Выберите Сохранить и закрыть, чтобы закрыть форму Отношение.
-
Выполнив настройки, опубликуйте их:
-
Чтобы опубликовать настройки только для компонента, изменяемого в данный момент, на панели инструментов «Действия» выберите Опубликовать.
-
Чтобы опубликовать настройки для всех неопубликованных компонентов одновременно, на панели навигации или в области переходов выберите Сущности, затем на панели инструментов «Действия» выберите Опубликовать все настройки.
-
Примечание
- Настраиваемая сущность не может быть основной в каскадном отношении со связанной системной сущностью. Это означает, что между основной настраиваемой сущностью и связанной системной сущностью не может быть отношений с каким-либо из действий, установленным в «Передавать всем», «Передавать активным» или «Передавать владельцу».
- У новых отношений действие не может иметь значение Передавать всем, Передавать активным или Передавать владельцу, если связанная сущность в этом отношении уже является связанной сущностью в любом другом отношении, действие которого имеет значение Передавать всем, Передавать активным или Передавать владельцу. Это позволяет избежать создания отношений с несколькими родительскими сущностями.
- После каждого изменения элементов пользовательского интерфейса или внедрения скриптов формы для сущности необходима публикация изменений. Все изменения в схеме данных приложения, таких как настраиваемые сущности, связи или поля, применяются сразу.
- Если отношение является частью управляемого решения, разработчик решения может ограничить настройку отношения пользователями.
- Установка решения или публикация настроек может помешать нормальной работе системы. Рекомендуется запланировать импорт решения в оптимальный для пользователей период.
Как определить связи между таблицами
При создании связи между таблицами связанные поля не должны иметь одни и те же имена. Однако связанные поля должны иметь один и тот же тип данных, если только поле первичного ключа не является полем AutoNumber. Вы можете сопоставить поле AutoNumber с полем Number, только если свойство FieldSize обоих совпадающих полей совпадает. Например, можно сопоставить поле AutoNumber и поле Number, если свойство theFieldSizeproperty обоих полей имеет значение Long Integer. Даже если оба совпадающих поля являются числовыми полями, они должны иметь параметр sameFieldSizeproperty.
Как определить связи «один ко многим» или «один к одному»
Чтобы создать связь «один ко многим» или «один к одному», выполните следующие действия.
-
Закройте все таблицы. Нельзя создавать или изменять связи между открытыми таблицами.
-
В Access 2002 и Access 2003 выполните следующие действия.
- Нажмите F11, чтобы переключиться в окно базы данных.
- В меню Инструменты выберите Связи.
В Access 2007, Access 2010 или Access 2013 нажмите Связи в группе Показать/Скрыть на вкладке Инструменты базы данных.
-
Если вы еще не определили какие-либо связи в базе данных, автоматически отобразится диалоговое окно Показать таблицу. Если вы хотите добавить таблицы, которые нужно связать, но диалоговое окно Добавление таблицы не отображается, нажмите Добавить таблицу в меню Связи.
-
Дважды щелкните имена таблиц, которые необходимо связать, а затем закройте диалоговое окно Добавление таблицы. Чтобы создать связь между одной и той же таблицей, добавьте эту таблицу два раза.
-
Перетащите поле, которое вы хотите связать, из одной таблицы в связанное поле в другой таблице. Чтобы перетащить несколько полей, нажмите Ctrl, нажмите на каждое поле, а затем перетащите их.
В большинстве случаев вы перетаскиваете поле первичного ключа (это поле отображается жирным текстом) из одной таблицы в аналогичное поле (это поле часто имеет одно и то же имя), которое называется внешним ключом в другой таблице.
-
Откроется диалоговое окно Изменение связей. Убедитесь, что имена полей, отображаемые в двух столбцах, верны. Вы можете изменить имена, если это необходимо.
При необходимости установите параметры связей. Если вам нужна информация о конкретном элементе в диалоговом окне Изменение связей, нажмите кнопку со знаком вопроса, а затем щелкните элемент. (Эти параметры будут подробно описаны ниже в этой статье.)
-
Нажмите кнопку Создать, чтобы создать связь.
-
Повторите шаги с 4 по 7 для каждой пары таблиц, которые вы хотите связать.
При закрытии диалогового окна Изменение связей Access спрашивает, хотите ли вы сохранить макет. Сохраняете ли вы макет или не сохраняете макет, созданные вами связи сохраняются в базе данных.
Примечание
Можно создавать связи не только в таблицах, но и в запросах. Однако целостность данных связывания не обеспечивается с помощью запросов.
Как определить связь «многие ко многим»
Чтобы создать связь «многие ко многим», выполните следующие действия.
-
Создайте две таблицы, которые будут иметь связь «многие ко многим».
-
Создайте третью таблицу. Это стыковочная таблица. В таблице соединения добавьте новые поля, которые имеют те же определения, что и основные ключевые поля из каждой таблицы, созданной в шаге 1. В связующей таблице основные ключевые поля функционируют как внешние ключи. Вы можете добавить другие поля в связующую таблицу, так же, как и в любую другую таблицу.
-
В связующей таблице установите первичный ключ, чтобы включить основные ключевые поля из двух других таблиц. Например, в связующей таблице «TitleAuthors» первичный ключ будет состоять из полей OrderID и ProductID.
Примечание
Чтобы создать первичный ключ, выполните следующие действия:
-
Откройте таблицу в Конструкторе.
-
Выберите поле или поля, которые вы хотите определить в качестве первичного ключа. Чтобы выбрать одно поле, нажмите на селектор строки для нужного поля. Чтобы выбрать несколько полей, удерживайте клавишу Ctrl, а затем нажмите селектор строки для каждого поля.
-
В Access 2002 или в Access 2003 нажмите на Первичный ключ на панели инструментов.
В Access 2007 нажмите на Первичный ключ в группе Инструменты на вкладке Дизайн.
Примечание
Если вы хотите, чтобы порядок полей в первичном ключе с несколькими полями отличался от порядка этих полей в таблице, нажмите Индексы на панели инструментов для отображения диалогового окна Indexes, а затем заново упорядочите имена полей для индекса с именем PrimaryKey.
-
-
Определите связь один-ко-многим между каждой основной и связующей таблицами.
Отношение многие ко многим (Many to Many)
Последнее обновление: 07.03.2018
Связь многие ко многим описывает ситуацию, когда объект первой модели может одновременно ассоциироваться с несколькими объектами второй модели. И наоборот,
один объект второй модели может также одновременно быть ассоциирован с несколькими объектами первой модели. Например, один студент может посещать несколько
курсов, а один курс могут посещать несколько студентов.
Для создания отношения многие ко многим применяется тип ManyToManyField.
from django.db import models class Course(models.Model): name = models.CharField(max_length=30) class Student(models.Model): name = models.CharField(max_length=30) courses = models.ManyToManyField(Course)
В конструктор передается сущность, с которой устанавливается отношение многие ко многим. В результате будет
создаваться промежуточная таблица, через которую собственно и будет осуществляться связь.
В результате миграции в базе данных SQLite будут создаваться следующие таблицы:
CREATE TABLE `firstapp_course` ( `id` integer NOT NULL PRIMARY KEY AUTOINCREMENT, `name` varchar(30) NOT NULL ); CREATE TABLE `firstapp_student` ( `id` integer NOT NULL PRIMARY KEY AUTOINCREMENT, `name` varchar(30) NOT NULL ); CREATE TABLE `firstapp_student_courses` ( `id` integer NOT NULL PRIMARY KEY AUTOINCREMENT, `student_id` integer NOT NULL, `course_id` integer NOT NULL, FOREIGN KEY(`student_id`) REFERENCES `firstapp_student`(`id`) DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY(`course_id`) REFERENCES `firstapp_course`(`id`) DEFERRABLE INITIALLY DEFERRED );
В данном случае «firstapp_student_courses» выступает в качестве связующей таблицы. Она называется по шаблону .
Операции с моделями
Через свойство в модели Student мы можем получать связанные со студентом курсы и управлять ими.
# создадим студента tom = Student.objects.create(name="Tom") # создадим один курс и добавим его в список курсов Тома tom.courses.create(name="Algebra") # получим все курсы студента courses = Student.objects.get(name="Tom").courses.all() # получаем всех студентов, которые посещают курс Алгебра studes = Student.objects.filter(courses__name="Algebra")
Стоит отметить последний случай, где производится фильтрация студентов по посещаемому курсу. Для передачи в метод filter названия курса
используется параметр, название которого начинается с названия свойства, через которое идет связь со второй моделью. И далее через два знака подчеркивания указывается
имя свойства второй модели, например, или .
Однако в данном случае мы можем получить информацию о курсах студента через свойство courses, которое определено в модели Student. Однако что если
мы хотим получить информацию о студентах по определенному курсу? В этом случае нам надо использовать синтаксис _set.
# создадим курс python = Course.objects.create(name="Python") # создаем студента и добавляем его на курс python.student_set.create(name="Bob") # отдельно создаем студента и добавляем его на курс sam = Student(name="Sam") sam.save() python.student_set.add(sam) # получим всех студентов курса students = python.student_set.all() # получим количество студентов по курсу number = python.student_set.count() # удялем с курса одного студента python.student_set.remove(sam) # удаляем всех студентов с курса python.student_set.clear()
Стоит учитывать, что не всегда такая организация связи Многие ко Многим может подойти. Например, в данном случае создается промежуточная таблица, которая
хранит id студента и id курса. Если нам надо в промежуточной таблице харнить еще какие-либо данные, например, дату зачисления студента на курс, его оценку и т.д.,
то такая конфигурация не подойдет. И в этом случае будет более оптимально создать промежуточную сущность вручную, которая связана отношением один ко многим с обеими моделями.
НазадВперед
Связь один ко многим
Последнее обновление: 17.12.2015
Связь один-ко-многим реализуется, если одна модель хранит ссылку на один объект другой модели, а вторая модель может ссылаться на коллекцию объектов первой модели.
Например, в одной команде играет несколько игроков:
public class Player { public int Id { get; set; } public string Name { get; set; } public string Position { get; set; } public int Age { get; set; } public int? TeamId { get; set; } public Team Team { get; set; } } public class Team { public int Id { get; set; } public string Name { get; set; } // название команды public ICollection<Player> Players { get; set; } public Team() { Players = new List<Player>(); } } public class SoccerContext : DbContext { public SoccerContext() : base("SoccerContext") {} public DbSet<Player> Players { get; set; } public DbSet<Team> Teams { get; set; } }
Добавление и вывод:
using(SoccerContext db = new SoccerContext()) { // создание и добавление моделей Team t1 = new Team { Name = "Барселона" }; Team t2 = new Team { Name = "Реал Мадрид" }; db.Teams.Add(t1); db.Teams.Add(t2); db.SaveChanges(); Player pl1 = new Player {Name = "Роналду", Age = 31, Position = "Нападающий", Team=t2 }; Player pl2 = new Player { Name = "Месси", Age = 28, Position = "Нападающий", Team=t1 }; Player pl3 = new Player { Name = "Хави", Age = 34, Position = "Полузащитник", Team=t1 }; db.Players.AddRange(new List<Player>{pl1, pl2,pl3}); db.SaveChanges(); // вывод foreach(Player pl in db.Players.Include(p=>p.Team)) Console.WriteLine("{0} - {1}", pl.Name, pl.Team!=null?pl.Team.Name:""); Console.WriteLine(); foreach(Team t in db.Teams.Include(t=>t.Players)) { Console.WriteLine("Команда: {0}", t.Name); foreach(Player pl in t.Players) { Console.WriteLine("{0} - {1}", pl.Name, pl.Position); } Console.WriteLine(); } }
Результат вывода:
Редактирование:
//редактирование t2.Name = "Реал М."; // изменим название db.Entry(t2).State = EntityState.Modified; // переведем игрока из одной команды в другую pl3.Team = t2; db.Entry(pl3).State = EntityState.Modified; db.SaveChanges();
При удалении объектов, связанных отношением «один-ко-многим» нам надо учитывать то, что по умолчанию даже если внешний ключ допускает значение null
(как в данном случае свойство TeamId в классе Player), мы не сможем просто так удалить одну модель, если она имеет ссылки на другую модель.
Например, удаление команды в данном случае выльется в ошибку, если какой-то объект Player имеет ссылку на эту команду. Что делать в этом случае?
В этом случае нам надо установить для внешнего ключа TeamId в таблице игроков ограничение . Данное ограничение позволит
при удалении связанного объекта устанавливать для внешнего ключа значение null.
Установку ограничения можно сделать вручную, изменим схему базы данных, либо программно. Рассмотрим, как это сделать программно. Для этого перед удалением нам надо выполнить
соответствующую команду SQL, чтобы добавить ограничение:
db.Database.ExecuteSqlCommand("ALTER TABLE dbo.Players ADD CONSTRAINT Players_Teams FOREIGN KEY (TeamId) REFERENCES dbo.Teams (Id) ON DELETE SET NULL");
Здесь добавляется ограничение «Players_Teams» в таблицу игроков, которая называется, как правило, dbo.Players или просто Players. Это ограничение указывает, что для
внешнего ключа TeamId из таблицы Players, который связан со столбцом Id из таблицы dbo.Teams, устанавливается правило «ON DELETE SET NULL», то есть установка null по удалению.
И после этого мы можем выполнить удаление:
//удаление игрока Player pl_toDelete = db.Players.First(p => p.Name == "Роналду"); db.Players.Remove(pl_toDelete); // удаление команды Team t_toDelete = db.Teams.First(); db.Teams.Remove(t_toDelete); db.SaveChanges();
При удалении команды свойство Team у объектов Player получает значение null.
НазадВперед
LEFT JOIN
Помимо INNER JOIN, есть ещё несколько операторов класса JOIN. Один из самых частоиспользуемых — LEFT JOIN. Он позволяет сделать запрос к двум таблицам, между которыми есть связь, и при этом для одной из таблиц вернуть записи, даже если они не соответствуют записям в другой таблице.
Как например, если бы мы хотели вывести не только пользователей, у которых есть статьи, но и тех, кто «халтурит» 🙂
Давайте для начала сделаем запрос с использованием INNER JOIN, который выведет пользователей и написанные ими статьи:
Теперь заменим INNER JOIN на LEFT JOIN:
Видите, вывелись записи из левой таблицы (users), которым не соответствует при этом ни одна запись из правой таблицы (articles).
INNER JOIN
Прежде чем идти дальше и рассматривать другие типы связей, стоит изучить ещё один оператор SQL — INNER JOIN. Он используется для объединения строк из двух и более таблиц, основываясь на отношениях между ними. Для запроса используется следующий синтаксис:
Чтобы получить всех пользователей вместе с их профилями нам нужно выполнить следующий запрос:
Каждая строка из левой таблицы, сопоставляется с каждой строкой из правой таблицы, после этого проверяется условие.
Если мы хотим выбрать только некоторые столбцы, то после оператора SELECT нужно перед именем поля явно указать название таблицы, из которой оно берется:
Элемент области навигации для основной сущности
Можно показать списки связанных сущностей в основной сущности, развернув панель навигации. С помощью параметров в этой группе можно управлять тем, что будет отображаться в этом списке и будет ли что-то отображаться вовсе. Следующие элементы навигации можно изменить с помощью редактора форм, а с помощью JavaScript разработчик может внести изменения в эти элементы, если отображается форма.
Поле | Описание |
---|---|
Параметры отображения | — Не отображать. Выберите этот параметр, требуется запретить пользователям переходить к списку записей связанных сущностей.- Использовать специальные метки. Выберите этот параметр, если требуется указать специальную метку для использования.- Использовать имя во множественном числе. Выберите этот параметр, если имя связанной сущности во множественном числе требуется использовать в качестве метки. |
Пользовательская метка | При выборе параметра Использовать специальные метки в качестве параметра отображения введите специальную метку, которую требуется использовать вместо имени связанной сущности во множественном числе. |
Область отображения | — Сведения. Выберите этот параметр для включения элемента навигации в группу Общие.- Маркетинг. Выберите этот параметр для включения элемента навигации в группу Маркетинг.- Продажи. Выберите этот параметр для включения элемента навигации в группу Продажи.- Служба. Выберите этот параметр для включения элемента навигации в группу Служба. |
Порядок отображения | Это значение управляет тем, будет ли элемент навигации включен в выбранную область отображения. Диапазон доступных номеров начинается с 10 000. Элементы области навигации с меньшим значением будут стоять в списке выше других отношений. |
Один-ко-многим или многие-к-одному
Это наиболее распространенный тип мощности, используемый в моделях данных. Этот тип количества элементов означает, что одна из таблиц имеет уникальные значения в каждой строке для поля отношения, а другая имеет несколько значений. Пример, который вы видели ранее между таблицами Stores и Sales на основе stor_id, представляет собой отношение «многие-к-одному» или «один-ко-многим».
Есть два способа назвать эти отношения: один-ко-многим или многие-к-одному. Зависит от того, что является исходной и целевой таблицей.
Например, приведенная ниже конфигурация означает, что от таблицы Sales до таблицы Stores есть отношение «многие-к-одному».
А ниже показано отношение «один-ко-многим» от таблицы Stores к таблице Sales:
Эти две таблицы заканчиваются созданием таких отношений:
Это означает, что нет разницы в отношении «один-ко-многим» или «многие-к-одному», кроме порядка, в котором вы читаете это. Если вы посмотрите от таблицы Stores, у вас будет отношение «один ко многим». Если вы посмотрите на это с точки зрения таблицы Sales, у вас будет отношение «многие к одному». И оба они одинаковы, без какой-либо разницы. Так что теперь, в этой статье, всякий раз, когда вы читаете «многие к одному» или «один ко многим», вы знаете, что вы можете читать их и наоборот.
В остальной части статьи мы будем использовать термины таблиц FACT и DIMENSION, которые мы объясним отдельно в другой статье. А пока вот краткое объяснение терминов:
- Таблица фактов (FACT): таблица с числовыми значениями, которые нам нужны либо в агрегированном уровне, либо в подробном выводе. Поля из этой таблицы обычно используются в качестве раздела VALUE визуальных элементов в Power BI.
- Таблица измерений (DIMENSION): таблица, содержащая описательную информацию, которая используется для нарезки данных таблицы фактов. Поля из этой таблицы часто используются в качестве слайсеров, фильтров или осей визуалов в Power BI.
Отношение «многие к одному» между таблицами фактов и измерений
“Многие-к-одному” — это отношение, обычно используемое между таблицей фактов и таблицами измерений. Приведенный выше пример находится между таблицами Sales (таблица фактов) и Stores (таблица измерений). Если мы приведем еще одну таблицу в модель: Titles (на основе title_id в обеих таблицах: Sales и Titles), то вы увидите, что существует тот же шаблон отношений «многие-к-одному».
Этот тип отношений, хотя часто используется во многих моделях, всегда может быть предметом исследования для лучшего моделирования. В идеальной модели данных вы НЕ должны иметь отношения между двумя таблицами измерений напрямую. Давайте проверим это на примере.
Допустим, модель отличается от того, что вы видели в этом примере: таблица Sales, таблица Product и две таблицы для информации о категории и подкатегории продукта:
Как вы можете видеть на приведенной выше диаграмме отношений, все отношения — “многие-к-одному”. Что хорошо. Однако, если вы хотите нарезать данные таблицы фактов (например, SalesAmount) по полю из таблицы DimProductCategory (например, по имени ProductCategory), для обработки потребуется три отношения:
Лучшей моделью было бы объединение таблиц категорий и подкатегорий с продуктом и наличие единого отношения «многие к одному» из таблицы фактов в таблицу DimProduct. Подробнее — в ссылке выше.
Извлечение данных для связи «многие ко многим» (SELECT)
Возникает логичный вопрос — как же получать данные из базы, используя таблицу связи?
Есть разные варианты для разных ситуаций, которые мы сейчас рассмотрим, но прежде чем проиллюстрировать их, заполните созданные выше таблицы (чтобы вы тоже могли поэкспериментировать с запросами)
Рассмотрим задачу извлечения участников, связанных с данной номинацией — или короче «номинации, и всех, кто подал в неё заявки» (алгоритм извлечения данных в обратную сторону — т.е. «участик и все его номинации» абсолютно аналогичен).
На практике приходится сталкиваться с двумя базовыми ситуациями:
- Извлечение одной сущности номинации и связанных с ней участников
- Извлечение списка сущностей номинаций и связанных с каждой из номинаций участников (т.е. фактически список участников для каждого элемента из списка номинаций).
Извлечение связанных (многие-ко-многим) данных для одной сущности
Пусть у нас известен id () номинации и мы хотим получить сведения об этой номинации и всех участниках в ней.
Во-первых, сделать это можно двумя sql запросами:
- Сначала просто получим кортеж этой номинации:
mysql> SELECT * FROM Nominations WHERE nominationID=4; +--------------+-----------------------------+ | nominationID | title | +--------------+-----------------------------+ | 4 | Лучшее пособие | +--------------+-----------------------------+
- После, опять же зная id номинации (используем в WHERE), достаточно просто сделать LEFT JOIN между таблицей связи и таблицей участников:
SELECT * FROM Tickets_Nominations LEFT JOIN Tickets ON ticket_id = ticketID WHERE Tickets_Nominations.nomination_id = 4;
Получим:
+-----------+---------------+----------+-------------------------- +----------------------------------------------+ | ticket_id | nomination_id | ticketID | name | info | +-----------+---------------+----------+---------------------------+----------------------------------------------+ | 3 | 4 | 3 | Программирование для всех | Некоммерческая образовательная организация | 4 | 4 | 4 | Юный программист | Кружок для детей в д. Простоквашино | 5 | 4 | 5 | IT FOR FREE | Русскоязычное IT-сообщество с уклоном в web | 6 | 4 | 6 | Саша Петров | Студент 2 курса, автор пособия по SQL
— как видим, тут мы получили вообще все колонки (т.к. в запросе указали звездочку *) двух соединённых таблиц (связи и заявок).
Также видим что на номинации с id=4 номинировалось 4-ре участника, кроме их имен видны также и описания.
Все эти данные можно использовать в приложении, после выполнения запроса к БД — например записать, то что нужно в поле, хранящее массив объекта конкретной номинации.
Если вам требуется от массива связанных сущностей только одно поле (напр. имена участников), то решить задачу можно вообще одним sql запросом, используя группировку (GROUP BY) и применимую к группируемым значения колонки функцию конкатенации GROUP_CONCAT():
SELECT Nominations.*, GROUP_CONCAT(Tickets.name SEPARATOR ', ') as participants_names FROM Nominations LEFT JOIN Tickets_Nominations ON Nominations.nominationID = Tickets_Nominations.nomination_id LEFT JOIN Tickets ON Tickets.ticketID = Tickets_Nominations.ticket_id WHERE Tickets_Nominations.nomination_id = 4 GROUP BY Nominations.nominationID;
Получим единственный кортеж:
+--------------+-----------------------------+-----------------------------------------------------------------------------------------------------------------------+ | nominationID | title | participants_names | +--------------+-----------------------------+-----------------------------------------------------------------------------------------------------------------------+ | 4 | Лучшее пособие | Программирование для всех, Юный программист, IT FOR FREE, Саша Петров | +--------------+-----------------------------+-----------------------------------------------------------------------------------------------------------------------+
— здесь мы:
- провели сразу тройной JOIN, как бы поставив таблицу связи между таблицами номинаций и заявок.
- нас интересовали имена участников для 4 номинации — поэтому использовали WHERE Tickets_Nominations.nomination_id = 4
- Группировка (чтобы в итоге получить только одну строку-кортеж) проходила по id номинации (Nominations.nominationID)
- Сконкатенированному полю мы назначили псевдоним (participants_names)
Плюсом такого подхода является то, что в приложении можно использовать готовую строку participants_names, а минусом то, что с этим значением уже нельзя работать как с массивом, явно не преобразовав.
Тестирование связи «многие ко многим»
При задании в кубе связи «многие ко многим» тестирование результатов является обязательным. Необходимо тестировать куб с помощью клиентского приложения, которое будут использовать конечные пользователи. В следующей процедуре мы откроем куб в Excel, чтобы проверить результаты запросов.
Откройте куб в Excel
Разверните проект, затем откройте куб, чтобы подтвердить правильность выполнения агрегирований.
в Excel щелкните данные | из других источников | из Analysis Services. Введите имя сервера, выберите базу данных и куб.
Создайте сводную таблицу, которая использует следующее:
Sales Amount как значение
Sales Reason Name для столбцов
Sales Order Number для строк
Проанализируйте результаты. Мы используем примеры данных, поэтому на первый взгляд кажется, что все заказы на продажу имеют одинаковые значения. Но если выполнить прокрутку результатов вниз, то можно видеть разброс данных.
Немного ниже вы увидите сумму продаж и причины для заказа с номером SO5382
Общая сумма этого заказа — 539,99, а в качестве причин указаны Promotion, Other и Price.
Обратите внимание, что столбец Sales Amount для этого заказа вычислен правильно; здесь показано 539,99 за весь заказ
Хотя 539,99 указано для каждой причины, это значение не суммируется для всех трех причин, что привело бы к ошибке в общем итоге.
А зачем вообще помещать сумму продаж под каждой причиной продажи? Это позволяет идентифицировать объем продаж, который можно приписать каждой причине.
Выполните прокрутку до нижней части листа
Теперь хорошо видно, что Price (Цена) является самой важной причиной для покупок клиентов по сравнению с другими причинами и общей суммой.
Советы по обработке непредвиденных результатов запросов
-
Скрывайте в промежуточной группе мер такие меры, как подсчеты, которые не возвращают в запросе значимые результаты. Это не позволит пользователям использовать агрегирования, дающие на выходе бессмысленные данные. Чтобы спрятать меру, задайте атрибуту Видимость значение False в конструкторе измерения.
-
Создавайте перспективы, использующие подмножество мер, и измерения, обеспечивающие аналитический процесс, который вы желаете предоставить пользователям. Возможно, куб, который содержит много групп мер и измерений, не во всех случаях будет работать как надо. Изолируя измерения и группы мер, которые будут использоваться вместе, вы обеспечиваете более прогнозируемый результат.
-
Всегда помните, что нужно развернуть модель и снова подключиться к ней после ее изменения. В Excel нажмите кнопку «Обновить» на ленте «Анализ» сводной таблицы.
-
Избегайте использования связанных групп мер в нескольких связях типа «многие ко многим», особенно если эти связи находятся в разных кубах. Это может привести к формированию неоднозначных агрегатов. Дополнительные сведения см. в разделе Неверные суммы для связанных мер в кубе, содержащих связи «многие ко многим».
Ассоциация ManyToOne / OneToMany.
Предположим, что каждый продукт в вашей заявке относится только к одной категории. В этом случае вам понадобится класс Category и способ связать объект Product объектом Category.
Начните с создания Category с полем name:
php bin/console make:entity Category New property name (press <return> to stop adding fields): > name Field type (enter ? to see all types) : > string Field length : > 255 Can this field be null in the database (nullable) (yes/no) : > no New property name (press <return> to stop adding fields): > (press enter again to finish)
Это сгенерирует ваш новый класс сущности:
// src/Entity/Category.php // ... class Category { /** * @ORM\Id * @ORM\GeneratedValue * @ORM\Column(type="integer") */ private $id; /** * @ORM\Column(type="string") */ private $name; // ... getters and setters }