web-dev-qa-db-fra.com

Boucler sur les jeux de résultats dans MySQL

J'essaie d'écrire une procédure stockée dans MySQL qui effectuera une requête de sélection assez simple, puis de survoler les résultats afin de décider s'il convient d'exécuter des requêtes supplémentaires, des transformations de données ou de les supprimer complètement. En fait, je veux implémenter ceci:

$result = mysql_query("SELECT something FROM somewhere WHERE some stuff");
while ($row = mysql_fetch_assoc($result)) {
    // check values of certain fields, decide to perform more queries, or not
    // tack it all into the returning result set
}

Seulement, je le veux seulement dans MySQL, donc ça peut être appelé comme une procédure. Je sais que pour les déclencheurs, il y a le FOR EACH ROW ... _ syntaxe, mais je ne trouve aucune mention de ce genre pour une utilisation en dehors de la CREATE TRIGGER ... syntaxe. J'ai lu quelques-uns des mécanismes de bouclage de MySQL, mais jusqu'à présent, tout ce que je peux imaginer, c'est que j'implémenterais quelque chose comme ceci:

SET @S = 1;
LOOP
    SELECT * FROM somewhere WHERE some_conditions LIMIT @S, 1
    -- IF NO RESULTS THEN
    LEAVE
    -- DO SOMETHING
    SET @S = @S + 1;
END LOOP

Même si cela reste un peu flou dans mon esprit.

Pour référence, bien que je ne pense pas que ce soit nécessairement pertinent, la requête initiale associera quatre tables pour former un modèle d'autorisations hiérarchiques, puis en fonction du niveau de la chaîne d'une autorisation spécifique, elle récupérera des informations supplémentaires sur les enfants à qui cette permission devrait être héritée.

54
Dereleased

Quelque chose comme cela devrait faire l'affaire (Cependant, lisez après l'extrait pour plus d'informations)

CREATE PROCEDURE GetFilteredData()
BEGIN
  DECLARE bDone INT;

  DECLARE var1 CHAR(16);    -- or approriate type
  DECLARE Var2 INT;
  DECLARE Var3 VARCHAR(50);

  DECLARE curs CURSOR FOR  SELECT something FROM somewhere WHERE some stuff;
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET bDone = 1;

  DROP TEMPORARY TABLE IF EXISTS tblResults;
  CREATE TEMPORARY TABLE IF NOT EXISTS tblResults  (
    --Fld1 type,
    --Fld2 type,
    --...
  );

  OPEN curs;

  SET bDone = 0;
  REPEAT
    FETCH curs INTO var1,, b;

    IF whatever_filtering_desired
       -- here for whatever_transformation_may_be_desired
       INSERT INTO tblResults VALUES (var1, var2, var3 ...);
    END IF;
  UNTIL bDone END REPEAT;

  CLOSE curs;
  SELECT * FROM tblResults;
END

Quelques points à considérer ...

En ce qui concerne l'extrait ci-dessus:

  • souhaitera peut-être transmettre une partie de la requête à la procédure stockée, notamment les critères de recherche, afin de la rendre plus générique.
  • Si cette méthode doit être appelée par plusieurs sessions, etc., vous souhaiterez peut-être passer un ID de session de type pour créer un nom de table temporaire unique (ce qui est inutile en réalité, car différentes sessions ne partagent pas le même espace de noms de fichiers temporaires; voir le commentaire de Gruber, ci-dessous). )
  • Quelques parties telles que les déclarations de variable, la requête SELECT, etc. doivent être correctement spécifiées

Plus généralement: en essayant d'éviter d'avoir besoin d'un curseur.

J'ai volontairement appelé la variable curseur curs [e], car les curseurs sont une bénédiction mixte. Ils peuvent nous aider à implémenter des règles métier compliquées qu'il peut être difficile d'exprimer sous la forme déclarative de SQL, mais cela nous amène ensuite à utiliser la forme procédurale (impérative) de SQL, qui est une caractéristique générale de SQL qui n'est pas très conviviale/expressif, en termes de programmation et souvent moins efficace en termes de performances.

Peut-être pourriez-vous chercher à exprimer la transformation et le filtrage souhaités dans le contexte d'une requête SQL "simple" (déclarative).

74
mjv

Utilisez les curseurs.

Un curseur peut être considéré comme un lecteur protégé lors de la lecture d’un document. Si vous considérez chaque ligne comme une ligne dans un document, lisez la ligne suivante, effectuez vos opérations, puis faites avancer le curseur.

4
AlishahNovin