Как расширить этот запрос, чтобы найти допустимые комбинации из трех элементов? - PullRequest
11 голосов
/ 15 декабря 2011

Я совершенно не рассчитываю получить какие-либо ответы здесь, но я все равно попробую.

Так что это вышло из игры в Skyrim.Я хотел простой способ найти, какие ингредиенты можно комбинировать для приготовления разных зелий / ядов, поэтому я создал таблицу ингредиентов, в которой есть ID и имя;таблица эффектов, которая имеет идентификатор, имя, флаг яда и флаг зелья (зелье и яд взаимоисключающие);и объединяющая таблица с идентификатором ингредиента и идентификатором эффекта.

Таким образом, каждый компонент имеет четыре различных эффекта, эффекты повторяются на нескольких ингредиентах.В игре вы можете комбинировать 2 или 3 ингредиента, и в результате получается зелье или яд со всеми эффектами, которые совпадают по крайней мере с 2 из используемых ингредиентов.Поэтому, если вы используете 3 ингредиента, и эффект 1 относится как к ингредиенту 1, так и к ингредиенту 2, а эффект 2 - как к ингредиенту 1, так и к ингредиенту 3, ваш результат будет зельем / ядом, обладающим эффектом 1 и эффектом.

Я смог придуматьмой собственный запрос, который покажет каждую возможную комбинацию из 2 ингредиентов, которая создает зелье без ядовитых эффектов.Сначала мне нужно найти каждую возможную комбинацию из 2 ингредиентов, которая имеет только совпадающие эффекты, которые не являются «ядом»:

SELECT i1.UniqIngredient UniqIngredient1, i2.UniqIngredient UniqIngredient2
FROM Ingredient i1
CROSS JOIN Ingredient i2
INNER JOIN IngredientEffectJT jt1 ON i1.UniqIngredient = jt1.UniqIngredient
INNER JOIN IngredientEffectJT jt2 ON i2.UniqIngredient = jt2.UniqIngredient
INNER JOIN Effect e ON jt1.UniqEffect = e.UniqEffect AND jt2.UniqEffect = e.UniqEffect
WHERE i1.UniqIngredient < i2.UniqIngredient
GROUP BY i1.UniqIngredient, i2.UniqIngredient
HAVING SUM(e.Poison) = 0

Ингредиент перекрестно соединен с Ингредиентом, чтобы получить каждую комбинацию, но потому что порядок ингредиентов неНе имеет значения, я бы в итоге удвоил результаты.Вот почему ГДЕ проверяет i1.UniqIngredient

Затем я использую этот результат в качестве таблицы, к которой я присоединяюсь к Ингредиенту иТаблицы эффектов, чтобы получить список всех возможных 2 комбинаций ингредиентов, которые производят зелья, и какие эффекты имеет каждая комбинация:

SELECT i1.Name, i2.Name, e.Name
FROM (SELECT i1.UniqIngredient UniqIngredient1, i2.UniqIngredient UniqIngredient2
FROM Ingredient i1
CROSS JOIN Ingredient i2
INNER JOIN IngredientEffectJT jt1 ON i1.UniqIngredient = jt1.UniqIngredient
INNER JOIN IngredientEffectJT jt2 ON i2.UniqIngredient = jt2.UniqIngredient
INNER JOIN Effect e ON jt1.UniqEffect = e.UniqEffect AND jt2.UniqEffect = e.UniqEffect
WHERE i1.UniqIngredient < i2.UniqIngredient
GROUP BY i1.UniqIngredient, i2.UniqIngredient
HAVING SUM(e.Poison) = 0) il
INNER JOIN Ingredient i1 ON il.UniqIngredient1 = i1.UniqIngredient
INNER JOIN Ingredient i2 ON il.UniqIngredient2 = i2.UniqIngredient
INNER JOIN IngredientEffectJT jt1 ON i1.UniqIngredient = jt1.UniqIngredient
INNER JOIN IngredientEffectJT jt2 ON i2.UniqIngredient = jt2.UniqIngredient
INNER JOIN Effect e ON jt1.UniqEffect = e.UniqEffect AND jt2.UniqEffect = e.UniqEffect
ORDER BY i1.Name, i2.Name, e.Name

Используя тот же запрос, я могу найти 2 комбинации ядовитых ингредиентов, которые не имеют эффектов зелья, простоизменив строку HAVING, чтобы проверить e.Potion вместо e.Poison.

Это все хорошо и хорошо, но когда я хочу представить третий ингредиент, вот тут-то и становится сложно.Я в тупике.Я могу изменить этот запрос, чтобы проверить 3 ингредиента, которые имеют одинаковый эффект, но это не то, что я хочу.Я хочу найти третий ингредиент, который по-разному влияет на один из ингредиентов.

Любая помощь?


РЕДАКТИРОВАТЬ


Обновление: так что после нескольких часов борьбы с этим я придумал большой, уродливый, медленный, трудный для выполнения запрос (я даже не помню, почему мне пришлось выполнить это безумное условие соединения в таблице EffectНо когда я изменяю его, весь запрос выполняется в 2 раза медленнее, так что на самом деле он быстрее, чем у меня, хотя я не знаю почему ...), что почти делает то, что я хочу.Это может быть как можно ближе, если только у кого-то нет других идей или способ улучшить мой новый запрос.

SELECT DISTINCT il.Name1, il.Name2, il.Name3, e.Name
FROM
(SELECT DISTINCT i1.UniqIngredient Ingredient1, i1.Name Name1, i2.UniqIngredient Ingredient2, i2.Name Name2, i3.UniqIngredient Ingredient3, i3.Name Name3
FROM Ingredient i1
INNER JOIN Ingredient i2 ON i1.UniqIngredient < i2.UniqIngredient
INNER JOIN Ingredient i3 ON i2.UniqIngredient < i3.UniqIngredient
INNER JOIN IngredientEffectJT jt1 ON i1.UniqIngredient = jt1.UniqIngredient
INNER JOIN IngredientEffectJT jt2 ON i2.UniqIngredient = jt2.UniqIngredient
INNER JOIN IngredientEffectJT jt3 ON i3.UniqIngredient = jt3.UniqIngredient
INNER JOIN Effect e ON (jt1.UniqEffect = e.UniqEffect AND (jt2.UniqEffect = e.UniqEffect OR jt3.UniqEffect = e.UniqEffect)) OR (jt2.UniqEffect = e.UniqEffect AND jt3.UniqEffect = e.UniqEffect)
WHERE (EXISTS (SELECT 1
               FROM IngredientEffectJT jt1
               INNER JOIN IngredientEffectJT jt2 ON jt1.UniqEffect = jt2.UniqEffect
               WHERE jt1.UniqIngredient = i1.UniqIngredient 
               AND jt2.UniqIngredient = i2.UniqIngredient)
       AND (EXISTS (SELECT 1
                    FROM IngredientEffectJT jt1
                    INNER JOIN IngredientEffectJT jt3 ON jt1.UniqEffect = jt3.UniqEffect
                    WHERE jt1.UniqIngredient = i1.UniqIngredient 
                    AND jt3.UniqIngredient = i3.UniqIngredient)
         OR EXISTS (SELECT 1
                    FROM IngredientEffectJT jt2
                    INNER JOIN IngredientEffectJT jt3 ON jt2.UniqEffect = jt3.UniqEffect
                    WHERE jt2.UniqIngredient = i2.UniqIngredient 
                    AND jt3.UniqIngredient = i3.UniqIngredient)))
       OR (EXISTS (SELECT 1
                  FROM IngredientEffectJT jt1
                  INNER JOIN IngredientEffectJT jt3 ON jt1.UniqEffect = jt3.UniqEffect
                  WHERE jt1.UniqIngredient = i1.UniqIngredient 
                  AND jt3.UniqIngredient = i3.UniqIngredient)
      AND EXISTS (SELECT 1
                  FROM IngredientEffectJT jt2
                  INNER JOIN IngredientEffectJT jt3 ON jt2.UniqEffect = jt3.UniqEffect
                  WHERE jt2.UniqIngredient = i2.UniqIngredient 
                  AND jt3.UniqIngredient = i3.UniqIngredient))
GROUP BY i1.UniqIngredient, i1.Name, i2.UniqIngredient, i2.Name, i3.UniqIngredient, i3.Name
HAVING SUM(e.Poison) = 0) il
INNER JOIN IngredientEffectJT jt1 ON il.Ingredient1 = jt1.UniqIngredient
INNER JOIN IngredientEffectJT jt2 ON il.Ingredient2 = jt2.UniqIngredient
INNER JOIN IngredientEffectJT jt3 ON il.Ingredient3 = jt3.UniqIngredient
INNER JOIN Effect e ON (jt1.UniqEffect = e.UniqEffect AND (jt2.UniqEffect = e.UniqEffect OR jt3.UniqEffect = e.UniqEffect)) OR (jt2.UniqEffect = e.UniqEffect AND jt3.UniqEffect = e.UniqEffect)
ORDER BY il.Name1, il.Name2, il.Name3, e.Name

