web-dev-qa-db-fra.com

Approche de plusieurs requêtes MySQL avec Node.js

Je suis un débutant à la fois sur la programmation de style événement/rappel et NodeJS. J'essaie d'implémenter un petit serveur http qui sert des données ddbb en utilisant le module node-mysql.

Mes problèmes proviennent de la structuration des requêtes. Puisqu'il y a souvent des requêtes qui nécessitent l'exécution des résultats des requêtes précédentes, je ne peux pas toutes les exécuter simultanément (de manière asynchrone) et je suis obligé d'attendre certains résultats.

Ma première approche a été d'exécuter toutes les requêtes non dépendantes en même temps, puis de boucler jusqu'à ce qu'elles aient toutes défini un indicateur indiquant que j'ai terminé pour que je puisse continuer avec les requêtes dépendantes (synchronisées), mais je ne le fais pas. savoir si c'est la bonne approche.

Quelque chose comme ça:

function x(){
    var result_for_asynch_query_1 = null
    var result_for_asynch_query_2 = null

    mainLoop(){
        // call non-dependant query 1
        // call non-dependant query 2

        // loop until vars are != null

        // continue with queries that require data from the first ones
    }
}

//for each browser request
httpServer{
     call_to_x();
}.listen();

De cette façon, je peux gagner du temps dans le résultat final car je n'attends pas toutes les réponses de manière sérielle mais juste à la plus longue.

Existe-t-il un moyen commun de procéder? Tout modèle de conception que je ne suis pas en train de suivre?

24
luso

essayez de penser autrement (il y a une bonne introduction sur le flux asynchrone howtonode.org )

var db = get_link_or_pool();

do_queries( callback ) {
    db.query(sql1, function(err, res1) {
        if (err) {
             callback(err);
             return;
        }
        // use res1 ...
        db.query(sql2, function(err, res2) {
             if (err) {
                 callback(err);
                 return;
             }
             callback(null, res2); // think 'return'
        }
    });
}

request_handler(req) {
    do_queries( function(err, result) {
        if(err)
            report_error(err);
        else
            write_result(req, result);
    });
}
11
Andrey Sidorov

Il faut éviter la pyramide de Doom:

var express = require('express');
var Q = require('Q');
var app = express();

app.get('/',function(req,res){
    var mysql      = require('mysql');

    var connection = mysql.createConnection({
        Host     : 'localhost',
        user     : 'root',
        password : ''
    });

    connection.connect();

    function doQuery1(){
        var defered = Q.defer();
        connection.query('SELECT 1 AS solution',defered.makeNodeResolver());
        return defered.promise;
    }

    function doQuery2(){
        var defered = Q.defer();
        connection.query('SELECT 2 AS solution',defered.makeNodeResolver());
        return defered.promise;
    }

    Q.all([doQuery1(),doQuery2()]).then(function(results){
        res.send(JSON.stringify(results[0][0][0].solution+results[1][0][0].solution));
        // Hint : your third query would go here
    });

    connection.end();

});

app.listen(80);
console.log('Listening on port 80');

Cet échantillon montre un résultat qui dépend de 2 valeurs calculées indépendantes. Chacune de ces valeurs est interrogée dans doQuery1 et doQuery2. Ils sont exécutés en séquence, mais de manière asynchrone.

Ensuite, vous pouvez voir Q.all(... qui appelle essentiellement le rappel "then" en cas de succès. Dans ce rappel, le calcul est effectué.

L'utilisation de promesses (détails: Github Q: promesse pour Javascript et wikipedia ) permet de rendre votre code plus propre, de séparer le calcul et la gestion des résultats et de faire bouger les choses.

Regardez comme il serait facile d'ajouter "doQuery3" comme pré-requis pour votre calcul!

Et ci-dessous le "package.json" bellonging à l'exemple de code:

{
    "name": "hello-world",
    "description": "hello world test app",
    "version": "0.0.1",
    "private": true,
    "dependencies": {
        "express": "3.2.0",
        "q": "0.9.3",
        "mysql":"2.0.0-alpha7"
    }
}
43
programaths

Une autre solution consiste à concaténer toutes les instructions, en terminant chacune par un point-virgule. Par exemple, pour sélectionner parmi plusieurs tables, vous pouvez utiliser cette requête:

var sql = 'select * from user; select * from admin;'

Ensuite, vous pouvez utiliser une seule connexion pour exécuter les multiples instructions:

var connection = mysql.createConnection({multipleStatements: true}) connection.query(sql)

Remarque: plusieurs instructions sont désactivées par défaut pour empêcher l'injection SQL. Assurez-vous d'échapper correctement toutes les valeurs (voir la documentation) .

14
Hafiz Arslan

J'ai trouvé les informations ci-dessous très utiles pour surmonter ce problème:

Tiré de http://book.mixu.net/node/ch7.html - Beaucoup d'autres bons exemples ici !!

function async(arg, callback) {
  console.log('do something with \''+arg+'\', return 1 sec later');
  //replace setTimeout with database query  
  setTimeout(function() { callback(arg * 2); }, 1000);
}
// Final task (return data / perform further operations)
function final() { console.log('Done', results); }

// A simple async series:
var items = [ 1, 2, 3, 4, 5, 6 ];
var results = [];
function series(item) {
  if(item) {
    async( item, function(result) {
      results.Push(result);
      return series(items.shift());
    });
  } else {
    return final();
  }
}
series(items.shift());

"Prenez un ensemble d'éléments et appelez la fonction de flux de contrôle de série avec le premier élément. La série lance une opération async () et lui passe un rappel. Le rappel pousse le résultat dans le tableau de résultats, puis appelle la série avec le suivant élément dans le tableau des éléments. Lorsque le tableau des éléments est vide, nous appelons la fonction final (). " (depuis http://book.mixu.net/node/ch7.html )

4
Dave