web-dev-qa-db-ja.com

Node.jsを使用した複数のMySQLクエリへのアプローチ

私は、イベント/コールバックスタイルのプログラミングとNodeJSの両方の初心者です。 node-mysqlモジュールを使用してddbbデータを提供する小さなhttpサーバーを実装しようとしています。

私の問題は、クエリの構造化に起因しています。多くの場合、以前のクエリの結果を実行する必要があるクエリがあるため、それらすべてを同時に(非同期で)実行することはできず、結果を待機する必要があります。

私の最初のアプローチは、すべての非依存クエリを同時に実行し、それらがすべて完了したことを示すフラグを設定するまでループすることでしたので、依存(同期)クエリを続行できますが、これが正しいアプローチかどうかを知る。

このようなもの:

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

このようにして、すべての応答を連続して待機するのではなく、最長の応答のみを待機するため、最終結果の時間を節約できます。

これを行う一般的な方法はありますか?私がフォローしていないデザインパターンはありますか?

24
luso

他の方法で考えてみてください(非同期フローの良い紹介があります 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

運命のピラミッドを避けるべきです:

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

このサンプルは、2つの独立した計算値に依存する結果を示しています。これらの各値は、doQuery1およびdoQuery2で照会されます。これらは順番に実行されますが、非同期に実行されます。

次に、Q.all(...基本的に、成功時に「then」コールバックを呼び出します。そのコールバック内で、計算が行われます。

Promise(詳細: Github Q:Javascriptのpromise および wikipedia )を使用すると、コードをよりクリーンにし、計算と結果の処理を分離し、物事を移動できます。

計算の前提条件として「doQuery3」を追加するのがどれほど簡単かを見てください。

そして、サンプルコードに続く「package.json」を以下に示します。

{
    "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

別の解決策は、すべてのステートメントを連結し、各ステートメントをセミコロンで終了することです。たとえば、複数のテーブルから選択するには、次のクエリを使用できます。

_var sql = 'select * from user; select * from admin;'_

次に、1つの接続のみを使用して複数のステートメントを実行できます。

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

注:SQLインジェクションを防ぐため、デフォルトでは複数のステートメントが無効になっています。すべての値を適切にエスケープしてください (ドキュメントを参照)

13
Hafiz Arslan

私はこの問題を克服するのに非常に役立つ以下を見つけました:

http://book.mixu.net/node/ch7.html から取得-他にも多くの素晴らしい例がここにあります!!

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

「アイテムのセットを取得し、最初のアイテムでシリーズ制御フロー関数を呼び出します。シリーズは1つのasync()操作を起動し、それにコールバックを渡します。コールバックは結果を結果配列にプッシュし、次に次でシリーズを呼び出しますアイテム配列のアイテム。アイテム配列が空の場合、final()関数を呼び出します。」 ( http://book.mixu.net/node/ch7.html から)

4
Dave