Во внутреннем запросе:

FROM Ingredient i1
INNER JOIN Ingredient i2 ON i1.UniqIngredient < i2.UniqIngredient
INNER JOIN Ingredient i3 ON i2.UniqIngredient < i3.UniqIngredient

Это создает каждую возможную комбинацию из 3 ингредиентов, где порядок не имеет значения и ничего не повторяется.Затем Joins to IngredientEffectJT и Effect ... Я на самом деле не помню, для чего предназначено безумное соединение в Effect.Глядя на это, я подумал, что нужно убедиться, что эффект существует как минимум на 2 ингредиентах, но это то, что делает предложение WHERE.И упрощение этого объединения эффектов приводит к тому, что оно работает значительно медленнее, поэтому ... что угодно.

Тогда появится GROUP BY, чтобы я мог посчитать количество подходящих ядовитых эффектов.Так как мне пришлось сгруппировать по 3 ингредиентам, я теряю индивидуальные эффекты соответствия, поэтому мне нужно снова объединить все эти ингредиенты обратно к их эффектам и найти соответствующие эффекты.

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

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

Ответы [ 6 ]

5 голосов
/ 15 декабря 2011

Попробуйте это

declare @combos table (comboId int identity, ingredient1 int, ingredient2 int, ingredient3 int null)

--create table of all unique 2 and 3 ingredient combinations (unique potions)
insert int @combos (ingredient1, ingredient2, ingredient3)
select 
    distinct
    i1.ID,
    i2.ID,
    i3.ID
from
    ingredient i1
    inner join ingredient i2 on i1.ID < i2.ID
    left outer join ingredient i3 on i2.ID < i3.ID

--create table to hold mapping between unique combinations and ingredients
declare @combo_ingredient table (ComboId int, IngredientId int)

--insert into the mapping table
insert into @combo_ingredient (ComboId, IngredientId)
select ID, ingredient1 from @combos

insert into @combo_ingredient (ComboId, IngredientId)
select ID, ingredient1 from @combos

insert into @combo_ingredient (ComboId, IngredientId)
select ID, ingredient3 from @combos where ingredient3 is not null

--create table to hold mapping between unique combinations (potions) and the effects it will have
declare @combo_effect (comboId int, effectId int)

insert into @combo_effect (comboId, effectId)
select 
    c.ComboId, ec.EffectId
from
    @combo_ingredient c
    inner join effect_ingredient ec on c.IngredientId = ec.IngredientId
having
    count(*) > 1
group by 
    c.comboId, ec.EffectId

--remove combinations that include an ingredient that do not contribute to an effect
delete from @combo_effect ce
where ce.ComboId in (
    select 
        ci.ComboId 
    from 
        @combo_ingredient ci
        inner join effect_ingredient ei on ci.IngredientId = ei.IngredientId
        left outer join @combo_effect ce on ce.ComboId = ci.ComboId and ce.EffectId = ei.EffectId
    where 
        ce.ComboId is null
)

--you can then query combo_effect for whatever information you want
--all combos with no poison effects
select comboId from 
    @combo_effect ce 
    left outer join effect e on ce.effectId = e.effectId and e.PoisonFlag = 1
group by 
    comboId
having 
    Count(e.id) = 0
2 голосов
/ 16 декабря 2011

Хорошо, вот мой шанс.

Он основан на следующих требованиях:

  • Только зелья, без эффекта яда
  • Два или три ингредиента
  • Нет дубликатов (1-2-3, 3-2-1 одинаковы)
  • Все ингредиенты должны способствовать эффекту
  • Ингредиенты, которые вызывают эффект, должны быть исключены, если только они не дают другого эффекта

Я надеюсь, что имена таблиц и полей в порядке, я начал с собственных таблиц, но с ваших данных.

select ing1.name, ing2.name, coalesce(ing3.name, ' ') from
(
-- Gives all unique combinations of two or three ingredients
    select ing1.UniqIngredient as id1, ing2.UniqIngredient as id2, 0 as id3
    from Ingredient as ing1
    inner join Ingredient as ing2 on ing1.UniqIngredient < ing2.UniqIngredient
    UNION
    select ing1.UniqIngredient as id1, ing2.UniqIngredient as id2, ing3.UniqIngredient as id3
    from Ingredient as ing1
    inner join Ingredient as ing2 on ing1.UniqIngredient < ing2.UniqIngredient
    inner join Ingredient as ing3 on ing2.UniqIngredient < ing3.UniqIngredient
) as MainRequest
join Ingredient as ing1 on ing1.UniqIngredient = id1
join Ingredient as ing2 on ing2.UniqIngredient = id2
left outer join Ingredient as ing3 on ing3.UniqIngredient = id3
where
(   -- Check if ingredients have common positive effects that are not covered by 3 ingredients (when a 3rd ingredient is present)
    exists(
    select eff.UniqEffect, count(*)
    from /Effect eff
    join IngredientEffectJT link on link.UniqEffect = eff.UniqEffect
    where eff.potion = 1 and link.UniqIngredient in (id1, id2, id3)
    group by eff.UniqEffect
    having count(*) = 2)
    AND
    not exists(
    select eff.UniqEffect, count(*)
    from Effect eff
    join IngredientEffectJT link on link.UniqEffect = eff.UniqEffect
    where eff.potion = 1 and link.UniqIngredient in (id1, id2, id3)
    group by eff.UniqEffect
    having count(*) > 2
    )
)
-- Check if ingredients have no common negative effects
AND not exists(
    select eff.UniqEffect, count(*)
    from Effect eff
    join IngredientEffectJT link on link.UniqEffect = eff.UniqEffect
    where eff.poison = 1 and link.UniqIngredient in (id1, id2, id3)
    group by eff.UniqEffect
    having count(*) >= 2)
-- Check if every ingredient is participating (No alchemist likes a freerider)
AND exists(select link1.UniqIngredient
    from IngredientEffectJT link1
    inner join IngredientEffectJT link2 on link1.UniqEffect = link2.UniqEffect
    where link1.UniqIngredient = id1 and link2.UniqIngredient in (id2, id3))
AND exists(select link1.UniqIngredient
    from IngredientEffectJT link1
    inner join IngredientEffectJT link2 on link1.UniqEffect = link2.UniqEffect
    where link1.UniqIngredient = id2 and link2.UniqIngredient in (id1, id3))
AND (id3 = 0 or
    exists(select link1.UniqIngredient
    from IngredientEffectJT link1
    inner join IngredientEffectJT link2 on link1.UniqEffect = link2.UniqEffect
    where link1.UniqIngredient = id3 and link2.UniqIngredient in (id1, id2)))
2 голосов
/ 15 декабря 2011

Вот один пример.

;WITH IngredientCombinations AS
(
SELECT i1.UniqIngredient AS i1_UniqIngredient,
       i1.Name AS i1_Name,
       i2.UniqIngredient AS i2_UniqIngredient,
       i2.Name AS i2_Name,
       i3.UniqIngredient AS i3_UniqIngredient,
       i3.Name AS i3_Name,
       i1.UniqIngredient AS i1_UniqIngredientB,
       i2.UniqIngredient AS i2_UniqIngredientB,
       i3.UniqIngredient AS i3_UniqIngredientB       
FROM   Ingredient i1
       JOIN Ingredient i2
         ON i1.UniqIngredient < i2.UniqIngredient
       JOIN Ingredient i3
         ON i2.UniqIngredient < i3.UniqIngredient 
)
, UnpivotedIngredientCombinations AS
(
SELECT i1_UniqIngredient,
       i1_Name,
       i2_UniqIngredient,
       i2_Name,
       i3_UniqIngredient,
       i3_Name,
       UniqIngredient
FROM IngredientCombinations
UNPIVOT
   (UniqIngredient FOR idx IN 
      (i1_UniqIngredientB, i2_UniqIngredientB, i3_UniqIngredientB)
)AS unpvt),
Effects AS
(
SELECT uic.i1_Name,
       uic.i1_UniqIngredient,
       uic.i2_Name,
       uic.i2_UniqIngredient,
       uic.i3_Name,
       uic.i3_UniqIngredient,
       uic.UniqIngredient,
       e.Name,
       e.Poison,
       e.Potion,
       e.UniqEffect,
       COUNT(*) OVER (PARTITION BY i1_UniqIngredient, 
                                   i2_UniqIngredient,
                                   i3_UniqIngredient, 
                                   e.UniqEffect) AS Cnt
FROM   UnpivotedIngredientCombinations uic
       JOIN IngredientEffectJT iej
         ON iej.UniqIngredient = uic.UniqIngredient
       JOIN Effect e
         ON e.UniqEffect = iej.UniqEffect  
)
SELECT i1_Name,
       i2_Name,
       i3_Name
