按照this link中提到的方法,我想将ORDER BY
和排序顺序动态传递给函数。
ORDER BY
工作正常,但我无法通过排序顺序(ASC
/ DESC
)。
我现在拥有的:
CREATE OR REPLACE FUNCTION list(_limit integer,_offset integer,sort_by varchar(100), _order varchar(100),_category varchar(100))
RETURNS TABLE(
id INTEGER,
name VARCHAR,
clientname VARCHAR,
totalcount BIGINT
) AS $$
DECLARE empty text := '';
BEGIN
RETURN Query EXECUTE
'SELECT d.id,
d.name,
d.clientname,
count(*) OVER() AS full_count FROM design_list as d
where ($5 = $6 Or d.category Ilike $5)
ORDER BY ' || quote_ident(sort_by) || ' LIMIT $1 offset $2'
USING _limit,_offset,sort_by, _order,_category, empty;
END;$$
LANGUAGE plpgsql;
答案 0 :(得分:5)
我会这样做:
CREATE OR REPLACE FUNCTION list(
_category varchar(100)
, _limit int
, _offset int
, _order_by varchar(100)
, _order_asc_desc text = 'ASC') -- last param with default value
RETURNS TABLE(id int, name varchar, clientname varchar, totalcount bigint) AS
$func$
DECLARE
_empty text := '';
BEGIN
-- Assert valid _order_asc_desc
IF upper(_order_asc_desc) IN ('ASC', 'DESC', 'ASCENDING', 'DESCENDING') THEN
-- proceed
ELSE
RAISE EXCEPTION 'Unexpected value for parameter _order_asc_desc.
Allowed: ASC, DESC, ASCENDING, DESCENDING. Default: ASC';
END IF;
RETURN QUERY EXECUTE format(
'SELECT id, name, clientname, count(*) OVER() AS full_count
FROM design_list
WHERE ($1 = $2 OR category ILIKE $1)
ORDER BY %I %s
LIMIT %s
OFFSET %s'
, _order_by, _order_asc_desc, _limit, _offset)
USING _category, _empty;
END
$func$ LANGUAGE plpgsql;
核心功能:使用format()
安全,优雅地连接您的查询字符串。相关:
ASC
/ DESC
(或ASCENDING
/ DESCENDING
)是固定关键字。我添加了一个手动检查(IF ...
),然后用简单的%s
连接。这是一种断言合法输入的方式。为方便起见,我为意外输入和参数默认添加了错误消息,因此如果调用中省略了最后一个参数,则函数默认为ASC
。相关:
解决Pavel's valid comment,我直接连接_limit
和_offset
,因此已经使用这些参数规划了查询。
_limit
和_offset
是integer
参数,因此我们可以使用普通%s
而不会出现SQL注入的危险。您可能希望在连接...之前断言合理的值(排除负值和值太高)
其他说明:
使用一致的命名约定。我使用下划线_
为所有参数和变量添加前缀,而不只是某些。
未在EXECUTE
内使用表格资格,因为只涉及一个表格,EXECUTE
有其独立的范围。
我重命名了一些参数来澄清。 _order_by
代替_sort_by
; _order_asc_desc
代替_order
。
答案 1 :(得分:0)
非动态sql解决方案。
CREATE OR REPLACE FUNCTION list(
...
in_use_asc boolean default false,
_order_by varchar(100)
..
)
..
CREATE TEMPORARY TABLE tempHolder ON COMMIT DROP AS
SELECT SELECT id, name, clientname, count(*) OVER() AS full_count
FROM design_list
WHERE ($1 = $2 OR category ILIKE $1);
IF in_use_asc = TRUE THEN
RETURN QUERY SELECT * FROM tempHolder ORDER BY _order_by asc LIMIT {} OFFSET {};
ELSE
RETURN QUERY SELECT * FROM tempHolder ORDER BY _order_by desc LIMIT {} OFFSET {};
END IF;
应该再慢一点,因为由于ORDER BY的缘故,SQL无论如何都要抢占一切,而且还要避免使用动态SQL。