NodeJS Mysql Query with multiple fields in where clause - mysql

I'm working with MySQL for a while and also built an API in NodeJS including mySQL for work
Basically i need a query like "SELECT * FROM table WHERE fieldA=varA AND/OR fieldB=valB"
I'm using "mysql.format(sql, args)" to format my query, so i'm using ? and ?? in my Queries.
I would like to write a basic query, that i could use and feed all the needed fields and values
I tried the following ways
-> "SELECT * FROM table WHERE ?" with "{fieldA: varA, fieldB: varB}" as replacement for ?
that leads to "SELECT * FROM table WHERE fieldA='varA', fieldB='varB'"
-> "SELECT * FROM table WHERE ?? = ?" with "['fieldA', 'fieldB']" and "['varA', 'varB']" as replacements
what leads to "SELECT * FROM table WHERE fieldA, fieldB = 'varA', 'varB'"
For now i "only" need 2 different fields, so i could add fixed "fieldA=? AND/OR fieldB=?" and fill only the values. But i would like a dynamic way and give all the fields i could need in it and also if i use AND or OR in combining.
I didn't find anything like this in the documentation, maybe somebody here had stumbled upon before.
Or might it be the only solution to dynamically add some "AND/OR ?? = ?" to the query and fill the arguments array with fieldName, values one after the other?

Consider theses are the fields that you need to build your dynamic query string.
var fields = [{fieldName:"A",value:"one",operator:"AND"},{fieldName:"B",value:"two",operator:""}];
Now try building your query string with that array. For now I have considered only two fields but you can add as per your needs.
var fields = [{fieldName:"A",value:"one",operator:"AND"},{fieldName:"B",value:"two",operator:""}];
var query = "SELECT * FROM table WHERE ";
fields.forEach((f)=>{
query=query+`${f.fieldName} = ${f.value} `
if(f.operator!="") query=query+f.operator+" "
})
console.log(query)

I have built a method for this now, with hints from Ameers answer. It will generate a query like
SELECT * FROM ?? WHERE ??=? AND/OR ??=? AND/OR ??=?
and then will add the fieldName and values to the arguments array for mysql.format
var fieldValues = [
{ field: 'command', value: command.command, operator: 'OR' },
{ field: 'alias', value: command.command, operator: 'OR' }
];
exists(fieldValues) {
let query = 'SELECT * FROM ??';
let args = ['tablename'];
for (let i = 0; i < fieldValues.length; i++) {
let operator = i == 0 ? 'WHERE' : fieldValues[i].operator;
query += ` ${operator} ?? = ?`;
args.push(fieldValues[i].field);
args.push(fieldValues[i].value);
}
return mysql.query(query, args);
}
mysql.query() here is my wrapper around mysql.connection.query and using promises
Maybe there is a better builtin method, but i didn't find one yet and this way i can still use the builtin mysql-formatter function.

Consider,
that you have fields in DB like first_name, last_name, personal_email, work_email and office_name.
and you want to get all result matching with search fields.
i.e. let search = req.query.search.
It will become easy with ORM like Objection.js
So the query will be like this using objection.js
if (search) {
table_name.where('column_name.last_name', 'like', `%${search}%`)
.orWhere('column_name.first_name', 'like', `%${search}%`)
.orWhere('column_name.personal_email', 'like', `%${search}%`)
.orWhere('column_name.work_email', 'like', `%${search}%`)
.orWhere('column_name.office_name', 'like', `%${search}%`)
}
Note: This code is in nodejs and you can also use table_name you can also add model_name.

Related

sequelize fulltext query using parameters

Hi I'm currently trying to query records from db and these are the conditions
I receive 'order by', 'order (desc/asc)', 'limit', 'offset' from the frontend
I also need to search the record using match...against. 'like' is too slow for searching.
There's a mapped model with this query.
so I tried
let order_by = req.query.orderby;
let order = req.query.order;
let page = req.query.pagenum;
let perpage = req.query.parpage;
let searchword = req.query.foodsearch;
let offset = (parseInt(page) - 1) * parpage;
let foods = await models.food.findAll({
limit: parseInt(perpage),
offset: offset,
order: [
[order_by, order]
],
// where: Sequelize.literal
// (
// `MATCH
// (Name, Place, RestoNum, Ingredient, ChefName, Region...)
// AGAINST
// ( ? IN NATURAL LANGUAGE MODE)`,
// { replacements: [ searchword ] }
// )
});
but the commented part seems wrong in this code.
I tried the raw query, but then I can't parameterize those order by, order, offset, limit variables.
I don't want to just add them like ${orderby} because it's risky.
Please let me know if you have any solution for this issue.
Thank you in advance!
You're confusing the Sequelize.literal() and sequelizeInstance.query() APIs.
.literal() only take a string. If you want to use the object notation for your query, your commented code will work. Except that there is no second argument. You will need to concatenate-in or interpolate-in your search term into the AGAINST clause. Also, don't forget your quotes. The output of the literal() is essentially a string. Your MySQL FTS parameter will need the correct type of quotes around it, just as they would appear in your raw SQL query.
.query() DOES take an options parameter. Through this, you don't have to use string interpolation, you can use named replacements or bound-parameters. This will not only allow you to place in your searchword parameter, but whatever ORDER BY clause you want, as well.
I would go with Option 1. That's what we are doing for our FTS, in MS SQL.