FROM   Effects
GROUP  BY i1_UniqIngredient,
          i2_UniqIngredient,
          i3_UniqIngredient,
          i1_Name,
          i2_Name,
          i3_Name
HAVING MAX(CASE
             WHEN Cnt = 2
                  AND Poison = 1 THEN 1
           END) IS NULL  
2 голосов
/ 15 декабря 2011

Не уверен, как это будет работать, но я думаю, что это намного проще:

select
          (select Name from Ingredient where ID = e1.UniqIngredient) as Ingredient1,
          (select Name from Ingredient where ID = jt1.UniqIngredient) as Ingredient2,
          (select Name from Ingredient where ID = jt2.UniqIngredient) as Ingredient3,
          ee1.Name as Effect1,
          ee2.Name as Effect2
   from IngredientEffectJT e1
       inner join IngredientEffectJT e2 on e1.UniqEffect < e2.UniqEffect and e1.UniqIngredient = e2.UniqIngredient
       inner join IngredientEffectJT jt1 on jt1.UniqEffect = e1.UniqEffect and e1.UniqIngredient != jt1.UniqIngredient
   inner join IngredientEffectJT jt2 on jt2.UniqEffect = e2.UniqEffect and e1.UniqIngredient != jt2.UniqIngredient and jt1.UniqIngredient != jt2.UniqIngredient
       inner join Effect ee1 on e1.UniqEffect = ee1.ID
       inner join Effect ee2 on e2.UniqEffect = ee2.ID
   where ee1.Poison = ee2.Poison
;

РЕДАКТИРОВАТЬ: Проверка забытого яда.

РЕДАКТИРОВАТЬ: try2: (отредактировано снова для обработки любого ингредиентакак общий, а не только с самым низким ID)

select
  (select Name from Ingredient where ID = i1) as Ingredient1,
  (select Name from Ingredient where ID = i2) as Ingredient2,
  (select Name from Ingredient where ID = i3) as Ingredient3,
  min(Poison) as Poison
  -- , group_concat(Name) as Effects
  from
(
select
      a.*,
      min(e.Name) as Name,
      min(e.Poison) as Poison
   from
(
   select -- straight_join
          i1.ID as i1,
          i2.ID as i2,
          i3.ID as i3
   from IngredientEffectJT e1
       inner join IngredientEffectJT e2 on e1.UniqEffect < e2.UniqEffect and e1.UniqIngredient = e2.UniqIngredient
       inner join Effect ee1 on e1.UniqEffect = ee1.ID
       inner join Effect ee2 on e2.UniqEffect = ee2.ID and ee1.Poison = ee2.Poison
       inner join IngredientEffectJT jt1 on jt1.UniqEffect = e1.UniqEffect and e1.UniqIngredient != jt1.UniqIngredient
       inner join IngredientEffectJT jt2 on jt2.UniqEffect = e2.UniqEffect and jt1.UniqIngredient != jt2.UniqIngredient and e1.UniqIngredient != jt2.UniqIngredient
       inner join Ingredient i1
         on (i1.ID = e1.UniqIngredient  and e1.UniqIngredient < jt1.UniqIngredient and e1.UniqIngredient < jt2.UniqIngredient)
         or (i1.ID = jt1.UniqIngredient and jt1.UniqIngredient < e1.UniqIngredient and jt1.UniqIngredient < jt2.UniqIngredient)
         or (i1.ID = jt2.UniqIngredient and jt2.UniqIngredient < jt1.UniqIngredient and jt2.UniqIngredient < e1.UniqIngredient)
       inner join Ingredient i3
         on (i3.ID = e1.UniqIngredient  and e1.UniqIngredient > jt1.UniqIngredient and e1.UniqIngredient > jt2.UniqIngredient)
         or (i3.ID = jt1.UniqIngredient and jt1.UniqIngredient > e1.UniqIngredient and jt1.UniqIngredient > jt2.UniqIngredient)
         or (i3.ID = jt2.UniqIngredient and jt2.UniqIngredient > jt1.UniqIngredient and jt2.UniqIngredient > e1.UniqIngredient)
       inner join Ingredient i2 on i2.ID = e1.UniqIngredient + jt1.UniqIngredient + jt2.UniqIngredient - i1.ID - i3.ID
     group by i1.ID, i2.ID, i3.ID
) as a
inner join IngredientEffectJT as jt on a.i1 = jt.UniqIngredient or a.i2 = jt.UniqIngredient or a.i3 = jt.UniqIngredient
inner join Effect e on jt.UniqEffect = e.ID
group by i1, i2, i3, e.ID
having count(*) >= 2
) as b
group by b.i1, b.i2, b.i3
having sum(Poison) = count(*) or sum(Poison) = 0
-- order by count(distinct Name) desc
 order by i1, i2, i3
