我正在创建一个将构建和执行动态sql语句的函数。基本上它正在构建ORDER BY子句,它可以根据用户传入的VARCHAR数组对多个级别进行深度ORDER。
例如,用户的输入将是一个数组:
{{orderColumn,orderDirection},{orderColumn,orderDirection},...}
显然,我理解声明中的问题所在,因为postgres相当清楚地将其打印出来。问题是在这种特殊情况下我无法弄清楚正确的语法。也许我做错了,而且我有更好的选择,我不会采用这种方法。如果我能在没有动态sql的情况下做到这一点,那就更好了。帮助或见解将不胜感激。
作为参考我使用的是PostgreSQL 9.6.9
我的代码到目前为止......
CREATE OR REPLACE FUNCTION profile.sp_rider_search(
_limit integer,
_offset integer,
_term text,
_orderBy VARCHAR[],
_registration_date timestamp with time zone DEFAULT
to_timestamp((0)::double precision))
RETURNS TABLE(customer_id integer
, first_name character varying
, last_name character varying
, dob date
, cell_phone character varying
, email character varying
, registration_date timestamp with time zone
, full_count integer)
AS $func$
DECLARE
sql TEXT := 'SELECT pp.user_id AS customer_id,
pp.first_name,
pp.last_name,
pp.date_of_birth AS dob,
pp.cell_phone,
aau.email,
pp.created AS registration_date,
count(*) OVER()::integer AS full_count
FROM profile.profile pp
LEFT JOIN authsvc.all_users aau ON aau.id = pp.user_id
WHERE pp.created > $5 AND (
pp.last_name ILIKE % || $3 || %
OR pp.first_name ILIKE % || $3 || %
OR pp.cell_phone ILIKE % || $3 || %
OR CAST(pp.user_id AS TEXT) ILIKE % || $3 || %
OR aau.email ILIKE % || $3 || %
OR CONCAT(pp.first_name,'' '',pp.last_name) ILIKE % || $3 || %
)
ORDER BY';
_item TEXT;
BEGIN
FOREACH _item IN ARRAY $4
LOOP
CASE WHEN _item[2] = 1 THEN
CASE WHEN _item[1] = 'last_name' THEN
sql := sql || ' pp.last_name, ';
WHEN _item[1] = 'first_name' THEN
sql := sql || ' pp.first_name, ';
WHEN _item[1] = 'email' THEN
sql := sql || ' aau.email, ';
WHEN _item[1] = 'phone' THEN
sql := sql || ' pp.cell_phone, ';
WHEN _item[1] = 'customer_id' THEN
sql := sql || ' pp.user_id::TEXT, ';
WHEN _item[1] = 'registration_date' THEN
sql := sql || ' pp.created::TEXT, ';
END
END
CASE WHEN _item[2] = -1 THEN
CASE WHEN _item[1] = 'last_name' THEN
sql := sql || ' pp.last_name DESC, ';
WHEN _item[1] = 'first_name' THEN
sql := sql || ' pp.first_name DESC, ';
WHEN _item[1] = 'email' THEN
sql := sql || ' aau.email DESC, ';
WHEN _item[1] = 'phone' THEN
sql := sql || ' pp.cell_phone DESC, ';
WHEN _item[1] = 'customer_id' THEN
sql := sql || ' pp.user_id::TEXT DESC, ';
WHEN _item[1] = 'registration_date' THEN
sql := sql || ' pp.created::TEXT DESC, ';
END
END
END LOOP;
sql := sql || 'LIMIT $1
OFFSET $2;'
RETURN QUERY EXECUTE sql
USING _limit,
_offset,
_term,
_orderBy,
_registration_date;
END;
$func$ LANGUAGE plpgsql;
在执行此语句时,我收到错误:
ERROR: syntax error at or near "END"
LINE 57: END
^
SQL state: 42601
Character: 998
我的陈述中有一些语法错误,我终于解决了,希望这会有助于其他人。我需要添加END CASE;对于每个case语句,我还必须在OFFSET $ 2之后添加一个分号;以及从语句中删除一个尾随逗号。
最终工作代码 - >
CREATE OR REPLACE FUNCTION profile.sp_rider_search(
_limit integer,
_offset integer,
_term text,
_orderBy VARCHAR[],
_registration_date timestamp with time zone DEFAULT
to_timestamp((0)::double precision),
_preferred_column TEXT DEFAULT '')
RETURNS TABLE(customer_id integer
, first_name character varying
, last_name character varying
, dob date
, cell_phone character varying
, email character varying
, registration_date timestamp with time zone
, full_count integer)
AS $func$
DECLARE
sql TEXT := 'SELECT pp.user_id AS customer_id,
pp.first_name,
pp.last_name,
pp.date_of_birth AS dob,
pp.cell_phone,
aau.email,
pp.created AS registration_date,
count(*) OVER()::integer AS full_count
FROM profile.profile pp
LEFT JOIN authsvc.all_users aau ON aau.id = pp.user_id
WHERE pp.created > $5 AND (
pp.last_name ILIKE % || $3 || %
OR pp.first_name ILIKE % || $3 || %
OR pp.cell_phone ILIKE % || $3 || %
OR CAST(pp.user_id AS TEXT) ILIKE % || $3 || %
OR aau.email ILIKE % || $3 || %
OR CONCAT(pp.first_name,'' '',pp.last_name) ILIKE % || $3 || %
)
ORDER BY';
_item TEXT;
BEGIN
FOREACH _item IN ARRAY $4
LOOP
CASE WHEN _item[2] = 1 THEN
CASE WHEN _item[1] = 'last_name' THEN
sql := sql || ' pp.last_name, ';
WHEN _item[1] = 'first_name' THEN
sql := sql || 'pp.first_name, ';
WHEN _item[1] = 'email' THEN
sql := sql || 'aau.email, ';
WHEN _item[1] = 'phone' THEN
sql := sql || 'pp.cell_phone, ';
WHEN _item[1] = 'customer_id' THEN
sql := sql || 'pp.user_id::TEXT, ';
WHEN _item[1] = 'registration_date' THEN
sql := sql || 'pp.created::TEXT, ';
END CASE;
WHEN _item[2] = -1 THEN
CASE WHEN _item[1] = 'last_name' THEN
sql := sql || ' pp.last_name DESC, ';
WHEN _item[1] = 'first_name' THEN
sql := sql || 'pp.first_name DESC, ';
WHEN _item[1] = 'email' THEN
sql := sql || 'aau.email DESC, ';
WHEN _item[1] = 'phone' THEN
sql := sql || 'pp.cell_phone DESC, ';
WHEN _item[1] = 'customer_id' THEN
sql := sql || 'pp.user_id::TEXT DESC, ';
WHEN _item[1] = 'registration_date' THEN
sql := sql || 'pp.created::TEXT DESC, ';
END CASE;
END CASE;
END LOOP;
sql := TRIM(TRAILING ',' FROM sql);
sql := sql || 'LIMIT $1
OFFSET $2;';
RETURN QUERY EXECUTE sql
USING _limit,
_offset,
_term,
_orderBy,
_registration_date;
END;
$func$ LANGUAGE plpgsql;