Django .raw查询失败(错误1064) - 但在SQL中工作

时间:2017-09-15 00:52:22

标签: python mysql sql django django-1.11

(使用django 1.11.2,python 2.7.10,mysql 5.7.18)

以下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 '',
    ]
)

运行并返回以下结果(如预期的那样):

print average_time_of_day.query

(这是从HeidiSQL运行的)

通过Django做类似的事情(我认为!但显然有些错误):

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
);

否则

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")

输出:

        $y = 1;
        $period = 3;
        $start = date('m/d/Y');
        $end = date('m/t/Y');
        echo "<table>";
        echo '<thead><th>From</th>';
        echo '<th>To</th></thead>';

        for ($y; $y <= $period; $y++) {
         echo '<tr><td>'.$start.'</td>';
         echo '<td>'.$end.'</td></tr>';

         $getLast = date('d',strtotime($end));

             if($getLast >= 28) {
                $start = date("m/t/Y", strtotime($start)); 
                $end = date("m/d/Y", strtotime("+15 day", strtotime($end))); 
             }else {
                $start = date("m/d/Y", strtotime("+15 day", strtotime($start))); 
                $end = date("m/t/Y",strtotime($end));   
             }
        }

        echo "</table>";

Django返回时出现以下错误:

From    To
09/15/2017  09/30/2017
09/30/2017  10/15/2017
10/15/2017  10/31/2017

知道我做错了吗?

3 个答案:

答案 0 :(得分:0)

您是否尝试过使用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优化查询,如果需要更细粒度的控制,则可以使用Q() object

我不喜欢手动编写SQL语句,所以这有点像一个副本,但我喜欢使用Django给我的工具。

答案 1 :(得分:0)

在SQL字符串中,某些变量必须是字符串。例如,datetime必须在“”或“我不记得但queryset.query显示您格式化的sql字符串无效的字符串。 所以只需尝试添加''。

答案 2 :(得分:0)

右。

这个会教我不要尝试:

  1. 太聪明了
  2. 过多地使用单行: - (
  3. 这不起作用:

    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 ''
        ]
    )
    

    请注意发送到raw()方法的'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 ''params。有点动态吗?

    ...但有效:

    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'
    );