;

EDIT3:

Для SQL Server замените строку group_concat () на:

  ,(
    (
    select min(e.Name) + ',' as [data()] from IngredientEffectJT jt
       inner join Effect e on jt.UniqEffect = e.ID
       where i1=jt.UniqIngredient or i2=jt.UniqIngredient or i3=jt.UniqIngredient
       group by jt.UniqEffect
       having COUNT(*) >= 2
       for xml path('')
    )
  ) as Effects
0 голосов
/ 16 декабря 2011

Этот работает довольно хорошо.http://rp.eliteskills.com/skyrim.html

0 голосов
/ 16 декабря 2011

Хотя решение Мартина Смита не полностью решает эту проблему, оно вдохновило меня на изучение использования CTE, и я думаю, что оно у меня есть.

После того, как я понял, что каждая комбинация из 3 ингредиентов - это действительно 2 разные комбинации из 2 ингредиентов, в которых есть один общий ингредиент, я решил найти все 2 комбинации ингредиентов, а затем найти каждую комбинацию из тех, которые имеют хотя бы один общий ингредиент и оба имеют эффект что другой не имеет.

Затем убедитесь, что каждая комбинация из 3 ингредиентов не имеет ядовитых эффектов (я уже знаю, что каждая отдельная комбинация из 2 ингредиентов не имеет ядовитых эффектов, но только потому, что А + В не имеет яда и В + С не имеет яда, не означает У A + B + C не будет яда. Возможно, что расчесывание A с C вызовет эффект яда).

Затем я присоединяю все 3 ингредиента обратно к таблице эффектов, чтобы отобразить, какие эффекты производятся с каждой комбинацией.

В моей системе этот запрос выполняется в течение 3 минут 50 секунд. Это не круто. Но, по крайней мере, я получаю результаты, которые хочу сейчас.

