我完全不希望在这里得到任何答案,但无论如何我都会尝试.
所以这出演了天际.我想要一种简单的方法来查找哪些成分可以组合成不同的药水/毒药,所以我制作了一个有ID和名字的成分表; 具有ID,名称,毒药标志和药水标志的效果表(药水和毒药是互斥的); 以及具有成分ID和效果ID的连接表.
因此它的工作方式是每种成分都有4种不同的效果,对多种成分重复效果.在游戏中你可以组合2或3种成分,结果是一种药水或毒药,所有的效果都与至少2种成分相匹配.因此,如果您使用3种成分并且effect1同时出现在ingredient1和ingredient2上,而effect2同时出现在ingredient1和ingredient3上,那么结果将是具有effect1和effect2的药水/毒药.
我能够自己提出一个查询,它将显示每种可能的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
成分与成分交叉连接以获得每种组合,但由于成分的顺序无关紧要,我最终会得到两倍的结果.这就是WHERE检查i1.UniqIngredient <i2.UniqIngredient的原因.我只会看到每种组合一次,2种成分的较低ID将始终位于第1列.我将两种成分加入到相同的效果中,因为我只关心产生结果的组合.然后我用2种成分对它们进行分组并计算它们共有多少毒害效果,因为我只想要具有0种毒素效果的组合.
然后我将这个结果用作一个表格,我将其加入到成分和效果表中,以获得生成药水的每种可能的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种成分,但这不是我想要的.我想找到一种与其中一种成分具有不同效果的第三种成分.
有帮助吗?
编辑
更新:所以经过几个小时的挣扎,我想出了一个大的,丑陋的,缓慢的,难以理解的查询(我实际上甚至不记得为什么我必须在效果表上做那个疯狂的连接条件.但是当我改变它整个查询慢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种成分的每种可能组合,其中顺序无关紧要,没有重复.然后加入到IngredientEffectJT和Effect ......我实际上不记得效果的疯狂加入是什么.看一下,我认为确保至少有两种成分存在效果,但这就是WHERE子句正在做的事情.并简化效果连接会使其运行速度明显变慢,所以......无论如何.
那么GROUP BY就在那里,所以我可以计算匹配毒药效果的数量.由于我不得不按照3种成分进行分组,因此我失去了单独的匹配效果,因此我需要将所有这些成分重新加入到它们的效果中并找到匹配的效果.
该查询的问题在于它将显示所有3种成分具有相同效果的组合.这些组合毫无意义,因为你可以通过仅使用其中的2个来做同样的事情,这样就有点浪费了.
所以,这是我能想到的最好的.它真的很慢所以也许我只是将它保存到一个新表中,以便将来再次查询更容易,更快.
试试这个
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
虽然 Martin Smith 的解决方案并没有完全解决这个问题,但它确实激发了我研究使用 CTE,我想我明白了。
一旦我意识到每 3 种成分组合实际上是 2 种不同的 2 种成分组合,它们共享 1 种共同成分,我决定找到所有 2 种成分组合,然后找到至少具有 1 种共同成分且两者都具有其他成分的效果的组合。不具有。
然后检查以确保每 3 种成分组合都没有毒性作用(我已经知道每种 2 种成分组合没有毒性作用,但仅仅因为 A+B 没有毒且 B+C 没有毒并不意味着 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
| 归档时间: | 
 | 
| 查看次数: | 535 次 | 
| 最近记录: |