Проблема выбора отношения «многие ко многим» - PullRequest
3 голосов
/ 13 апреля 2011

У меня очень досадная проблема с приложением, в котором я работал по контракту.

Структура базы данных, , которую я не могу изменить из соображений совместимости - беспорядок. Это в основном отношения «многие-ко-многим-многим-многим-к-кому-то-бить-по-твоему-на-столе» . Это выглядит так:

контакт

---------------------------
| contact_id | name | ... |
---------------------------
| 1          | foo  |     |
---------------------------

metadefinition

-----------------------------
| metadefinition_id | name  |
-----------------------------
| 1                 | title |
-----------------------------
| 2                 | job   |
-----------------------------

contact_metadata

----------------------------
| contact_id | metadata_id |
----------------------------
| 1          | 1           |
----------------------------
| 1          | 2           |
----------------------------

метаданные

-------------------------------------------
| metadata_id | metadefinition_id | value |
-------------------------------------------
| 1           | 1                 | mrs   |
-------------------------------------------
| 2           | 2                 | coder |
-------------------------------------------

Итак, для одного контакта я хочу найти все это и получить что-то вроде этого:

-----------------------------------------------------
| contact_id | name | metadata.title | metadata.job |
-----------------------------------------------------
| 1          | foo  | mrs            | coder        |
-----------------------------------------------------

Так что теперь я попробовал. Я могу получить список metadefinition заранее, это не проблема. Поэтому я строю запрос следующим образом:

SELECT contact.*,m1.value AS `metadata.title` FROM contact
   LEFT JOIN contact_metadata ON contact.contact_id = contact_metadata.contact_id
   LEFT JOIN metadata m1 ON contact_metadata.metadata_id = m1.metadata_id AND m1.metadefinition_id = 1
   GROUP BY contact_id

Это работает для одного metadefinition, я получаю что-то вроде этого:

--------------------------------------
| contact_id | name | metadata.title |
--------------------------------------
| 1          | foo  | mrs            |
--------------------------------------

Если я попробую с двумя, однако:

SELECT contact.*,m1.value AS `metadata.title`,m2.value AS `metadata.job` FROM contact
   LEFT JOIN contact_metadata ON contact.contact_id = contact_metadata.contact_id
   LEFT JOIN metadata m1 ON contact_metadata.metadata_id = m1.metadata_id AND m1.metadefinition_id = 1
   LEFT JOIN metadata m2 ON contact_metadata.metadata_id = m2.metadata_id AND m2.metadefinition_id = 2
   GROUP BY contact_id

Я получаю:

-----------------------------------------------------
| contact_id | name | metadata.title | metadata.job |
-----------------------------------------------------
| 1          | foo  | mrs            | NULL         |
-----------------------------------------------------

Если я удалю предложение GROUP BY, конечно, я получу:

-----------------------------------------------------
| contact_id | name | metadata.title | metadata.job |
-----------------------------------------------------
| 1          | foo  | mrs            | NULL         |
-----------------------------------------------------
| 1          | foo  | NULL           | coder        |
-----------------------------------------------------

Я открыт для всего, пока время запросов относительно приемлемо (учитывая структуру, если для 100000 записей требуется 10 секунд, это лучше, чем ничего)

Будут ли это временные таблицы, хранимые процедуры, мне все равно, если мне не нужно менять фактическую структуру базы данных.

Поверь мне, когда придет время переделывать, я с удовольствием сниму все это.

Есть ли решение для этого, возможно ли это?

Заранее спасибо.

Ответы [ 3 ]

6 голосов
/ 13 апреля 2011

Я бы использовал пару коррелированных подзапросов.Что-то вроде

SELECT c.*
  ,( Select value from metadata m1 
       INNER JOIN contact_metadata cm 
       on cm.metadata_id = m1.metadata_id 
     WHERE m1.metadefinition_id = 1 
       AND cm.contact_id = c.contact_id )AS `metadata.title`
  ,( Select value from metadata m1 
       INNER JOIN contact_metadata cm 
       ON cm.metadata_id = m1.metadata_id 
     WHERE m1.metadefinition_id = 2 
       AND cm.contact_id = c.contact_id )AS `metadata.job`
FROM contact c
WHERE c.contact_id = 1
4 голосов
/ 13 апреля 2011

Вам нужно будет выбрать каждый элемент метаданных (заголовок, задание и т. Д.) Как отдельный подзапрос, а затем присоединиться к contact_id, чтобы объединить эти элементы метаданных для каждого контакта.Например:

SELECT contact.*, metadata_title, metadata_job FROM contact 
INNER JOIN 
(SELECT contact_id,m.value AS `metadata_title` FROM contact_metadata 
   LEFT JOIN metadata m ON contact_metadata.metadata_id = m.metadata_id AND m.metadefinition_id = 1) metaTitle ON metaTitle.contact_id=contact.contact_id
INNER JOIN 
(SELECT contact_id,m.value AS `metadata_job` FROM contact_metadata 
   LEFT JOIN metadata m ON contact_metadata.metadata_id = m.metadata_id AND m.metadefinition_id = 2) metaJob ON (metaJob.contact_id=contact.contact_id)

Если вы хотите, чтобы клиент не имел определенного элемента метаданных, используйте вместо него LEFT JOINs, поскольку в настоящее время запрос будет возвращать только те клиенты, которые имеютзадание и должность определены.

0 голосов
/ 19 апреля 2011

Теперь, когда я наконец прочитал вопрос правильно, вот мои два цента, для чего это стоит (без каламбура).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...