【问题标题】:Create SQL command with a query parameter that checks for NULL but also for other values创建带有查询参数的 SQL 命令,该参数检查 NULL 以及其他值
【发布时间】:2019-12-19 17:35:01
【问题描述】:

我正在尝试使用 Python / Postgres 编写动态 SQL 命令。在我的 where 子句中,我想使用一个查询参数(它是用户定义的),它必须在代码列(varchar)中查找 NULL 值,但在其他情况下也可以查找特定数字。

如果我必须检查某个值,我会使用这个:

cursor.execute("""
                    select
                        z.code as code
                    from
                        s_order as o
                    LEFT JOIN
                        s_code as z ON o.id = z.id
                    where
                        z.code = %(own_id)s;
                    """, {
                        'own_id': entry
                    })

但是,如果我必须检查 NULL 值,SQL,更具体地说,WHERE 子句必须是

select
    z.code as code
from
    s_order as o
LEFT JOIN
    s_code as z ON o.id = z.id
WHERE
    z.code IS NULL;

第一个语句中使用的查询参数不适用于第二个语句,因为它只能替换等式右侧的值,而不能替换运算符。我在这里(https://realpython.com/prevent-python-sql-injection/#using-query-parameters-in-sql)读到,表名也可以使用 psycopg2 提供的 SQL 标识符替换,但无法找到如何替换整个 WHERE 子句或至少是运算符。

不幸的是,我无法更改代码列中的 NULL 值(例如,使用默认值),因为这些 NULL 值是通过 JOIN 操作创建的。

目前我唯一的选择是根据输入值进行不同的 SQL 查询,但由于 SQL 查询很长(我为这个问题缩短了它)而且我有很多类似的查询,它会导致很多类似的代码......那么我怎样才能让这个 WHERE 子句动态呢?

编辑: 除了标记为正确的答案之外,我还想添加自己的解决方案,它不是那么优雅,但在更复杂的场景中可能会有所帮助,因为 NULL 字段被替换为 COALESCE 的“默认”值:

create view orsc as
    select
        coalesce(z.code), 'default') as code
    from
        s_order as o
    LEFT JOIN
        s_code as z ON o.id = z.id;
SELECT
    orsc.code as code2
from
    orsc
WHERE
    code2 = 'default'; //or any other value

EDIT2:查看标记答案的 cmets,为什么视图可能根本不需要。

EDIT3:这个question 没有帮助,因为它只要求检查NULL 值。除此之外,答案中显示的 IF 语句将大大增加我的整个代码库(每个查询都很长,并且经常以稍微适应的方式使用)。

【问题讨论】:

  • 不,很遗憾没有。答案建议使用 IF 创建一个或另一个 SQL 命令,这是可行的,但如上所述对我来说并不理想。
  • @richyen 你能确认链接的讨论没有回答我的问题吗? SO 给了我一个方框,上面写着一个类似的问题可能会回答我的问题并且它不会消失。或者,我的问题可能会有所帮助......

标签: python sql postgresql psycopg2


【解决方案1】:

考虑COALESCENULL 一个默认值。下面假设 z.code 是一个 varchar 或文本。如果是整数/数字,请将 'default' 更改为数值(例如 9999)。

sql = """SELECT
               z.code as code
         FROM
               s_order as o
         LEFT JOIN
               s_code as z ON o.id = z.id
         WHERE
               COALESCE(z.code, 'default') = %(own_id)s;
      """

cursor.execute(sql, {'own_id': entry})    
cursor.execute(sql, {'own_id': 'default'})     # RETURNS NULLs IN z.code

Online Demo

【讨论】:

  • 感谢优雅的解决方案。我发现了一个类似但更复杂的解决方案,我已将其添加为我的问题的编辑。
  • 很高兴听到并乐于提供帮助。但是,对于您的解决方案,为什么要创建视图?用于其他地方?它在功能上与此解决方案没有区别。视图仍然在运行时处理。如果要进行演示,可以考虑CTE
  • 再次感谢您!我在第一步使用了一个视图,因为我有一个 SUM 操作需要在第二个查询中完成。以前从未听说过 CTE,但它们使整个查询更加紧凑,现在已经使用它!
【解决方案2】:

我不知道 phyton 的语法,但这是我的想法:

condition String;

if own_id = "NULL"{
    condition= " z.code IS NULL ";
}else{
    condition= " z.code = " + own_id;
}

cursor.execute("""
                    select
                        z.code as code
                    from
                        s_order as o
                    LEFT JOIN
                        s_code as z ON o.id = z.id
                    where
                        %(condition);
                    """, {
                        'condition': entry
                    })

【讨论】:

    【解决方案3】:

    如果我对您的理解正确,您希望能够发送 null 以及其他值并返回正确的结果。也就是说,问题在于如果 insent 值为 null,则比较不会返回任何内容。这将解决问题 - 可能会稍微降低性能。

    cursor.execute("""
                        select
                            z.code as code
                        from
                            s_order as o
                        LEFT JOIN
                            s_code as z ON o.id = z.id
                        where
                            z.code is not distinct from %(own_id)s;
                        """, {
                            'own_id': entry
                        })
    
    

    最好的问候,
    比亚尼

    【讨论】:

      【解决方案4】:

      正如我在 cmets 中链接的解决方案一样,使用if 块确实没有办法。你可以试试这个:

      pre_query="""
                          select
                              z.code as code
                          from
                              s_order as o
                          LEFT JOIN
                              s_code as z ON o.id = z.id
                          where
      """
      args={entry}
      zcode="z.code = %s"
      if entry == None:
         zcode="z.code IS NULL"
         args={}
      
      end_query=";" // other conditions
      full_query=pre_query+zcode+end_query
      
      cursor.execute(full_query,args)
      

      【讨论】:

        【解决方案5】:

        您可以使用 z.code IS NOT DISTINCT FROM %(own_id)s,它类似于 =,但 NULL 被视为正常值(即 NULL = NULL,NULL != 任何非空值)。

        Postgres comparison operators

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-12-28
          • 2018-09-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多