Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to build dynamic query by binding parameters in node.js-sql?

Tags:

node.js

mysql

I'm using nodejs-mysql module to do query in node.js recently, and in my working case I could only use the parameter-binding syntax like:

SELECT * FROM table WHERE name = ?

Now I want to build dynamic sql with these ? OR ?? parameters. Assume that I have 2 conditions(name and age) which either of them could be null (if user doesn't provide it), So I want to build MySQL in 3 cases:

  1. only name=Bob: SELECT * FROM table WHERE name = 'Bob'
  2. only age=40: SELECT * FROM table WHERE age > 40
  3. both: SELECT * FROM table WHERE name = 'Bob' AND age > 40

I know it's easy if you build the query on your own, but how can I achieve it when using placeholders which can only bind field or values ?

In document of nodejs-mysql, placeholder ? only stands for values and ?? stands for fields:

  • https://github.com/felixge/node-mysql/#escaping-query-values
  • https://github.com/felixge/node-mysql/#escaping-query-identifiers

My first thinking of solution is to insert query piece by using these placeholders, but it comes to failure because both ? and ?? will escape my query piece, and my query will be executed incorrectly.

My code so far is as below, which I'm defenitly sure it's not correct because query piece has been escaped:

// achieve paramters from url request
var condition = {};
if(params.name)condition["name"] = ["LIKE", "%" + params.name + "%"];
if(params.age)condition["age"] = parseInt(params.age, 10);

//build query
var sqlPiece = buildQuery(condition);

//try to replace ? with query
var sql = 'SELECT * FROM table WHERE ?';

connection.query(sql, sqlPiece, function(err, results) {
  // do things
});

// my own query build function to proceed conditions
function buildQuery(condition) {
  var conditionArray = [];
  for(var field in condition){
    var con = condition[field];
    if(con !== undefined){
      field = arguments[1] ? arguments[1] + "." + field : field;
      var subCondition;
      if(con instanceof Array) {
        subCondition = field + " " + con[0] + " " + wrapString(con[1]);
      }else{
        subCondition = field + " = " + wrapString(con);
      }
      conditionArray.push(subCondition);
    }
  }
  return conditionArray.length > 0 ? conditionArray.join(" AND ") : "1";
}

//wrap string value
function wrapString(value){
  return typeof value === "string" ? "'" + value + "'" : value;
}

So is there any way I can fix this problem?

Update

Thanks to Jordan's Offer, it's working, but :

I know building query by string concat is very good, but in my case I can't use that, because I'm using some middleware or handle mysql and controller, so what I can do is to define interface, which is a sql string with placeholders. So, the interface string is predefined before, and I can't modify it during my controller function.

like image 846
Bandon Avatar asked Aug 05 '15 03:08

Bandon


People also ask

What are the three ways that dynamic SQL can be executed?

What are the three ways that Dynamic SQL can be executed? Writing a query with parameters. Using EXEC. Using sp_executesql.

How do I run a dynamic SQL query?

To run a dynamic SQL statement, run the stored procedure sp_executesql as shown below : EXEC sp_executesql N'SELECT statement'; Use prefix N with the sp_executesql to use dynamic SQL as a Unicode string.


2 Answers

For Inserting into MYSQL like DB:

function generateInsertQuery(data, tableName) {
    let part1 = `INSERT INTO ${tableName} (`;
    let part2 = ")",
        part3 = "VALUES (",
        part4 = ")";
    let tableKeys = "",
        tableValues = "";
    for (let key in data) {
        tableKeys += `${key},`;
        tableValues += `'${data[key]}',`
    }

    tableKeys = tableKeys.slice(0, -1);
    tableValues = tableValues.slice(0, -1);
    let query = `${part1}${tableKeys}${part2} ${part3}${tableValues}${part4}`;
    return query;
}

generateInsertQuery({name: "Sam", tel: 09090909, email: "[email protected]"}, "Person")

Output:

INSERT INTO Person (name,tel,email) VALUES ('Sam','9090909','[email protected]');

Code Snippet for Update query:

function generateUpdateQuery(data, tableName, clauseKey, clauseValue) {
    let part1 = `UPDATE ${tableName} SET`;
    let part2 = `WHERE ${clauseKey} = ${clauseValue};`; //Add any number of filter clause statements here
    let updateString = "";
    for (let key in data) {
        updateString += `${key} = '${data[key]}',`;
    }
    updateString = updateString.slice(0, -1);
    let query = `${part1} ${updateString} ${part2}`;
    return query;
}

generateUpdateQuery({
    name: "Tanjiro",
    tel: 77777777,
    email: "[email protected]"
}, "Person", "ID", 111);

Output:

UPDATE Person SET name = 'Tanjiro',tel = '77777777',email = '[email protected]' WHERE ID = 111;
like image 180
Fayaz Avatar answered Nov 16 '22 02:11

Fayaz


You're off to a really good start, but you may have been overthinking it a bit. The trick is to build a query with placeholders (?) as a string and simultaneously build an array of values.

So, if you have params = { name: 'foo', age: 40 }, you want to build the following objects:

where = 'name LIKE ? AND age = ?';
values = [ '%foo%', 40 ];

If you only have { name: 'foo' }, you'll build these instead:

where = 'name LIKE ?';
values = [ '%foo%' ];

Either way, you can use those objects directly in the query method, i.e.:

var sql = 'SELECT * FROM table WHERE ' + where;
connection.query(sql, values, function...);

How do we build those objects, then? In fact, the code is really similar to your buildQuery function, but less complex.

function buildConditions(params) {
  var conditions = [];
  var values = [];
  var conditionsStr;

  if (typeof params.name !== 'undefined') {
    conditions.push("name LIKE ?");
    values.push("%" + params.name + "%");
  }

  if (typeof params.age !== 'undefined') {
    conditions.push("age = ?");
    values.push(parseInt(params.age));
  }

  return {
    where: conditions.length ?
             conditions.join(' AND ') : '1',
    values: values
  };
}

var conditions = buildConditions(params);
var sql = 'SELECT * FROM table WHERE ' + conditions.where;

connection.query(sql, conditions.values, function(err, results) {
  // do things
});
like image 33
Jordan Running Avatar answered Nov 16 '22 03:11

Jordan Running