2009-07-18 179 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)