IN clause in mysql nodejs

I have a simple nodejs application which executes the following query.
select * from User where userid in (?)
The userids i get is a JSON array send from client side. How can i use that in this select query ? I tried
1. As itself but not working.
2. Convert this to Javascript array, not working
If you are using node module like mysql, the 2nd approach should work.
var query=select * from User where userid in (?);
var data=['a','b','c'];
var queryData=[data];
conn.query(query, queryData, function (err, results) {})
According to the documentation, "Arrays are turned into list, e.g. ['a', 'b'] turns into 'a', 'b'". So this approach should work (I have used it practically).
If you pass an array to the parameter it works with node mysql2. Parameters are already passed as arrays, so your first parameter needs to be an array [[1,2,3]].
select * from User where userid in (?)
const mysql = require('mysql2/promise');
async function main(){
let db = await mysql.createPool(process.env.MYSQL_URL);
let SQL = 'select * from User where userid in (?)';
let [res, fields] = await db.query(SQL, [[1,2,3]]);
console.log(res)
return res;
}
main().then(() => {process.exit()})
Revisiting this, since the original approach on the question is valid, but with some caveats. If your only escaped argument is the one on the IN clause, then you have to specify it as nested array; something like: [['usrId1', 'usrId2', 'usrIdN']]. This is because the un-escaping functionality expects an array, replacing each '?' with the corresponding array element. So, if you want to replace your only '?' with an array, that array should be the first element of all arguments passed. If you had more than one '?', the syntax is more intuitive, but at the end consistent and the same; in this case, you could have your arguments similar to: ['myOtherArgument1', 'myOtherArgument2', ['usrId1', 'usrId2', 'usrIdN'], 'myOtherArgument3']
Something like this could work!
// get your possible IDs in an array
var ids = [1,2,3,4,5];
// then, create a dynamic list of comma-separated question marks
var tokens = new Array(ids.length).fill('?').join(',');
// create the query, passing in the `tokens` variable to the IN() clause
var query = `SELECT * FROM User WHERE userid IN (${tokens})`;
// perform the query
connection.query(query, ids, (err, data) => {
// do something with `err` or `data`
});
You can do like this:
select * from User where userid in (?,?,?,?)
var array = [];
array.push(value);
array.push(value);
array.push(value);
array.push(value);
then use array as parameter that should be bind.
// get query string data with commas
var param=req.params['ids'];
//damy data var param = [1,2,3,4,5];
var array = params.split(",").map(Number);
//Note in select query don't use " and ' ( inverted commas & Apostrophe)
// Just use ` (Grave accent) first key off numeric keys on keyboard before one
con.query(`select * from TB_NAME where COL IN(?)`,[array],(err,rows,fields)=>{
res.json(rows);
});
let val = ["asd","asd"]
let query = 'select * from testTable where order_id in (?)';
connection.query(query, [val], function (err, rows) {
});
In Node, you need to put array in the array.
Update: Please see this answer. It is the correct way to do what is asked in the question.
The methods I have tried are:
Expand JSON array to a string in the required format. Concatenate it with query using '+'. (Beware of SQL injections)
Dynamically add '?' using length of JSON array holding user ids. Then use the array to provide user ids.
Both works. I then changed my logic with a better approach so now i don't need then 'in' clause anymore.

PDO query does not return data when inserting date as variable

Im trying to get a hold of OOP and PDO. Did some tutorials. In the tutorial i got the query method (so thats not mine...)
but im having troubles with a pdo query
I want to select orders from the database matching a date..... de date comes from a datepicker and returns 2012-12-16 for example therefor
$dateInputQuery = date("Y-m-d", strtotime(Input::get('datepick')));
$data = $order->getAllOrders('order', 'WHERE DATE(orderdate) = DATE({$dateInputQuery})', false, false);
the strange thing is that when i replace the WHERE clause to WHERE DATE(orderdate) = \'2013-12-16\' it returns all the data but when inserting my date like above it does not....
in the db class the method looks like this
public function getAll($table, $where = NULL, $orderSort = NULL, $limit = NULL) {
$this->query("SELECT * FROM {$table} {$where} {$orderSort} {$limit}")->error();
return $this;
}
and query method in db class
public function query($sql, $params = array()) {
//reset error
$this->_error = false;
if ($this->_query = $this->_pdo->prepare($sql)) {
$x = 1;
if (count($params)) {
foreach ($params as $param) {
$this->_query->bindValue($x,$param);
$x++;
}
}
if ($this->_query->execute()) {
$this->_results = $this->_query->fetchAll(PDO::FETCH_OBJ);
$this->_count = $this->_query->rowCount();
} else {
$this->_error = true;
}
}
return $this;
}
why is this ?
Your immediate problem is caused the fact that $dateInputQuery is unquoted. Date is a string literal and should be quoted. And even though you can easily add quotes around it you really shouldn't do this. See next point.
order is a reserved word in MySQL, therefore the table name should be put in backticks
$data = $order->getAllOrders('`order`', "WHERE DATE(orderdate) = DATE('$dateInputQuery')", false, false);
^ ^ ^ ^
You're not leveraging parameter binding in query() function. Instead on top of it you're using query string interpolation leaving your code vulnerable to sql injections and diminishing the usage of prepared statements. When you use parameter binding you no longer need to quote parameter values.
Your sql query is not index-friendly. You shouldn't apply any functions (in your case DATE()) to the column you're searching on (orderdate). Instead you can rewrite your condition to apply necessary transformations/calculations to the arguments which are constants.
You should avoid using SELECT *. Read Which is faster/best? SELECT * or SELECT column1, colum2, column3, etc and Why is using '*' to build a view bad?
That being said your query should look something like
$sql = "SELECT order_id, orderdate, ...
FROM `order`
WHERE orderdate >= ?
AND orderdate < ? + INTERVAL 1 DAY";
And you should execute it
$this->query($sql, array($dateInputQuery, $dateInputQuery));
Instead of passing whole clauses (e.g. WHERE) you should pass values

