2009-07-18 5 views
3

Dans un serveur MMORPG que je suis en train de refactoriser, j'ai deux tables. Un pour les objets, et un pour les sorts. Chaque objet a jusqu'à 5 sorts, donc je suis allé avec un format de matrice clairsemée, ayant 5 colonnes pour les ID de sorts.Éviter d'utiliser la même sous-requête plusieurs fois dans une requête

Les concepteurs originaux de cette structure ont choisi d'utiliser MyISAM qui ne prend pas en charge le référencement, ce qui fait que la table des éléments contient des éléments avec des ID de sorts inexistants. Je souhaite savoir quels éléments ont des ID de sorts incorrects afin de les corriger et peut-être à la longue convertir en InnoDB.

Jusqu'à présent, j'ai pu venir avec seulement ceci:

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); 

Y at-il une façon plus élégante de le faire?

EDIT: NULL spellid_n compte comme valide car cela signifie que l'élément n'a pas un sort dans cet emplacement.

Répondre

2

Il aurait été plus élégant de concevoir les tables de manière à ce que vous n'ayez pas 5 colonnes spellid dans la même table, c'est-à-dire d'avoir une table item_spell qui autoriserait un nombre illimité de sorts. En plus d'être plus à l'épreuve (lorsque vous vous trouvez besoin maintenant 6 sorts), votre requête deviendrait:

SELECT COUNT(DISTINCT item_id) 
    FROM item_spells 
WHERE spell_id NOT IN (SELECT entry FROM research.spell); 

Comme il est, vous êtes obligé d'effectuer la vérification 5 fois.

+0

est vrai, mais il n'y aura jamais plus de 5 sorts par article, et l'utilisation cas commun est d'aller chercher un article dans son intégralité, donc je crois encore rares matrice correspond mieux à cela. – MoshiBin

+0

@Spidey Contrairement à l'eau, les spécifications gèlent rarement. Je suggérerais de suivre le conseil de Tony pour normaliser et pérenniser. –

+1

Voté en baisse. Ne répond pas à la question. – hobodave

0

L'étape de normalisation proposée serait utile (pour avoir une table de connexion pour la relation plusieurs-à-plusieurs élément-épeler). Un inconvénient de la version dénormalisée est que les sorts d'objets ont un ordre implicite, nous devons toujours les traiter tous, par exemple en vérifiant si un élément a un sort spécifique ou non.

Toutefois, le moteur de stockage optimise le SQL long avec la sous-requête 5 identiques, il ne devrait pas causer de problèmes de performance. Un phrasé alternative serait, en utilisant le SQL '99 norme 'avec' clause:

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 ; 

Pas beaucoup plus court, et, malheureusement, MySQL ne supporte pas la clause 'avec' (voir this question) encore.

0

Spidey, bonne question. Effectuez les actions suivantes:

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 

La clé ici est que vous voulez LEFT JOIN votre table de research.spell, de sorte qu'il comprend des éléments qui ne possèdent pas de ligne correspondante pour la condition join donnée. Ensuite, vous filtrez ce jeu de tables où le côté droit de la jointure est NULL. Cela vous donne des lignes de la table de gauche (items) sans ligne correspondante dans la table de droite (research.spell).

EDIT:

Notez également, que je quitte votre compte initial SELECT (*) inchangé. Cela vous donnera le nombre total d'éléments qui ont 1 ou plusieurs sorts invalides. Vous devrez le changer pour SELECT i.id ou quelque chose de similaire pour récupérer les ID des éléments qui ont des sorts invalides.

+0

Cela se rapproche de ce que je suis après. J'ai mis à jour la table des items parce que les mainteneurs de DB précédents pensaient que l'utilisation d'un mélange de 0 et -1 au lieu de NULL était préférable. Toutefois, cette requête renvoie également des éléments avec des colonnes NULL spellid_n. On dirait que j'ai négligé de mentionner que je ne les considère pas comme invalides, ce dont je m'excuse. – MoshiBin

0

essayer le "inverse pas" comme on l'appelle:

SELECT COUNT(*) 
FROM items 
WHERE (SELECT entry FROM research.spell) NOT IN (spellid_1, spellid_2, 
               spellid_3, spellid_4, 
               spellid_5) 

EDIT: ah pensé qu'il y avait seulement 1 valeur. alors vous pouvez le faire dans une jointure interne:

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) 
+0

Erreur 1242: sous-requête renvoie plus de 1 ligne. Je ne vois pas comment cela fonctionnera de toute façon, puisqu'il vérifie si le plus grand groupe est dans le petit groupe. – MoshiBin

+0

Il a fallu 5 minutes 53 minutes à MySQL pour utiliser la nouvelle requête. Opposé à 3 secondes de ma requête originale, je pense que quelque chose ne va pas – MoshiBin

+0

lol. ok alors ce n'est certainement pas utile :) –

Questions connexes