Я пытаюсь вставить строку фрейма данных в таблицу mysql, но у меня есть значения NA в символьных и числовых столбцах. Я получаю эту ошибку: Ошибка в .local(conn, statement,...): не удалось выполнить оператор: неизвестный столбец «NA» в «списке полей»
Это мой запрос:
sql <- sprintf("insert into payment (amount,payment_type,date,customer_total,base_price, p2c_total, partner_total, pay_online, pay_at_lot,tax,first_name_on_card,last_name_on_card,address)
values (%f, %d, '%s',%f,%f,%f,%f,%f,%f,%f,'%s','%s','%s');",
payments[i,]$amount,payments[i,]$payment_type,payments[i,]$date, payments[i,]$customer_total,
payments[i,]$base_price, payments[i,]$p2c_total, payments[i,]$partner_total,
payments[i,]$pay_online,payments[i,]$pay_at_lot,payments[i,]$tax,
payments[i,]$first_name_on_card, payments[i,]$last_name_on_card, payments[i,]$address)
rs <- dbSendQuery(db, sql[i])
dbClearResult(rs)
Это код sql:
insert into reservation (reservation_number, driver_name, number_passengers, checkin_datetime, checkout_datetime, days, reservation_date, reservation_email,id_reservation_status, id_payment, id_ship, id_facility, id_user) values ('j990j','CB', 4, '2020-01-12 10:00:00', '2020-01-19 10:30:00', 8, 'NA', '[email protected]',NA, 1, 2, 547, 6);
И это ошибка mysql: #1054 - Столбец 'NA' в списке полей desconocida
Версия MySQL: 8.0.27
Р-версия: 4.03
Пакет RMySQL: 0.10.22
Но если вы действительно хотите обойти удобство и гарантии, которые обеспечивает dbAppendTable, вам нужно указать значение, где, если это NA, то значение в вашем запросе должно быть null, иначе '%f'. Да, это означает, что вам нужно включать одинарные кавычки, только если вставляемое значение не равно NA.
Я использую dbSendQuery для каждой строки платежного набора данных, потому что мне нужен вставленный идентификатор для вставки в другую таблицу. id_payment является автоматическим числом в платеже и внешним ключом в другой таблице с именем reservation. Если я использую dbAppenTable, как мне получить идентификатор каждого платежа?
Ладно, да, это всегда мешает. Тьфу, я помню эту проблему, одну из причин, по которой я не люблю автоматически увеличивающиеся идентификаторы. К сожалению, я думаю, что MySQL делает это сложным, не имея безопасного способа обойти это. В этом случае см. 2-й вариант в моем ответе.






оберните NA в 'NA'
insert into reservation (reservation_number, driver_name, number_passengers, checkin_datetime, checkout_datetime, days, reservation_date, reservation_email,id_reservation_status, id_payment, id_ship, id_facility, id_user) values ('j990j','CB', 4, '2020-01-12 10:00:00', '2020-01-19 10:30:00', 8, 'NA', '[email protected]','NA', 1, 2, 547, 6);
Три взгляда на это:
Не добавляйте sprintf/pasteданные в строку запроса. В дополнение к проблемам безопасности, связанным с злонамеренныйSQL-инъекция (например, Подвиги мамы XKCD, также известным как «таблицы Little Bobby»), это также связано с искаженными строками или ошибками Unicode-VS-ANSI, даже если запрос выполняет один аналитик данных.
Удобно, что есть функция, которая заботится о вставке данных из data.frame в таблицу более безопасным способом: dbAppendTable. Вы могли бы сделать просто
dbAppendTable(db, "payment", payments[i,])
если все столбцы должны быть вставлены, в противном случае необходимо что-то более подробное:
dbAppendTable(db, "payment", payments[i,c("amount", "payment_type", "date", "customer_total", "base_price", "p2c_total", "partner_total", "pay_online", "pay_at_lot", "tax", "first_name_on_card", "last_name_on_card", "address")])
Если вы планируете сделать это для более чем 1 строки, то dbAppendTable может без проблем занять несколько строк.
Если вы действительно хотите выполнять одну строку за раз со своим собственным оператором insert, я настоятельно рекомендую вам использовать параметризованные запросы, возможно, что-то вроде:
qry <- "insert into payment (amount,payment_type,date,customer_total,base_price, p2c_total, partner_total, pay_online, pay_at_lot,tax,first_name_on_card,last_name_on_card,address)
values (?, ?, ?,?,?,?,?,?,?,?,?,?,?);"
dbExecute(db, qry, params = payments[i, c("amount", "payment_type", ...)])
(Это напомнило мне... dbExecute - это хорошая оболочка, которая делает dbSendStatement всегда за которым следует dbClearResult. Есть также dbGetQuery, за которым на самом деле dbSendQuery всегда следует dbClearResult, возвращая данные. Вы не возвращаете строки из таблицы, поэтому первое в любом случае предпочтительнее)
ПРИМЕЧАНИЕ: для этой функции требуется обновленный драйвер для доступа к базе данных. Если вы используете RMySQL, то есть проблема: этот пакет не видел существенных обновлений в течение многих лет (на данный момент) и не поддерживает параметризованные запросы. Я считаю, что пакет RMariaDB полностью совместим с MySQL и, он поддерживает параметризованные запросы.
Если вы действительно должны делать это вручную (и на самом деле, я сильно не одобряю этого, слишком много раз я думал, что смогу обойти риски, только чтобы каждый раз быть укушенным), тогда R's NA переводится в null (без кавычек!). Для этого нужно условно добавить кавычки. Что-то вроде:
ifelse(is.na(payments[i,]$date), "null", sQuote(payments[i,]$date))
для строкового поля каждый в вашем запросе и обязательно измените '%s' на %s в вашем формате. Почти наверняка есть лучшие способы автоматизировать это, чтобы вы не набирали дюжину или более ifelse, но, на мой взгляд, это действительно не стоит того, чтобы делать это.
(Если вы полагаетесь на другую семантику sprintf("%s", ..) по сравнению с неявной строковой классификацией с sQuote, то вам может понадобиться еще больше усилий.)
Я использую dbSendQuery для каждой строки платежного набора данных, потому что мне нужен вставленный идентификатор для вставки в другую таблицу. id_payment является автоматическим числом в платеже и внешним ключом в другой таблице с именем reservation. С dbAppendTable я получил ошибку в списке полей, но я смог сделать это, используя dbWriteTable с добавлением и параметрами row.names в FALSE. Благодарю вас!
Есть ли причина, по которой вы не используете
DBI::dbAppendTable? Поскольку кажется, чтоpaymentsуже является фреймом, вы можете подмножить свой фрейм (при необходимости) и либо вставить одну строку, либо все сразу. Использованиеsprintfдля формулировки запроса для вставки данных в форме, удобной для SQL, кажется ненужным.