Найти записи с точными совпадениями по многим отношениям - PullRequest
8 голосов
/ 14 июня 2011

У меня есть три таблицы, которые выглядят так:

PROD

Prod_ID|Desc
------------
P1|Foo1
P2|Foo2
P3|Foo3
P4|Foo4
...

RAM

Ram_ID|Desc
------------
R1|Bar1
R2|Bar2
R3|Bar3
R4|Bar4
...

PROD_RAM ​​

Prod_ID|Ram_ID
------------
P1|R1
P2|R2
P3|R1
P3|R2
P3|R3
P4|R3
P5|R1
P5|R2
...

Между PROD и RAM существует отношение «многие ко многим», описываемое таблицей PROD_RAM ​​.

Учитывая Ram_ID набор, такой как (R1,R3) Я хотел бы найти все PROD, которые имеют ровно ONE или ALL RAM данного набора ,

Учитывая, (R1,R3) должно возвращаться, например, P1, P4 и P5; P3 не должен быть возвращен, поскольку имеет R1 и R3, но также R2.

Какой самый быстрый запрос для получения всех PROD, имеющих ровно ONE или ALL из Ram_ID данного RAM набора?

EDIT:
Таблица PROD_RAM может содержать отношения больше чем 1-> 3, поэтому «жестко закодированные» проверки для count = 1 OR = 2 не являются жизнеспособным решением.

Ответы [ 3 ]

2 голосов
/ 14 июня 2011

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

;WITH CANDIDATES AS (
    SELECT  pr1.Prod_ID
            , pr2.Ram_ID
    FROM    PROD_RAM pr1
            INNER JOIN PROD_RAM pr2 ON pr2.Prod_ID = pr1.Prod_ID
    WHERE   pr1.Ram_ID IN ('R1', 'R3')
)
SELECT  *
FROM    CANDIDATES
WHERE   CANDIDATES.Prod_ID NOT IN (
            SELECT  Prod_ID
            FROM    CANDIDATES
            WHERE   Ram_ID NOT IN ('R1', 'R3')
        )           

или если вам не нравится повторять установленные условия

;WITH SUBSET (Ram_ID) AS (
    SELECT  'R1'
    UNION ALL SELECT 'R3'
)   
, CANDIDATES AS (
    SELECT  pr1.Prod_ID
            , pr2.Ram_ID
    FROM    PROD_RAM pr1
            INNER JOIN PROD_RAM pr2 ON pr2.Prod_ID = pr1.Prod_ID
            INNER JOIN SUBSET s ON s.Ram_ID = pr1.Ram_ID    
)
, EXCLUDES AS (
    SELECT  Prod_ID
    FROM    CANDIDATES
            LEFT OUTER JOIN SUBSET s ON s.Ram_ID = CANDIDATES.Ram_ID
    WHERE   s.Ram_ID IS NULL
)
SELECT  *
FROM    CANDIDATES
        LEFT OUTER JOIN EXCLUDES ON EXCLUDES.Prod_ID = CANDIDATES.Prod_ID
WHERE   EXCLUDES.Prod_ID IS NULL        
0 голосов
/ 14 июня 2011

Один из способов сделать это будет выглядеть примерно так:

SELECT PROD.Prod_ID FROM PROD WHERE

(SELECT COUNT(*) FROM PROD_RAM WHERE PROD_RAM.Prod_ID = PROD.Prod_ID) > 0 AND

(SELECT COUNT(*) FROM PROD_RAM WHERE PROD_RAM.Prod_ID = PROD.Prod_ID AND PROD.Ram_ID <> 
IFNULL((SELECT TOP 1 Ram_ID FROM PROD_RAM WHERE PROD_RAM.Prod_ID = PROD.Prod_ID),0)) = 0
0 голосов
/ 14 июня 2011
SELECT Prod_ID
FROM 
  ( SELECT Prod_ID
         , COUNT(*) AS cntAll
         , COUNT( CASE WHEN Ram_ID IN (1,3)
                       THEN 1
                       ELSE NULL
                  END 
                ) AS cntGood
    FROM PROD_RAM
    GROUP BY Prod_ID
  ) AS grp
WHERE cntAll = cntGood
  AND ( cntGood = 1
     OR cntGood = 2               --- number of items in list (1,3)
      )

Не совсем уверен, что это самый быстрый способ.Вам придется попробовать разные способы написания этого запроса (используя JOIN s и NOT EXISTS) и проверить скорость.

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