WITH Combination AS
(
    --Finds all 2 ingredient combinations that have shared effects that are not poisons
    select ROW_NUMBER() OVER (ORDER BY i1.Name, i2.Name) UniqCombination, i1.UniqIngredient UniqIngredient1, i2.UniqIngredient UniqIngredient2, COUNT(1) NumberOfEffects
    from Ingredient i1
    cross join Ingredient i2
    INNER JOIN IngredientEffectJT jt1 ON i1.UniqIngredient = jt1.UniqIngredient
    INNER JOIN IngredientEffectJT jt2 ON i2.UniqIngredient = jt2.UniqIngredient
    INNER JOIN Effect e ON jt1.UniqEffect = e.UniqEffect AND jt2.UniqEffect = e.UniqEffect
    WHERE i1.UniqIngredient < i2.UniqIngredient
    GROUP BY i1.UniqIngredient, i1.name, i2.UniqIngredient, i2.Name
    HAVING SUM(e.poison) = 0
),
Potion AS
(
    --Matches up all 2 ingredient combinations in the Combination CTE with the effects for that combination
    SELECT DISTINCT c.UniqCombination, c.UniqIngredient1, i1.Name Ingredient1, c.UniqIngredient2, i2.Name Ingredient2, e.UniqEffect, e.Name Effect
    FROM Combination c
    INNER JOIN Ingredient i1 ON c.UniqIngredient1 = i1.UniqIngredient
    INNER JOIN Ingredient i2 ON c.UniqIngredient2 = i2.UniqIngredient
    INNER JOIN IngredientEffectJT jt1 ON c.UniqIngredient1 = jt1.UniqIngredient
    INNER JOIN IngredientEffectJT jt2 ON c.UniqIngredient2 = jt2.UniqIngredient
    INNER JOIN Effect e ON jt1.UniqEffect = e.UniqEffect AND jt2.UniqEffect = e.UniqEffect

),
BigCombination AS
(
    --Matches 2 combinations together where 1 ingredient is the same in both combinations.
    SELECT c1.UniqIngredient1, CASE WHEN c1.UniqIngredient1 = c2.UniqIngredient1 THEN c1.UniqIngredient2 ELSE c2.UniqIngredient1 END UniqIngredient2, c2.UniqIngredient2 UniqIngredient3
    FROM Combination c1
    INNER JOIN Combination c2 ON (c1.UniqIngredient1 = c2.UniqIngredient1 OR c1.UniqIngredient2 = c2.UniqIngredient1 OR c1.UniqIngredient2 = c2.UniqIngredient2) AND c1.UniqCombination < c2.UniqCombination
    --This WHERE clause sucks because there are 2 different select queries that must run twice each.
    --They have to run twice because I have to EXCEPT 1 from 2 and 2 from 1 to make sure both combinations are contributing something new.
    WHERE EXISTS( SELECT p1.UniqEffect
                  FROM Potion p1
                  WHERE p1.UniqCombination = c1.UniqCombination
                  EXCEPT
                  SELECT p2.UniqEffect
                  FROM Potion p2
                  WHERE p2.UniqCombination = c2.UniqCombination)
    AND EXISTS( SELECT p2.UniqEffect
                FROM Potion p2
                WHERE p2.UniqCombination = c2.UniqCombination
                EXCEPT
                SELECT p1.UniqEffect
                FROM Potion p1
                WHERE p1.UniqCombination = c1.UniqCombination)
),
BigPotionCombination AS
(
    --Combinations were made only from other combinations that made potions, but it's possible the new
    --ingredients mixing together could create a new poison effect. This will remove combinations that create new poison effects
    SELECT DISTINCT c.*
    FROM BigCombination c
    INNER JOIN IngredientEffectJT jt1 ON c.UniqIngredient1 = jt1.UniqIngredient
    INNER JOIN IngredientEffectJT jt2 ON c.UniqIngredient2 = jt2.UniqIngredient
    INNER JOIN IngredientEffectJT jt3 ON c.UniqIngredient3 = jt3.UniqIngredient
    INNER JOIN Effect e ON (jt1.UniqEffect = e.UniqEffect AND (jt2.UniqEffect = e.UniqEffect OR jt3.UniqEffect = e.UniqEffect)) OR (jt2.UniqEffect = e.UniqEffect AND jt3.UniqEffect = e.UniqEffect)
    GROUP BY c.UniqIngredient1, c.UniqIngredient2, c.UniqIngredient3
    HAVING SUM(e.Poison) = 0
)

--Combinations have to be joined back to Effect again to display the effects that the potions have.
SELECT DISTINCT i1.Name Ingredient1, i2.Name Ingredient2, i3.Name Ingredient3, e.Name Effect
FROM BigPotionCombination c
INNER JOIN Ingredient i1 ON c.UniqIngredient1 = i1.UniqIngredient
INNER JOIN Ingredient i2 ON c.UniqIngredient2 = i2.UniqIngredient
INNER JOIN Ingredient i3 ON c.UniqIngredient3 = i3.UniqIngredient
INNER JOIN IngredientEffectJT jt1 ON c.UniqIngredient1 = jt1.UniqIngredient
INNER JOIN IngredientEffectJT jt2 ON c.UniqIngredient2 = jt2.UniqIngredient
INNER JOIN IngredientEffectJT jt3 ON c.UniqIngredient3 = jt3.UniqIngredient
INNER JOIN Effect e ON (jt1.UniqEffect = e.UniqEffect AND (jt2.UniqEffect = e.UniqEffect OR jt3.UniqEffect = e.UniqEffect)) OR (jt2.UniqEffect = e.UniqEffect AND jt3.UniqEffect = e.UniqEffect)
ORDER BY Ingredient1, Ingredient2, Ingredient3, Effect
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...