Много ко многим запрос - PullRequest
       13

Много ко многим запрос

2 голосов
/ 17 сентября 2009

У меня есть две таблицы продуктов и разделы в отношениях "многие ко многим" и таблица соединений Товар может быть в одном или нескольких разделах (новый, автомобиль, самолет, старый).

Products
id    name
-----------------
1     something
2     something_else
3     other_thing


Sections
id    name
-----------------
1     new
2     car

Products_sections
product_id     section_id
--------------------------
1              1
1              2
2              1
3              2

Я хочу извлечь все продукты, которые есть как в новой, так и в автомобильной секции. В этом примере возвращаемый результат должен быть продуктом 1. Какой правильный запрос MySQL для его получения?

Ответы [ 8 ]

6 голосов
/ 17 сентября 2009
SELECT Products.name
FROM Products
WHERE NOT EXISTS (
  SELECT id
  FROM Sections
  WHERE name IN ('new','car')
  AND NOT EXISTS (
    SELECT *
    FROM Products_sections
    WHERE Products_sections.section_id = Sections.id
    AND Products_sections.product_id = Products.id
  )
)

Другими словами, выберите те продукты, для которых ни одно из требуемых значений Section.id отсутствует в таблице Products_sections для этого продукта.

Ответ andho's comment:

Можно поставить

  NOT EXISTS (<select query>)

в предложение WHERE, как и в любом другом предикате. Он оценивается как ИСТИНА, если в результирующем наборе нет строк, описанных

Пошагово, вот как можно получить ответ на этот запрос:

Шаг 1. Требуется указать все продукты, которые находятся «в разделах« новый »и« автомобиль »».

Шаг 2. Продукт находится в разделах «новый» и «автомобиль», если продукт содержит разделы «новый» и «автомобиль». Эквивалентно, продукт находится в разделах «новый» и «автомобиль», если ни один из этих разделов не содержит продукт. (Обратите внимание на двойной минус: ни не содержит .) Повторно, мы хотим, чтобы все продукты, для которых есть нет обязательный раздел не удалось для содержания продукта.

Обязательные разделы:

SELECT id
FROM Sections
WHERE name IN ('new','car')

Следовательно, желаемыми продуктами являются:

SELECT Products.name
FROM Products
WHERE NOT EXISTS ( -- there does not exist
  SELECT id  -- a section
  FROM Sections
  WHERE name IN ('new','car') -- that is required
   AND (the section identified by Sections.id fails to contain the product identified by Products.id)
)

Шаг 3. В данном разделе (например, «новый» или «автомобиль») содержит конкретный товар, если в разделе Products_sections есть строка для данного раздела и конкретного товара. Таким образом, данный раздел не может содержать конкретный продукт, если в Products_sections такой строки нет.

Шаг 4. Если запрос ниже содержит содержит строку, раздел section_id содержит содержит product_id product:

SELECT *
FROM Products_sections
WHERE Products_sections.section_id = Sections.id
AND Products_sections.product_id = Products_id

Таким образом, section_id section не может содержать продукта (и это то, что нам нужно выразить), если приведенный выше запрос не создает строку в своем результате, или если NOT EXISTS ().

Кажется сложным, но как только вы получаете это в голову, оно придерживается: все необходимые элементы присутствуют? Да, если не существует обязательного элемента, которого нет.

2 голосов
/ 17 сентября 2009

Я всегда так делаю так:

Начните с того, что вы пытаетесь получить (products), а затем просмотрите таблицу поиска (products_sections) и найдите то, что вы пытаетесь отфильтровать (sections). Таким образом, вы можете получить наглядное представление о том, что ищете, и вам никогда не придется запоминать суррогатные ключи (что очень важно иметь, а не запоминать).

select distinct
    p.name
from
    products p
    inner join products_sections ps on
        p.product_id = ps.product_id
    inner join sections s1 on
        ps.section_id = s1.section_id
    inner join sections s2 on
        ps.section_id = s2.section_id
where
    s1.name = 'new'
    and s2.name = 'car'

Вуаля. Три inner join с, и у вас есть хороший, ясный, лаконичный запрос, который очевидно, что он возвращает. Надеюсь, это поможет!

0 голосов
/ 17 сентября 2009
select
`p`.`id`,
`p`.`name`
from `Sections` as `s`
join `Products_sections` as `ps` on `ps`.`section_id` = `s`.`id`
join `Products` as `p` on `p`.`id` = `ps`.`product_id`
where `s`.`id` in ( 1,2 )
having count( distinct `s`.`name` = 2 )

вернется ...

id    name
-----------------
1     something

Это то, что вы искали?

0 голосов
/ 17 сентября 2009

Этот запрос даст вам результат без необходимости добавлять больше внутренних объединений, когда вам нужно искать больше разделов. Что изменится здесь:

  1. значения внутри паранеза IN ()
  2. Значение в предложении where для количества, которое должно быть заменено количеством искомых разделов

    ВЫБРАТЬ id, name ОТ
    (
    SELECT
    products. id
    products. name
    sections. name AS section_name,
    COUNT (*) AS count ОТ products
    ВНУТРЕННИЙ РЕЙТИНГ products_sections
    ON products_sections. product_id = products. id
    ВНУТРЕННИЙ РЕЙТИНГ sections
    ON sections. id = products_sections. section_id
    ГДЕ sections. name IN («автомобиль», «новый»)
    GROUP BY products. id
    ) AS P
    ГДЕ count = 2

0 голосов
/ 17 сентября 2009
SELECT p.*
FROM   Products p
       INNER JOIN (SELECT   ps.product_id
                   FROM     Products_sections ps
                            INNER JOIN Sections s
                              ON s.id = ps.section_id
                   WHERE    s.name IN ("new","car")
                   GROUP BY ps.product_id
                   HAVING   Count(ps.product_id) = 2) pp
         ON p.id = pp.product_id
0 голосов
/ 17 сентября 2009

Самостоятельное объединение в двух подмножествах объединяемой таблицы с последующим выбором уникальных идентификаторов продукта.

SELECT DISTINCT car.product_id 
FROM ( SELECT product_id 
         FROM Product_sections 
        WHERE section_id = 2
     ) car JOIN 
     ( SELECT product_id 
         FROM Product_sections 
        WHERE section_id = 1
     ) neww 
     ON (car.product_id = neww.product_id) 

Этот запрос является вариантом более общего решения:

SELECT DISTINCT car.product_id
  FROM product_sections car join 
       product_sections neww ON (car.product_id = neww.product_id AND 
                                 car.section_id = 2 AND 
                                 neww.section_id = 2)   

Менее эффективное, но более простое решение:

SELECT p.name FROM Products p WHERE
 EXISTS (SELECT 'found car' 
           FROM Products_sections ps 
          WHERE ps.product_id = p.id AND ps.section_id = 2)
 AND
 EXISTS (SELECT 'found new' 
           FROM products_sections ps 
          WHERE ps.product_id = p.id AND ps.section_id = 1)

----------------

Я манипулировал с идентификаторами для ясности. При необходимости заменить выражения section_id = 2 и section_id = 1 на

section_id = (SELECT s.id FROM Sections s WHERE s.name = 'car')

section_id = (SELECT s.id FROM Sections s WHERE s.name = 'new')

Кроме того, вы можете выбрать названия продуктов, подключив любой из указанных выше запросов, например:

SELECT Products.name FROM Products 
 WHERE EXISTS (
         SELECT 'found product'
           FROM product_sections car join 
                product_sections neww ON (car.product_id = neww.product_id AND 
                                          car.section_id = 2 AND 
                                          neww.section_id = 2)
          WHERE car.product_id = Products.id
       )
0 голосов
/ 17 сентября 2009

Запрос ниже немного громоздкий, но он должен ответить на ваш вопрос:

select products.id
from products
where products.id in 
  (
    select products_sections.product_id
    from products_sections
    where products_sections.section_id=1
  )
  and products.id in
  (
    select products_sections.product_id
    from products_sections
    where products_sections.section_id=2
  )
0 голосов
/ 17 сентября 2009
SELECT product_id, count(*) AS TotalSection
FROM Products_sections
GROUP BY product_id
WHERE section_id IN (1,2)
HAVING TotalSection = 2;

Посмотрите, работает ли это в mysql.

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