我在 PostgreSQL 10 中使用 PL/pgSQL 来创建复杂的查询。我正在使用几个
JOIN
和 AND
测试查询。这是我到目前为止所拥有的:
DROP FUNCTION IF EXISTS search_person(name text);
CREATE FUNCTION search_person(name text) RETURNS TABLE(address_id integer, address_geom text, event_name text) AS $$
--DECLARE
BEGIN
RETURN QUERY EXECUTE
'SELECT address.id, event.name, address.geom
FROM event JOIN person JOIN address JOIN person_address JOIN event_person
WHERE
person_address.event_id = event.id AND
event_person.event_id = event.id AND
person.id = event_person.person_id AND
person.name like
$1'
USING name;
END;
$$
LANGUAGE plpgsql;
创建此函数时没有出现任何错误。我这样称呼它
select search_person('nick');
,我得到:
ERROR: syntax error at or near "WHERE"
LINE 3: WHERE
^
QUERY: SELECT address.id, event.name, address.geom
FROM event JOIN person JOIN address JOIN person_address JOIN event_person
WHERE
person_address.event_id = event.id AND
event_person.event_id = event.id AND
person.id = event_person.person_id AND
person.name like
$1
CONTEXT: PL/pgSQL function search_creator(text) line 5 at RETURN QUERY
SQL state: 42601
我尝试在
AND
子句中将 ||
替换为 WHERE
,但没有任何改变。
这是我现在拥有的代码,尽管根据我检查的数据库数据我应该得到结果,但我得到一个空表。
CREATE FUNCTION search_person(name character(600)) RETURNS TABLE(address_id bigint, address_geom geometry, event_name character(200)) AS $$
BEGIN
RETURN QUERY EXECUTE
'SELECT address.id, address.geom, event.name
FROM
person
JOIN event_creator ON event_person.person_id = person.id
JOIN event ON event.id = event_person.event_id
JOIN person_address ON person_address.event_id = event.id
JOIN address ON address.id = cep.address_id
WHERE person.name LIKE $1'
USING name;
END;
$$
LANGUAGE plpgsql;
我该怎么办?
PL/pgSQL 函数的函数体按原样保存为字符串文字。在创建时仅运行表面语法检查。包含的语句并未实际执行或进行更深层次的测试。
在实际的 SQL 语句中仍然会检测到查询字符串中的基本语法错误。但您正在使用动态 SQL 和 EXECUTE
(除非您的数据分布非常不均匀并且想要强制 Postgres 为每个输入值生成自定义计划。)
普通的 SQL 语句会立即生成错误消息:
CREATE OR REPLACE FUNCTION search_person(name text) -- still incorrect!
RETURNS TABLE(address_id integer, address_geom text, event_name text)
LANGUAGE plpgsql AS
$func$
BEGIN
RETURN QUERY
SELECT address.id, event.name, address.geom
FROM event JOIN person JOIN address JOIN person_address JOIN event_person
WHERE
person_address.event_id = event.id AND
event_person.event_id = event.id AND
person.id = event_person.person_id AND
person.name like $1; -- this $1 refers to function parameter!
END
$func$;
SQL语句仍然无效。 需要一个连接条件 - 就像 Nick 评论的那样。而且我根本不认为有必要使用 PL/pgSQL。一个简单的 SQL 函数 应该可以很好地发挥作用:
CREATE FUNCTION search_person(name text)
RETURNS TABLE(address_id integer, address_geom text, event_name text)
LANGUAGE sql AS
$func$
SELECT a.id, a.geom, e.name -- column order matching return type!
FROM person AS p
JOIN event_person AS ep ON ep.person_id = p.id
JOIN event AS e ON e.id = ep.event_id
JOIN person_address AS pa ON pa.event_id = e.id
JOIN address AS a ON a.id = pa.address_id -- missing join condition!
WHERE p.name LIKE $1;
$func$;
我重写了查询以修复语法错误,使用表别名以获得更好的可读性。还根据有根据的猜测添加了一个缺失的条件:a.id = pa.address_id
。现在应该可以了。
相关:
或者根本没有函数,只需使用
prepared statements 代替。示例:
如果您毕竟需要动态 SQL,请像以前一样使用USING
子句传递 values
,并确保在concatenating 查询时防止 SQL 注入。 Postgres 提供了各种工具: