参数化原始sql查询比实际值查询慢得多

问题描述 投票:0回答:3

我正在尝试在连接到 SQL Server 的 Django 应用程序中使用connections[].cursor() 执行原始sql 查询。查询执行速度更快 (<1s) when I provide the actual vlaues in the query string.

from django.db import connections
     with connections['default'].cursor() as cursor:
        cursor.execute("""                   
                        select c.column1 as c1
                        , ve.column2 as c2
                        from view_example c
                        left join view_slow_view ve on c.k1 = ve.k2
                        where c.column_condition = value_1 and c.column_cd_2 = value2
                        """)
        result = dictfetchall(cursor)

但是当我在cursor.execute()方法中提供值作为params时,查询变得慢得多(2分钟)。

from django.db import connections
     with connections['default'].cursor() as cursor:
        cursor.execute("""                   
                        select c.column1 as c1
                        , ve.column2 as c2
                        from view_example c
                        left join view_slow_view ve on c.k1 = ve.k2
                        where c.column_condition = %s and c.column_condition_2 = %s
                        """, [value_1, value_2])
        contracts_dict_lst = dictfetchall(cursor)

我还应该提到,仅当未提供条件时,在 SSMS 上执行查询时实际上很慢:

 where c.column_condition = value_1 and c.column_cd_2 = value2

就好像Django发送查询时,是在不带参数的情况下执行的(因此响应时间很长),然后提供参数,以便过滤结果。

有问题的值是由用户提供的,因此它们会发生变化,并且必须作为参数传递,而不是直接在查询中传递,以避免 SQL 注入。 该查询也比上面给出的示例复杂得多,并且不能清楚地映射到模型,因此我必须使用connection[].cursor()

sql-server ssms django-pyodbc-azure
3个回答
3
投票

这可能是参数嗅探问题。如果是这种情况,有两种解决方案。最简单的解决方案是使用查询提示。

选项1:

from django.db import connections
     with connections['default'].cursor() as cursor:
        cursor.execute("""                   
                        select c.column1 as c1
                        , ve.column2 as c2
                        from view_example c
                        left join view_slow_view ve on c.k1 = ve.k2
                        where c.column_condition = %s and c.column_condition_2 = %s
                        OPTION(RECOMPILE) -- add this line to your query
                        """, [value_1, value_2])
        contracts_dict_lst = dictfetchall(cursor)

选项2:

from django.db import connections
     with connections['default'].cursor() as cursor:
        cursor.execute(""" 
                        declare v1 varchar(100) = %s  -- declare variable and use them
                        declare v2 varchar(100) = %s
              
                        select c.column1 as c1
                        , ve.column2 as c2
                        from view_example c
                        left join view_slow_view ve on c.k1 = ve.k2
                        where c.column_condition = v1 and c.column_condition_2 = v2
                        """, [value_1, value_2])
        contracts_dict_lst = dictfetchall(cursor)

这是一个很好的链接,可供更多阅读。


0
投票
msql = "Select count(1) as Bil from customer where AcctNo = '" + ACCTNO +"'"

不要使用 AcctNo 参数执行。

下面会运行得更慢:

result = cursor1.execute('Select count(1) as Bil from customer where AcctNo = ?', ACCTNO) 

0
投票

我也有类似的问题。对于大型数据集尤其会发生这种情况,因为驱动程序从 Python 获取 unicode 值,并使用

NVARCHAR
来实现更广泛的兼容性。现在,这意味着在 NVARCHAR 子句中进行比较时,查询必须临时将每个列值(
从相应的参数
)转换为
where
。我的解决方法是在查询中强制转换,这就是我在下面提供选项 3的原因。

选项3:

from django.db import connections
     with connections['default'].cursor() as cursor:
        cursor.execute("""                   
                        select c.column1 as c1
                        , ve.column2 as c2
                        from view_example c
                        left join view_slow_view ve on c.k1 = ve.k2
                        where c.column_condition = CAST(%s as VARCHAR(100) and c.column_condition_2 = CAST(%s as VARCHAR(100)
                        """, [value_1, value_2])
        contracts_dict_lst = dictfetchall(cursor)
最新问题
© www.soinside.com 2019 - 2024. All rights reserved.