避免在查询中多次使用相同的子查询
在我重构的MMORPG服务器中,我有两个表。一个用于物品,一个用于法术。每个物品最多有5个法术,所以我采用了稀疏矩阵格式,其中有5列用于拼写ID。避免在查询中多次使用相同的子查询
该结构的原始设计者选择使用不支持引用的MyISAM,从而导致items表中包含具有不存在拼写ID的项目。我希望找出哪些项目具有不正确的拼写ID以便修复它们,并且可能最终转换为InnoDB。
到目前为止,我已经能够想出只有这个:
SELECT COUNT(*)
FROM items
WHERE spellid_1 NOT IN (SELECT entry FROM research.spell)
OR spellid_2 NOT IN (SELECT entry FROM research.spell)
OR spellid_3 NOT IN (SELECT entry FROM research.spell)
OR spellid_4 NOT IN (SELECT entry FROM research.spell)
OR spellid_5 NOT IN (SELECT entry FROM research.spell);
有没有更优雅的方式来做到这一点?
编辑: NULL spellid_n算作是有效的,因为它只是意味着该项目没有在插槽中的法术。
设计表格会让你更优雅,因此你在同一个表格中没有5个spellid列 - 也就是拥有一个item_spell表格,它允许每个item有任意数量的法术。除了更加面向未来的(当你发现你现在需要6个法术),您的查询就会变成:
SELECT COUNT(DISTINCT item_id)
FROM item_spells
WHERE spell_id NOT IN (SELECT entry FROM research.spell);
正因为如此,你不得不执行检查5次。
标准化步骤建议将是有用的(有一个多对多项目拼写关系的连接表)。非规范化版本的一个缺点是,物品的法术有一个隐含的排序,我们总是必须处理所有这些,例如检查一个物品是否有特定的法术。
但是,存储引擎使用5个相同的子查询来优化长sql,它不应该导致性能问题。另一种措辞是,使用SQL '99标准“与”条款:
WITH spellids(entry) AS SELECT entry FROM research.spell
SELECT COUNT(*)
FROM items
WHERE spellid_1 NOT IN spellids OR spellid_2 NOT IN spellids
OR spellid_3 NOT IN spellids OR spellid_4 NOT IN spellids
OR spellid_5 NOT IN spellids ;
不是要短得多,可惜MySQL不支持“与”条款(见this question)呢。
蜘蛛侠,好问题。请尝试以下操作:
SELECT COUNT(*)
FROM items i
LEFT JOIN research.spell spell1 ON i.spellid_1 = spell1.entry
LEFT JOIN research.spell spell2 ON i.spellid_2 = spell2.entry
LEFT JOIN research.spell spell3 ON i.spellid_3 = spell3.entry
LEFT JOIN research.spell spell4 ON i.spellid_4 = spell4.entry
LEFT JOIN research.spell spell5 ON i.spellid_5 = spell5.entry
WHERE spell1.entry IS NULL
OR spell2.entry IS NULL
OR spell3.entry IS NULL
OR spell4.entry IS NULL
OR spell5.entry IS NULL
的这里关键是要LEFT JOIN你research.spell表,以便它包括不具有给定的JOIN条件对应的行项目。然后过滤该连接右侧的表集合为空。这会为您提供左侧表(项目)中的行,而右侧表(research.spell)中没有相应的行。
编辑:
还要注意,我离开了你最初的SELECT COUNT(*)不变。这会给你一件或多件无效法术的总数。您需要将其更改为SELECT i.id或类似的东西来获取具有无效法术的物品的ID。
尝试,因为它被称为 “不逆转”:
SELECT COUNT(*)
FROM items
WHERE (SELECT entry FROM research.spell) NOT IN (spellid_1, spellid_2,
spellid_3, spellid_4,
spellid_5)
编辑:想到啊,只有1的值。那么你可以这样做在内部联接:
SELECT COUNT(*)
FROM items i
join (SELECT entry FROM research.spell) t
on t.entry NOT IN (spellid_1, spellid_2, spellid_3, spellid_4, spellid_5)
没错,但绝不会有每个项目超过5个法术,和普通的用例是在它的全部,所以我仍然相信稀疏获取项目矩阵更适合这一点。 – MoshiBin 2009-07-18 11:27:45
@Spidey与水不同,specs很少冻结。我会建议遵循托尼的建议来规范和面向未来。 – 2009-07-20 08:31:12
已投票。不回答这个问题。 – hobodave 2009-07-22 13:59:28