如果提供了空的元素列表,如何使 IN 表达式成为可选参数?
How to make IN expression an optional parameter if empty list of elements was provided?
如果提供了空列表作为参数,但我没有这样做,我想让 IN 语句成为我的 sql 查询的可选部分。我可以解决问题并在代码中使用一些默认值而不是空列表(所有 cam_ids),但我想知道如何正确地做到这一点。
我有以下sql表达式(真正的表达式要长得多):
SELECT
id, cam_id
FROM sometable
WHERE id > %(_id)s
// if use = instead of IN, it works well (of course if cameras is just one value, not array)
AND (%(camera)s is NULL OR cam_id IN %(camera)s)
在 python 中,我通过以下方式为查询提供参数:
values = {"_id": 10, camera: tuple(1, 2, 3)]}
curs.execute(query, values)
如果元组不为空则一切正常,否则:
如果相机=None,我得到以下错误:
psycopg2.errors.SyntaxError:“NULL”处或附近的语法错误
第 6 行:WHERE(NULL 为 NULL 或 cam_id IN NULL)
如果camera = tuple(),我得到以下错误:
psycopg2.errors.SyntaxError: ")" 处或附近的语法错误
第 6 行:WHERE (() 为 NULL 或 cam_id IN ())
为了更清楚:
如果提供了空数组,我想获得所有可能cam_ids的所有结果,所以结果应该与SELECT * FROM tablename;
[=相同14=]
编辑:为 Maurice Meyer
我在尝试进行更大的查询时发现了以下问题
select * from vworker_tracks
where
// still need some default value: cam_id != ''
(cam_id = any('{}') or cam_id != '')
and
// unexpected results when both are true, provides all tracks > 0.0
(track_duration_seconds = 2.5 or track_duration_seconds > 0.0)
and
id < 100
order by id desc limit 10;
您可以在 PostgreSQL 中使用 =ANY(),其中 ANY 将数组作为输入。而且那个数组可以为空,没问题。 Simpel 示例:
SELECT 1 = ANY('{}') -- false
, 2 = ANY('{2,3,4}'); -- true
您可以在 WHERE
条件中使用 OR
到 'emulate' if/else
:
query = """
with cameras as (
select
%(ids)s::int[] as ids,
%(idsLen)s as count /* simplify */
)
select
count(conversations.*)
from
conversations,
cameras
where
(
(cameras.count >= 1 and id = ANY(cameras.ids))
or
(cameras.count < 1 and id > %(_id)s)
)
"""
for ids in [[81, 60], []]:
values = {"_id": 10, "ids": ids, "idsLen": len(ids)}
curs.execute(query, values)
print(curs.fetchone())
输出:
(2,) # 2 ids given, returns 2 records
(118,) # empty array, returns ALL records
答案不提供任何原生sql解决方案,它基于https://use-the-index-luke.com/sql/myth-directory/dynamic-sql-is-slow,遵循KISS原则:
from aiopg.cursor import Cursor
async def my_test_query(curs: Cursor, params: dict) -> list:
"""
the snippet demonstrates how to create IN filters
The idea is build on the article
https://use-the-index-luke.com/sql/myth-directory/dynamic-sql-is-slow
follows KISS principle
"""
query = f"""
SELECT
id,
cam_id,
track_duration_seconds,
top_color,
bottom_color,
crossed_lines->>'entrance',
crossed_lines->>'cross_direction'
FROM vworker_tracks
WHERE id < %(_id)s
"""
if params.get("camera", None) is not None:
query += " AND cam_id IN %(camera)s"
if params.get("bottom_color", None) is not None:
query += " AND bottom_color IN %(bottom_color)s"
if params.get("top_color", None) is not None:
query += " AND top_color IN %(top_color)s"
if params.get("cross_direction", None) is not None:
query += " AND crossed_lines->>'cross_direction' IN %(cross_direction)s"
if params.get("entrance", None) is not None:
query += " AND crossed_lines->>'entrance' IN %(entrance)s"
query += " ORDER BY id DESC LIMIT 50;"
await curs.execute(query, params)
res = await curs.fetchall()
return res
如果提供了空列表作为参数,但我没有这样做,我想让 IN 语句成为我的 sql 查询的可选部分。我可以解决问题并在代码中使用一些默认值而不是空列表(所有 cam_ids),但我想知道如何正确地做到这一点。
我有以下sql表达式(真正的表达式要长得多):
SELECT
id, cam_id
FROM sometable
WHERE id > %(_id)s
// if use = instead of IN, it works well (of course if cameras is just one value, not array)
AND (%(camera)s is NULL OR cam_id IN %(camera)s)
在 python 中,我通过以下方式为查询提供参数:
values = {"_id": 10, camera: tuple(1, 2, 3)]}
curs.execute(query, values)
如果元组不为空则一切正常,否则:
如果相机=None,我得到以下错误:
psycopg2.errors.SyntaxError:“NULL”处或附近的语法错误 第 6 行:WHERE(NULL 为 NULL 或 cam_id IN NULL)
如果camera = tuple(),我得到以下错误:
psycopg2.errors.SyntaxError: ")" 处或附近的语法错误 第 6 行:WHERE (() 为 NULL 或 cam_id IN ())
为了更清楚:
如果提供了空数组,我想获得所有可能cam_ids的所有结果,所以结果应该与 编辑:为 Maurice Meyer 我在尝试进行更大的查询时发现了以下问题SELECT * FROM tablename;
[=相同14=]
select * from vworker_tracks
where
// still need some default value: cam_id != ''
(cam_id = any('{}') or cam_id != '')
and
// unexpected results when both are true, provides all tracks > 0.0
(track_duration_seconds = 2.5 or track_duration_seconds > 0.0)
and
id < 100
order by id desc limit 10;
您可以在 PostgreSQL 中使用 =ANY(),其中 ANY 将数组作为输入。而且那个数组可以为空,没问题。 Simpel 示例:
SELECT 1 = ANY('{}') -- false
, 2 = ANY('{2,3,4}'); -- true
您可以在 WHERE
条件中使用 OR
到 'emulate' if/else
:
query = """
with cameras as (
select
%(ids)s::int[] as ids,
%(idsLen)s as count /* simplify */
)
select
count(conversations.*)
from
conversations,
cameras
where
(
(cameras.count >= 1 and id = ANY(cameras.ids))
or
(cameras.count < 1 and id > %(_id)s)
)
"""
for ids in [[81, 60], []]:
values = {"_id": 10, "ids": ids, "idsLen": len(ids)}
curs.execute(query, values)
print(curs.fetchone())
输出:
(2,) # 2 ids given, returns 2 records
(118,) # empty array, returns ALL records
答案不提供任何原生sql解决方案,它基于https://use-the-index-luke.com/sql/myth-directory/dynamic-sql-is-slow,遵循KISS原则:
from aiopg.cursor import Cursor
async def my_test_query(curs: Cursor, params: dict) -> list:
"""
the snippet demonstrates how to create IN filters
The idea is build on the article
https://use-the-index-luke.com/sql/myth-directory/dynamic-sql-is-slow
follows KISS principle
"""
query = f"""
SELECT
id,
cam_id,
track_duration_seconds,
top_color,
bottom_color,
crossed_lines->>'entrance',
crossed_lines->>'cross_direction'
FROM vworker_tracks
WHERE id < %(_id)s
"""
if params.get("camera", None) is not None:
query += " AND cam_id IN %(camera)s"
if params.get("bottom_color", None) is not None:
query += " AND bottom_color IN %(bottom_color)s"
if params.get("top_color", None) is not None:
query += " AND top_color IN %(top_color)s"
if params.get("cross_direction", None) is not None:
query += " AND crossed_lines->>'cross_direction' IN %(cross_direction)s"
if params.get("entrance", None) is not None:
query += " AND crossed_lines->>'entrance' IN %(entrance)s"
query += " ORDER BY id DESC LIMIT 50;"
await curs.execute(query, params)
res = await curs.fetchall()
return res