Есть ли способ динамически построить запрос MySQL из объекта JSON, значения которого, вероятно, будут пустыми.
Например, из такого объекта:
{
"a": 1
"b": ""
"c": "foo"
}
создайте запрос, подобный этому ("b" пусто, его не следует принимать во внимание):
SELECT * FROM db.table
WHERE a = 1
AND c = "foo"
или
SELECT * FROM db.table
WHERE a = 1
AND b = ????
AND c = "foo"
Обновлено: это, вероятно, действительно дублируется. Но я подумал, что для этого есть более SQL-способ, например, с использованием переменных и операторов IF.
Редактировать 2: я нашел способ (работает в API node.js, но он должен быть похож на другие языки):
const jsonObj = {
"a": 1,
"b": "",
"c": "foo"
}
const query = `
SELECT * FROM db.table
WHERE
IF('${jsonObj.a}' != '', a = '${jsonObj.a}', 1=1)
AND
IF('${jsonObj.b}' != '', b = '${jsonObj.b}', 1=1)
AND
IF('${jsonObj.c}' != '', c = '${jsonObj.c}', 1=1)
`
Конечно, этот код нельзя использовать в его нынешнем виде, его необходимо адаптировать с учетом проблем с внедрением.
Да, он отправляется клиентом веб-браузера в теле запроса POST.
Жаль, я не могу опубликовать ответ... Кажется, это работает в гораздо большей степени SQL, чем предыдущий дубликат (мы находимся в среде node.js): const jsonObj = { "a": 1 "b ": "" "c": "foo" } const query = ` SELECT * FROM db.table WHERE IF(${jsonObj.a} != "", a = ${jsonObj.a}, 1=1) И ЕСЛИ(${jsonObj.b} != "", b = ${jsonObj.b}, 1=1) и т.д.
Похоже, это очень подвержено SQL-инъекциям.
Да, конечно, это включает в себя проверку данных и подготовленные запросы.
Бармар прав, проблема и ее решение одинаковы независимо от языка.
Справедливости ради, я на самом деле не заметил, что он не указал язык, и PHP, как правило, является наиболее распространенным.
Я повторно открыл вопрос, пожалуйста, добавьте языковой тег и покажите свое решение. Вы должны быть в состоянии адаптировать алгоритм в связанном вопросе к своему языку.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


ВАЖНЫЙ: эта стратегия открыта для Атаки SQL-инъекций. Вы должен экранируете значения - предпочтительно используя подготовленные запросы. Без дополнительных знаний о вашем клиенте базы данных невозможно направить вас, как это сделать.
КРОМЕ ТОГО: Я настоятельно рекомендую иметь белый список разрешенных столбцов и разрешать использование в запросе только тех ключей столбцов, которые находятся в белом списке. Пример ниже включает белый список, чтобы продемонстрировать это.
Вот MVP, который будет обрабатывать произвольный/динамический объект и строить оператор SQL по вашему запросу:
const obj = {
"a": 1,
"b": "",
"c": "foo",
"bad": "disallowed"
}
// example of how to use a whitelist
const whitelist = ['a', 'c'];
// set up an empty array to contain the WHERE conditions
let where = [];
// Iterate over each key / value in the object
Object.keys(obj).forEach(function (key) {
// if the key is not whitelisted, do not use
if ( ! whitelist.includes(key) ) {
return;
}
// if the value is an empty string, do not use
if ( '' === obj[key] ) {
return;
}
// if we've made it this far, add the clause to the array of conditions
where.push(`\`${key}\` = "${obj[key]}"`);
});
// convert the where array into a string of AND clauses
where = where.join(' AND ');
// if there IS a WHERE string, prepend with WHERE keyword
if (where) {
where = `WHERE ${where}`;
}
const sql = `SELECT * FROM table ${where}`;
console.info(sql);
// SELECT * FROM table WHERE `a` = "1" AND `c` = "foo"ПРИМЕЧАНИЯ:
")Давайте попробуем создать функцию, которая может обрабатывать много сложных запросов.
function prepareStmtFromObject(params) {
const constraints = [];
const data = [];
Object.keys(params).forEach((item) => {
if (!params[item] || params[item] == "") {
return;
}
if (Array.isArray(params[item])) {
constraints.push(`${item} in (?)`);
data.push(params[item]);
} else if (typeof params[item] === "string" && params[item].indexOf(",") > -1) {
constraints.push(`${item} in (?)`);
data.push(params[item].split(","));
} else if (params[item] instanceof RegExp) {
constraints.push(`${item} REGEXP ?`);
data.push(params[item]);
} else if (params[item] && typeof params[item] === "object") {
Object.keys(params[item]).forEach((value) => {
if (value === "$gte") {
constraints.push(`${item} >= ?`);
data.push(params[item][value]);
} else if (value === "$lte") {
constraints.push(`${item} <= ?`);
data.push(params[item][value]);
} else if (value === "$gt") {
constraints.push(`${item} > ?`);
data.push(params[item][value]);
} else if (value === "$lt") {
constraints.push(`${item} < ?`);
data.push(params[item][value]);
} else if (value === "$like") {
if (Array.isArray(params[item][value])) {
const localConstraints = [];
params[item][value].forEach((likeValues) => {
localConstraints.push(`${item} LIKE ?`);
data.push(`%${likeValues}%`);
});
constraints.push(`(${localConstraints.join(" OR ")})`);
} else if (typeof params[item][value] === "string" && params[item][value].indexOf(",") > -1) {
const localConstraints = [];
params[item][value] = params[item][value].split(",");
params[item][value].forEach((likeValues) => {
localConstraints.push(`${item} LIKE ?`);
data.push(`%${likeValues}%`);
});
constraints.push(`(${localConstraints.join(" OR ")})`);
} else {
constraints.push(`${item} LIKE ?`);
data.push(`%${params[item][value]}%`);
}
}
});
} else {
constraints.push(`${item} = ?`);
data.push(params[item]);
}
});
return { constraints, data };
}
const data = {
userId: 1,
company: ["google", "microsoft"],
username: { $like: "Test" },
name: { $like: [ "Test1", "Test2" ] },
age: { $gt: 10 }
}
const stmt = prepareStmtFromObject(data);
console.info("SELECT * FROM user WHERE ", stmt.constraints.join(" and "));
console.info(stmt.data);Приведенная выше функция возвращает ограничения и массив запросов, которые можно использовать для экранирования символов. Таким образом, вы также можете предотвратить внедрение SQL. Я предполагаю, что вы используете пакет mysql
Откуда берется объект JSON... я предполагаю, что здесь задействован какой-то клиентский язык программирования?