What is the best index for this query - if condition? - mysql

I have a query with multiple if conditions, which means only execute if have the input or data.
Below is the query
var sql = `SELECT
COALESCE(commerce_order.total_price_usd, 0) AS 'Total price (USD)',
commerce_order.country AS Country,
commerce_order.customer_id AS 'Customer Id',
COALESCE(commerce_order.ffm_status, "unfulfilled") AS 'Fulfillment status'
FROM commerce_order
WHERE merchant_id = ?`;
var bind = [p.data.store_id]
const fromIndex = sql.indexOf('FROM');
if (p.data.hasOwnProperty('email')){
sql += " AND commerce_order.customer_email REGEXP ? "
bind.push(p.data.email);
};
if (p.data.hasOwnProperty('startDate')){
sql += " AND DATE(commerce_order.processed_at) >= ? "
bind.push(p.data.startDate);
};
if (p.data.hasOwnProperty('endDate')){
sql += " AND DATE(commerce_order.processed_at) <= ? "
bind.push(p.data.endDate);
};
if (p.data.hasOwnProperty('paymentStatus')){
if (p.data.paymentStatus !== 'all'){
sql += " AND commerce_order.financial_status = ? "
bind.push(p.data.paymentStatus);
};
};
if (p.data.hasOwnProperty('fulfillmentStatus')){
if (p.data.fulfillmentStatus !== 'all'){
if (p.data.fulfillmentStatus === 'fulfilled'){
sql += " AND commerce_order.ffm_status = ? ";
bind.push(p.data.fulfillmentStatus);
} else if (p.data.fulfillmentStatus === 'unfulfilled'){
sql += " AND commerce_order.ffm_status IS NULL "
};
};
};
if (p.data.hasOwnProperty('order_number')){
sql += " AND commerce_order.order_number = ? "
bind.push(p.data.order_number);
};
sql += " ORDER BY commerce_order.processed_at "
How to make a composite index for this query? Or do I only need single column index? Please advise, thank you.

Related

Using the LIKE operator in MYSQL voids the "id > ? operation"

I am using the like operator to select where like %x% and using the id > ? at the same time but it doesn't work for whatever reason where I keep pulling in the same row of information.
My code is concise and works well I think but the like operator is making it so I keep pulling in the same data (I used = and it worked). Any help please, thank you.
var query = 'SELECT * FROM products WHERE 1 = 1';
var arr = [];
if(typeof(req.body.category) == 'string' && req.body.category.trim() !== '') {
// query += ' AND category LIKE ? OR title LIKE ?'; //doesnt work keep pulling in same data
// arr.push('%' + req.body.category + '%', '%' + req.body.category + '%'); //doesnt work keep pulling in same data
// query += ' AND category = ? OR title = ?'; //works
// arr.push(req.body.category, req.body.category); //works
}
if(typeof(req.body.reset_index) == 'boolean' && req.body.reset_index == true) {
req.session.last_id_main_products = 0;
}
query += ' AND id > ?';
arr.push(req.session.last_id_main_products);
if(
typeof(req.body.longitude) == 'number' &&
typeof(req.body.latitude) == 'number' &&
typeof(req.body.if_location) == 'boolean' && req.body.if_location == true
) {
var left_long = req.body.longitude - 0.3;
var right_long = req.body.longitude + 0.3;
var upper_lat = req.body.latitude + 0.3;
var lower_lat = req.body.latitude - 0.3;
query += ' AND (room_geo_longitude > ? AND room_geo_longitude < ?) AND (room_geo_latitude > ? AND room_geo_latitude < ?)';
arr.push(left_long, right_long, lower_lat, upper_lat);
}
query += ' AND hidden = false LIMIT 1';
pool.query(query, arr, (err, result) => {
if(err) {
res.json(err);
return;
}
if(result.length > 0) {
req.session.last_id_main_products = result[result.length - 1].id;
}
res.json({
room_products: result,
query: query,
arr: arr
});
});
UPDATE: When using query, use parentheses around specific types of operations or else will return bad data.

Node js - Mysql query is not executed as coded

i created several sql statements in node.js and now i want to execute them on my db. However, the query string is not executed as coded.
This is my function to generate the query string.
function insertProducts(products) {
if (!connection) {
// Create MYSQL-Connection
console.log('BUILDING connection to DB');
connection = getConnection();
connection.connect();
}
let query = "";
for (let i = 0; i < products.length; i++) {
// Iterate trough the products array and create a sql query
query += "INSERT INTO `tShortDescription`(`ShortDescription`, `Language`) VALUES ('" + products[i].short_description + "', 'DE'); " +
"INSERT INTO `tDescription`(`Description`, `Language`) VALUES ('" + products[i].description + "', 'DE'); " +
"INSERT INTO `tManufacturer`(`Name`) VALUES ('" + products[i].manufactur + "'); " +
"INSERT INTO `tSupplier`(`Name`) VALUES ('" + products[i].supplier + "'); " +
"INSERT INTO `tProduct`(`Sku`, `Title`, `ShortDescriptionId`, `DescriptionId`, `WohlesalePrice`, `SellingPrice`, `Quantity`, " +
"`ManufacturerId`, `SupplierId`, `Ean`) VALUES ('" + products[i].sku + "', '" + products[i].name + "', " +
"(SELECT id FROM tShortDescription WHERE ShortDescription = '" + products[i].short_description + "' LIMIT 1), " +
"(SELECT id FROM tDescription WHERE Description LIKE '" + products[i].description + "' LIMIT 1), " +
products[i].wholesale_price + ", " + products[i].selling_price + ", " + products[i].quantity + ", " +
"(SELECT id FROM tManufacturer WHERE Name = '" + products[i].manufactur + "' LIMIT 1), " +
"(SELECT id FROM tSupplier WHERE Name = '" + products[i].supplier + "' LIMIT 1), " + products[i].ean + "); ";
for (let j = 0; j < products[i].categories.length; j++) {
// Ad all categories to query
query += "INSERT INTO `rtCategory`(`ProductId`, `CategoryId`) " +
"VALUES ((SELECT `Id` FROM `tProduct` WHERE sku = '" + products[i].sku + "' LIMIT 1), " +
"(SELECT `Id` FROM `tCategory` WHERE Id = " + products[i].categories[j].src + " LIMIT 1)); "
for (let c = 0; c < products[i].images.length; c++) {
// Ad all images to query
query += "INSERT INTO `tImage`(`Url`) VALUES ('" + products[i].images[c].src + "'); " +
"INSERT INTO `rtImage`(`ProductId`, `ImageId`) " +
"VALUES ((SELECT `Id` FROM `tProduct` WHERE sku = '" + products[i].sku + "' LIMIT 1), " +
"(SELECT `Id` FROM `tImage` WHERE url = '" + products[i].images[c].src + "' LIMIT 1)); "
}
}
}
query = query.replace(/[\n\r\t]/g,);
if (query != "") {
// Create new Product in DB
return new Promise((resolve, reject) => {
connection.query(query, function (error, results, fields) {
if (error) { console.log(error) };
console.log('INSERTING successful');
resolve(results);
});
});
} else {
console.log('There are no new products to insert in db');
}
}
If i console.log(query) (before the query is ecexuted on my db) and execute the string directly in php myadmin, everything works fine but if i execute the query in code like connection.query(query, function (error, results, fields)....., i got several errors.
Error msg in terminal:
code: 'ER_PARSE_ERROR',
errno: 1064,
sqlMessage: "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INSERT INTO `tDescription`(`Description`, `Language`) VALUES ('<p><strong>Tantra' at line 1",
sqlState: '42000',
index: 0,
I also get the sql query returned in terminal because of the error, and if i execute this query directly in php myadmin i also get an error ->
SQL query: Documentation
INSERT INTO `rtImage`(`ProductId`, `ImageId`) VALUES ((SELECT `Id` FROM `tProduct` WHERE sku = 'H1500148' LM
IT 1), (SELECT `Id` FROM `tImage` WHERE url = 'https://cdnbigbuy.com/images/H1500148_409897.jpg' LIMIT 1))
MySQL said: Documentation
#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'LM
IT 1), (SELECT `Id` FROM `tImage` WHERE url = 'https://cdnbigbuy.com/images' at line 1
It looks as if the LIMIT is somehow divided ...use near 'LM IT 1)....
I hope you understand where the problem is and someone might have a tip.
Your query is processed as 'LIMIT' it's just a new line in the console where the error showed up.
You should not be using string concatenation (or even template literals) for SQL queries under any circumstances because 1. It very likely the source of your problem. 2. It's very dangerous as it allows SQL injection attacks.
Use parameters instead. Here's a example:
connection.query("SELECT * FROM bank_accounts WHERE dob = ? AND bank_account = ?",[
req.body.dob,
req.body.account_number
],function(error, results){});
To read more about SQL injections and placeholders read this article.
Thanks for the helpful tips.
The problem was that I didn't set multiple statements: true in my code. This var is by default false and should be true, otherwise it is not possible to execute several queries once at a request!

Mysql NodeJs syncronous queries

Thank you for the answer,
I did that : i used "sync-mysql" :
but now its very very slow...
Maybe i could do the same code using Mysql NPM
Do you know how my code must look like if I want to use asyncronous function and doing the same thing as below ? It will help me a lot :)
I have almost finished my project and I only have this function left
const customer_booked = []
customer_booked[0] = []
customer_booked[1] = []
let sql = "SELECT * " +
"FROM customer as C " +
"WHERE customer_reference REGEXP '^[c]i*' "
if (filters[0].value.length){
sql += "AND C.customer_name LIKE '%" + filters[0].value + "%' "
}
if (filters[3].value.length){
sql += "LIMIT " + filters[3].value
}
var result = connection.query(sql);
const customers = [];
const booked = connection.query('SELECT cr.customer_id, a.codeAgent ' +
'FROM customer_reservation as cr ' +
'INNER JOIN agent as a ' +
'ON a.id = cr.agent_id')
booked.forEach(customer_booking => {
customer_booked[0].push(customer_booking.customer_id)
customer_booked[1].push(customer_booking.codeAgent)
});
result.forEach( customer => {
var months;
let d1 = new Date(customer.last_order);
let d2 = new Date();
months = (d2.getFullYear() - d1.getFullYear()) * 12;
months -= d1.getMonth() + 1;
months += d2.getMonth();
months = months <= 0 ? 0 : months;
if (customer_booked[0].includes(customer.customer_id)){
let code_agent_index = customer_booked[0].indexOf(customer.customer_id)
customer.available = 'booked'
customer._rowVariant = 'warning'
customer.agent_code = customer_booked[1][code_agent_index]
}
else if (months >= 12){
customer.available = 'available'
customer._rowVariant = 'success'
} else {
customer.available = 'notAvailable'
customer._rowVariant = 'danger'
}
let sql2 = "SELECT * " +
"FROM customer_addresses AS CA " +
"WHERE CA.customer_id = " + customer.id
customer.addresses = connection.query(sql2)
customers.push(customer);
//customers[customers.length].push()
})
callback(false, result)
You can use node.js async/await using IIFE, like this:
(async() => {
const users = await getUsers();
for(const user of users){
user.addresses = await getAddresses(user.id);
// your other code just translated to JS.
}
return users;
})()
So, the main idea is to await your async code.
For example we use IIFE (Immediately Invoked Function Expression) to access needed async/await and for tests.
In real code you should name functions with keyword async
Here is nice tutorials which could explain how to use async/await 1, 2

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

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:
only name=Bob: SELECT * FROM table WHERE name = 'Bob'
only age=40: SELECT * FROM table WHERE age > 40
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.
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
});
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: "address#domain.com"}, "Person")
Output:
INSERT INTO Person (name,tel,email) VALUES ('Sam','9090909','address#domain.com');
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: "tanjiro#demonslayer.com"
}, "Person", "ID", 111);
Output:
UPDATE Person SET name = 'Tanjiro',tel = '77777777',email = 'tanjiro#demonslayer.com' WHERE ID = 111;
I modify your code #Jordan-Running
describe("Test generateFilterQuery", () => {
it("Query filter with params", () => {
let params = []
params.push(Query.generateParams("title", "%_%", "Coding"))
params.push(Query.generateParams("published", "=", true))
console.log(Query.generateFilterQuery(params))
});
});
const qInclude = require('./QueryInclude');
exports.generateParams = (name, eq, value) => {
return {
name: name,
eq: eq, // %_%, %_, _%, =, >, <, !=,
value: value
}
}
exports.generateFilterQuery = (params) => {
let conditions, values = []
let conditionsStr;
if (params.length == 0) {
return false
}
[conditions, values] = qInclude.queryCondition(params)
let build = {
where: conditions.length ?
conditions.join(' AND ') : '1',
values: values
};
let query = 'SELECT * FROM table WHERE ' + build.where;
return [query, build.values]
}
exports.queryCondition = (params) => {
var conditions = [];
var values = [];
params.forEach(item => {
switch (item.eq) {
case '=': {
conditions.push(item.name + " = ?");
values.push(item.value);
break;
}
case '!=': {
conditions.push(item.name + " != ?");
values.push(item.value);
break;
}
case '<': {
conditions.push(item.name + " < ?");
values.push(item.value);
break;
}
case '>': {
conditions.push(item.name + " > ?");
values.push(item.value);
break;
}
case '%_%': {
conditions.push(item.name + " LIKE ?");
values.push("%" + item.value + "%");
break;
}
case '%_': {
conditions.push(item.name + " LIKE ?");
values.push("%" + item.value);
break;
}
case '_%': {
conditions.push(item.name + " LIKE ?");
values.push(item.value + "%");
break;
}
}
});
return [conditions, values]
}

