2009-07-18 185 views
3

在我重构的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算作是有效的,因为它只是意味着该项目没有在插槽中的法术。

回答

2

设计表格会让你更优雅,因此你在同一个表格中没有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次。

+0

没错,但绝不会有每个项目超过5个法术,和普通的用例是在它的全部,所以我仍然相信稀疏获取项目矩阵更适合这一点。 – MoshiBin 2009-07-18 11:27:45

+0

@Spidey与水不同,specs很少冻结。我会建议遵循托尼的建议来规范和面向未来。 – 2009-07-20 08:31:12

+1

已投票。不回答这个问题。 – hobodave 2009-07-22 13:59:28

0

标准化步骤建议将是有用的(有一个多对多项目拼写关系的连接表)。非规范化版本的一个缺点是,物品的法术有一个隐含的排序,我们总是必须处理所有这些,例如检查一个物品是否有特定的法术。

但是,存储引擎使用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)呢。

0

蜘蛛侠,好问题。请尝试以下操作:

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。

0

尝试,因为它被称为 “不逆转”:

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)