Как передать название столбца таблицы в качестве аргумента функции?
К примеру есть функция которая выдаёт оставшиеся для игрока квесты одного из НПС. Первый аргумент название НПС, второй - имя игрока, по которому естественно должна идти проверка. Таблица на сервере хранит такие столбцы как имя зарегестрированного игрока и названия нпс со значениями по умолчанию (VARCHAR = "Список названий квестов через запятую"). Этих НПС очень много. Потому решено было сделать их не на отдельных таблицах, а качестве колонок, где для каждого игрока будет хранится свой список оставшихся квестов.
import datasql
import MySQLdb
def chek(npc, who):
db = MySQLdb.connect(host=datasql.sql[0], user=datasql.sql[1],
passwd=datasql.sql[2], db=datasql.sql[3], charset=datasql.sql[4])
cur = db.cursor()
try:
cur.execute(
"SELECT %s FROM quest_list WHERE name = %s", [npc, who])
result = cur.fetchall()[0][0].split(',')
if result == ['']:
print("👍 Вы выполнили все задания этого НПС")
else:
cur.close()
db.close()
print("\n".join(result))
except TypeError as e:
print(f"Возникла неприятность '{e}' ")
И если вызвать эту функцию с существующими нпс и игроком в качестве аргументов, то ответом будет название нпс, вместо ожидаемого ответа на запрос. А если переписать функцию так:
import datasql
import MySQLdb
def chek(who):
db = MySQLdb.connect(host=datasql.sql[0], user=datasql.sql[1],
passwd=datasql.sql[2], db=datasql.sql[3], charset=datasql.sql[4])
cur = db.cursor()
try:
cur.execute(
"SELECT name_npc FROM quest_list WHERE name = %s", [who])
result = cur.fetchall()[0][0].split(',')
if result == ['']:
print("👍 Вы выполнили все задания этого НПС")
else:
cur.close()
db.close()
print("\n".join(result))
except TypeError as e:
print(f"Возникла неприятность '{e}' ")
~ То выдаст ожидаемый результат, который и нужен был. Разве для колонок таблицы в запросе нельзя принимать вот такое выражение - %s? Оно принимается только по поиску значений в записях? Если да, то как лучше поступить? НПС слишком много и неохото под каждого копипастить в коде одну и ту же функцию с одним лишь изменяемым значением в запросе.
Ответы (1 шт):
Это ожидаемое поведение, когда вы передаете параметр в запрос, БД подставляет значение параметра. Откуда ей знать, что вы в данном случае хотите, чтобы это трактовалось как имя колонки, а не значение (в этом месте синтаксически допустимо любое выражение, и константа и имя колонки)?
Напрямую это решается так, что нужно на стороне питона сгенерировать строку запроса с правильной колонкой:
Начиная с python 3.6
cur.execute(
f"SELECT {npc} FROM quest_list WHERE name = %s", [who])
для более ранних версий:
cur.execute(
"SELECT {} FROM quest_list WHERE name = %s".format(npc), [who])
Оба этих способа сгенерируют строку подставив в соответствующее место значение npc еще до вызова метода execute.
Но по-хорошему, вашу задачу нужно решать нормализацией БД. То есть нужно изменить структура БД, а именно то, как хранятся названия заданий.
Имеет смысл создать новую таблицу quest:
CREATE TABLE quest (
id INT NOT NULL AUTO_INCREMENT,
player_name CHAR(30) NOT NULL,
npc_name CHAR(30) NOT NULL, -- имя НПС или другой идентификатор
name CHAR(30) NOT NULL, -- имя самого задания
PRIMARY KEY (id),
FOREIGN (player_name) KEY REFERENCES player(name),
FOREIGN (npc_name) KEY REFERENCES npc(name) -- ссылка должна быть на уникальный
-- идентификатор (первичный ключ)
-- тут я предположил, что это имя
);
Тогда в запросе не нужно будет делать манипуляций со строками:
select name
from quest
where user_name = %s and npc_name = %s