在 MySQL 上使用 Promise 进行并行插入查询失败

Using Promise for parallel insert query on MySQL fails

我为 运行 在 Node.js 中并行插入查询编写了代码,我也在使用 Promise.js。

但是代码失败并引发了 "Duplicate Primary Key" 条目的异常。

代码如下,

 var Promise = require("promise");
 var mySql = require("mysql");
 var _ = require("underscore");

 var connection = mySql.createConnection({
   host : "localhost",
   user : "root",
   password : "rahul",
   database : "testDb" //schema
 });

 connection.connect();

function insertDept(name){
   return new Promise(fn);

   function fn(resolve,reject){
    getMaxDept().then(function(rows){
        var deptId = rows[0]["DeptId"];
        deptId = (_.isNull(deptId) === true) ? 125 : deptId;
        var sql = "insert into departmentTbl values("+deptId+",'"+name+"')";
        console.log(sql);
        connection.query(sql,function(err,rows,fields){
            if(err){
                console.log(err);
                return reject(err);
            }else{
                return resolve(rows);
            }
        });

    }).catch(function(error){
        return reject(error);
    }); 
   }//fn            
 }//insertDept

function getMaxDept(){
return new Promise(fn);

function fn(resolve,reject){
    var sql = "select max(deptId) + 1 as 'DeptId' from departmentTbl";

    connection.query(sql,function(err,rows,fields){
        if(err){
            console.log(err.stack);

            return reject(err);
        }else{
            return resolve(rows);
        }
    });
  }// fn    
} //getMaxDept 

function createDeptForAll(){
  var promiseObj = [];

  if(arguments.length > 0){
    _.each(arguments,callback);
  }else{
    throw "No departments passed";
  }

  function callback(deptName){
    promiseObj.push(insertDept(deptName))       
  }

  return Promise.all(promiseObj);
}//createDeptForAll


  createDeptForAll("Archiology","Anthropology").then(function(createDepartment){
   createDepartment.then(function(rows){
    console.log("Rows inserted "+rows["affectedRows"]);
  }).catch(function(error){
    console.log(error);
  }).done(function(){
    connection.end();
  });
});

当我运行以上代码代码时, 输出是

rahul@rahul:~/myPractise/NodeWebApp/NodeMySqlv1.0$ node queryUsingPromise02.js 
insert into departmentTbl values(125,'Archiology')
insert into departmentTbl values(125,'Anthropology')
{ [Error: ER_DUP_ENTRY: Duplicate entry '125' for key 'PRIMARY'] code: 'ER_DUP_ENTRY', errno: 1062, sqlState: '23000', index: 0 }

由于 Department Id 是主键并且 promises 运行 是并行的, 第二部门的插入查询主键失败。

如您所见,在任何插入查询之前,我获取部门的最大值 + 1。

如果上述查询失败,我分配'125'。

现在,我应该更改什么以使我上面编写的代码 运行s。

我应该使用 "before insert" 的触发器在数据库级别本身计算 "department ID" 的主键的下一个值,还是应该在我自己的 Node.js 代码中做一些事情?

此问题不限于节点或 JavaScript,但您会遇到任何尝试并行写入 SQL 数据库的技术的问题。在这种情况下生成唯一 ID 并非易事。

如果您可以选择这样做,请在您的数据库中创建您的 id 字段 AUTO_INCREMENT,这会在这种情况下为您省去很多麻烦。

More about AUTO_INCREMENT.

AUTO_INCREMENT 上的建议看起来不错。

您也可以考虑为 connection.query() 编写一个 promisier,以便整理剩余的代码。

因此,在清除了 getMaxDept() 并安装了 connection.queryAsync() 实用程序后,您可能会得到类似这样的结果:

var Promise = require("promise");
var mySql = require("mysql");

var connection = mySql.createConnection({
   host: "localhost",
   user: "root",
   password: "rahul",
   database: "testDb" //schema
});

connection.connect();

// promisifier for connection.query()
connection.queryAsync = function(sql) {
    return new Promise((resolve, reject) => {
        connection.query(sql, (err, rows, fields) => {
            if(err) { reject(err); }
            else { resolve({'rows':rows, 'fields':fields}); }
        });
    });
};

function insertDept(name) {
    var sql = "insert into departmentTbl values(" +  name + "')"; // assumed - needs checking
    return connection.queryAsync(sql);
}

function createDeptForAll(departments) {
    if(departments.length > 0) {
        return Promise.all(departments.map(insertDept));
    } else {
        return Promise.reject(new Error('No departments passed'));
    }
}

createDeptForAll(['Archiology', 'Anthropology']).then((results) => {
    results.forEach((result) => {
        console.log("Rows inserted " + result.rows.affectedRows);
        connection.end();
    });
}).catch((error) => {
    console.log(error);
    connection.end();
});