Propel Query and Mysql aggregate functions: cannot use default syntax (<column> = ?, value) inside where filter

I'm having some troubles creating query conditions in Propel 1.6 using Mysql date functions, such as:
$query = PostQuery::create()->where('YEAR(Post.PublishedAt) = ?', $year)->find();
The following works:
$query = PostQuery::create()->where('YEAR(Post.PublishedAt) = 2011')->find();
But in order to handle a $year variable, I then have to write something like:
$query = PostQuery::create()->where(sprintf('YEAR(Post.PublishedAt) = %d', $year))->find();
which seems wrong.
I've also tried to specify param type by adding: PDO::PARAM_INT const:
$query = PostQuery::create()->where('YEAR(Post.PublishedAt) = ?', $year, PDO::PARAM_INT)->find();
This doesn't work.
UPD: There are no errors. Just an empty collection as a result.
Could anybody help me? Thanks in advance.
According to OP
// works
PostQuery::create()->where('YEAR(Post.PublishedAt) = 2011')->find();
// doesn't work
PostQuery::create()->where('YEAR(Post.PublishedAt) = ?', $year)->find();
This would indicate that PDO is quoting the $year variable so it's actually doing the following when using params
PostQuery::create()->where('YEAR(Post.PublishedAt) = \'2011\'')->find();
As detailed in this bug report https://bugs.php.net/bug.php?id=44639
Do either of these work
PostQuery::create()->where('YEAR(Post.PublishedAt) = CONVERT(?,UNSIGNED)', $year,PDO::PARAM_INT)->find();
or (this is slightly cleaner)
settype($year,'int');
PostQuery::create()->where('YEAR(Post.PublishedAt) = ?', $year,PDO::PARAM_INT)->find();
Seems a bit idiotic to have to do it that way but it should convert the numeric string into a MySQL UNSIGNED INT suitable for the WHERE statement. Either way the above are either having MySQL convert it into a numeric type explicitly, or alternatively trying to ensure that PHP has it explicitly as an INTEGER type before binding it.

Django: raw SQL queries with a dynamic number of variables

Is it possible to construct raw SQL queries in Django so that they accept a dynamic number of arguments? So for example say that I have the following url structure in my app:
/books/category/history/
/books/category/history/1800s/
For the first query, I'm looking for all books with the keyword 'history', and for the second, I'm looking for all books with the keyword 'history' AND the keyword '1800s'.
I currently have two separate queries for each of these:
keyword1 = 'history'
SELECT appname_book.name AS name FROM appname_book WHERE keyword=%s,[keyword1]
keyword1 = 'history'
keyword2 = '1800s'
SELECT appname_book.name AS name FROM appname_book WHERE keyword=%s AND keyword=%s,[keyword1, keyword2]
Anyone know of a cleaner and more efficient way to do this?
I'm using Django 1.3 and MySQL.
Thanks.
Why dont you use Django QuerySet, like this:
Book.objects.all().filter(keyword__in=['history','1800s']).values('name')
Another possible solution using RAW SQL, coud be:
keywords = []
SQL = 'SELECT appname_book.name AS name FROM appname_book WHERE 1=1 '
SQL += ' '.join(['AND keyword=%s' for _ in params])
Sure, you could do something like this to dynamically generate a raw SQL query
sql = 'SELECT id FROM table WHERE 1 = 1'
params = []
if 'description' in args.keys():
sql += ' AND description LIKE %s'
params.append('%'+args['description']+'%')
if 'is_active' in args.keys():
sql += ' AND is_active LIKE %s'
params.append(args['is_active'])
... you can put as many "ifs" you want to construct the query
with connections['default'].cursor() as cursor:
cursor.execute(sql, params)
This way would still be completely safe against SQL Injections vulnerability