Querying multiple rows using node-mysql

This issue pertains to node-mysql.
I seem to be having a problem with the following code, and it seems to be related to the use of user defined variables. The rendered query runs fine inside of any mysql IDE, returning multiple records. However when executed by node-mysql, it only returns a single record.
I've verified that eliminating the WHERE clause specifying T3.gender_rank creates predictable results in both node-mysql and my mysql IDE.
Has this issue been raised before, or is it something that I'm doing wrong?
var mysql = require('mysql'); // Establish SQL Support
var sql =
"select team, `div`, sum(points) AS teampoints "+
"from ( "+
" select "+
" *, "+
" (#genderRank := CASE WHEN #genderTMP <> gender THEN 1 ELSE #genderRank+1 END) AS gender_rank, "+
" (#genderTMP := gender) AS _gt "+
" from ( "+
" select * "+
" from racepoints "+
" WHERE "+
" ltrim(rtrim(race)) = '"+data.eventName+"' AND "+
" racedate = '"+getSimpleDate(new Date(data.eventStart))+"' "+
" ORDER BY team,gender,points DESC "+
" ) T1, "+
" (select #genderRank:=0 AS gender_rank_start) T2 "+
") T3 "+
"WHERE "+
" (T3.gender_rank <= 4 AND rtrim(ltrim(`div`)) = 'D2') OR "+
" (T3.gender_rank <= 3 AND rtrim(ltrim(`div`)) = 'D1') "+
"GROUP BY team, `div` "+
"ORDER BY `div`, teampoints DESC, team";
var db = new mysql.createConnection({
host : 'localhost',
user : 'root',
password : 'admin',
database : 'nps',
multipleResults : true
});
db.connect();
db.query(
sql,
function(err,rows,fields){
if(err){
console.log(err);
return;
}
console.log(rows);
}
);
db.end();
The SQL database file is also available here: https://snipt.net/download/dde8b4b5ce8cd5fca21ac2334bae634f/-5369.sql
Instead of directly adding your variables to the sql string, let the mysql package do it for you. This usually solves problems like this one and additionally protects against SQL injection.
This is done using question marks (one for every variable) followed by an array of variables.
For example
connection.query('SELECT * FROM users WHERE id = ?', [userId], function(err, results) {
// ...
});