Postgres。 FORMAT 不会用提供的值替换类型参数
Postgres. FORMAT doesn't replace type argument with provided value
老实说,我对 postgres 和 sql 一点经验都没有。
我正在尝试使用 EXECUTE FORMAT(...) 查询使用动态 table 和列名创建函数。但是无论我尝试什么,FORMAT 都不考虑为类型参数提供的值,例如 %s 和 %I。由于缺乏有用的信息,谷歌搜索无济于事。我看到了很多示例,我的代码与示例没有区别但不起作用。我在这里做了 fiddle -> db-fiddle
这是我的 DDL 代码:
CREATE TABLE IF NOT EXISTS tbl (
first_name VARCHAR(100) NOT NULL,
last_name VARCHAR(100) NOT NULL
);
CREATE FUNCTION occurences_number(varchar, varchar) RETURNS integer AS '
DECLARE
text varchar := LOWER();
str varchar := LOWER();
BEGIN
RETURN (CHAR_LENGTH(text) - CHAR_LENGTH(REPLACE(text, str, " "))) / CHAR_LENGTH(str);
END; '
LANGUAGE PLPGSQL;
CREATE FUNCTION records_with_string(regclass, varchar, varchar)
RETURNS integer AS '
DECLARE
result integer;
tbl ALIAS FOR ;
col ALIAS FOR ;
str ALIAS FOR ;
BEGIN
EXECUTE format("SELECT COUNT(*) FROM %I WHERE occurences_number(%I, %s) > 0", tbl::varchar, col::varchar, str::varchar)
INTO result;
RETURN result;
END; '
LANGUAGE PLPGSQL;
这是查询:
INSERT INTO tbl VALUES ('a', 'aaa bbb ccc ddd'), ('b', 'aaa bbb'), ('c', 'ccc ddd'), ('d', 'aaa ccc ddd'), ('e', 'aaa bbb ccc ddd eee');
SELECT "records_with_string"('tbl'::regclass, 'last_name', 'eee') FROM tbl;
查询结果为:
Query Error: error: column "SELECT COUNT(*) FROM %I WHERE occurences_number(%I, %s) > 0" does not exist
我做错了什么?
更新
db-fiddle 不接受 $$
你不应该对字符串文字使用双引号,而只能使用单引号,你应该在使用 format
:
时对 SQL 文字使用 %L
select * from tbl;
first_name | last_name
------------+---------------------
a | aaa bbb ccc ddd
b | aaa bbb
c | ccc ddd
d | aaa ccc ddd
e | aaa bbb ccc ddd eee
(5 rows)
CREATE OR REPLACE FUNCTION occurences_number(varchar, varchar) RETURNS integer AS
$$
DECLARE
text varchar := LOWER();
str varchar := LOWER();
BEGIN
RETURN (CHAR_LENGTH(text) - CHAR_LENGTH(REPLACE(text, str, ' '))) / CHAR_LENGTH(str);
END;
$$
LANGUAGE PLPGSQL;
CREATE FUNCTION
CREATE OR REPLACE FUNCTION records_with_string(tbl varchar, col varchar, str varchar)
RETURNS integer AS
$$
DECLARE
result integer;
BEGIN
EXECUTE format('SELECT COUNT(*) FROM %I WHERE occurences_number(%L, %L) > 0', tbl, col, str)
INTO result;
RETURN result;
END;
$$
LANGUAGE PLPGSQL;
CREATE FUNCTION
SELECT "records_with_string"('tbl', 'last_name', 'eee') FROM tbl;
records_with_string
---------------------
0
0
0
0
0
(5 rows)
为了避免与 普通 引号冲突,您需要用 dollar quotes
(基本上是两个 $
字符,中间有一些东西来引用您的函数体. Something 可以为空,但开始引用和结束引用必须匹配):
CREATE TABLE IF NOT EXISTS ztbl (
first_name VARCHAR(100) NOT NULL,
last_name VARCHAR(100) NOT NULL
);
INSERT INTO ztbl(first_name, last_name) VALUES ('Barack' , 'Obama') , ('Donald' , 'Trump') ;
CREATE FUNCTION occurences_number(varchar, varchar) RETURNS integer AS
$zz$ -- <<-- here
DECLARE
txt varchar := LOWER();
str varchar := LOWER();
BEGIN
RETURN (CHAR_LENGTH(txt) - CHAR_LENGTH(REPLACE(txt, str, '' ))) / CHAR_LENGTH(str);
END;
$zz$ -- <<-- here
LANGUAGE PLPGSQL;
CREATE FUNCTION records_with_string(regclass, varchar, varchar)
RETURNS integer AS
$zzz$ -- <<-- here
DECLARE
result integer;
tbl ALIAS FOR ;
col ALIAS FOR ;
str ALIAS FOR ;
BEGIN
EXECUTE format('
SELECT COUNT(*) FROM %I
WHERE occurences_number(%I, %L ) > 0 -- <<-- here
;', tbl::varchar, col::varchar, str::varchar)
INTO result;
RETURN result;
END;
$zzz$ -- <<-- here
LANGUAGE PLPGSQL;
SELECT records_with_string('ztbl', 'first_name', 'a');
老实说,我对 postgres 和 sql 一点经验都没有。
我正在尝试使用 EXECUTE FORMAT(...) 查询使用动态 table 和列名创建函数。但是无论我尝试什么,FORMAT 都不考虑为类型参数提供的值,例如 %s 和 %I。由于缺乏有用的信息,谷歌搜索无济于事。我看到了很多示例,我的代码与示例没有区别但不起作用。我在这里做了 fiddle -> db-fiddle
这是我的 DDL 代码:
CREATE TABLE IF NOT EXISTS tbl (
first_name VARCHAR(100) NOT NULL,
last_name VARCHAR(100) NOT NULL
);
CREATE FUNCTION occurences_number(varchar, varchar) RETURNS integer AS '
DECLARE
text varchar := LOWER();
str varchar := LOWER();
BEGIN
RETURN (CHAR_LENGTH(text) - CHAR_LENGTH(REPLACE(text, str, " "))) / CHAR_LENGTH(str);
END; '
LANGUAGE PLPGSQL;
CREATE FUNCTION records_with_string(regclass, varchar, varchar)
RETURNS integer AS '
DECLARE
result integer;
tbl ALIAS FOR ;
col ALIAS FOR ;
str ALIAS FOR ;
BEGIN
EXECUTE format("SELECT COUNT(*) FROM %I WHERE occurences_number(%I, %s) > 0", tbl::varchar, col::varchar, str::varchar)
INTO result;
RETURN result;
END; '
LANGUAGE PLPGSQL;
这是查询:
INSERT INTO tbl VALUES ('a', 'aaa bbb ccc ddd'), ('b', 'aaa bbb'), ('c', 'ccc ddd'), ('d', 'aaa ccc ddd'), ('e', 'aaa bbb ccc ddd eee');
SELECT "records_with_string"('tbl'::regclass, 'last_name', 'eee') FROM tbl;
查询结果为:
Query Error: error: column "SELECT COUNT(*) FROM %I WHERE occurences_number(%I, %s) > 0" does not exist
我做错了什么?
更新 db-fiddle 不接受 $$
你不应该对字符串文字使用双引号,而只能使用单引号,你应该在使用 format
:
%L
select * from tbl;
first_name | last_name
------------+---------------------
a | aaa bbb ccc ddd
b | aaa bbb
c | ccc ddd
d | aaa ccc ddd
e | aaa bbb ccc ddd eee
(5 rows)
CREATE OR REPLACE FUNCTION occurences_number(varchar, varchar) RETURNS integer AS
$$
DECLARE
text varchar := LOWER();
str varchar := LOWER();
BEGIN
RETURN (CHAR_LENGTH(text) - CHAR_LENGTH(REPLACE(text, str, ' '))) / CHAR_LENGTH(str);
END;
$$
LANGUAGE PLPGSQL;
CREATE FUNCTION
CREATE OR REPLACE FUNCTION records_with_string(tbl varchar, col varchar, str varchar)
RETURNS integer AS
$$
DECLARE
result integer;
BEGIN
EXECUTE format('SELECT COUNT(*) FROM %I WHERE occurences_number(%L, %L) > 0', tbl, col, str)
INTO result;
RETURN result;
END;
$$
LANGUAGE PLPGSQL;
CREATE FUNCTION
SELECT "records_with_string"('tbl', 'last_name', 'eee') FROM tbl;
records_with_string
---------------------
0
0
0
0
0
(5 rows)
为了避免与 普通 引号冲突,您需要用 dollar quotes
(基本上是两个 $
字符,中间有一些东西来引用您的函数体. Something 可以为空,但开始引用和结束引用必须匹配):
CREATE TABLE IF NOT EXISTS ztbl (
first_name VARCHAR(100) NOT NULL,
last_name VARCHAR(100) NOT NULL
);
INSERT INTO ztbl(first_name, last_name) VALUES ('Barack' , 'Obama') , ('Donald' , 'Trump') ;
CREATE FUNCTION occurences_number(varchar, varchar) RETURNS integer AS
$zz$ -- <<-- here
DECLARE
txt varchar := LOWER();
str varchar := LOWER();
BEGIN
RETURN (CHAR_LENGTH(txt) - CHAR_LENGTH(REPLACE(txt, str, '' ))) / CHAR_LENGTH(str);
END;
$zz$ -- <<-- here
LANGUAGE PLPGSQL;
CREATE FUNCTION records_with_string(regclass, varchar, varchar)
RETURNS integer AS
$zzz$ -- <<-- here
DECLARE
result integer;
tbl ALIAS FOR ;
col ALIAS FOR ;
str ALIAS FOR ;
BEGIN
EXECUTE format('
SELECT COUNT(*) FROM %I
WHERE occurences_number(%I, %L ) > 0 -- <<-- here
;', tbl::varchar, col::varchar, str::varchar)
INTO result;
RETURN result;
END;
$zzz$ -- <<-- here
LANGUAGE PLPGSQL;
SELECT records_with_string('ztbl', 'first_name', 'a');