Django .raw 查询失败(错误 1064)——但在 SQL 中有效
Django .raw query fails (error 1064) -- but works in SQL
(使用 django 1.11.2,python 2.7.10,mysql 5.7.18)
以下SQL查询:
SELECT
transaction_transaction.id,
sec_to_time(avg(time_to_sec(extract(HOUR_SECOND from transaction_transaction.transaction_datetime))))
AS average_time_of_day
FROM transaction_transaction
INNER JOIN store_store ON (transaction_transaction.store_id=store_store.id)
INNER JOIN payment_method_card ON (transaction_transaction.card_id=payment_method_card.id)
WHERE (
transaction_transaction.transaction_datetime BETWEEN '2017-08-31 00:00:00' AND '2017-08-31 23:59:59'
AND store_store.company_id=2
AND payment_method_card.profile_id=8
);
运行并returns以下结果(符合预期):
+== id ==+== average_time_of_day ==+
|= 9422 =|===== 20:42:22.8695 =====|
(这是来自海蒂SQL的运行)
通过 Django 做类似的事情(我想!但有些事情显然是错误的):
average_time_of_day = Transaction.objects.raw(
'''
SELECT
transaction_transaction.id,
sec_to_time(avg(time_to_sec(extract(HOUR_SECOND from transaction_transaction.transaction_datetime))))
AS average_time_of_day
FROM transaction_transaction
INNER JOIN store_store ON (transaction_transaction.store_id=store_store.id)
%s
WHERE (
transaction_transaction.transaction_datetime BETWEEN %s AND %s
AND store_store.company_id=%s
%s
);
''',
[
'INNER JOIN payment_method_card ON (transaction_transaction.card_id=payment_method_card.id) ' if profile_pk else '',
start_datetime,
end_datetime,
company_pk,
'AND payment_method_card.profile_id=' + str(profile_pk) if profile_pk else '',
]
)
正在做
print average_time_of_day.query
输出:
SELECT
transaction_transaction.id,
sec_to_time(avg(time_to_sec(extract(HOUR_SECOND from transaction_transaction.transaction_datetime))))
AS average_time_of_day
FROM transaction_transaction
INNER JOIN store_store ON (transaction_transaction.store_id=store_store.id)
INNER JOIN payment_method_card ON (transaction_transaction.card_id=payment_method_card.id)
WHERE (
transaction_transaction.transaction_datetime BETWEEN 2017-08-31 00:00:00 AND 2017-08-31 00:00:00
AND store_store.company_id=2
AND payment_method_card.profile_id=8
);
而 Django returns 出现以下错误:
ProgrammingError: (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 ''INNER JOIN payment_method_card ON (transaction_transaction.card_id=payment_meth' at line 7")
知道我做错了什么吗?
您是否尝试过使用 Django 的 ORM 来构建该查询?它可以使调试更容易。它可能看起来像这样:
Transaction.objects.filter(
store__company_id=company_pk,
payment_method_card__profile_id=profile_pk,
transaction_datetime__range(start_datetime, end_datetime)
)
如果您需要更细粒度的控制,您可以使用 Django's select_related
and prefetch_related
on your query, and you can use a Q() object
优化查询。
我不喜欢手动写出 SQL 语句,所以这有点逃避,但我喜欢使用 Django 提供的工具。
在SQL字符串中一些变量必须是字符串。例如日期时间必须在 "" 或 '' 我不记得了但是 queryset.query
显示你格式化的 sql 字符串不是有效的字符串。
所以只需尝试添加 ''.
没错。
这个会教我不要尝试并且:
- 太聪明了
- 过度使用单行:-(
这不起作用:
average_time_of_day = Transaction.objects.raw(
'''
SELECT
transaction_transaction.id,
sec_to_time(avg(time_to_sec(extract(HOUR_SECOND from transaction_transaction.transaction_datetime))))
AS average_time_of_day
FROM transaction_transaction
INNER JOIN store_store ON (transaction_transaction.store_id=store_store.id)
%s
WHERE (
transaction_transaction.transaction_datetime BETWEEN %s AND %s
AND store_store.company_id=%s
%s
);
''',
[
'INNER JOIN payment_method_card ON (transaction_transaction.card_id=payment_method_card.id) ' if profile_pk else '',
start_datetime,
end_datetime,
company_pk,
'AND payment_method_card.profile_id=' + str(profile_pk) if profile_pk else ''
]
)
注意 params
中的 'INNER JOIN payment_method_card ON (transaction_transaction.card_id=payment_method_card.id) ' if profile_pk else ''
和 'AND payment_method_card.profile_id=' + str(profile_pk) if profile_pk else ''
发送到 raw() 方法。有点动态?
...但是这个有效:
if profile_pk:
average_time_of_day = Transaction.objects.raw(
'''
SELECT
transaction_transaction.id,
sec_to_time(avg(time_to_sec(extract(HOUR_SECOND from transaction_transaction.transaction_datetime))))
AS average_time_of_day
FROM transaction_transaction
INNER JOIN store_store ON (transaction_transaction.store_id=store_store.id)
INNER JOIN payment_method_card ON (transaction_transaction.card_id=payment_method_card.id)
WHERE (
transaction_transaction.transaction_datetime BETWEEN %s AND %s
AND store_store.company_id=%s
AND payment_method_card.profile_id=%s
);
''',
[
start_datetime,
end_datetime,
company_pk,
profile_pk
]
)
else:
average_time_of_day = Transaction.objects.raw(
'''
SELECT
transaction_transaction.id,
sec_to_time(avg(time_to_sec(extract(HOUR_SECOND from transaction_transaction.transaction_datetime))))
AS average_time_of_day
FROM transaction_transaction
INNER JOIN store_store ON (transaction_transaction.store_id=store_store.id)
WHERE (
transaction_transaction.transaction_datetime BETWEEN %s AND %s
AND store_store.company_id=%s
);
''',
[
start_datetime,
end_datetime,
company_pk
]
)
那么 Django 可以不喜欢 params
的动态构建吗?我很高兴知道答案......我仍然怀疑我做错了什么......这可能与 SQL 注入保护有关吗?
显然 Django 将 INNER JOIN
表达式用单引号括起来 - 因此导致 MySQL 拒绝查询。实际上看起来是这样的,尽管 print average_time_of_day.query
什么也没说:
SELECT
transaction_transaction.id,
sec_to_time(avg(time_to_sec(extract(HOUR_SECOND from transaction_transaction.transaction_datetime))))
AS average_time_of_day
FROM transaction_transaction
INNER JOIN store_store ON (transaction_transaction.store_id=store_store.id)
'INNER JOIN payment_method_card ON (transaction_transaction.card_id=payment_method_card.id)'
WHERE (
transaction_transaction.transaction_datetime BETWEEN 2017-08-31 00:00:00 AND 2017-08-31 23:59:59
AND store_store.company_id=2
'AND payment_method_card.profile_id=8'
);
(使用 django 1.11.2,python 2.7.10,mysql 5.7.18)
以下SQL查询:
SELECT
transaction_transaction.id,
sec_to_time(avg(time_to_sec(extract(HOUR_SECOND from transaction_transaction.transaction_datetime))))
AS average_time_of_day
FROM transaction_transaction
INNER JOIN store_store ON (transaction_transaction.store_id=store_store.id)
INNER JOIN payment_method_card ON (transaction_transaction.card_id=payment_method_card.id)
WHERE (
transaction_transaction.transaction_datetime BETWEEN '2017-08-31 00:00:00' AND '2017-08-31 23:59:59'
AND store_store.company_id=2
AND payment_method_card.profile_id=8
);
运行并returns以下结果(符合预期):
+== id ==+== average_time_of_day ==+
|= 9422 =|===== 20:42:22.8695 =====|
(这是来自海蒂SQL的运行)
通过 Django 做类似的事情(我想!但有些事情显然是错误的):
average_time_of_day = Transaction.objects.raw(
'''
SELECT
transaction_transaction.id,
sec_to_time(avg(time_to_sec(extract(HOUR_SECOND from transaction_transaction.transaction_datetime))))
AS average_time_of_day
FROM transaction_transaction
INNER JOIN store_store ON (transaction_transaction.store_id=store_store.id)
%s
WHERE (
transaction_transaction.transaction_datetime BETWEEN %s AND %s
AND store_store.company_id=%s
%s
);
''',
[
'INNER JOIN payment_method_card ON (transaction_transaction.card_id=payment_method_card.id) ' if profile_pk else '',
start_datetime,
end_datetime,
company_pk,
'AND payment_method_card.profile_id=' + str(profile_pk) if profile_pk else '',
]
)
正在做
print average_time_of_day.query
输出:
SELECT
transaction_transaction.id,
sec_to_time(avg(time_to_sec(extract(HOUR_SECOND from transaction_transaction.transaction_datetime))))
AS average_time_of_day
FROM transaction_transaction
INNER JOIN store_store ON (transaction_transaction.store_id=store_store.id)
INNER JOIN payment_method_card ON (transaction_transaction.card_id=payment_method_card.id)
WHERE (
transaction_transaction.transaction_datetime BETWEEN 2017-08-31 00:00:00 AND 2017-08-31 00:00:00
AND store_store.company_id=2
AND payment_method_card.profile_id=8
);
而 Django returns 出现以下错误:
ProgrammingError: (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 ''INNER JOIN payment_method_card ON (transaction_transaction.card_id=payment_meth' at line 7")
知道我做错了什么吗?
您是否尝试过使用 Django 的 ORM 来构建该查询?它可以使调试更容易。它可能看起来像这样:
Transaction.objects.filter(
store__company_id=company_pk,
payment_method_card__profile_id=profile_pk,
transaction_datetime__range(start_datetime, end_datetime)
)
如果您需要更细粒度的控制,您可以使用 Django's select_related
and prefetch_related
on your query, and you can use a Q() object
优化查询。
我不喜欢手动写出 SQL 语句,所以这有点逃避,但我喜欢使用 Django 提供的工具。
在SQL字符串中一些变量必须是字符串。例如日期时间必须在 "" 或 '' 我不记得了但是 queryset.query
显示你格式化的 sql 字符串不是有效的字符串。
所以只需尝试添加 ''.
没错。
这个会教我不要尝试并且:
- 太聪明了
- 过度使用单行:-(
这不起作用:
average_time_of_day = Transaction.objects.raw(
'''
SELECT
transaction_transaction.id,
sec_to_time(avg(time_to_sec(extract(HOUR_SECOND from transaction_transaction.transaction_datetime))))
AS average_time_of_day
FROM transaction_transaction
INNER JOIN store_store ON (transaction_transaction.store_id=store_store.id)
%s
WHERE (
transaction_transaction.transaction_datetime BETWEEN %s AND %s
AND store_store.company_id=%s
%s
);
''',
[
'INNER JOIN payment_method_card ON (transaction_transaction.card_id=payment_method_card.id) ' if profile_pk else '',
start_datetime,
end_datetime,
company_pk,
'AND payment_method_card.profile_id=' + str(profile_pk) if profile_pk else ''
]
)
注意 params
中的 'INNER JOIN payment_method_card ON (transaction_transaction.card_id=payment_method_card.id) ' if profile_pk else ''
和 'AND payment_method_card.profile_id=' + str(profile_pk) if profile_pk else ''
发送到 raw() 方法。有点动态?
...但是这个有效:
if profile_pk:
average_time_of_day = Transaction.objects.raw(
'''
SELECT
transaction_transaction.id,
sec_to_time(avg(time_to_sec(extract(HOUR_SECOND from transaction_transaction.transaction_datetime))))
AS average_time_of_day
FROM transaction_transaction
INNER JOIN store_store ON (transaction_transaction.store_id=store_store.id)
INNER JOIN payment_method_card ON (transaction_transaction.card_id=payment_method_card.id)
WHERE (
transaction_transaction.transaction_datetime BETWEEN %s AND %s
AND store_store.company_id=%s
AND payment_method_card.profile_id=%s
);
''',
[
start_datetime,
end_datetime,
company_pk,
profile_pk
]
)
else:
average_time_of_day = Transaction.objects.raw(
'''
SELECT
transaction_transaction.id,
sec_to_time(avg(time_to_sec(extract(HOUR_SECOND from transaction_transaction.transaction_datetime))))
AS average_time_of_day
FROM transaction_transaction
INNER JOIN store_store ON (transaction_transaction.store_id=store_store.id)
WHERE (
transaction_transaction.transaction_datetime BETWEEN %s AND %s
AND store_store.company_id=%s
);
''',
[
start_datetime,
end_datetime,
company_pk
]
)
那么 Django 可以不喜欢 params
的动态构建吗?我很高兴知道答案......我仍然怀疑我做错了什么......这可能与 SQL 注入保护有关吗?
显然 Django 将 INNER JOIN
表达式用单引号括起来 - 因此导致 MySQL 拒绝查询。实际上看起来是这样的,尽管 print average_time_of_day.query
什么也没说:
SELECT
transaction_transaction.id,
sec_to_time(avg(time_to_sec(extract(HOUR_SECOND from transaction_transaction.transaction_datetime))))
AS average_time_of_day
FROM transaction_transaction
INNER JOIN store_store ON (transaction_transaction.store_id=store_store.id)
'INNER JOIN payment_method_card ON (transaction_transaction.card_id=payment_method_card.id)'
WHERE (
transaction_transaction.transaction_datetime BETWEEN 2017-08-31 00:00:00 AND 2017-08-31 23:59:59
AND store_store.company_id=2
'AND payment_method_card.profile_id=8'
);