{"kind": "type", "slug": "composite", "name": "Composite types", "major": "18", "snapshot": {"casts": [], "facts": [{"label": "对象边界", "value": "用户定义类型族；不代表用户对象的有限清单"}], "ranges": [], "aliases": [], "catalog": {}, "related": [], "release": {"ref": "https://ftp.postgresql.org/pub/source/v18.6/postgresql-18.6.tar.bz2", "label": "18.6", "major": "18", "channel": "stable", "revision": "555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f", "source_files": {"doc/src/sgml/datatype.sgml": "86328daa77e20d81d222376ec0306d9841a17104e436c08d138eb763aefb3700", "src/include/catalog/pg_am.h": "3426799df799f32163fcc0765f43dbdb5e88e626acf31284fef9fc6014b69d8e", "src/include/catalog/pg_am.dat": "b3cb86b102a42fb0024cbd99779c9897afb40d12a71f5cc5a0e5aee7ff2f9969", "src/include/catalog/pg_cast.h": "de585c7df687d698e7d1d100dce8a794ac9da52414ea48169191019178c8f053", "src/include/catalog/pg_proc.h": "f7d59f07c5b95e2f3c7141762a0f576e8a3af7581ba54c27537f4d500472bea5", "src/include/catalog/pg_type.h": "8fb198749fd82b6c1818a3c18455f226f66116fcfeb3d4959c65ce3fda22802e", "src/include/catalog/pg_range.h": "45546d952b5181f989bd9234721897fc2b37005c8e49ce2ddc156f26974c3ca4", "src/include/catalog/pg_cast.dat": "97911281ca2c81917394ccb2ec13367e37c6801fd461d5e1a3d2f28b911b46b2", "src/include/catalog/pg_proc.dat": "1f934ce80d460159dda137714374a35f9896315ea2476709cbb4da13b38ef95a", "src/include/catalog/pg_type.dat": "5f5887b75677cba2d4a1a0cfeb355df5ed91f85d385fac88bd8d7c605b3578f9", "src/include/catalog/pg_opclass.h": "9c218537806c8ef0398302faaca7f91315dfc1198c48706cebffb479b68defaf", "src/include/catalog/pg_range.dat": "5c2271f8e89e9378d1887204e5785060b77405b5f0ff3e6c719250c9c1dcfcc4", "src/include/catalog/pg_operator.h": "621b18cfffe102413d79b746ce7a10f77fc60fdd097bac34c22b31070d3ee3c4", "src/include/catalog/pg_opfamily.h": "e1f5fc8aebd3042df847455bf66bf755f768c2aa8a9b03d22f7ec8563eba59c0", "src/include/catalog/pg_opclass.dat": "4ee7d3619a6aa106e1c0db55de903931c7d11b1c60ba053a022519c55f8ee668", "src/include/catalog/pg_operator.dat": "5d35b9b2ef5f9797cc815263927ef8a12367fedace53e828b4e8b320e2a7f703", "src/include/catalog/pg_opfamily.dat": "3b694879027b858f2ecf1fa1152b7e30c6e15a216ba99c2384291c30e9d52311"}, "manual_sha256": {"arrays.html": "0e1d5c5a7b4a949d4439995f44f74ad4264e050d16eb9ad2a850fbaee04032df", "domains.html": "82d486973ccc35d14276627a67ac76b434452f23f9ed4d8aa3fbb01fdf285fc2", "datatype.html": "e581f67c74e42006289638c8659e9b338adbfd7f068bd8bf2b3be3880fcd2254", "rowtypes.html": "74f99e029c3a9edfd3e18810ba3c8cb66d3aada91eb67b86476560871e6617e9", "rangetypes.html": "e4960abc7ce8e51d794f94f1e57b099d30b7f75dd7f2b23a960eef91f6291c33", "datatype-bit.html": "c49124aad561636c18080b9e09572fde7785a6b9eae86f5a33283f0fd6774c37", "datatype-oid.html": "8b3360302225f08ae72716ce1804596de95b568e5d31f788cb4597427b28cc38", "datatype-xml.html": "061fc2295b7b69be704f0a741b8decd10ad721059f871fb0243c669bb54d7822", "datatype-enum.html": "cab38e53222e30cc94b24de4b33634fd22d7c93f58ebcf35f646c9b8da78fae8", "datatype-json.html": "650162a05b660146b500a205d523374d00aee117a6a6240d30262be51b6e33e5", "datatype-uuid.html": "292b307b3223c6182bdba687e7f69e6dca9038430143ad79d56327f6ed4c3246", "datatype-money.html": "8b29c14b90679683ac5c472b59a0a7e2853f32cc7db43fab417b2697a6879964", "functions-info.html": "78ac80bf81da2e4f4a33f3b850faa86f0f58ec54a083d6c31da557907de0df87", "catalog-pg-type.html": "ea9c9313bab8e92a7da18ebc50c7ff1f908dd80999a7e0ca3bbc82ba626c58d4", "datatype-binary.html": "0c03ec47b76e37888a3da7f5dcfd340128a5d356642a85185cc9ebe17856a904", "datatype-pg-lsn.html": "339172f5107cc7eb7139bec911277647219e92a532cebd0f3da0a5dacf8195af", "datatype-pseudo.html": "c7cf0b8214304bd8702c95f2142671ae5f0d631f83717f9c07d9e1f2579961b4", "datatype-boolean.html": "b633f663e6c8276e7056287640c413aa79dbc15d5353cee0d41653d58d376956", "datatype-numeric.html": "b74619f7ac2f1ca9f85c48073df78c33dd30e464e61baba6008634534edcb500", "sql-createdomain.html": "e94f927196de1b0ea1ce35cd4603c1f6f544439adcc0edf1c4222358e832245b", "datatype-datetime.html": "e366275d8b13845bfa4ef0d68faa093e5f3ecb708a1ec870cbcbf7e425b85699", "datatype-character.html": "c73eebe413ea709a7792e4cfc3cfe9fb68cb496ebe83c3ebaa9d09f8c40d0f73", "datatype-geometric.html": "0eb3053cfe7d6b0a4b5592c5d7af8d97cae527e87f063c5793413bcba5c14cd9", "datatype-net-types.html": "96846e641cc37727ddcd8b8a1aa0dbe33cd9787760a2b4efb0dcb9403d5be63f", "datatype-textsearch.html": "4b15e05be8b49a71b28aea22f2c46d0dbf639a91e7b4fe129a4c737c6daad47c"}, "source_sha256": "555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f"}, "sources": [{"url": "https://pg.center/docs/18/rowtypes.html", "path": "rowtypes.html", "label": "PostgreSQL 18 English manual", "sha256": "74f99e029c3a9edfd3e18810ba3c8cb66d3aada91eb67b86476560871e6617e9", "language": "en", "original_url": "/docs/18/rowtypes.html"}], "coverage": "documented type family or SQL syntax; not a catalog object", "sections": [], "operators": [], "signature": "Composite types", "description": ["复合类型 表示一行或一条记录的结构；本质上它就是字段名及其数据类型的列表。 PostgreSQL 允许像使用简单类型那样，在许多相同的场合使用复合类型。例如，表中的一列可以声明为复合类型。"], "manual_html": "<div class=\"sect1\" id=\"ROWTYPES\">\n<div class=\"titlepage\">\n<div>\n<div>\n<h2 class=\"title\" lang=\"zh\">\n<div>\n<div>\n<h2 class=\"title\" style=\"clear: both\">8.16. 复合类型 </h2>\n</div>\n</div>\n</h2>\n</div>\n</div>\n</div>\n<p lang=\"zh\"><em class=\"firstterm\">复合类型</em>表示一行或一条记录的结构；本质上它就是字段名及其数据类型的列表。<span class=\"productname\">PostgreSQL</span>允许像使用简单类型那样，在许多相同的场合使用复合类型。例如，表中的一列可以声明为复合类型。</p>\n<div class=\"sect2\" id=\"ROWTYPES-DECLARING\">\n<div class=\"titlepage\">\n<div>\n<div>\n<h3 class=\"title\" lang=\"zh\"><span class=\"sect2\"><a href=\"/docs/18/rowtypes.html#ROWTYPES-DECLARING\">8.16.1. 复合类型的声明</a></span></h3>\n</div>\n</div>\n</div>\n<p lang=\"zh\">下面是两个定义复合类型的简单示例：</p>\n<pre class=\"programlisting\">CREATE TYPE complex AS (\n    r       double precision,\n    i       double precision\n);\n\nCREATE TYPE inventory_item AS (\n    name            text,\n    supplier_id     integer,\n    price           numeric\n);\n</pre>\n<p lang=\"zh\">这种语法与<code class=\"command\">CREATE TABLE</code>类似，但只能指定字段名和类型；目前还不能包含约束（例如<code class=\"literal\">NOT NULL</code>）。注意，<code class=\"literal\">AS</code>关键字必不可少；如果没有它，系统会认为你想使用另一种<code class=\"command\">CREATE TYPE</code>命令，并产生令人费解的语法错误。</p>\n<p lang=\"zh\">定义了这些类型之后，我们可以用它们来创建表：</p>\n<pre class=\"programlisting\">CREATE TABLE on_hand (\n    item      inventory_item,\n    count     integer\n);\n\nINSERT INTO on_hand VALUES (ROW('fuzzy dice', 42, 1.99), 1000);\n</pre>\n<p lang=\"zh\">或者创建函数：</p>\n<pre class=\"programlisting\">CREATE FUNCTION price_extension(inventory_item, integer) RETURNS numeric\nAS 'SELECT $1.price * $2' LANGUAGE SQL;\n\nSELECT price_extension(item, 10) FROM on_hand;\n</pre>\n<p lang=\"zh\">每当你创建一个表时，系统也会自动创建一个与该表同名的复合类型，用来表示该表的行类型。例如，如果我们这样写：</p>\n<pre class=\"programlisting\">CREATE TABLE inventory_item (\n    name            text,\n    supplier_id     integer REFERENCES suppliers,\n    price           numeric CHECK (price &gt; 0)\n);\n</pre>\n<p lang=\"zh\">那么上面展示的同一个<code class=\"literal\">inventory_item</code>复合类型也会顺带生成，并且同样可以像前面那样使用。不过，当前实现有一个重要限制：由于复合类型本身不关联任何约束，表定义中给出的那些约束在表之外的复合类型值上<span class=\"emphasis\"><em>并不适用</em></span>。（解决办法是：在该复合类型之上创建一个<a class=\"glossterm\" href=\"/docs/18/glossary.html#GLOSSARY-DOMAIN\"></a><a class=\"glossterm\" href=\"/docs/18/glossary.html#GLOSSARY-DOMAIN\" title=\"域\">域</a>，并把所需的约束作为该域的<code class=\"literal\">CHECK</code>约束。）</p>\n</div>\n<div class=\"sect2\" id=\"ROWTYPES-CONSTRUCTING\">\n<div class=\"titlepage\">\n<div>\n<div>\n<h3 class=\"title\" lang=\"zh\"><span class=\"sect2\"><a href=\"/docs/18/rowtypes.html#ROWTYPES-CONSTRUCTING\">8.16.2. 构造复合值</a></span></h3>\n</div>\n</div>\n</div>\n<p lang=\"zh\">要把复合值写成字面常量，请将各字段值放在圆括号内，并用逗号分隔。你可以给任意字段值加双引号；如果它包含逗号或圆括号，则必须这样做。（更多细节见<a class=\"link\" href=\"/docs/18/rowtypes.html#ROWTYPES-IO-SYNTAX\" title=\"8.16.6. 复合类型的输入和输出语法\">下文</a>。）因此，复合常量的一般格式如下：</p>\n<pre class=\"synopsis\">'( <em class=\"replaceable\"><code>val1</code></em> , <em class=\"replaceable\"><code>val2</code></em> , ... )'\n</pre>\n<p lang=\"zh\">一个示例是：</p>\n<pre class=\"programlisting\">'(\"fuzzy dice\",42,1.99)'\n</pre>\n<p lang=\"zh\">这就是上文定义的<code class=\"literal\">inventory_item</code>类型的一个合法值。要让某个字段为 NULL，就在列表中对应的位置什么也不写。例如，这个常量指定第三个字段为 NULL：</p>\n<pre class=\"programlisting\">'(\"fuzzy dice\",42,)'\n</pre>\n<p lang=\"zh\">如果想写空字符串而不是 NULL，请写双引号：</p>\n<pre class=\"programlisting\">'(\"\",42,)'\n</pre>\n<p lang=\"zh\">这里第一个字段是非 NULL 的空字符串，第三个字段是 NULL。</p>\n<p lang=\"zh\">（这些常量实际上只是<a class=\"xref\" href=\"/docs/18/sql-syntax-lexical.html#SQL-SYNTAX-CONSTANTS-GENERIC\" title=\"4.1.2.7. 其他类型的常量\">第 4.1.2.7 节</a>中讨论的通用类型常量的一种特例。该常量最初会被当作字符串处理，然后传递给复合类型输入转换例程。可能需要显式指定类型，以指明应将该常量转换为哪种类型。）</p>\n<p lang=\"zh\"><code class=\"literal\">ROW</code>表达式语法也可以用于构造复合值。在大多数情况下，它比字符串字面量语法简单得多，因为你不必担心多层引号。我们在上文已经用过这种方法：</p>\n<pre class=\"programlisting\">ROW('fuzzy dice', 42, 1.99)\nROW('', 42, NULL)\n</pre>\n<p lang=\"zh\">只要表达式中有多个字段，ROW 关键字就可以省略，因此可简写为：</p>\n<pre class=\"programlisting\">('fuzzy dice', 42, 1.99)\n('', 42, NULL)\n</pre>\n<p lang=\"zh\">关于<code class=\"literal\">ROW</code>表达式语法的更多细节，见<a class=\"xref\" href=\"/docs/18/sql-expressions.html#SQL-SYNTAX-ROW-CONSTRUCTORS\" title=\"4.2.13. 行构造器\">第 4.2.13 节</a>。</p>\n</div>\n<div class=\"sect2\" id=\"ROWTYPES-ACCESSING\">\n<div class=\"titlepage\">\n<div>\n<div>\n<h3 class=\"title\" lang=\"zh\"><span class=\"sect2\"><a href=\"/docs/18/rowtypes.html#ROWTYPES-ACCESSING\">8.16.3. 访问复合类型</a></span></h3>\n</div>\n</div>\n</div>\n<p lang=\"zh\">要访问复合列中的某个字段，可以写一个点号再加字段名，这很像通过表名选取字段。实际上，它与通过表名选取字段太像了，以至于你通常必须使用圆括号，以免让解析器混淆。例如，你可能尝试从示例表<code class=\"literal\">on_hand</code>中选取一些子字段：</p>\n<pre class=\"programlisting\">SELECT item.name FROM on_hand WHERE item.price &gt; 9.99;\n</pre>\n<p lang=\"zh\">这不会起作用，因为根据 SQL 语法规则，名称<code class=\"literal\">item</code>会被当成表名，而不是<code class=\"literal\">on_hand</code>的列名。你必须写成这样：</p>\n<pre class=\"programlisting\">SELECT (item).name FROM on_hand WHERE (item).price &gt; 9.99;\n</pre>\n<p lang=\"zh\">或者，如果你还需要使用表名（例如在多表查询中），可以这样写：</p>\n<pre class=\"programlisting\">SELECT (on_hand.item).name FROM on_hand WHERE (on_hand.item).price &gt; 9.99;\n</pre>\n<p lang=\"zh\">现在，加上括号的对象就会被正确解释为对<code class=\"literal\">item</code>列的引用，然后就可以从中选出子字段。</p>\n<p lang=\"zh\">无论何时从复合值中选择字段，都会遇到类似的语法问题。例如，要从一个返回复合值的函数结果中只选取一个字段，你需要这样写：</p>\n<pre class=\"programlisting\">SELECT (my_func(...)).field FROM ...\n</pre>\n<p lang=\"zh\">如果没有额外的圆括号，这将生成一个语法错误。</p>\n<p lang=\"zh\">特殊字段名<code class=\"literal\">*</code>表示<span class=\"quote\">“<span class=\"quote\">所有字段</span>”</span>，其进一步解释见<a class=\"xref\" href=\"/docs/18/rowtypes.html#ROWTYPES-USAGE\" title=\"8.16.5. 在查询中使用复合类型\">第 8.16.5 节</a>。</p>\n</div>\n<div class=\"sect2\" id=\"ROWTYPES-MODIFYING\">\n<div class=\"titlepage\">\n<div>\n<div>\n<h3 class=\"title\" lang=\"zh\"><span class=\"sect2\"><a href=\"/docs/18/rowtypes.html#ROWTYPES-MODIFYING\">8.16.4. 修改复合类型</a></span></h3>\n</div>\n</div>\n</div>\n<p lang=\"zh\">下面是一些插入和更新复合列时正确语法的示例。先看插入或更新整个列值的情况：</p>\n<pre class=\"programlisting\">INSERT INTO mytab (complex_col) VALUES((1.1,2.2));\n\nUPDATE mytab SET complex_col = ROW(1.1,2.2) WHERE ...;\n</pre>\n<p lang=\"zh\">第一个示例省略了<code class=\"literal\">ROW</code>，第二个示例使用了它；两种写法都可以。</p>\n<p lang=\"zh\">我们也可以更新复合列中的单个子字段：</p>\n<pre class=\"programlisting\">UPDATE mytab SET complex_col.r = (complex_col).r + 1 WHERE ...;\n</pre>\n<p lang=\"zh\">注意，这里不需要（事实上也不能）给紧跟在<code class=\"literal\">SET</code>后面的列名加圆括号，但在等号右侧的表达式中引用同一列时，则需要加圆括号。</p>\n<p lang=\"zh\">我们也可以把子字段指定为<code class=\"command\">INSERT</code>的目标：</p>\n<pre class=\"programlisting\">INSERT INTO mytab (complex_col.r, complex_col.i) VALUES(1.1, 2.2);\n</pre>\n<p lang=\"zh\">如果我们没有为该列的所有子字段提供值，其余子字段就会填充为 NULL 值。</p>\n</div>\n<div class=\"sect2\" id=\"ROWTYPES-USAGE\">\n<div class=\"titlepage\">\n<div>\n<div>\n<h3 class=\"title\" lang=\"zh\"><span class=\"sect2\"><a href=\"/docs/18/rowtypes.html#ROWTYPES-USAGE\">8.16.5. 在查询中使用复合类型</a></span></h3>\n</div>\n</div>\n</div>\n<p lang=\"zh\">在查询中，复合类型有多种特殊的语法规则和行为。这些规则提供了有用的简写形式，但如果不了解背后的逻辑，也可能让人困惑。</p>\n<p lang=\"zh\">在<span class=\"productname\">PostgreSQL</span>中，查询中对表名（或别名）的引用，实际上就是对该表当前行的复合值的引用。例如，如果我们有一个如<a class=\"link\" href=\"/docs/18/rowtypes.html#ROWTYPES-DECLARING\" title=\"8.16.1. 复合类型的声明\">上文</a>所示的表<code class=\"structname\">inventory_item</code>，就可以写：</p>\n<pre class=\"programlisting\">SELECT c FROM inventory_item c;\n</pre>\n<p lang=\"zh\">这个查询会产生一个单独的复合值列，因此我们可能得到如下输出：</p>\n<pre class=\"programlisting\">           c\n------------------------\n (\"fuzzy dice\",42,1.99)\n(1 row)\n</pre>\n<p lang=\"zh\">不过要注意，简单名称会先与列名匹配，再与表名匹配，因此这个示例之所以可行，只是因为该查询涉及的表中没有名为<code class=\"structfield\">c</code>的列。</p>\n<p lang=\"zh\">普通的限定列名语法<em class=\"replaceable\"><code>table_name</code></em><code class=\"literal\">.</code><em class=\"replaceable\"><code>column_name</code></em>可以理解为对该表当前行的复合值进行<a class=\"link\" href=\"/docs/18/sql-expressions.html#FIELD-SELECTION\" title=\"4.2.4. 字段选择\">字段选择</a>。（出于效率原因，实际上并不是这样实现的。）</p>\n<p lang=\"zh\">当我们写</p>\n<pre class=\"programlisting\">SELECT c.* FROM inventory_item c;\n</pre>\n<p lang=\"zh\">时，根据 SQL 标准，应该得到把该表内容展开为独立列后的结果：</p>\n<pre class=\"programlisting\">    name    | supplier_id | price\n------------+-------------+-------\n fuzzy dice |          42 |  1.99\n(1 row)\n</pre>\n<p lang=\"zh\">就好像查询写成了</p>\n<pre class=\"programlisting\">SELECT c.name, c.supplier_id, c.price FROM inventory_item c;\n</pre>\n<p lang=\"zh\"><span class=\"productname\">PostgreSQL</span>会对任何结果为复合类型的表达式应用这种展开行为，不过正如<a class=\"link\" href=\"/docs/18/rowtypes.html#ROWTYPES-ACCESSING\" title=\"8.16.3. 访问复合类型\">上文</a>所示，只要<code class=\"literal\">.*</code>所作用的值不是简单表名，就需要给该值加圆括号。例如，如果<code class=\"function\">myfunc()</code>是一个返回复合类型的函数，该复合类型有<code class=\"structfield\">a</code>、<code class=\"structfield\">b</code>和<code class=\"structfield\">c</code>三列，那么下面两个查询的结果相同：</p>\n<pre class=\"programlisting\">SELECT (myfunc(x)).* FROM some_table;\nSELECT (myfunc(x)).a, (myfunc(x)).b, (myfunc(x)).c FROM some_table;\n</pre>\n<div class=\"tip\">\n<h3 class=\"title\" lang=\"zh\">提示</h3>\n<p lang=\"zh\"><span class=\"productname\">PostgreSQL</span>处理列展开时，实际上会把第一种形式转换成第二种形式。因此，在这个示例中，无论使用哪种语法，<code class=\"function\">myfunc()</code>每行都会被调用三次。如果它是一个开销较大的函数，你可能希望避免这种情况，可以使用如下查询：</p>\n<pre class=\"programlisting\">SELECT m.* FROM some_table, LATERAL myfunc(x) AS m;\n</pre>\n<p lang=\"zh\">将函数放在 LATERAL FROM 项中，可以避免每行调用多次。m.* 仍会展开为 m.a、m.b、m.c，但此时这些变量只是对 FROM 项输出的引用。（此处 LATERAL 关键字可省略，写出它是为了说明函数从 some_table 获取 x。）</p>\n</div>\n<p lang=\"zh\">composite_value.* 出现在 SELECT 输出列表、INSERT／UPDATE／DELETE／MERGE 的 RETURNING 列表、VALUES 子句或行构造器的顶层时，会按上述方式展开各列。在其他上下文中，包括嵌套于这些结构内部时，为复合值添加 .* 不会改变其值，因为它表示“所有列”，产生的仍是同一个复合值。例如，如果 somefunc() 接受复合类型参数，以下查询等价：</p>\n<pre class=\"programlisting\">SELECT somefunc(c.*) FROM inventory_item c;\nSELECT somefunc(c) FROM inventory_item c;\n</pre>\n<p lang=\"zh\">在这两种情况下，inventory_item 的当前行都作为一个复合值参数传给函数。虽然 .* 在这种场景中没有实际作用，但使用它仍是良好的风格，因为它明确表明这里需要复合值。具体来说，解析器会将 c.* 中的 c 视为表名或别名，而非列名，因此不会产生歧义；没有 .* 时，c 究竟表示表名还是列名就不明确，而如果存在名为 c 的列，实际上会优先将它解释为列名。</p>\n<p lang=\"zh\">另一个说明这些概念的例子是，下面这些查询的含义都相同：</p>\n<pre class=\"programlisting\">SELECT * FROM inventory_item c ORDER BY c;\nSELECT * FROM inventory_item c ORDER BY c.*;\nSELECT * FROM inventory_item c ORDER BY ROW(c.*);\n</pre>\n<p lang=\"zh\">所有这些<code class=\"literal\">ORDER BY</code>子句都指定了该行的复合值，因此会按照<a class=\"xref\" href=\"/docs/18/functions-comparisons.html#COMPOSITE-TYPE-COMPARISON\" title=\"9.25.6. 复合类型比较\">第 9.25.6 节</a>中描述的规则对行进行排序。不过，如果<code class=\"structname\">inventory_item</code>包含一个名为<code class=\"structfield\">c</code>的列，第一种情况就会不同于其他情况，因为它表示只按那一列排序。按照前面展示的列名，下面这些查询也与上述查询等效：</p>\n<pre class=\"programlisting\">SELECT * FROM inventory_item c ORDER BY ROW(c.name, c.supplier_id, c.price);\nSELECT * FROM inventory_item c ORDER BY (c.name, c.supplier_id, c.price);\n</pre>\n<p lang=\"zh\">（最后一种情况使用的是省略了关键字<code class=\"literal\">ROW</code>的行构造器。）</p>\n<p lang=\"zh\">另一种与复合值有关的特殊语法行为是，我们可以使用<em class=\"firstterm\">函数记法</em>来提取复合值中的字段。简单来说，记法<code class=\"literal\"><em class=\"replaceable\"><code>field</code></em>(<em class=\"replaceable\"><code>table</code></em>)</code>和<code class=\"literal\"><em class=\"replaceable\"><code>table</code></em>.<em class=\"replaceable\"><code>field</code></em></code>可以互换。例如，这些查询是等价的：</p>\n<pre class=\"programlisting\">SELECT c.name FROM inventory_item c WHERE c.price &gt; 1000;\nSELECT name(c) FROM inventory_item c WHERE price(c) &gt; 1000;\n</pre>\n<p lang=\"zh\">此外，如果我们有一个接受单个复合类型参数的函数，也可以用这两种记法来调用它。这些查询都等价：</p>\n<pre class=\"programlisting\">SELECT somefunc(c) FROM inventory_item c;\nSELECT somefunc(c.*) FROM inventory_item c;\nSELECT c.somefunc FROM inventory_item c;\n</pre>\n<p lang=\"zh\">函数记法与字段记法之间的这种等价性，使得我们可以通过在复合类型上使用函数来实现<span class=\"quote\">“<span class=\"quote\">计算字段</span>”</span>。<a class=\"indexterm\" id=\"id-1.5.7.24.9.10.2\" name=\"id-1.5.7.24.9.10.2\"></a><a class=\"indexterm\" id=\"id-1.5.7.24.9.10.3\" name=\"id-1.5.7.24.9.10.3\"></a>使用上面最后一种查询形式的应用程序，无需直接知道<code class=\"literal\">somefunc</code>并不是该表中的真实列。</p>\n<div class=\"tip\">\n<h3 class=\"title\" lang=\"zh\">提示</h3>\n<p lang=\"zh\">由于这种行为，不宜让接受单个复合类型参数的函数与该复合类型中的任何字段同名。如果存在歧义，在使用字段名语法时会选择字段名解释，而在使用函数调用语法时会选择函数解释。不过，在 11 之前的<span class=\"productname\">PostgreSQL</span>版本中，除非调用的语法要求它必须是函数调用，否则总是选择字段名解释。在旧版本中，强制按函数解释的一种方法是为函数名加模式限定，也就是写成<code class=\"literal\"><em class=\"replaceable\"><code>schema</code></em>.<em class=\"replaceable\"><code>func</code></em>(<em class=\"replaceable\"><code>compositevalue</code></em>)</code>。</p>\n</div>\n</div>\n<div class=\"sect2\" id=\"ROWTYPES-IO-SYNTAX\">\n<div class=\"titlepage\">\n<div>\n<div>\n<h3 class=\"title\" lang=\"zh\"><span class=\"sect2\"><a href=\"/docs/18/rowtypes.html#ROWTYPES-IO-SYNTAX\">8.16.6. 复合类型的输入和输出语法</a></span></h3>\n</div>\n</div>\n</div>\n<p lang=\"zh\">复合值的外部文本表示由两部分组成：一部分是按照各字段类型的 I/O 转换规则解释的项，另一部分是表明复合结构的附加符号。这些附加符号包括包围整个值的圆括号（<code class=\"literal\">(</code>和<code class=\"literal\">)</code>），以及相邻项之间的逗号（<code class=\"literal\">,</code>）。圆括号外部的空白会被忽略；但在圆括号内部，空白会被视为字段值的一部分，其是否有意义取决于该字段数据类型的输入转换规则。例如，在</p>\n<pre class=\"programlisting\">'(  42)'\n</pre>\n<p lang=\"zh\">中，如果字段类型是 integer，则空白会被忽略；如果是 text，则不会被忽略。</p>\n<p lang=\"zh\">如前所示，在写复合值时，你可以给任意单个字段值加双引号。如果字段值本身可能让复合值解析器混淆，则<span class=\"emphasis\"><em>必须</em></span>这样做。特别是，包含圆括号、逗号、双引号或反斜杠的字段必须用双引号括起来。要在带引号的复合字段值中写入双引号或反斜杠，需要在其前面加一个反斜杠。（另外，带双引号的字段值内部成对出现的双引号会被视为一个双引号字符，这与 SQL 字面字符串中单引号的规则类似。）或者，你也可以完全不使用引号，而改用反斜杠转义，保护所有原本会被当作复合语法的数据字符。</p>\n<p lang=\"zh\">完全空的字段值（即逗号或圆括号之间一个字符也没有）表示 NULL。要写一个空字符串值而不是 NULL，可以写成<code class=\"literal\">\"\"</code>。</p>\n<p lang=\"zh\">如果字段值是空字符串，或者包含圆括号、逗号、双引号、反斜杠或空白字符，复合类型输出例程就会在其周围加上双引号。（对空白字符这样做并非必需，但有助于提高可读性。）嵌入字段值中的双引号和反斜杠会被加倍。</p>\n<div class=\"note\">\n<h3 class=\"title\" lang=\"zh\">注意</h3>\n<p lang=\"zh\">记住，你在 SQL 命令中写的内容会先被解释为字符串字面量，然后才会被解释为复合值。这会使所需的反斜杠数量翻倍（假定使用的是转义字符串语法）。例如，要在复合值中插入一个包含双引号和反斜杠的<code class=\"type\">text</code>字段，需要写成：</p>\n<pre class=\"programlisting\">INSERT ... VALUES ('(\"\\\"\\\\\")');\n</pre>\n<p lang=\"zh\">字符串字面量处理器会去掉一层反斜杠，因此传到复合值解析器时看起来是<code class=\"literal\">(\"\\\"\\\\\")</code>。随后，送入<code class=\"type\">text</code>数据类型输入例程的字符串就变成了<code class=\"literal\">\"\\</code>。（如果我们使用的数据类型的输入例程也会把反斜杠当作特殊字符处理，例如<code class=\"type\">bytea</code>，那么为了在存储的复合字段中得到一个反斜杠，命令里可能需要多达八个反斜杠。）美元引用（见<a class=\"xref\" href=\"/docs/18/sql-syntax-lexical.html#SQL-SYNTAX-DOLLAR-QUOTING\" title=\"4.1.2.4. 美元引用的字符串常量\">第 4.1.2.4 节</a>）可用于避免反斜杠加倍的需要。</p>\n</div>\n<div class=\"tip\">\n<h3 class=\"title\" lang=\"zh\">提示</h3>\n<p lang=\"zh\">在 SQL 命令中编写复合值时，<code class=\"literal\">ROW</code>构造器语法通常比复合字面量语法更容易使用。在<code class=\"literal\">ROW</code>中，各个字段值的写法与它们不是复合成员时完全相同。</p>\n</div>\n</div>\n</div>", "manual_path": "rowtypes.html", "localization": {"status": "complete", "sources": [{"url": "/docs/18/rowtypes.html", "method": "same-major semantic node", "sha256": "a163b5f492a3173e4bbeb728910bdaa57b80d8ca8bbfee0f66b2b1aa15222c9b", "language": "zh", "matched_nodes": ["#ROWTYPES/div[0]", "#ROWTYPES/div[10]/div[7]/h3[0]", "#ROWTYPES/div[10]/div[7]/p[1]", "#ROWTYPES/div[10]/div[7]/p[3]", "#ROWTYPES/div[10]/div[8]/p[1]", "#ROWTYPES/div[10]/p[1]", "#ROWTYPES/div[10]/p[3]", "#ROWTYPES/div[10]/p[4]", "#ROWTYPES/div[10]/p[5]", "#ROWTYPES/div[10]/p[6]", "#ROWTYPES/div[1]/dl[0]/dt[0]", "#ROWTYPES/div[1]/dl[0]/dt[1]", "#ROWTYPES/div[1]/dl[0]/dt[2]", "#ROWTYPES/div[1]/dl[0]/dt[3]", "#ROWTYPES/div[1]/dl[0]/dt[4]", "#ROWTYPES/div[1]/dl[0]/dt[5]", "#ROWTYPES/div[5]/p[10]", "#ROWTYPES/div[5]/p[1]", "#ROWTYPES/div[5]/p[3]", "#ROWTYPES/div[5]/p[4]", "#ROWTYPES/div[5]/p[6]", "#ROWTYPES/div[5]/p[8]", "#ROWTYPES/div[6]/p[10]", "#ROWTYPES/div[6]/p[11]", "#ROWTYPES/div[6]/p[12]", "#ROWTYPES/div[6]/p[16]", "#ROWTYPES/div[6]/p[2]", "#ROWTYPES/div[6]/p[4]", "#ROWTYPES/div[6]/p[6]", "#ROWTYPES/div[6]/p[8]", "#ROWTYPES/div[7]/p[10]", "#ROWTYPES/div[7]/p[11]", "#ROWTYPES/div[7]/p[1]", "#ROWTYPES/div[7]/p[3]", "#ROWTYPES/div[7]/p[5]", "#ROWTYPES/div[7]/p[7]", "#ROWTYPES/div[7]/p[8]", "#ROWTYPES/div[8]/p[1]", "#ROWTYPES/div[8]/p[3]", "#ROWTYPES/div[8]/p[4]", "#ROWTYPES/div[8]/p[6]", "#ROWTYPES/div[8]/p[7]", "#ROWTYPES/div[8]/p[9]", "#ROWTYPES/div[9]/div[16]/h3[0]", "#ROWTYPES/div[9]/div[16]/p[1]", "#ROWTYPES/div[9]/div[30]/p[1]", "#ROWTYPES/div[9]/p[10]", "#ROWTYPES/div[9]/p[12]", "#ROWTYPES/div[9]/p[14]", "#ROWTYPES/div[9]/p[1]", "#ROWTYPES/div[9]/p[20]", "#ROWTYPES/div[9]/p[22]", "#ROWTYPES/div[9]/p[24]", "#ROWTYPES/div[9]/p[25]", "#ROWTYPES/div[9]/p[27]", "#ROWTYPES/div[9]/p[29]", "#ROWTYPES/div[9]/p[2]", "#ROWTYPES/div[9]/p[4]", "#ROWTYPES/div[9]/p[6]", "#ROWTYPES/div[9]/p[7]", "#ROWTYPES/div[9]/p[8]", "#ROWTYPES/p[4]"]}], "language": "zh", "original_text": {"/versions/18/description/0": "A composite type represents the structure of a row or record; it is essentially just a list of field names and their data types. PostgreSQL allows composite types to be used in many of the same ways that simple types can be used. For example, a column of a table can be declared to be of a composite type.", "/versions/18/facts/0/label": "Object boundary", "/versions/18/facts/0/value": "User-defined type family; not a finite list of user objects"}, "fallback_fields": [], "source_language": "en", "original_snapshot_sha256": "51ab075d55c9c60c77fa11be231c6115682887441c7c9e6f6f880c9f1ee2cca8"}, "manual_language": "zh", "operator_classes": []}, "from": "17", "comparison": {"available": true, "changes": [], "prose_changed": true}}