Как заполнить значения CTE из списка? - PullRequest
3 голосов
/ 14 марта 2020

В следующем примере я хотел бы динамически заполнить значения CTE в запросе из списка, используя стандартный Python SQLite3 API:

-- test schema
CREATE TABLE t AS SELECT "" AS k FROM (VALUES (1),(3));

-- query to build
WITH cte(k) AS (VALUES
    (1),
    (2),
    (3)
) SELECT * FROM t INNER JOIN cte USING(k);

Это не работает:

import sqlite3

SQL_INIT="""
CREATE TABLE t AS SELECT "" AS k FROM (VALUES (1),(3));
"""

SQL_QUERY="""
WITH cte(k) AS (VALUES
    (:values)
) SELECT * FROM t INNER JOIN cte USING(k);
"""
conn = sqlite3.connect(':memory:')
conn.executescript(SQL_INIT)

params = dict(
    values=(1,2,3)
)
for row in conn.execute(SQL_QUERY, params):
    print(row)

Так есть ли способ использовать заполнители для заполнения значений CTE из кода Python? Если нет, что было бы хорошим решением? Действительно ли мне приходится прибегать к простой обработке строк и ручной очистке значений?

Для простоты в приведенном выше примере заполнение CTE целочисленными значениями. Но решение должно работать с числами и строками.

Ответы [ 2 ]

1 голос
/ 14 марта 2020

На самом деле я не знаю, можно ли использовать синтаксис CTE таким образом из Python API. Предполагая, что вы можете , проблема, с которой вы сталкиваетесь, заключается в том, что вы пытаетесь привязать коллекцию к одному заполнителю в запросе. Вместо этого вы можете попробовать связать коллекцию с подготовленным оператором, который имеет правильное число ? заполнителей.

vals = (1, 2, 3)
vals_clause = '(?)' + ',(?)'*(len(vals)-1)
sql = 'WITH cte(k) AS (VALUES ' + vals_clause + ') '
sql = sql + 'SELECT * FROM t INNER JOIN cte USING(k)'
for row in conn.execute(SQL_QUERY, vals):
    print(row)

Запрос SQL, сгенерированный приведенным выше сценарием:

WITH cte(k) AS (VALUES (?),(?),(?))
SELECT * FROM t INNER JOIN cte USING(k)

Вы можете видеть, что мы сгенерировали сколько угодно ? заполнителей, необходимых для покрытия кортежа, который вы передаете в вызов execute().

Если вы не можете использовать Приведенное выше решение, возможно, может быть, что используемый вами API не позволяет использовать CTE. Мы все еще можем перефразировать ваш запрос следующим образом:

SELECT *
FROM t INNER JOIN
(
    SELECT 1 AS k UNION ALL
    SELECT 2 UNION ALL
    SELECT 3
) cte USING(k);

В этом случае мы просто используем формальный подзапрос для вставки значений во временную таблицу. Подход сценариев, который вы использовали бы для генерации вышеупомянутого набора динамических c буквенных значений, идентичен тому, что я уже представил выше.

0 голосов
/ 14 марта 2020

Одним из решений, хотя и не очень элегантным, было бы динамическое создание списка заполнителей:

SQL_QUERY_HEAD="""
WITH cte(k) AS (VALUES
"""

SQL_QUERY_TAIL="""
) SELECT * FROM t INNER JOIN cte USING(k);
"""

[...]

values=(1,2,3)

SQL_QUERY=(
    SQL_QUERY_HEAD +
    ",".join(len(values)*("(?)",)) +
    SQL_QUERY_TAIL
)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...