问题描述
我需要将列名正确地用双引号括起来。 quote_ident()
似乎没有这样做?
select 1 first; -- fails
select quote_ident('first'); -- produces first,not "first"
我可以使用什么命令来成功引用标识符。我正在尝试用它动态构建一个 SELECT
语句:
SELECT 'select '
|| string_agg(
case when udt_name in ('varchar','text')
then 'left(' || quote_ident(column_name) || ',65535) ' || quote_ident(column_name)
else quote_ident(column_name)
end,',' order by ordinal_position)
|| ' from "public"."MyTableName"'
FROM information_schema.columns c
join parse_ident('"public"."MyTableName"') t
on t[1] = table_schema and t[2] = table_name
这是生成:
SELECT id,left(first,65535) first from "public"."MyTableName";
这是因为首先作为列名需要用双引号括起来。
解决方法
不要省略列别名的 AS
关键字
SELECT id,left(first,65535) first from "public"."MyTableName";
这是因为首先作为列名需要用双引号括起来。
不完全是。它爆炸是因为您在不应省略的地方省略了关键字 AS
。
这有效:
SELECT 'select '
|| string_agg(
case when udt_name in ('varchar','text')
then 'left(' || quote_ident(column_name) || ',65535) AS ' -- !!
|| quote_ident(column_name)
else quote_ident(column_name)
end,',' order by ordinal_position)
|| ' from "public"."MyTableName"'
FROM information_schema.columns c
join parse_ident('"public"."MyTableName"') t
on t[1] = table_schema and t[2] = table_name;
产生:
SELECT id,65535) AS first from "public"."MyTableName";
依次按预期工作。
manual about "Omitting the AS Key Word":
在SQL标准中,可选关键字AS
之前可以省略
每当新列名是有效列时的输出列名
名称(即,与任何保留关键字不同)。 PostgreSQL 是
稍微严格一点:如果新列名是 AS
,则需要
完全匹配任何关键字,保留与否。推荐做法是
使用 AS 或双引号输出列名,以防止任何可能的
与未来的关键字添加冲突。
可以省略表别名的关键字 AS
,但不能省略列别名。
first
不是 Postgres 中的 reserved word。 (它曾经在古老的 SQL 标准 SQL-92 中被“保留”,但在标准 SQL 中也不再存在。)准确地说,它是 “非保留”*。 The manual:
非保留关键词只在特定语境中具有特殊意义,在其他语境中可以作为标识符使用。
省略 AS
使其成为这样的上下文。
quote_ident()
工作可靠。 The manual:
返回适当引用的给定字符串以用作标识符 在 SQL 语句字符串中。仅在必要时才添加引号(即 如果字符串包含非标识符字符或将是 案例折叠)。嵌入的引号正确地加倍。
带有说明符format()
的 %I
也是如此。
保留词未提及,但无论如何正确引用。准确地说:the SQL Key Words table的“PostgreSQL”列中所有标记为“保留”或“(不能是函数或类型)”的关键字。 >
我将提交一个文档错误来添加它。
绝对肯定:quote_all_identifiers
如果您想绝对确定并且不介意所有添加的噪音,您可以强制 Postgres 使用配置参数 quote_all_identifiers
引用 all 标识符。 The manual:
当数据库生成 SQL 时,强制引用所有标识符,即使它们不是(当前)关键字。
这包括来自 quote_ident()
和 format()
的输出。我不会那样做,害怕所有增加的噪音。
您可以在同一事务中使用 SET LOCAL
在本地设置参数。喜欢:
BEGIN;
SET LOCAL quote_all_identifiers = true;
SELECT ...
END;
更快
也就是说,我会使用 format()
和 concat()
并以目录表 pg_attribute
为目标:更干净、更简单、更快。但不能移植到其他 RDBMS:
SELECT format('SELECT %s FROM %s;',string_agg(CASE WHEN atttypid = ANY ('{text,bpchar,varchar}'::regtype[])
THEN concat('left(',col,65535) AS ',col)
ELSE col END,'),attrelid)
FROM (
SELECT attrelid::regclass,atttypid,quote_ident(attname) AS col
FROM pg_catalog.pg_attribute
WHERE attrelid = 'public."MyTableName"'::regclass -- provide once,optionally schema-qualified
AND attnum > 0
AND NOT attisdropped
ORDER BY attnum
) sub
GROUP BY attrelid;
产生:
SELECT id,65535) AS first FROM "MyTableName";
dbfiddle here
值得注意的是,...
- ...您只需要提供一次表名,可选择模式限定。
- ...如果表不存在,查询会立即失败并显示有用的错误消息。
- ... 输出表名称仅在必要时使用模式限定和双引号。
- ...这也包括
character(N)
(内部名称bpchar
)。
进一步阅读: