使用 PDO 在 PostgreSQL 中针对 JSON 键抛出 "must appear in the GROUP BY clause" 的准备语句
Prepared statements against a JSON key throwing "must appear in the GROUP BY clause" in PostgreSQL using PDO
我正在尝试按 json 键动态分组以找到平均答案。
select
tbl.data->>'Example' as response,
count(tbl.data->>'Example') as total
from table tbl
group by tbl.data->>'Example'
order by total
limit 1
这个查询在 Postgre运行 内部时工作正常SQL,我得到了我的预期结果:
| response | total |
|--------------------------|
| Hello World | 4 |
当我不知道密钥时,就会出现这个问题。它们是动态创建的,因此我需要遍历它们。
$sql = <<<END
select
tbl.data->>? as response,
count(tbl.data->>?) as total
from table tbl
group by tbl.data->>?
order by total
limit 1
END;
$stmt = (new \PDO(...))->Prepare($sql);
$stmt->execute(array_fill(1, 3, 'Example'));
$stmt->fetch(\PDO::FETCH_ASSOC);
'Example' 来自用户输入。 JSON 由用户创建,键可以是任何东西。在这种情况下,它是硬编码的,但我 运行 一个单独的 SQL 查询来获取所有键并循环遍历它们:
但我总是得到以下错误:
tbl.data must appear in the GROUP BY clause or be used in an aggregate function
现在,我假设这是因为准备好的语句将列视为数据,但此信息源自用户输入,因此我需要使用准备好的语句。
select json_object_keys(data) as keys from table
想知道如何解决这个问题吗?
我不知道 Php。但是从这个 link(https://kb.objectrocket.com/postgresql/postgres-stored-procedure-call-in-php-1475) 看来,在 php.
中使用函数似乎还可以
CREATE OR REPLACE FUNCTION find_answer (_key text)
RETURNS json
AS $$
DECLARE
is_exists boolean;
_sql text;
_return json;
BEGIN
_sql := $sql$
SELECT
row_to_json(cte.*)
FROM (
SELECT
tbl.data ->> AS response,
count(tbl.data ->> ) AS total
FROM
tbl
GROUP BY
1
ORDER BY
total DESC
LIMIT 1) cte $sql$;
SELECT
(data[_key] IS NULL) INTO is_exists
FROM
tbl;
IF is_exists THEN
RAISE EXCEPTION '% not exists.', _key;
ELSE
RAISE NOTICE '% sql', _sql;
EXECUTE _sql
USING _key INTO _return;
RETURN _return;
END IF;
END
$$
LANGUAGE plpgsql;
然后调用它。 select * from find_answer('example');
关于预处理语句
Prepared statements only last for the duration of the current
database session. When the session ends, the prepared statement is
forgotten, so it must be recreated before being used again. This also
means that a single prepared statement cannot be used by multiple
simultaneous database clients; however, each client can create their
own prepared statement to use. Prepared statements can be manually
cleaned up using the DEALLOCATE command.
我正在尝试按 json 键动态分组以找到平均答案。
select
tbl.data->>'Example' as response,
count(tbl.data->>'Example') as total
from table tbl
group by tbl.data->>'Example'
order by total
limit 1
这个查询在 Postgre运行 内部时工作正常SQL,我得到了我的预期结果:
| response | total |
|--------------------------|
| Hello World | 4 |
当我不知道密钥时,就会出现这个问题。它们是动态创建的,因此我需要遍历它们。
$sql = <<<END
select
tbl.data->>? as response,
count(tbl.data->>?) as total
from table tbl
group by tbl.data->>?
order by total
limit 1
END;
$stmt = (new \PDO(...))->Prepare($sql);
$stmt->execute(array_fill(1, 3, 'Example'));
$stmt->fetch(\PDO::FETCH_ASSOC);
'Example' 来自用户输入。 JSON 由用户创建,键可以是任何东西。在这种情况下,它是硬编码的,但我 运行 一个单独的 SQL 查询来获取所有键并循环遍历它们:
但我总是得到以下错误:
tbl.data must appear in the GROUP BY clause or be used in an aggregate function
现在,我假设这是因为准备好的语句将列视为数据,但此信息源自用户输入,因此我需要使用准备好的语句。
select json_object_keys(data) as keys from table
想知道如何解决这个问题吗?
我不知道 Php。但是从这个 link(https://kb.objectrocket.com/postgresql/postgres-stored-procedure-call-in-php-1475) 看来,在 php.
中使用函数似乎还可以CREATE OR REPLACE FUNCTION find_answer (_key text)
RETURNS json
AS $$
DECLARE
is_exists boolean;
_sql text;
_return json;
BEGIN
_sql := $sql$
SELECT
row_to_json(cte.*)
FROM (
SELECT
tbl.data ->> AS response,
count(tbl.data ->> ) AS total
FROM
tbl
GROUP BY
1
ORDER BY
total DESC
LIMIT 1) cte $sql$;
SELECT
(data[_key] IS NULL) INTO is_exists
FROM
tbl;
IF is_exists THEN
RAISE EXCEPTION '% not exists.', _key;
ELSE
RAISE NOTICE '% sql', _sql;
EXECUTE _sql
USING _key INTO _return;
RETURN _return;
END IF;
END
$$
LANGUAGE plpgsql;
然后调用它。 select * from find_answer('example');
关于预处理语句
Prepared statements only last for the duration of the current database session. When the session ends, the prepared statement is forgotten, so it must be recreated before being used again. This also means that a single prepared statement cannot be used by multiple simultaneous database clients; however, each client can create their own prepared statement to use. Prepared statements can be manually cleaned up using the DEALLOCATE command.