02a3a991a70e181fbb09f148430711a3da6b69c9
PostgreSQL.md
| ... | ... | @@ -0,0 +1,1823 @@ |
| 1 | +## PostgreSQL学习 |
|
| 2 | + |
|
| 3 | +1.https://www.rockdata.net/zh-cn/tutorial/dml-select-distinct/ |
|
| 4 | + |
|
| 5 | + |
|
| 6 | + |
|
| 7 | +## PostgreSQL 基础教程 |
|
| 8 | + |
|
| 9 | +https://www.rockdata.net/zh-cn/tutorial/toc/ |
|
| 10 | + |
|
| 11 | +本 **PostgreSQL 教程**可帮助您快速了解 PostgreSQL。您将通过许多实际示例快速掌握 PostgreSQL,并将这些知识应用于使用 PostgreSQL 开发应用程序。 |
|
| 12 | + |
|
| 13 | + |
|
| 14 | + |
|
| 15 | +如果你是 … |
|
| 16 | + |
|
| 17 | +- 寻求快速学习 PostgreSQL。 |
|
| 18 | +- 使用 PostgreSQL 作为后端数据库管理系统开发应用程序。 |
|
| 19 | +- 从其他数据库管理系统(例如 MySQL、Oracle 和 Microsoft SQL Server)迁移到 PostgreSQL。 |
|
| 20 | + |
|
| 21 | +您将在此网站上找到快速有效地开始使用 PostgreSQL 所需的所有信息。 |
|
| 22 | + |
|
| 23 | +PostgreSQL 教程演示了 PostgreSQL 的许多独特功能,这些功能使其成为最先进的开源数据库管理系统。 |
|
| 24 | + |
|
| 25 | +### [PostgreSQL 入门](https://www.rockdata.net/zh-cn/tutorial/getting-started/) |
|
| 26 | + |
|
| 27 | + |
|
| 28 | + |
|
| 29 | +本部分向您展示如何在 Windows、Linux 和 macOS 上安装 PostgreSQL,帮助您开始使用 PostgreSQL。您还将学习如何使用 psql 工具连接到 PostgreSQL,以及如何将示例数据库加载到 PostgreSQL 中进行练习。 |
|
| 30 | + |
|
| 31 | +## PostgreSQL 基础教程 |
|
| 32 | + |
|
| 33 | +首先,您将学习如何使用基本数据查询技术从单个表中查询数据,包括查询数据、对结果集进行排序和过滤行。然后,您将了解高级查询,例如连接多个表、使用集合操作以及构造子查询。最后,您将学习如何管理数据库表,例如创建新表或修改现有表的结构。 |
|
| 34 | + |
|
| 35 | +### 第 1 节. 查询数据 |
|
| 36 | + |
|
| 37 | +- [简单查询](https://www.rockdata.net/zh-cn/tutorial/dml-select/) – 向您展示如何从单个表中查询数据。 |
|
| 38 | +- [列别名](https://www.rockdata.net/zh-cn/tutorial/dml-column-alias/) – 了解如何为查询中的列或表达式分配临时名称。 |
|
| 39 | +- [排序](https://www.rockdata.net/zh-cn/tutorial/dml-order-by/) – 指导您如何对查询返回的结果集进行排序。 |
|
| 40 | +- [去重查询](https://www.rockdata.net/zh-cn/tutorial/dml-select-distinct/) – 为您提供一个删除结果集中重复行的子句。 |
|
| 41 | +- [分页查询](https://www.rockdata.net/zh-cn/tutorial/dml-paginate/) – 指导您如何对查询返回的结果集进行分页。 |
|
| 42 | + |
|
| 43 | +### 第 2 节. 过滤数据 |
|
| 44 | + |
|
| 45 | +- [WHERE](https://www.rockdata.net/zh-cn/tutorial/dml-where/) – 根据指定条件过滤行。 |
|
| 46 | +- [LIMIT](https://www.rockdata.net/zh-cn/tutorial/dml-limit/) – 获取查询生成的行的子集。 |
|
| 47 | +- [FETCH](https://www.rockdata.net/zh-cn/tutorial/dml-fetch/) – 限制查询返回的行数。 |
|
| 48 | +- [IN](https://www.rockdata.net/zh-cn/tutorial/dml-in/) – 选择与值列表中的任何值匹配的数据。 |
|
| 49 | +- [BETWEEN](https://www.rockdata.net/zh-cn/tutorial/dml-between/) – 选择值范围内的数据。 |
|
| 50 | +- [LIKE](https://www.rockdata.net/zh-cn/tutorial/dml-like/) – 基于模式匹配过滤数据。 |
|
| 51 | +- [IS NULL](https://www.rockdata.net/zh-cn/tutorial/dml-is-null/) – 检查值是否为空。 |
|
| 52 | + |
|
| 53 | +### 第 3 节. 连接多个表 |
|
| 54 | + |
|
| 55 | +- [连接](https://www.rockdata.net/zh-cn/tutorial/dml-joins/) – 向您展示 PostgreSQL 中连接的简要概述。 |
|
| 56 | +- [表别名](https://www.rockdata.net/zh-cn/tutorial/dml-table-alias/) – 描述如何在查询中使用表别名。 |
|
| 57 | +- [内连接](https://www.rockdata.net/zh-cn/tutorial/dml-inner-join/) – 从一个表中选择在其他表中具有相应行的行。 |
|
| 58 | +- [左连接](https://www.rockdata.net/zh-cn/tutorial/dml-left-join/) – 从一个表中选择行,这些行在其他表中可能有也可能没有对应的行。 |
|
| 59 | +- [自连接](https://www.rockdata.net/zh-cn/tutorial/dml-self-join/) – 通过将表与自身进行比较来将表与其自身连接。 |
|
| 60 | +- [完全外连接](https://www.rockdata.net/zh-cn/tutorial/dml-full-outer-join/) – 使用完全连接查找一个表中在另一个表中没有匹配行的行。 |
|
| 61 | +- [交叉连接](https://www.rockdata.net/zh-cn/tutorial/dml-cross-join/) – 生成两个或多个表中的行的笛卡尔积。 |
|
| 62 | +- [自然连接](https://www.rockdata.net/zh-cn/tutorial/dml-natural-join/) – 根据连接表中的公共列名称,使用隐式连接条件连接两个或多个表。 |
|
| 63 | +- [横向连接](https://www.rockdata.net/zh-cn/tutorial/dml-lateral-join/) – 通过连接交叉引用子查询中的行,和构建复合结果集。 |
|
| 64 | + |
|
| 65 | +### 第 4 节. 数据分组 |
|
| 66 | + |
|
| 67 | +- [GROUP BY](https://www.rockdata.net/zh-cn/tutorial/dml-group-by/) – 将行分成组并对每个组应用聚合函数。 |
|
| 68 | +- [HAVING](https://www.rockdata.net/zh-cn/tutorial/dml-having/) – 对组应用条件。 |
|
| 69 | +- [PARTITION BY](https://www.rockdata.net/zh-cn/tutorial/dml-partition-by/) – 将行分成组并对每个组应用窗口函数。 |
|
| 70 | + |
|
| 71 | +### 第 5 节. 集合运算 |
|
| 72 | + |
|
| 73 | +- [UNION](https://www.rockdata.net/zh-cn/tutorial/dml-union/) – 将多个查询的结果集合并为一个结果集。 |
|
| 74 | +- [INTERSECT](https://www.rockdata.net/zh-cn/tutorial/dml-intersect/) – 组合两个或多个查询的结果集并返回一个结果集,该结果集的行都出现在两个结果集中。 |
|
| 75 | +- [EXCEPT](https://www.rockdata.net/zh-cn/tutorial/dml-except/) – 返回第一个查询中未出现在第二个查询的输出中的行。 |
|
| 76 | + |
|
| 77 | +### 第 6 节. 分组集、多维分组和汇总 |
|
| 78 | + |
|
| 79 | +- [分组集](https://www.rockdata.net/zh-cn/tutorial/dml-grouping-sets/) – 在报告中生成多个分组集。 |
|
| 80 | +- [CUBE](https://www.rockdata.net/zh-cn/tutorial/dml-cube/) – 定义多个分组集,其中包括所有可能的维度组合。 |
|
| 81 | +- [ROLLUP](https://www.rockdata.net/zh-cn/tutorial/dml-rollup/) – 生成包含总计和小计的报告。 |
|
| 82 | + |
|
| 83 | +### 第 7 节. 子查询 |
|
| 84 | + |
|
| 85 | +- [子查询](https://www.rockdata.net/zh-cn/tutorial/dml-subquery/) – 编写一个嵌套在另一个查询中的查询。 |
|
| 86 | +- [相关子查询](https://www.rockdata.net/zh-cn/tutorial/dml-correlated-subquery/) – 向您展示如何使用相关子查询,来执行依赖于正在处理的当前行的值的查询。 |
|
| 87 | +- [ANY](https://www.rockdata.net/zh-cn/tutorial/dml-any/) – 通过将某个值与子查询返回的一组值进行比较来检索数据。 |
|
| 88 | +- [ALL](https://www.rockdata.net/zh-cn/tutorial/dml-all/) – 通过将值与子查询返回的值列表进行比较来查询数据。 |
|
| 89 | +- [EXISTS](https://www.rockdata.net/zh-cn/tutorial/dml-exists/) – 检查子查询返回的行是否存在。 |
|
| 90 | + |
|
| 91 | +### 第 8 节. 公共表表达式 |
|
| 92 | + |
|
| 93 | +- [PostgreSQL CTE](https://www.rockdata.net/zh-cn/tutorial/dml-cte/) – 向您介绍 PostgreSQL 公共表表达式或 CTE。 |
|
| 94 | +- [使用 CTE 的递归查询](https://www.rockdata.net/zh-cn/tutorial/dml-recursive-query/) – 讨论递归查询并学习如何在各种上下文中应用它。 |
|
| 95 | + |
|
| 96 | +### 第 9 节. 修改数据 |
|
| 97 | + |
|
| 98 | +在本节中,您将学习如何使用`INSERT`语句向表中插入数据、使用`UPDATE`语句修改现有数据以及使用`DELETE`语句删除数据。此外,您还将学习如何使用 UPSERT 语句来合并数据。 |
|
| 99 | + |
|
| 100 | +- [插入](https://www.rockdata.net/zh-cn/tutorial/dml-insert/) – 指导您如何将单行插入表中。 |
|
| 101 | +- [插入多行](https://www.rockdata.net/zh-cn/tutorial/dml-insert-multiple-rows/) – 向您展示如何在表中插入多行。 |
|
| 102 | +- [更新](https://www.rockdata.net/zh-cn/tutorial/dml-update/) – 更新表中的现有数据。 |
|
| 103 | +- [连接更新](https://www.rockdata.net/zh-cn/tutorial/dml-update-join/) – 根据另一个表中的值更新表中的值。 |
|
| 104 | +- [删除](https://www.rockdata.net/zh-cn/tutorial/dml-delete/) – 删除表中的数据。 |
|
| 105 | +- [连接删除](https://www.rockdata.net/zh-cn/tutorial/dml-delete-join/) – 根据另一个表中的值删除表中的行。 |
|
| 106 | +- [级联删除](https://www.rockdata.net/zh-cn/tutorial/dml-delete-cascade/) – 在删除父表中的行时级联删除子表中的相关行。 |
|
| 107 | +- [UPSERT](https://www.rockdata.net/zh-cn/tutorial/dml-upsert/) – 如果新行已存在于表中,则插入或更新数据。 |
|
| 108 | +- [合并](https://www.rockdata.net/zh-cn/tutorial/dml-merge/) – 有条件地插入、更新和删除一个表中的行。 |
|
| 109 | + |
|
| 110 | +### 第 10 节. 事务 |
|
| 111 | + |
|
| 112 | +- [PostgreSQL 事务](https://www.rockdata.net/zh-cn/tutorial/dml-transaction/) – 向您展示如何使用`BEGIN`、`COMMIT`和`ROLLBACK`语句,来处理 PostgreSQL 中的事务。 |
|
| 113 | +- [PostgreSQL 子事务](https://www.rockdata.net/zh-cn/tutorial/dml-subtransaction/) – 向您展示如何使用`SAVEPOINT`、`ROLLBACK TO SAVEPOINT`和`RELEASE SAVEPOINT`语句,来处理 PostgreSQL 子事务。 |
|
| 114 | +- [并发锁定和组合事务](https://www.rockdata.net/zh-cn/tutorial/dml-multixacts/) – 向您介绍什么是组合事务,它为什么会存在,以及会在什么情况下出现。 |
|
| 115 | +- [SKIP LOCKED 跳过锁定](https://www.rockdata.net/zh-cn/tutorial/dml-skip-locked/) – 向您介绍如何在 PostgreSQL 中使用`SKIP LOCKED`,以避免和解决死锁的问题。 |
|
| 116 | + |
|
| 117 | +### 第 11 节. 导入和导出数据 |
|
| 118 | + |
|
| 119 | +您将学习如何使用`COPY`命令、DBeaver 工具,以 CSV、SQL 文件格式对 PostgreSQL 数据进行导入和导出。 |
|
| 120 | + |
|
| 121 | +- [将 CSV 文件导入表中](https://www.rockdata.net/zh-cn/tutorial/admin-import-table-csv/) – 向您展示如何将 CSV 文件导入表中。 |
|
| 122 | +- [导出表到 CSV 文件](https://www.rockdata.net/zh-cn/tutorial/admin-export-table-csv/) – 向您展示如何将表导出到 CSV 文件。 |
|
| 123 | +- [使用 DBeaver 导入数据](https://www.rockdata.net/zh-cn/tutorial/dbeaver-import-data/) – 向您展示如何使用 DBeaver 将数据从文件导入到表中。 |
|
| 124 | +- [使用 DBeaver 导出表](https://www.rockdata.net/zh-cn/tutorial/dbeaver-export-data/) – 向您展示如何使用 DBeaver 将表导出到不同类型和格式的文件。 |
|
| 125 | +- [使用 DBeaver 合并数据](https://www.rockdata.net/zh-cn/tutorial/dbeaver-merge-data/) – 向您展示如何使用 DBeaver 将文件中的数据合并到表中。 |
|
| 126 | + |
|
| 127 | +### 第 12 节. 管理表 |
|
| 128 | + |
|
| 129 | +在本节中,您将开始探索 PostgreSQL 数据类型,并向您展示如何创建新表和修改现有表的结构。 |
|
| 130 | + |
|
| 131 | +- [数据类型](https://www.rockdata.net/zh-cn/tutorial/data-types/) – 涵盖最常用的 PostgreSQL 数据类型。 |
|
| 132 | +- [创建表](https://www.rockdata.net/zh-cn/tutorial/ddl-create-table/) – 指导您如何在数据库中创建新表。 |
|
| 133 | +- [SELECT INTO](https://www.rockdata.net/zh-cn/tutorial/dml-select-into/) 和 [CREATE TABLE AS](https://www.rockdata.net/zh-cn/tutorial/ddl-create-table-as/) – 向您展示如何从查询的结果集创建新表。 |
|
| 134 | +- 使用 [SERIAL 自增列](https://www.rockdata.net/zh-cn/tutorial/type-serial/) – 使用 SERIAL 将自动增量列添加到表中。 |
|
| 135 | +- [序列](https://www.rockdata.net/zh-cn/tutorial/ddl-sequences/) – 向您介绍序列并描述如何使用序列生成数字序列。 |
|
| 136 | +- [标识列](https://www.rockdata.net/zh-cn/tutorial/ddl-identity-column/) – 向您展示如何使用标识列。 |
|
| 137 | +- [生成列](https://www.rockdata.net/zh-cn/tutorial/ddl-generated-columns/) – 向您展示如何使用生成列。 |
|
| 138 | +- [更改表](https://www.rockdata.net/zh-cn/tutorial/ddl-alter-table/) – 修改现有表的结构。 |
|
| 139 | +- [重命名表](https://www.rockdata.net/zh-cn/tutorial/ddl-rename-table/) – 将表的名称更改为新名称。 |
|
| 140 | +- [添加列](https://www.rockdata.net/zh-cn/tutorial/ddl-add-column/) – 向您展示如何向现有表添加一列或多列。 |
|
| 141 | +- [删除列](https://www.rockdata.net/zh-cn/tutorial/ddl-drop-column/) – 演示如何删除表的列。 |
|
| 142 | +- [更改列数据类型](https://www.rockdata.net/zh-cn/tutorial/ddl-change-column-type/) – 向您展示如何更改列的数据。 |
|
| 143 | +- [重命名列](https://www.rockdata.net/zh-cn/tutorial/ddl-rename-column/) – 说明如何重命名表中的一列或多列。 |
|
| 144 | +- [删除表](https://www.rockdata.net/zh-cn/tutorial/ddl-drop-table/) – 删除现有表及其所有依赖对象。 |
|
| 145 | +- [截断表](https://www.rockdata.net/zh-cn/tutorial/ddl-truncate-table/) – 快速有效地删除大表中的所有数据。 |
|
| 146 | +- [临时表](https://www.rockdata.net/zh-cn/tutorial/ddl-temporary-table/) – 向您展示如何使用临时表。 |
|
| 147 | +- [复制表](https://www.rockdata.net/zh-cn/tutorial/admin-copy-table/) – 向您展示如何将表格复制到新表格。 |
|
| 148 | +- [表分区](https://www.rockdata.net/zh-cn/tutorial/ddl-table-partitioning/) – 向您展示如何使用表分区。 |
|
| 149 | +- [管理分区表](https://www.rockdata.net/zh-cn/tutorial/ddl-manage-partitions/) – 向您展示如何管理分区表。 |
|
| 150 | +- [引发表重写的 DDL 命令](https://www.rockdata.net/zh-cn/tutorial/ddl-table-rewrite/) – 向您介绍哪些 DDL 命令会导致表的重写。 |
|
| 151 | +- [以最短的停机时间更改列](https://www.rockdata.net/zh-cn/tutorial/admin-alter-column-online/) – 向您介绍如何以最短的停机时间更改列。 |
|
| 152 | + |
|
| 153 | +### 第 13 节. 了解 PostgreSQL 约束 |
|
| 154 | + |
|
| 155 | +- [主键](https://www.rockdata.net/zh-cn/tutorial/constraint-primary-key/) – 说明在创建表或向现有表添加主键时如何定义主键。 |
|
| 156 | +- [外键](https://www.rockdata.net/zh-cn/tutorial/constraint-foreign-key/) – 展示如何在创建新表时定义外键约束或为现有表添加外键约束。 |
|
| 157 | +- [检查约束](https://www.rockdata.net/zh-cn/tutorial/constraint-check/) – 添加逻辑以基于布尔表达式检查值。 |
|
| 158 | +- [唯一约束](https://www.rockdata.net/zh-cn/tutorial/constraint-unique/) – 确保一列或一组列中的值在整个表中是唯一的。 |
|
| 159 | +- [非空约束](https://www.rockdata.net/zh-cn/tutorial/constraint-not-null/) – 确保列中的值不是`NULL`。 |
|
| 160 | + |
|
| 161 | +### 第 14 节. 深入了解 PostgreSQL 数据类型 |
|
| 162 | + |
|
| 163 | +- [布尔型](https://www.rockdata.net/zh-cn/tutorial/type-boolean/) – 使用布尔数据类型存储`TRUE`和`FALSE`值。 |
|
| 164 | +- [字符型](https://www.rockdata.net/zh-cn/tutorial/character-types/) – 了解如何使用各种字符类型,包括`char`、`varchar`和`text`。 |
|
| 165 | +- [numeric](https://www.rockdata.net/zh-cn/tutorial/type-numeric/) – 向您展示如何使用`numeric`类型来存储需要精度的值。 |
|
| 166 | +- [double precision](https://www.rockdata.net/zh-cn/tutorial/type-double-precision/) – 了解如何在数据库中存储不准确的可变精度数字。double precision 类型也称为 float 类型。 |
|
| 167 | +- [real](https://www.rockdata.net/zh-cn/tutorial/type-real/) – 指导您如何在数据库中使用单精度浮点数。 |
|
| 168 | +- [整型](https://www.rockdata.net/zh-cn/tutorial/type-integer/) – 向您介绍 PostgreSQL 中的各种整数类型,包括`smallint`、`int`和`bigint`。 |
|
| 169 | +- [date](https://www.rockdata.net/zh-cn/tutorial/type-date/) – 引入`date`用于存储日期值的数据类型。 |
|
| 170 | +- [时间戳](https://www.rockdata.net/zh-cn/tutorial/type-timestamp/) – 快速了解时间戳数据类型。 |
|
| 171 | +- [间隔](https://www.rockdata.net/zh-cn/tutorial/type-interval/) – 向您展示如何使用间隔数据类型有效地处理一段时间。 |
|
| 172 | +- [time](https://www.rockdata.net/zh-cn/tutorial/type-time/) – 使用`time`数据类型来管理一天中的时间值。 |
|
| 173 | +- [UUID](https://www.rockdata.net/zh-cn/tutorial/type-uuid/) – 指导您如何使用`UUID`数据类型以及如何使用提供的模块生成`UUID`值。 |
|
| 174 | +- [数组](https://www.rockdata.net/zh-cn/tutorial/type-array/) – 向您展示如何使用数组,并向您介绍一些用于数组操作的方便函数。 |
|
| 175 | +- [hstore](https://www.rockdata.net/zh-cn/tutorial/type-hstore/) – 向您介绍数据类型,它是存储在 PostgreSQL 中单个值中的一组键/值对。 |
|
| 176 | +- [JSON](https://www.rockdata.net/zh-cn/tutorial/type-json/) – 说明如何使用 JSON 数据类型,并向您展示如何使用一些最重要的 JSON 运算符和函数。 |
|
| 177 | +- [用户定义的数据类型](https://www.rockdata.net/zh-cn/tutorial/user-defined-data-types/) – 向您展示如何使用`CREATE DOMAIN`和`CREATE TYPE`语句创建用户定义的数据类型。 |
|
| 178 | +- [bytea](https://www.rockdata.net/zh-cn/tutorial/type-bytea/) – 了解如何在数据库中存储二进制字节串。 |
|
| 179 | + |
|
| 180 | +### 第 15 节. 条件表达式和运算符 |
|
| 181 | + |
|
| 182 | +- [CASE](https://www.rockdata.net/zh-cn/tutorial/dml-case/) – 向您展示如何使用`CASE`表达式构成条件查询。 |
|
| 183 | +- [COALESCE](https://www.rockdata.net/zh-cn/tutorial/dml-coalesce/) – 返回第一个非空参数。您可以使用它将`NULL`替换为一个默认值。 |
|
| 184 | +- [NULLIF](https://www.rockdata.net/zh-cn/tutorial/dml-nullif/) – 如果第一个参数等于第二个参数则返回`NULL`。 |
|
| 185 | +- [CAST](https://www.rockdata.net/zh-cn/tutorial/type-cast/) – 从一种数据类型转换为另一种数据类型,例如,从字符串转换为整数,从字符串转换为日期。 |
|
| 186 | + |
|
| 187 | +### 第 16 节. PostgreSQL 实用程序 |
|
| 188 | + |
|
| 189 | +- [psql 命令](https://www.rockdata.net/zh-cn/tutorial/admin-psql-commands/) – 向您展示最常见的 psql 命令,帮助您更快、更有效地与 psql 交互。 |
|
| 190 | + |
|
| 191 | +### 第 17 节. 故障处理 |
|
| 192 | + |
|
| 193 | +- [应对死锁](https://www.rockdata.net/zh-cn/tutorial/troubleshooting-deadlocks/) – 指导您如何在 PostgreSQL 中处理死锁的问题。 |
|
| 194 | +- [处理数据块损坏](https://www.rockdata.net/zh-cn/tutorial/troubleshooting-corrupted-blocks/) – 指导您如何处理 PostgreSQL 中损坏的数据块。 |
|
| 195 | +- [处理 TOAST 数据损坏](https://www.rockdata.net/zh-cn/tutorial/troubleshooting-corrupted-toast/) – 指导您如何处理 PostgreSQL 中损坏的 TOAST 数据。 |
|
| 196 | +- [处理统计信息损坏](https://www.rockdata.net/zh-cn/tutorial/troubleshooting-statistics-corruption/) – 指导您如何处理 PostgreSQL 中的统计信息损坏。 |
|
| 197 | +- [处理 PL/pgSQL 运行时错误](https://www.rockdata.net/zh-cn/tutorial/troubleshooting-plpgsql-runtime-errors/) – 指导您如何对 PostgreSQL 中的 PL/pgSQL 运行时错误进行处理。 |
|
| 198 | +- [页面缓存如何影响查询性能?](https://www.rockdata.net/zh-cn/tutorial/troubleshooting-page-caches/) – 指导您如何分析页面缓存对 PostgreSQL 性能的影响。 |
|
| 199 | +- [处理事务 ID 回卷的故障](https://www.rockdata.net/zh-cn/tutorial/troubleshooting-txn-wraparound/) – 指导您如何处理 PostgreSQL 中事务 ID 回卷的故障。 |
|
| 200 | +- [检查后端进程的内存使用情况](https://www.rockdata.net/zh-cn/tutorial/troubleshooting-memory-usage/) – 指导您如何检查 PostgreSQL 中后端进程的内存使用情况,和进行故障处理。 |
|
| 201 | +- [记录函数内错误的调用栈](https://www.rockdata.net/zh-cn/tutorial/logging-error-backtraces/) – 指导您如何记录在指定函数内发生错误时的调用栈。 |
|
| 202 | +- [分区剪枝不起作用的原因](https://www.rockdata.net/zh-cn/tutorial/troubleshooting-partition-pruning/) – 向您介绍在 PostgreSQL 中分区剪枝不起作用的主要原因。 |
|
| 203 | +- [处理 pg_wal 目录中的 WAL 积压](https://www.rockdata.net/zh-cn/tutorial/troubleshooting-wal-accumulation/) – 指导您如何在 PostgreSQL 中排查 pg_wal 目录中的 WAL 积压问题。 |
|
| 204 | +- [处理服务器可用内存不足的问题](https://www.rockdata.net/zh-cn/tutorial/troubleshooting-server-low-free-memory/) – 指导您如何在 PostgreSQL 服务器上处理可用内存不足的问题。 |
|
| 205 | +- [处理缓存命中率低的问题](https://www.rockdata.net/zh-cn/tutorial/troubleshooting-low-cache-hit-ratio/) – 指导您如何在 PostgreSQL 中处理缓存命中率低的问题。 |
|
| 206 | + |
|
| 207 | +### 第 18 节. PostgreSQL 技巧 |
|
| 208 | + |
|
| 209 | +- [如何比较两个表](https://www.rockdata.net/zh-cn/tutorial/admin-compare-tables/) – 描述如何比较数据库中两个表中的数据。 |
|
| 210 | +- [使用 pgAdmin 4 对比数据架构](https://www.rockdata.net/zh-cn/tutorial/pgadmin-compare-schema/) – 指导您如何使用 pgAdmin 4 提供的架构差异功能,比较两个数据库或两个模式之间的对象。 |
|
| 211 | +- [使用 DBeaver 迁移表数据](https://www.rockdata.net/zh-cn/tutorial/dbeaver-migrate-data/) – 向您介绍使用 DBeaver 工具,在不同数据库之间或同一数据库内的表之间传输数据。 |
|
| 212 | +- [如何在 PostgreSQL 中删除重复行](https://www.rockdata.net/zh-cn/tutorial/admin-delete-duplicate-rows/) – 向您展示从表中删除重复行的各种方法。 |
|
| 213 | +- [如何生成某个范围内的随机数](https://www.rockdata.net/zh-cn/tutorial/function-random-range/) – 说明如何生成特定范围内的随机数。 |
|
| 214 | +- [查询 JSON 列中内嵌的数组](https://www.rockdata.net/zh-cn/tutorial/query-json-arrays/) – 描述如何在 PostgreSQL 中查询 JSON 列中内嵌的数组。 |
|
| 215 | +- [更改 JSON 列中内嵌的数组](https://www.rockdata.net/zh-cn/tutorial/update-json-arrays/) – 描述如何在 PostgreSQL 中修改 JSON 列中内嵌的数组。 |
|
| 216 | +- [PostGIS 基础用法](https://www.rockdata.net/zh-cn/tutorial/postgis-basics/) – 向您介绍 PostGIS 的一些基础用法。 |
|
| 217 | +- [使用 PostGIS 进行基础的地理空间数据查询](https://www.rockdata.net/zh-cn/tutorial/postgis-basic-queries/) – 向您演示用于处理地理空间数据的基础 PostGIS 查询。 |
|
| 218 | + |
|
| 219 | +## PostgreSQL 高级教程 |
|
| 220 | + |
|
| 221 | +这个 PostgreSQL 高级教程涵盖了高级概念,包括存储过程、索引、视图、触发器和数据库管理。 |
|
| 222 | + |
|
| 223 | +### [PostgreSQL 函数](https://www.rockdata.net/zh-cn/tutorial/postgres-functions/) |
|
| 224 | + |
|
| 225 | + |
|
| 226 | + |
|
| 227 | +PostgreSQL 为内置数据类型提供了大量的函数。本节向您展示如何使用一些最常用的 PostgreSQL 函数。 |
|
| 228 | + |
|
| 229 | +### [PostgreSQL PL/pgSQL](https://www.rockdata.net/zh-cn/tutorial/postgres-plpgsql/) |
|
| 230 | + |
|
| 231 | + |
|
| 232 | + |
|
| 233 | +此 PostgreSQL 存储过程部分将逐步向您展示如何使用 PL/pgSQL 过程语言开发 PostgreSQL 用户定义函数。 |
|
| 234 | + |
|
| 235 | +### [PostgreSQL 触发器](https://www.rockdata.net/zh-cn/tutorial/ddl-triggers/) |
|
| 236 | + |
|
| 237 | + |
|
| 238 | + |
|
| 239 | +本节向您介绍 PostgreSQL 触发器概念,并展示如何在 PostgreSQL 中管理触发器。 |
|
| 240 | + |
|
| 241 | +### [PostgreSQL 视图](https://www.rockdata.net/zh-cn/tutorial/ddl-views/) |
|
| 242 | + |
|
| 243 | + |
|
| 244 | + |
|
| 245 | +我们将向您介绍数据库视图概念,并向您展示如何管理视图,例如在数据库中创建、更改和删除视图。 |
|
| 246 | + |
|
| 247 | +### [PostgreSQL 索引](https://www.rockdata.net/zh-cn/tutorial/indexes/) |
|
| 248 | + |
|
| 249 | + |
|
| 250 | + |
|
| 251 | +PostgreSQL 索引是增强数据库性能的有效工具。索引可以帮助数据库服务器比没有索引时更快地找到特定行。 |
|
| 252 | + |
|
| 253 | +### [PostgreSQL 优化](https://www.rockdata.net/zh-cn/tutorial/postgres-optimization/) |
|
| 254 | + |
|
| 255 | + |
|
| 256 | + |
|
| 257 | +本节向您介绍 PostgreSQL 性能优化,并展示如何在 PostgreSQL 中优化各种场景的性能问题。 |
|
| 258 | + |
|
| 259 | +### [PostgreSQL 管理](https://www.rockdata.net/zh-cn/tutorial/postgres-admin/) |
|
| 260 | + |
|
| 261 | + |
|
| 262 | + |
|
| 263 | +PostgreSQL 管理涵盖 PostgreSQL 数据库服务器最重要的活动,包括角色和数据库管理、备份和恢复。 |
|
| 264 | + |
|
| 265 | +### [PostgreSQL 监控](https://www.rockdata.net/zh-cn/tutorial/postgres-monitoring/) |
|
| 266 | + |
|
| 267 | + |
|
| 268 | + |
|
| 269 | +PostgreSQL 监控涵盖 PostgreSQL 数据库服务器最重要的监控和运维活动。 |
|
| 270 | + |
|
| 271 | +### [应用程序编程接口](https://www.rockdata.net/zh-cn/docs/14/client-interfaces.html) |
|
| 272 | + |
|
| 273 | +本节向您展示,如何从使用流行编程语言(例如 Java、Python 和 PHP)的应用程序,与 PostgreSQL 数据库进行交互。 |
|
| 274 | + |
|
| 275 | +- [PostgreSQL Java 教程](https://www.rockdata.net/zh-cn/tutorial/postgres-java/) – 此 PostgreSQL JDBC 部分向您展示,如何使用 Java JDBC 驱动程序与 PostgreSQL 数据库进行交互。 |
|
| 276 | +- [PostgreSQL Python 教程](https://www.rockdata.net/zh-cn/tutorial/postgres-python/) – 此 PostgreSQL Python 部分向您展示,如何使用 Python 编程语言与 PostgreSQL 数据库进行交互。 |
|
| 277 | +- [使用 Golang 连接到 PostgreSQL](https://www.rockdata.net/zh-cn/tutorial/golang-setup/) – 向您介绍如何使用 Go 编程语言与 PostgreSQL 数据库进行交互。 |
|
| 278 | + |
|
| 279 | + |
|
| 280 | + |
|
| 281 | + |
|
| 282 | + |
|
| 283 | +## 语法差异 |
|
| 284 | + |
|
| 285 | +如果列别名包含一个或多个空格,则需要用双引号将其引起来,如下所示: |
|
| 286 | + |
|
| 287 | + |
|
| 288 | + |
|
| 289 | +## 包含空格的列别名 |
|
| 290 | + |
|
| 291 | +如果列别名包含一个或多个空格,则需要用双引号将其引起来,如下所示: |
|
| 292 | + |
|
| 293 | +```sql |
|
| 294 | +column_name AS "column alias" |
|
| 295 | +``` |
|
| 296 | + |
|
| 297 | +例如: |
|
| 298 | + |
|
| 299 | +```sql |
|
| 300 | +SELECT |
|
| 301 | + first_name || ' ' || last_name "full name" |
|
| 302 | +FROM |
|
| 303 | + customer; |
|
| 304 | +``` |
|
| 305 | + |
|
| 306 | + |
|
| 307 | + |
|
| 308 | + |
|
| 309 | + |
|
| 310 | +## PostgreSQL SELECT DISTINCT 子句简介 |
|
| 311 | + |
|
| 312 | +在这种情况下,`column1`和`column2`列中的值的组合将用于计算重复项。 |
|
| 313 | + |
|
| 314 | +PostgreSQL 还提供了`DISTINCT ON (expression)`来保留每组重复项的第一行的功能,使用以下语法: |
|
| 315 | + |
|
| 316 | +```sql |
|
| 317 | +SELECT |
|
| 318 | + DISTINCT ON (column1) column_alias, |
|
| 319 | + column2 |
|
| 320 | +FROM |
|
| 321 | + table_name |
|
| 322 | +ORDER BY |
|
| 323 | + column1, |
|
| 324 | + column2; |
|
| 325 | +``` |
|
| 326 | + |
|
| 327 | +从`SELECT`语句返回的行的顺序是未指定的,因此每组重复项的第一行也是未指定的。 |
|
| 328 | + |
|
| 329 | +最好始终使用带有`DISTINCT ON(expression)`的 [ORDER BY](https://www.rockdata.net/zh-cn/tutorial/dml-order-by/) 子句,以使结果集可预测。 |
|
| 330 | + |
|
| 331 | +请注意,`DISTINCT ON`表达式必须与`ORDER BY`子句中最左边的表达式匹配。 |
|
| 332 | + |
|
| 333 | + |
|
| 334 | + |
|
| 335 | + |
|
| 336 | + |
|
| 337 | +## PostgreSQL ORDER BY 子句和 NULL |
|
| 338 | + |
|
| 339 | +在数据库世界中,`NULL`是一个标记,指示丢失的数据或数据在记录时未知。 |
|
| 340 | + |
|
| 341 | +对包含`NULL`的行进行排序时,可以使用`ORDER BY`子句的`NULLS FIRST`或`NULLS LAST`选项,指定`NULL`与其他非空值的顺序: |
|
| 342 | + |
|
| 343 | +```sql |
|
| 344 | +ORDER BY sort_expresssion [ASC | DESC] [NULLS FIRST | NULLS LAST] |
|
| 345 | +``` |
|
| 346 | + |
|
| 347 | +在此示例中,`ORDER BY`子句按升序对`sort_demo`表的`num`列中的值进行排序。它将`NULL`置于其他值之后。 |
|
| 348 | + |
|
| 349 | +因此,如果您使用`ASC`选项,`ORDER BY`子句默认使用`NULLS LAST`选项。因此,以下查询返回相同的结果: |
|
| 350 | + |
|
| 351 | +```sql |
|
| 352 | +SELECT num |
|
| 353 | +FROM sort_demo |
|
| 354 | +ORDER BY num NULLS LAST; |
|
| 355 | +``` |
|
| 356 | + |
|
| 357 | +要放置`NULL`在其他非空值之前,可以使用`NULLS FIRST`选项: |
|
| 358 | + |
|
| 359 | +```sql |
|
| 360 | +SELECT num |
|
| 361 | +FROM sort_demo |
|
| 362 | +ORDER BY num NULLS FIRST; |
|
| 363 | +``` |
|
| 364 | + |
|
| 365 | + |
|
| 366 | + |
|
| 367 | + |
|
| 368 | + |
|
| 369 | +## PostgreSQL 的分页和过滤技术 |
|
| 370 | + |
|
| 371 | +[PostgreSQL](https://geek-docs.com/postgresql/postgresql-top-tutorials/1000100_postgresql_index.html) 提供了多种方法来实现分页和过滤功能。其中,键集分页方法是在处理大型表时的一个非常有效的方法。 |
|
| 372 | + |
|
| 373 | +https://www.rockdata.net/zh-cn/tutorial/dml-paginate/ |
|
| 374 | + |
|
| 375 | + |
|
| 376 | + |
|
| 377 | +### 键集分页方法 |
|
| 378 | + |
|
| 379 | +键集分页方法基于结果集中的唯一键(通常是主键)进行分页。它通过保存上一次查询的最后一行的键值,并将其作为下一次查询的起点来实现分页。 |
|
| 380 | + |
|
| 381 | +以下是一个使用键集分页方法的示例查询: |
|
| 382 | + |
|
| 383 | +```sql |
|
| 384 | +SELECT * |
|
| 385 | +FROM large_table |
|
| 386 | +WHERE id > last_key |
|
| 387 | +ORDER BY id |
|
| 388 | +LIMIT 10; |
|
| 389 | +``` |
|
| 390 | + |
|
| 391 | + |
|
| 392 | + |
|
| 393 | +在此示例中,我们使用唯一键 id 进行分页。上一次查询的最后一条数据的 id 值将用于下一次查询的过滤条件。通过以此方式逐步递增分页查询,我们可以避免加载整个结果集,从而提高性能和效率。 |
|
| 394 | + |
|
| 395 | +### OFFSET 和 LIMIT 分页 |
|
| 396 | + |
|
| 397 | +除了键集分页方法外,PostgreSQL 还提供了使用 OFFSET 和 LIMIT 子句进行分页的方式。OFFSET 子句用于指定开始返回结果的位置,LIMIT 子句用于指定返回结果的数量。 |
|
| 398 | + |
|
| 399 | +以下是一个使用 OFFSET 和 LIMIT 分页的示例查询: |
|
| 400 | + |
|
| 401 | +```sql |
|
| 402 | +SELECT * |
|
| 403 | +FROM large_table |
|
| 404 | +ORDER BY id |
|
| 405 | +OFFSET 1000 |
|
| 406 | +LIMIT 10; |
|
| 407 | +``` |
|
| 408 | + |
|
| 409 | +## PostgreSQL LIMIT 子句简介 |
|
| 410 | + |
|
| 411 | +https://www.rockdata.net/zh-cn/tutorial/dml-limit/ |
|
| 412 | + |
|
| 413 | +PostgreSQL 的`LIMIT`是 [SELECT](https://www.rockdata.net/zh-cn/tutorial/dml-select/) 语句的可选子句,用于限制查询返回的行数。 |
|
| 414 | + |
|
| 415 | +下面说明了`LIMIT`子句的语法: |
|
| 416 | + |
|
| 417 | +```sql |
|
| 418 | +SELECT select_list |
|
| 419 | +FROM table_name |
|
| 420 | +ORDER BY sort_expression |
|
| 421 | +LIMIT row_count |
|
| 422 | +``` |
|
| 423 | + |
|
| 424 | +该语句返回查询生成的`row_count`行。如果`row_count`为零,则查询返回空集。如果`row_count`是`NULL`,查询将返回与没有`LIMIT`子句相同的结果集。 |
|
| 425 | + |
|
| 426 | +如果您想在返回`row_count`行之前跳过一些行,请在`LIMIT`子句之后放置`OFFSET`子句,如下所示: |
|
| 427 | + |
|
| 428 | +```sql |
|
| 429 | +SELECT select_list |
|
| 430 | +FROM table_name |
|
| 431 | +LIMIT row_count OFFSET row_to_skip; |
|
| 432 | +``` |
|
| 433 | + |
|
| 434 | +该语句首先跳过`row_to_skip`行,然后返回查询生成的`row_count`行。如果`row_to_skip`为零,则该语句将像没有`OFFSET`子句一样工作。 |
|
| 435 | + |
|
| 436 | +由于表可能以未指定的顺序存储行,因此当您使用`LIMIT`子句时,应始终使用 [ORDER BY](https://www.rockdata.net/zh-cn/tutorial/dml-order-by/) 子句来控制行顺序。如果不使用`ORDER BY`子句,则可能会得到具有未指定行顺序的结果集。 |
|
| 437 | + |
|
| 438 | + |
|
| 439 | + |
|
| 440 | +## PostgreSQL FETCH 子句简介 |
|
| 441 | + |
|
| 442 | +为了限制查询返回的行数,您经常使用`LIMIT`子句。`LIMIT`子句被许多关系数据库管理系统广泛使用,例如 MySQL、H2 和 HSQLDB。但是,`LIMIT`子句不是 SQL 标准。 |
|
| 443 | + |
|
| 444 | +为了符合 SQL 标准,PostgreSQL 支持`FETCH`子句来检索查询返回的行数。 |
|
| 445 | + |
|
| 446 | +> 请注意,`FETCH`子句是在 SQL:2008 中作为 SQL 标准的一部分引入的。 |
|
| 447 | + |
|
| 448 | +下面说明了 PostgreSQL 的`FETCH`子句的语法: |
|
| 449 | + |
|
| 450 | +```sql |
|
| 451 | +OFFSET start { ROW | ROWS } |
|
| 452 | +FETCH { FIRST | NEXT } [ row_count ] { ROW | ROWS } ONLY |
|
| 453 | +``` |
|
| 454 | + |
|
| 455 | +## FETCH 对比 LIMIT |
|
| 456 | + |
|
| 457 | +`FETCH`子句在功能上等同于`LIMIT`子句。如果您计划使您的应用程序与其他数据库系统兼容,则应该使用`FETCH`子句,因为它遵循 SQL 标准。 |
|
| 458 | + |
|
| 459 | + |
|
| 460 | + |
|
| 461 | + |
|
| 462 | + |
|
| 463 | +## PostgreSQL WHERE 子句概述 |
|
| 464 | + |
|
| 465 | +https://www.rockdata.net/zh-cn/tutorial/dml-where/ |
|
| 466 | + |
|
| 467 | +PostgreSQL 的`WHERE`子句的语法如下: |
|
| 468 | + |
|
| 469 | +```sql |
|
| 470 | +SELECT select_list |
|
| 471 | +FROM table_name |
|
| 472 | +WHERE condition |
|
| 473 | +ORDER BY sort_expression |
|
| 474 | +``` |
|
| 475 | + |
|
| 476 | +`WHERE`子句出现在`SELECT`语句的`FROM`子句之后。`WHERE`子句使用`condition`来过滤从`SELECT`列表子句返回的行。 |
|
| 477 | + |
|
| 478 | +`condition`的计算结果必须为真、假或未知。它可以是布尔表达式或使用`AND`和`OR`运算符的布尔表达式的组合。 |
|
| 479 | + |
|
| 480 | +该查询仅返回满足`WHERE`子句中`condition`的行。换句话说,只有导致`condition`计算结果为 true 的行才会包含在结果集中。 |
|
| 481 | + |
|
| 482 | +PostgreSQL 计算`WHERE`子句的时间点,在`FROM`子句之后,在`SELECT`列表和`ORDER BY`子句之前: |
|
| 483 | + |
|
| 484 | +如果在`SELECT`列表子句中使用列别名,则不能在`WHERE`子句中使用它们。 |
|
| 485 | + |
|
| 486 | +除了`SELECT`语句之外,您还可以使用`UPDATE`和`DELETE`语句中的`WHERE`子句来指定要更新或删除的行。 |
|
| 487 | + |
|
| 488 | +要构成`WHERE`子句中的条件,请使用比较运算符和逻辑运算符: |
|
| 489 | + |
|
| 490 | +| 运算符 | 描述 | |
|
| 491 | +| :----------------------------------------------------------- | :-------------------------------------- | |
|
| 492 | +| = | 等于 | |
|
| 493 | +| > | 大于 | |
|
| 494 | +| < | 小于 | |
|
| 495 | +| >= | 大等于 | |
|
| 496 | +| <= | 小等于 | |
|
| 497 | +| <> 或 != | 不等于 | |
|
| 498 | +| AND | 逻辑运算符 AND | |
|
| 499 | +| OR | 逻辑运算符 OR | |
|
| 500 | +| [IN](https://www.rockdata.net/zh-cn/tutorial/dml-in/) | 如果值与列表中的任何值匹配,则返回 true | |
|
| 501 | +| [BETWEEN](https://www.rockdata.net/zh-cn/tutorial/dml-between/) | 如果值介于某个值范围之间,则返回 true | |
|
| 502 | +| [LIKE](https://www.rockdata.net/zh-cn/tutorial/dml-like/) | 如果值与模式匹配则返回 true | |
|
| 503 | +| [IS NULL](https://www.rockdata.net/zh-cn/tutorial/dml-is-null/) | 如果值为 NULL,则返回 true | |
|
| 504 | +| NOT | 对其他运算符的结果求反 | |
|
| 505 | + |
|
| 506 | +### 7) 使用带有不等于运算符 (<>) 的 WHERE 子句示例 |
|
| 507 | + |
|
| 508 | +此示例查找名字以`Bra`开头且姓氏不是`Motley`的客户: |
|
| 509 | + |
|
| 510 | +```sql |
|
| 511 | +SELECT |
|
| 512 | + first_name, |
|
| 513 | + last_name |
|
| 514 | +FROM |
|
| 515 | + customer |
|
| 516 | +WHERE |
|
| 517 | + first_name LIKE 'Bra%' AND |
|
| 518 | + last_name <> 'Motley'; |
|
| 519 | +``` |
|
| 520 | + |
|
| 521 | + |
|
| 522 | + |
|
| 523 | +## PostgreSQL BETWEEN 运算符简介 |
|
| 524 | + |
|
| 525 | +您可以使用`BETWEEN`运算符将一个值与一系列值进行匹配。下面说明了`BETWEEN`运算符的语法: |
|
| 526 | + |
|
| 527 | +```sql |
|
| 528 | +value BETWEEN low AND high; |
|
| 529 | +``` |
|
| 530 | + |
|
| 531 | +如果`value`大等于`low`值且小等于`high`值,则表达式返回 true,否则返回 false。 |
|
| 532 | + |
|
| 533 | +您可以使用大等于 (`>=`) 或小等于 (`<=`) 运算符重写`BETWEEN`运算符,如下所示: |
|
| 534 | + |
|
| 535 | +```sql |
|
| 536 | +value >= low and value <= high |
|
| 537 | +``` |
|
| 538 | + |
|
| 539 | +如果要检查值是否超出范围,可以将`NOT`运算符与`BETWEEN`运算符组合起来,如下所示: |
|
| 540 | + |
|
| 541 | +```sql |
|
| 542 | +value NOT BETWEEN low AND high; |
|
| 543 | +``` |
|
| 544 | + |
|
| 545 | +以下表达式等效于使用`NOT`和`BETWEEN`运算符的表达式: |
|
| 546 | + |
|
| 547 | +```sql |
|
| 548 | +value < low OR value > high |
|
| 549 | +``` |
|
| 550 | + |
|
| 551 | + |
|
| 552 | + |
|
| 553 | +如果要检查日期范围内的值,则应使用 ISO 8601 格式的文字日期,即 YYYY-MM-DD。例如,要获取付款日期在`2007-02-07`和`2007-02-15`之间的付款,请使用以下查询: |
|
| 554 | + |
|
| 555 | +```sql |
|
| 556 | +SELECT |
|
| 557 | + customer_id, |
|
| 558 | + payment_id, |
|
| 559 | + amount, |
|
| 560 | + payment_date |
|
| 561 | +FROM |
|
| 562 | + payment |
|
| 563 | +WHERE |
|
| 564 | + payment_date BETWEEN '2007-02-07' AND '2007-02-15'; |
|
| 565 | +``` |
|
| 566 | + |
|
| 567 | + |
|
| 568 | + |
|
| 569 | + |
|
| 570 | + |
|
| 571 | +## PostgreSQL LIKE 运算符简介 |
|
| 572 | + |
|
| 573 | +假设您想要找到一位客户,但您不记得她的确切名字。但是,您可以记得她的名字以类似`Jen`开头。 |
|
| 574 | + |
|
| 575 | +如何从数据库中找到准确的客户?您可以通过查看名字列来查找`customer`表中的客户,看看是否有任何以`Jen`开头的值。但是,如果客户表包含大量行,则此过程可能会非常耗时。 |
|
| 576 | + |
|
| 577 | +幸运的是,您可以使用 PostgreSQL 的`LIKE`运算符,通过以下查询将客户的名字与字符串进行匹配: |
|
| 578 | + |
|
| 579 | +```sql |
|
| 580 | +SELECT |
|
| 581 | + first_name, |
|
| 582 | + last_name |
|
| 583 | +FROM |
|
| 584 | + customer |
|
| 585 | +WHERE |
|
| 586 | + first_name LIKE 'Jen%'; |
|
| 587 | +``` |
|
| 588 | + |
|
| 589 | + |
|
| 590 | + |
|
| 591 | +请注意,其中`WHERE`子句包含一个特殊表达式:`first_name`、`LIKE`运算符和包含百分号 (`%`) 的字符串。字符串`'Jen%'`称为模式。 |
|
| 592 | + |
|
| 593 | +该查询返回`first_name`列值以`Jen`开头且后跟任意字符序列的行。这种技术称为模式匹配。 |
|
| 594 | + |
|
| 595 | +您可以通过将文字值与通配符组合来构造模式,并使用`LIKE`或`NOT LIKE`运算符来查找匹配项。PostgreSQL 为您提供了两个通配符: |
|
| 596 | + |
|
| 597 | +- 百分号 (`%`) 匹配任何零个或多个字符的序列。 |
|
| 598 | +- 下划线符号 (`_`) 匹配任何单个字符。 |
|
| 599 | + |
|
| 600 | +PostgreSQL `LIKE`运算符的语法如下: |
|
| 601 | + |
|
| 602 | +```sql |
|
| 603 | +value LIKE pattern |
|
| 604 | +``` |
|
| 605 | + |
|
| 606 | +如果`value`与`pattern`匹配,则表达式返回 true。 |
|
| 607 | + |
|
| 608 | +要否定`LIKE`运算符,请按如下方式使用`NOT`运算符: |
|
| 609 | + |
|
| 610 | +```sql |
|
| 611 | +value NOT LIKE pattern |
|
| 612 | +``` |
|
| 613 | + |
|
| 614 | +当`value`与`pattern`不匹配时,`NOT LIKE`运算符返回 true。 |
|
| 615 | + |
|
| 616 | +如果模式不包含任何通配符,则`LIKE`运算符的行为类似于等于 (`=`) 运算符。 |
|
| 617 | + |
|
| 618 | + |
|
| 619 | + |
|
| 620 | +## PostgreSQL 对 LIKE 运算符的扩展 |
|
| 621 | + |
|
| 622 | +PostgreSQL 支持类似于`LIKE`运算符的`ILIKE`运算符。此外,`ILIKE`运算符匹配值时不区分大小写。例如: |
|
| 623 | + |
|
| 624 | +```sql |
|
| 625 | +SELECT |
|
| 626 | + first_name, |
|
| 627 | + last_name |
|
| 628 | +FROM |
|
| 629 | + customer |
|
| 630 | +WHERE |
|
| 631 | + first_name ILIKE 'BAR%'; |
|
| 632 | +``` |
|
| 633 | + |
|
| 634 | + |
|
| 635 | + |
|
| 636 | +模式`BAR%`匹配以`BAR`、`Bar`、`BaR`等开头的任何字符串。如果您改用`LIKE`运算符,查询将不会返回任何行。 |
|
| 637 | + |
|
| 638 | +PostgreSQL 还提供了一些类似于`LIKE`, `NOT LIKE`, `ILIKE`和`NOT ILIKE`的运算符,如下所示: |
|
| 639 | + |
|
| 640 | +| 运算符 | 等价于 | |
|
| 641 | +| :----- | :-------- | |
|
| 642 | +| ~~ | LIKE | |
|
| 643 | +| ~~* | ILIKE | |
|
| 644 | +| !~~ | NOT LIKE | |
|
| 645 | +| !~~* | NOT ILIKE | |
|
| 646 | + |
|
| 647 | +在本教程中,您学习了如何使用 PostgreSQL 的`LIKE`和`ILIKE`运算符,通过模式匹配来查询数据。 |
|
| 648 | + |
|
| 649 | + |
|
| 650 | + |
|
| 651 | + |
|
| 652 | + |
|
| 653 | +## NULL 和 IS NULL 运算符简介 |
|
| 654 | + |
|
| 655 | +在数据库世界中,NULL 意味着缺少信息或不适用。NULL 不是一个值,因此,您不能将它与任何其他值(例如数字或字符串)进行比较。NULL 与值的比较将始终得到 NULL,这意味着结果未知。 |
|
| 656 | + |
|
| 657 | +此外,NULL 不等于 NULL,因此以下表达式返回 NULL: |
|
| 658 | + |
|
| 659 | +```sql |
|
| 660 | +NULL = NULL |
|
| 661 | +``` |
|
| 662 | + |
|
| 663 | +假设您有一个`contacts`表存储联系人的名字、姓氏、电子邮件和电话号码。在记录联系人时,您可能不知道联系人的电话号码。 |
|
| 664 | + |
|
| 665 | +为了解决这个问题,您可以将`phone`列定义为可为空列,并在保存联系人信息时将 NULL [插入](https://www.rockdata.net/zh-cn/tutorial/dml-insert/)到`phone`列中。 |
|
| 666 | + |
|
| 667 | +```sql |
|
| 668 | +CREATE TABLE contacts( |
|
| 669 | + id INT GENERATED BY DEFAULT AS IDENTITY, |
|
| 670 | + first_name VARCHAR(50) NOT NULL, |
|
| 671 | + last_name VARCHAR(50) NOT NULL, |
|
| 672 | + email VARCHAR(255) NOT NULL, |
|
| 673 | + phone VARCHAR(15), |
|
| 674 | + PRIMARY KEY (id) |
|
| 675 | +); |
|
| 676 | +``` |
|
| 677 | + |
|
| 678 | +因此,要获取电话列中没有存储任何电话号码的联系人,请使用以下语句: |
|
| 679 | + |
|
| 680 | +```sql |
|
| 681 | +SELECT |
|
| 682 | + id, |
|
| 683 | + first_name, |
|
| 684 | + last_name, |
|
| 685 | + email, |
|
| 686 | + phone |
|
| 687 | +FROM |
|
| 688 | + contacts |
|
| 689 | +WHERE |
|
| 690 | + phone IS NULL; |
|
| 691 | +``` |
|
| 692 | + |
|
| 693 | +## IS NOT NULL 运算符 |
|
| 694 | + |
|
| 695 | +要检查值是否不为 NULL,请使用`IS NOT NULL`运算符: |
|
| 696 | + |
|
| 697 | +```sql |
|
| 698 | +value IS NOT NULL |
|
| 699 | +``` |
|
| 700 | + |
|
| 701 | +如果值不为 NULL,则表达式返回 true;如果值为 NULL,则表达式返回 false。 |
|
| 702 | + |
|
| 703 | +例如,要查找有电话号码的联系人,您可以使用以下语句: |
|
| 704 | + |
|
| 705 | +```sql |
|
| 706 | +SELECT |
|
| 707 | + id, |
|
| 708 | + first_name, |
|
| 709 | + last_name, |
|
| 710 | + email, |
|
| 711 | + phone |
|
| 712 | +FROM |
|
| 713 | + contacts |
|
| 714 | +WHERE |
|
| 715 | + phone IS NOT NULL; |
|
| 716 | +``` |
|
| 717 | + |
|
| 718 | + |
|
| 719 | + |
|
| 720 | + |
|
| 721 | + |
|
| 722 | +### 3) 使用 DELETE 从表中删除多行 |
|
| 723 | + |
|
| 724 | +以下语句从`links`表中删除两行并返回已删除行的`id`列中的值: |
|
| 725 | + |
|
| 726 | +```sql |
|
| 727 | +DELETE FROM links |
|
| 728 | +WHERE id IN (6,5) |
|
| 729 | +RETURNING *; |
|
| 730 | +``` |
|
| 731 | + |
|
| 732 | +输出: |
|
| 733 | + |
|
| 734 | +### 4) 使用 DELETE 删除表中的所有行 |
|
| 735 | + |
|
| 736 | +以下语句使用不带`WHERE`子句的`DELETE`语句删除`links`表中的所有行: |
|
| 737 | + |
|
| 738 | +```sql |
|
| 739 | +DELETE FROM links; |
|
| 740 | +``` |
|
| 741 | + |
|
| 742 | +现在`links`表是空的。 |
|
| 743 | + |
|
| 744 | + |
|
| 745 | + |
|
| 746 | + |
|
| 747 | + |
|
| 748 | +## PostgreSQL upsert 简介 |
|
| 749 | + |
|
| 750 | +在关系数据库中,术语 upsert 称为合并。这个想法是,当您[向表中插入新行](https://www.rockdata.net/zh-cn/tutorial/dml-insert/)时,如果该行已存在,PostgreSQL 将[更新](https://www.rockdata.net/zh-cn/tutorial/dml-update/)该行,否则,它将插入新行。这就是为什么我们称该操作为 upsert(更新或插入的组合)。 |
|
| 751 | + |
|
| 752 | +要在 PostgreSQL 中使用 upsert 功能,请使用`INSERT ON CONFLICT`语句,如下: |
|
| 753 | + |
|
| 754 | +```postgresql |
|
| 755 | +INSERT INTO table_name(column_list) |
|
| 756 | +VALUES(value_list) |
|
| 757 | +ON CONFLICT target action; |
|
| 758 | +``` |
|
| 759 | + |
|
| 760 | +PostgreSQL 在`INSERT`语句中添加了`ON CONFLICT target action`子句以支持 upsert 功能。 |
|
| 761 | + |
|
| 762 | +在此语句中,`target`可以是以下之一: |
|
| 763 | + |
|
| 764 | +- `(column_name)` – 列名称。 |
|
| 765 | +- `ON CONSTRAINT constraint_name` – 其中约束名称可以是 [UNIQUE 约束](https://www.rockdata.net/zh-cn/tutorial/constraint-unique/)的名称。 |
|
| 766 | +- `WHERE predicate` –带有谓词的[WHERE 子句](https://www.rockdata.net/zh-cn/tutorial/dml-where/)。 |
|
| 767 | + |
|
| 768 | +`action`可以是以下之一: |
|
| 769 | + |
|
| 770 | +- `DO NOTHING` – 表示如果该行已存在于表中,则不执行任何操作。 |
|
| 771 | +- `DO UPDATE SET column_1 = value_1, .. WHERE condition` – 更新表中的一些字段。 |
|
| 772 | + |
|
| 773 | +> 请注意,`ON CONFLICT`子句仅在 PostgreSQL 9.5 和以上版本可用。如果您使用的是早期版本,则需要一种解决方法才能拥有更新插入功能。 |
|
| 774 | + |
|
| 775 | +如果你也在使用 MySQL,你会发现 upsert 功能与 MySQL 中的`insert on duplicate key update`语句类似。 |
|
| 776 | + |
|
| 777 | + |
|
| 778 | + |
|
| 779 | +以下语句与上面的语句等效,但它使用`name`列而不是唯一约束名称作为`INSERT`语句的目标。 |
|
| 780 | + |
|
| 781 | +```postgresql |
|
| 782 | +INSERT INTO customers (name, email) |
|
| 783 | +VALUES('Microsoft','hotline@microsoft.com') |
|
| 784 | +ON CONFLICT (name) |
|
| 785 | +DO NOTHING; |
|
| 786 | +``` |
|
| 787 | + |
|
| 788 | +假设,您想在插入已存在的客户时将新电子邮件与旧电子邮件连接起来,在这种情况下,您使用`UPDATE`子句作为`INSERT`语句的操作,如下所示: |
|
| 789 | + |
|
| 790 | +```postgresql |
|
| 791 | +INSERT INTO customers (name, email) |
|
| 792 | +VALUES('Microsoft','hotline@microsoft.com') |
|
| 793 | +ON CONFLICT (name) |
|
| 794 | +DO |
|
| 795 | + UPDATE SET email = EXCLUDED.email || ';' || customers.email; |
|
| 796 | +``` |
|
| 797 | + |
|
| 798 | + |
|
| 799 | + |
|
| 800 | + |
|
| 801 | + |
|
| 802 | + |
|
| 803 | + |
|
| 804 | +## 子事务简介 |
|
| 805 | + |
|
| 806 | +在专业的应用程序中,很难在不遇到任何错误的情况下编写相当长的事务。为了解决这个问题,用户可以使用一种叫做 SAVEPOINT 的东西。顾名思义,保存点是事务中的一个安全位置,如果出现严重错误,应用程序可以返回到该位置。 |
|
| 807 | + |
|
| 808 | +保存点是一个数据库特性,它允许您在事务中创建命名点,以后可以回滚到该命名点,同时保持事务的其余部分不变。当您想要处理事务中的错误或异常,并有选择地回滚到事务中的特定点,而不必撤消到目前为止所做的所有更改时,保存点非常有用。 |
|
| 809 | + |
|
| 810 | +## 怎么工作的 |
|
| 811 | + |
|
| 812 | +以下是在 PostgreSQL 中使用保存点的方法: |
|
| 813 | + |
|
| 814 | +使用`BEGIN`语句启动一个事务: |
|
| 815 | + |
|
| 816 | +```postgresql |
|
| 817 | +BEGIN; |
|
| 818 | +``` |
|
| 819 | + |
|
| 820 | +这将开始一个新的事务。 |
|
| 821 | + |
|
| 822 | +在事务中,您可以使用`SAVEPOINT`语句创建一个保存点,并指定一个名称: |
|
| 823 | + |
|
| 824 | +```postgresql |
|
| 825 | +SAVEPOINT my_savepoint; |
|
| 826 | +``` |
|
| 827 | + |
|
| 828 | +在此示例中,在事务中创建了一个名为`my_savepoint`的保存点。 |
|
| 829 | + |
|
| 830 | +在事务中执行一个或多个 SQL 操作,例如`INSERT`、`UPDATE`、`DELETE`等。 |
|
| 831 | + |
|
| 832 | +在任何时候,如果需要回滚到保存点,可以使用`ROLLBACK TO`语句: |
|
| 833 | + |
|
| 834 | +```postgresql |
|
| 835 | +ROLLBACK TO my_savepoint; |
|
| 836 | +``` |
|
| 837 | + |
|
| 838 | +这将撤消在创建`my_savepoint`保存点后所做的所有更改,从而有效地将事务还原到该点。 |
|
| 839 | + |
|
| 840 | +您还可以使用`RELEASE`语句释放保存点: |
|
| 841 | + |
|
| 842 | +```postgresql |
|
| 843 | +RELEASE my_savepoint; |
|
| 844 | +``` |
|
| 845 | + |
|
| 846 | +这将删除保存点,并允许事务从当前位置继续。 |
|
| 847 | + |
|
| 848 | +最后,当您准备好提交事务中所做的所有更改时,可以使用`COMMIT`语句: |
|
| 849 | + |
|
| 850 | +```postgresql |
|
| 851 | +COMMIT; |
|
| 852 | +``` |
|
| 853 | + |
|
| 854 | +这会将事务中所做的所有更改保存到数据库中。 |
|
| 855 | + |
|
| 856 | +## 示例 |
|
| 857 | + |
|
| 858 | +下面是一个完整的示例: |
|
| 859 | + |
|
| 860 | +```postgresql |
|
| 861 | +CREATE TABLE test0 AS SELECT 1 AS i; |
|
| 862 | +-- Start a main transaction |
|
| 863 | +BEGIN; |
|
| 864 | +-- Perform some operations within the transaction |
|
| 865 | +UPDATE test0 SET i = i + 1; |
|
| 866 | +-- Start a subtransaction |
|
| 867 | +SAVEPOINT s1; |
|
| 868 | +-- Continue with more operations |
|
| 869 | +UPDATE test0 SET i = i - 1000; |
|
| 870 | +-- Check the content in the table |
|
| 871 | +SELECT * FROM test0; |
|
| 872 | + i |
|
| 873 | +------ |
|
| 874 | + -998 |
|
| 875 | +(1 row) |
|
| 876 | +-- Something went wrong, let's roll back to the savepoint |
|
| 877 | +ROLLBACK TO SAVEPOINT s1; |
|
| 878 | +-- Continue with other operations |
|
| 879 | +UPDATE test0 SET i = i + 1; |
|
| 880 | +-- Finally, when everything is fine, commit the transaction |
|
| 881 | +COMMIT; |
|
| 882 | +-- Check the content in the table again |
|
| 883 | +SELECT * FROM test0; |
|
| 884 | + i |
|
| 885 | +--- |
|
| 886 | + 3 |
|
| 887 | +(1 row) |
|
| 888 | +``` |
|
| 889 | + |
|
| 890 | + |
|
| 891 | + |
|
| 892 | + |
|
| 893 | + |
|
| 894 | +## 13.3. 显式锁定 |
|
| 895 | + |
|
| 896 | +- [13.3.1. 表级锁](https://www.rockdata.net/zh-cn/docs/14/explicit-locking.html#LOCKING-TABLES) |
|
| 897 | +- [13.3.2. 行级锁](https://www.rockdata.net/zh-cn/docs/14/explicit-locking.html#LOCKING-ROWS) |
|
| 898 | +- [13.3.3. 页级锁](https://www.rockdata.net/zh-cn/docs/14/explicit-locking.html#LOCKING-PAGES) |
|
| 899 | +- [13.3.4. 死锁](https://www.rockdata.net/zh-cn/docs/14/explicit-locking.html#LOCKING-DEADLOCKS) |
|
| 900 | +- [13.3.5. 咨询锁](https://www.rockdata.net/zh-cn/docs/14/explicit-locking.html#ADVISORY-LOCKS) |
|
| 901 | + |
|
| 902 | + |
|
| 903 | + |
|
| 904 | +PostgreSQL提供了多种锁模式用于控制对表中数据的并发访问。 这些模式可以用于在MVCC无法给出期望行为的情境中由应用控制的锁。 同样,大多数PostgreSQL命令会自动要求恰当的锁以保证被引用的表在命令的执行过程中 不会以一种不兼容的方式删除或修改(例如,`TRUNCATE`无法安全地与同一表中上的其他操作并发地执行,因此它在表上获得一个`ACCESS EXCLUSIVE` 锁来强制这种行为)。 |
|
| 905 | + |
|
| 906 | +要检查在一个数据库服务器中当前未解除的锁列表,可以使用[`pg_locks`](https://www.rockdata.net/zh-cn/docs/14/view-pg-locks.html)系统视图。 有关监控锁管理器子系统状态的更多信息,请参考[第 28 章](https://www.rockdata.net/zh-cn/docs/14/monitoring.html)。 |
|
| 907 | + |
|
| 908 | +### 13.3.1. 表级锁 |
|
| 909 | + |
|
| 910 | + |
|
| 911 | + |
|
| 912 | +下面的列表显示了可用的锁模式和PostgreSQL自动使用它们的场合。 你也可以用[LOCK](https://www.rockdata.net/zh-cn/docs/14/sql-lock.html)命令显式获得这些锁。请记住所有这些锁模式都是表级锁,即使它们的名字包含“row”单词(这些名称是历史遗产)。 在一定程度上,这些名字反应了每种锁模式的典型用法 — 但是语意却都是一样的。 两种锁模式之间真正的区别是它们有着不同的冲突锁模式集合(参考[表 13.2](https://www.rockdata.net/zh-cn/docs/14/explicit-locking.html#TABLE-LOCK-COMPATIBILITY))。 两个事务在同一时刻不能在同一个表上持有属于相互冲突模式的锁(但是,一个事务决不会和自身冲突。例如,它可以在同一个表上获得`ACCESS EXCLUSIVE`锁然后接着获取`ACCESS SHARE`锁)。非冲突锁模式可以由许多事务同时持有。 请特别注意有些锁模式是自冲突的(例如,在一个时刻`ACCESS EXCLUSIVE`锁不能被多于一个事务持有)而其他锁模式不是自冲突的(例如,`ACCESS SHARE`锁可以被多个事务持有)。 |
|
| 913 | + |
|
| 914 | +**表级锁模式** |
|
| 915 | + |
|
| 916 | +- `ACCESS SHARE` |
|
| 917 | + |
|
| 918 | + 只与`ACCESS EXCLUSIVE`锁模式冲突。`SELECT`命令在被引用的表上获得一个这种模式的锁。通常,任何只***读取\***表而不修改它的查询都将获得这种锁模式。 |
|
| 919 | + |
|
| 920 | +- `ROW SHARE` |
|
| 921 | + |
|
| 922 | + 与`EXCLUSIVE`和`ACCESS EXCLUSIVE`锁模式冲突。`SELECT FOR UPDATE`和`SELECT FOR SHARE`命令在目标表上取得一个这种模式的锁 (加上在被引用但没有选择`FOR UPDATE/FOR SHARE`的任何其他表上的`ACCESS SHARE`锁)。 |
|
| 923 | + |
|
| 924 | +- `ROW EXCLUSIVE` |
|
| 925 | + |
|
| 926 | + 与`SHARE`、`SHARE ROW EXCLUSIVE`、`EXCLUSIVE`和`ACCESS EXCLUSIVE`锁模式冲突。命令`UPDATE`、`DELETE`和`INSERT`在目标表上取得这种锁模式(加上在任何其他被引用表上的`ACCESS SHARE`锁)。通常,这种锁模式将被任何***修改表中数据\***的命令取得。 |
|
| 927 | + |
|
| 928 | +- `SHARE UPDATE EXCLUSIVE` |
|
| 929 | + |
|
| 930 | + 与`SHARE UPDATE EXCLUSIVE`、`SHARE`、`SHARE ROW EXCLUSIVE`、`EXCLUSIVE`和`ACCESS EXCLUSIVE`锁模式冲突。这种模式保护一个表不受并发模式改变和`VACUUM`运行的影响。由`VACUUM`(不带`FULL`)、`ANALYZE`、 `CREATE INDEX CONCURRENTLY`、`REINDEX CONCURRENTLY`、 `CREATE STATISTICS`以及某些[`ALTER INDEX`](https://www.rockdata.net/zh-cn/docs/14/sql-alterindex.html) 和 [`ALTER TABLE`](https://www.rockdata.net/zh-cn/docs/14/sql-altertable.html)的变体获得(详细内容请参考这些命令的文档)。 |
|
| 931 | + |
|
| 932 | +- `SHARE` |
|
| 933 | + |
|
| 934 | + 与`ROW EXCLUSIVE`、`SHARE UPDATE EXCLUSIVE`、`SHARE ROW EXCLUSIVE`、`EXCLUSIVE`和`ACCESS EXCLUSIVE`锁模式冲突。这种模式保护一个表不受并发数据改变的影响。由`CREATE INDEX`(不带`CONCURRENTLY`)取得。 |
|
| 935 | + |
|
| 936 | +- `SHARE ROW EXCLUSIVE` |
|
| 937 | + |
|
| 938 | + 与`ROW EXCLUSIVE`、`SHARE UPDATE EXCLUSIVE`、`SHARE`、`SHARE ROW EXCLUSIVE`、`EXCLUSIVE`和`ACCESS EXCLUSIVE`锁模式冲突。这种模式保护一个表不受并发数据修改所影响,并且是自排他的,这样在一个时刻只能有一个会话持有它。由`CREATE TRIGGER`和某些形式的 [`ALTER TABLE`](https://www.rockdata.net/zh-cn/docs/14/sql-altertable.html)所获得。 |
|
| 939 | + |
|
| 940 | +- `EXCLUSIVE` |
|
| 941 | + |
|
| 942 | + 与`ROW SHARE`、`ROW EXCLUSIVE`、`SHARE UPDATE EXCLUSIVE`、`SHARE`、`SHARE ROW EXCLUSIVE`、`EXCLUSIVE`和`ACCESS EXCLUSIVE`锁模式冲突。这种模式只允许并发的`ACCESS SHARE`锁,即只有来自于表的读操作可以与一个持有该锁模式的事务并行处理。由`REFRESH MATERIALIZED VIEW CONCURRENTLY`获得。 |
|
| 943 | + |
|
| 944 | +- `ACCESS EXCLUSIVE` |
|
| 945 | + |
|
| 946 | + 与所有模式的锁冲突(`ACCESS SHARE`、`ROW SHARE`、`ROW EXCLUSIVE`、`SHARE UPDATE EXCLUSIVE`、`SHARE`、`SHARE ROW EXCLUSIVE`、`EXCLUSIVE`和`ACCESS EXCLUSIVE`)。这种模式保证持有者是访问该表的唯一事务。由`ALTER TABLE`、`DROP TABLE`、`TRUNCATE`、`REINDEX`、`CLUSTER`、`VACUUM FULL`和`REFRESH MATERIALIZED VIEW`(不带`CONCURRENTLY`)命令获取。 很多形式的`ALTER INDEX`和`ALTER TABLE`也在这个层面上获得锁(见[ALTER TABLE](https://www.rockdata.net/zh-cn/docs/14/sql-altertable.html))。这也是未显式指定模式的`LOCK TABLE`命令的默认锁模式。 |
|
| 947 | + |
|
| 948 | +### 提示 |
|
| 949 | + |
|
| 950 | +只有一个`ACCESS EXCLUSIVE`锁阻塞一个`SELECT`(不带`FOR UPDATE/SHARE`)语句。 |
|
| 951 | + |
|
| 952 | +一旦被获取,一个锁通常将被持有直到事务结束。 但是如果在建立保存点之后才获得锁,那么在回滚到这个保存点的时候将立即释放该锁。 这与`ROLLBACK`取消保存点之后所有的影响的原则保持一致。 同样的原则也适用于在PL/pgSQL异常块中获得的锁:一个跳出块的错误将释放在块中获得的锁。 |
|
| 953 | + |
|
| 954 | +**表 13.2. 冲突的锁模式** |
|
| 955 | + |
|
| 956 | +| 请求的锁模式 | 已存在的锁模式 | | | | | | | | |
|
| 957 | +| -------------------- | -------------- | ----------- | -------------------- | ------- | ----------------- | ------- | -------------- | ---- | |
|
| 958 | +| `ACCESS SHARE` | `ROW SHARE` | `ROW EXCL.` | `SHARE UPDATE EXCL.` | `SHARE` | `SHARE ROW EXCL.` | `EXCL.` | `ACCESS EXCL.` | | |
|
| 959 | +| `ACCESS SHARE` | | | | | | | | X | |
|
| 960 | +| `ROW SHARE` | | | | | | | X | X | |
|
| 961 | +| `ROW EXCL.` | | | | | X | X | X | X | |
|
| 962 | +| `SHARE UPDATE EXCL.` | | | | X | X | X | X | X | |
|
| 963 | +| `SHARE` | | | X | X | | X | X | X | |
|
| 964 | +| `SHARE ROW EXCL.` | | | X | X | X | X | X | X | |
|
| 965 | +| `EXCL.` | | X | X | X | X | X | X | X | |
|
| 966 | +| `ACCESS EXCL.` | X | X | X | X | X | X | X | X | |
|
| 967 | + |
|
| 968 | +### 13.3.2. 行级锁 |
|
| 969 | + |
|
| 970 | +除了表级锁以外,还有行级锁,在下文列出了行级锁以及在哪些情境下PostgreSQL会自动使用它们。 行级锁的完整冲突表请见[表 13.3](https://www.rockdata.net/zh-cn/docs/14/explicit-locking.html#ROW-LOCK-COMPATIBILITY)。注意一个事务可能会在相同的行上保持冲突的锁,甚至是在不同的子事务中。 但是除此之外,两个事务永远不可能在相同的行上持有冲突的锁。行级锁不影响数据查询,它们只阻塞对同一行的***写入者和加锁者\***。 行级锁在事务结束时或保存点回滚的时候释放,就像表级锁一样。 |
|
| 971 | + |
|
| 972 | +**行级锁模式** |
|
| 973 | + |
|
| 974 | +- `FOR UPDATE` |
|
| 975 | + |
|
| 976 | + `FOR UPDATE`会导致由`SELECT`语句检索到的行被锁定,就好像它们要被更新。这可以阻止它们被其他事务锁定、修改或者删除,一直到当前事务结束。也就是说其他尝试`UPDATE`、`DELETE`、`SELECT FOR UPDATE`、`SELECT FOR NO KEY UPDATE`、`SELECT FOR SHARE`或者`SELECT FOR KEY SHARE`这些行的事务将被阻塞,直到当前事务结束。反过来,`SELECT FOR UPDATE`将等待已经在相同行上运行以上这些命令的并发事务,并且接着锁定并且返回被更新的行(或者没有行,因为行可能已被删除)。不过,在一个`REPEATABLE READ`或`SERIALIZABLE`事务中,如果一个要被锁定的行在事务开始后被更改,将会抛出一个错误。进一步的讨论请见[第 13.4 节](https://www.rockdata.net/zh-cn/docs/14/applevel-consistency.html)。任何在一行上的`DELETE`命令也会获得`FOR UPDATE`锁模式,以及修改某些列的值的`UPDATE`也会获得该锁模式。 当前`UPDATE`情况中被考虑的列集合是那些具有能用于外键的唯一索引的列(所以部分索引和表达式索引不被考虑),但是这种要求未来有可能会改变。 |
|
| 977 | + |
|
| 978 | +- `FOR NO KEY UPDATE` |
|
| 979 | + |
|
| 980 | + 行为与`FOR UPDATE`类似,不过获得的锁较弱:这种锁将不会阻塞尝试在相同行上获得锁的`SELECT FOR KEY SHARE`命令。任何不获取`FOR UPDATE`锁的`UPDATE`也会获得这种锁模式。 |
|
| 981 | + |
|
| 982 | +- `FOR SHARE` |
|
| 983 | + |
|
| 984 | + 行为与`FOR NO KEY UPDATE`类似,不过它在每个检索到的行上获得一个共享锁而不是排他锁。一个共享锁会阻塞其他事务在这些行上执行`UPDATE`、`DELETE`、`SELECT FOR UPDATE`或者`SELECT FOR NO KEY UPDATE`,但是它不会阻止它们执行`SELECT FOR SHARE`或者`SELECT FOR KEY SHARE`。 |
|
| 985 | + |
|
| 986 | +- `FOR KEY SHARE` |
|
| 987 | + |
|
| 988 | + 行为与`FOR SHARE`类似,不过锁较弱:`SELECT FOR UPDATE`会被阻塞,但是`SELECT FOR NO KEY UPDATE`不会被阻塞。一个键共享锁会阻塞其他事务执行修改键值的`DELETE`或者`UPDATE`,但不会阻塞其他`UPDATE`,也不会阻止`SELECT FOR NO KEY UPDATE`、`SELECT FOR SHARE`或者`SELECT FOR KEY SHARE`。 |
|
| 989 | + |
|
| 990 | +PostgreSQL不会在内存里保存任何关于已修改行的信息,因此对一次锁定的行数没有限制。 不过,锁住一行会导致一次磁盘写,例如, `SELECT FOR UPDATE`将修改选中的行以标记它们被锁住,并且因此会导致磁盘写入。 |
|
| 991 | + |
|
| 992 | +**表 13.3. 冲突的行级锁** |
|
| 993 | + |
|
| 994 | +| 要求的锁模式 | 当前的锁模式 | | | | |
|
| 995 | +| ----------------- | ------------ | ----------------- | ---------- | ---- | |
|
| 996 | +| FOR KEY SHARE | FOR SHARE | FOR NO KEY UPDATE | FOR UPDATE | | |
|
| 997 | +| FOR KEY SHARE | | | | X | |
|
| 998 | +| FOR SHARE | | | X | X | |
|
| 999 | +| FOR NO KEY UPDATE | | X | X | X | |
|
| 1000 | +| FOR UPDATE | X | X | X | X | |
|
| 1001 | + |
|
| 1002 | +### 13.3.3. 页级锁 |
|
| 1003 | + |
|
| 1004 | +除了表级别和行级别的锁以外,页面级别的共享/排他锁被用来控制对共享缓冲池中表页面的读/写。 这些锁在行被抓取或者更新后马上被释放。应用开发者通常不需要关心页级锁,我们在这里提到它们只是为了完整。 |
|
| 1005 | + |
|
| 1006 | +### 13.3.4. 死锁 |
|
| 1007 | + |
|
| 1008 | + |
|
| 1009 | + |
|
| 1010 | +显式锁定的使用可能会增加*死锁*的可能性,死锁是指两个(或多个)事务相互持有对方想要的锁。例如,如果事务 1 在表 A 上获得一个排他锁,同时试图获取一个在表 B 上的排他锁, 而事务 2 已经持有表 B 的排他锁,同时却正在请求表 A 上的一个排他锁,那么两个事务就都不能进行下去。PostgreSQL能够自动检测到死锁情况并且会通过中断其中一个事务从而允许其它事务完成来解决这个问题(具体哪个事务会被中断是很难预测的,而且也不应该依靠这样的预测)。 |
|
| 1011 | + |
|
| 1012 | +要注意死锁也可能会作为行级锁的结果而发生(并且因此,它们即使在没有使用显式锁定的情况下也会发生)。考虑如下情况,两个并发事务在修改一个表。第一个事务执行: |
|
| 1013 | + |
|
| 1014 | +``` |
|
| 1015 | +UPDATE accounts SET balance = balance + 100.00 WHERE acctnum = 11111; |
|
| 1016 | +``` |
|
| 1017 | + |
|
| 1018 | +这样就在指定帐号的行上获得了一个行级锁。然后,第二个事务执行: |
|
| 1019 | + |
|
| 1020 | +``` |
|
| 1021 | +UPDATE accounts SET balance = balance + 100.00 WHERE acctnum = 22222; |
|
| 1022 | +UPDATE accounts SET balance = balance - 100.00 WHERE acctnum = 11111; |
|
| 1023 | +``` |
|
| 1024 | + |
|
| 1025 | +第一个`UPDATE`语句成功地在指定行上获得了一个行级锁,因此它成功更新了该行。 但是第二个`UPDATE`语句发现它试图更新的行已经被锁住了,因此它等待持有该锁的事务结束。事务二现在就在等待事务一结束,然后再继续执行。现在,事务一执行: |
|
| 1026 | + |
|
| 1027 | +``` |
|
| 1028 | +UPDATE accounts SET balance = balance - 100.00 WHERE acctnum = 22222; |
|
| 1029 | +``` |
|
| 1030 | + |
|
| 1031 | +事务一试图在指定行上获得一个行级锁,但是它得不到:事务二已经持有了这样的锁。所以它要等待事务二完成。因此,事务一被事务二阻塞,而事务二也被事务一阻塞:一个死锁。 PostgreSQL将检测这样的情况并中断其中一个事务。 |
|
| 1032 | + |
|
| 1033 | +防止死锁的最好方法通常是保证所有使用一个数据库的应用都以一致的顺序在多个对象上获得锁。在上面的例子里,如果两个事务以同样的顺序更新那些行,那么就不会发生死锁。 我们也应该保证一个事务中在一个对象上获得的第一个锁是该对象需要的最严格的锁模式。如果我们无法提前验证这些,那么可以通过重试因死锁而中断的事务来即时处理死锁。 |
|
| 1034 | + |
|
| 1035 | +只要没有检测到死锁情况,寻求一个表级或行级锁的事务将无限等待冲突锁被释放。这意味着一个应用长时间保持事务开启不是什么好事(例如等待用户输入)。 |
|
| 1036 | + |
|
| 1037 | +### 13.3.5. 咨询锁 |
|
| 1038 | + |
|
| 1039 | + |
|
| 1040 | + |
|
| 1041 | +PostgreSQL提供了一种方法创建由应用定义其含义的锁。这种锁被称为*咨询锁*,因为系统并不强迫其使用 — 而是由应用来保证其正确的使用。咨询锁可用于 MVCC 模型不适用的锁定策略。例如,咨询锁的一种常用用法是模拟所谓“平面文件”数据管理系统典型的悲观锁策略。虽然一个存储在表中的标志可以被用于相同目的,但咨询锁更快、可以避免表膨胀并且会由服务器在会话结束时自动清理。 |
|
| 1042 | + |
|
| 1043 | +有两种方法在PostgreSQL中获取一个咨询锁:在会话级别或在事务级别。一旦在会话级别获得了咨询锁,它将被保持直到被显式释放或会话结束。不同于标准锁请求,会话级咨询锁请求不尊重事务语义:在一个后来被回滚的事务中得到的锁在回滚后仍然被保持,并且同样即使调用它的事务后来失败一个解锁也是有效的。一个锁在它所属的进程中可以被获取多次;对于每一个完成的锁请求必须有一个相应的解锁请求,直至锁被真正释放。在另一方面,事务级锁请求的行为更像普通锁请求:在事务结束时会自动释放它们,并且没有显式的解锁操作。这种行为通常比会话级别的行为更方便,因为它使用一个咨询锁的时间更短。对于同一咨询锁标识符的会话级别和事务级别的锁请求按照期望将彼此阻塞。如果一个会话已经持有了一个给定的咨询锁,由它发出的附加请求将总是成功,即使有其他会话在等待该锁;不管现有的锁和新请求是处在会话级别还是事务级别,这种说法都是真的。 |
|
| 1044 | + |
|
| 1045 | +和所有PostgreSQL中的锁一样,当前被任何会话所持有的咨询锁的完整列表可以在[`pg_locks`](https://www.rockdata.net/zh-cn/docs/14/view-pg-locks.html)系统视图中找到。 |
|
| 1046 | + |
|
| 1047 | +咨询锁和普通锁都被存储在一个共享内存池中,它的尺寸由[max_locks_per_transaction](https://www.rockdata.net/zh-cn/docs/14/runtime-config-locks.html#GUC-MAX-LOCKS-PER-TRANSACTION)和[max_connections](https://www.rockdata.net/zh-cn/docs/14/runtime-config-connection.html#GUC-MAX-CONNECTIONS)配置变量定义。 必须当心不要耗尽这些内存,否则服务器将不能再授予任何锁。这对服务器可以授予的咨询锁数量设置了一个上限,根据服务器的配置不同,这个限制通常是数万到数十万。 |
|
| 1048 | + |
|
| 1049 | +在使用咨询锁方法的特定情况下,特别是查询中涉及显式排序和`LIMIT`子句时,由于 SQL 表达式被计算的顺序,必须小心控制锁的获取。例如: |
|
| 1050 | + |
|
| 1051 | +``` |
|
| 1052 | +SELECT pg_advisory_lock(id) FROM foo WHERE id = 12345; -- ok |
|
| 1053 | +SELECT pg_advisory_lock(id) FROM foo WHERE id > 12345 LIMIT 100; -- danger! |
|
| 1054 | +SELECT pg_advisory_lock(q.id) FROM |
|
| 1055 | +( |
|
| 1056 | + SELECT id FROM foo WHERE id > 12345 LIMIT 100 |
|
| 1057 | +) q; -- ok |
|
| 1058 | +``` |
|
| 1059 | + |
|
| 1060 | +在上述查询中,第二种形式是危险的,因为不能保证在锁定函数被执行之前应用`LIMIT`。这可能导致获得某些应用不期望的锁,并因此在会话结束之前无法释放。 从应用的角度来看,这样的锁将被挂起,虽然它们仍然在`pg_locks`中可见。 |
|
| 1061 | + |
|
| 1062 | +提供的操作咨询锁函数在[第 9.27.10 节](https://www.rockdata.net/zh-cn/docs/14/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS)中描述。 |
|
| 1063 | + |
|
| 1064 | + |
|
| 1065 | + |
|
| 1066 | + |
|
| 1067 | + |
|
| 1068 | +## 14. 在 PostgreSQL 中,`UPDATE` 语句的 `SET` 子句中不能使用表别名来引用字段 |
|
| 1069 | + |
|
| 1070 | +在 PostgreSQL 中,`UPDATE` 语句的 `SET` 子句中不能使用表别名来引用字段。例如: |
|
| 1071 | + |
|
| 1072 | +sql |
|
| 1073 | + |
|
| 1074 | +复制 |
|
| 1075 | + |
|
| 1076 | +``` |
|
| 1077 | +UPDATE "tenant_view_ref_model" t |
|
| 1078 | +SET t."view_meta_id" = 'value' -- 错误:不能使用 t."view_meta_id" |
|
| 1079 | +WHERE t.id = '04ifm5h3y4a13'; |
|
| 1080 | +``` |
|
| 1081 | + |
|
| 1082 | +正确的写法应该是直接使用字段名,而不需要表别名: |
|
| 1083 | + |
|
| 1084 | +sql |
|
| 1085 | + |
|
| 1086 | +复制 |
|
| 1087 | + |
|
| 1088 | +``` |
|
| 1089 | +UPDATE "tenant_view_ref_model" |
|
| 1090 | +SET "view_meta_id" = 'value' -- 正确:直接使用字段名 |
|
| 1091 | +WHERE id = '04ifm5h3y4a13'; |
|
| 1092 | +``` |
|
| 1093 | + |
|
| 1094 | +和oracle不同,pgsql的update语法中关联表不使用join ,而是使用from,关联条件不用on,而是写在where中,和条件写在一起。 |
|
| 1095 | + |
|
| 1096 | +使用别名更新时,被更新表的字段不能用别名,如m.code = n.code是不行的。 |
|
| 1097 | + |
|
| 1098 | + |
|
| 1099 | + |
|
| 1100 | +# 15.PostgreSQL 自定义自动类型转换(CAST) |
|
| 1101 | + |
|
| 1102 | +PostgreSQL是一个强类型数据库,因此你输入的变量、常量是什么类型,是强绑定的,例如 |
|
| 1103 | + |
|
| 1104 | +在调用操作符时,需要通过操作符边上的数据类型,选择对应的操作符。 |
|
| 1105 | + |
|
| 1106 | +在调用函数时,需要根据输入的类型,选择对应的函数。 |
|
| 1107 | + |
|
| 1108 | +如果类型不匹配,就会报操作符不存在,或者函数不存在的错误。 |
|
| 1109 | + |
|
| 1110 | +``` |
|
| 1111 | +postgres=# select '1' + '1'; |
|
| 1112 | +ERROR: operator is not unique: unknown + unknown |
|
| 1113 | +LINE 1: select '1' + '1'; |
|
| 1114 | + ^ |
|
| 1115 | +HINT: Could not choose a best candidate operator. You might need to add explicit type casts. |
|
| 1116 | +``` |
|
| 1117 | + |
|
| 1118 | + |
|
| 1119 | + |
|
| 1120 | +那么使用起来是不是很不方便呢? |
|
| 1121 | + |
|
| 1122 | +PostgreSQL开放了类型转换的接口,同时也内置了很多的自动类型转换。来简化操作。 |
|
| 1123 | + |
|
| 1124 | +查看目前已有的类型转换: |
|
| 1125 | + |
|
| 1126 | +``` |
|
| 1127 | +postgres=# \dC+ |
|
| 1128 | + List of casts |
|
| 1129 | + Source type | Target type | Function | Implicit? | Description |
|
| 1130 | +-----------------------------+-----------------------------+--------------------+---------------+------------- |
|
| 1131 | + "char" | character | bpchar | in assignment | |
|
| 1132 | + "char" | character varying | text | in assignment | |
|
| 1133 | + "char" | integer | int4 | no | |
|
| 1134 | + "char" | text | text | yes | |
|
| 1135 | + abstime | date | date | in assignment | |
|
| 1136 | + abstime | integer | (binary coercible) | no | |
|
| 1137 | + abstime | time without time zone | time | in assignment | |
|
| 1138 | + |
|
| 1139 | + ................................ |
|
| 1140 | + |
|
| 1141 | + timestamp without time zone | timestamp with time zone | timestamptz | yes | |
|
| 1142 | + timestamp without time zone | timestamp without time zone | timestamp | yes | |
|
| 1143 | + xml | character | (binary coercible) | in assignment | |
|
| 1144 | + xml | character varying | (binary coercible) | in assignment | |
|
| 1145 | + xml | text | (binary coercible) | in assignment | |
|
| 1146 | +(246 rows) |
|
| 1147 | +``` |
|
| 1148 | + |
|
| 1149 | + |
|
| 1150 | + |
|
| 1151 | +注意Implicit列,实际上是pg_cast里面的context转换为可读的内容(e表示no, a表示assignment, 否则表示implicit)。 |
|
| 1152 | + |
|
| 1153 | +``` |
|
| 1154 | +SELECT pg_catalog.format_type(castsource, NULL) AS "Source type", |
|
| 1155 | + pg_catalog.format_type(casttarget, NULL) AS "Target type", |
|
| 1156 | + CASE WHEN castfunc = 0 THEN '(binary coercible)' |
|
| 1157 | + ELSE p.proname |
|
| 1158 | + END as "Function", |
|
| 1159 | + CASE WHEN c.castcontext = 'e' THEN 'no' |
|
| 1160 | + WHEN c.castcontext = 'a' THEN 'in assignment' |
|
| 1161 | + ELSE 'yes' |
|
| 1162 | + END as "Implicit?" |
|
| 1163 | +FROM pg_catalog.pg_cast c LEFT JOIN pg_catalog.pg_proc p |
|
| 1164 | + ON c.castfunc = p.oid |
|
| 1165 | + LEFT JOIN pg_catalog.pg_type ts |
|
| 1166 | + ON c.castsource = ts.oid |
|
| 1167 | + LEFT JOIN pg_catalog.pg_namespace ns |
|
| 1168 | + ON ns.oid = ts.typnamespace |
|
| 1169 | + LEFT JOIN pg_catalog.pg_type tt |
|
| 1170 | + ON c.casttarget = tt.oid |
|
| 1171 | + LEFT JOIN pg_catalog.pg_namespace nt |
|
| 1172 | + ON nt.oid = tt.typnamespace |
|
| 1173 | +WHERE (true AND pg_catalog.pg_type_is_visible(ts.oid) |
|
| 1174 | +) OR (true AND pg_catalog.pg_type_is_visible(tt.oid) |
|
| 1175 | +) |
|
| 1176 | +ORDER BY 1, 2; |
|
| 1177 | +``` |
|
| 1178 | + |
|
| 1179 | + |
|
| 1180 | + |
|
| 1181 | +如果你发现有些类型转换没有内置,怎么办呢?我们可以自定义转换。 |
|
| 1182 | + |
|
| 1183 | +当然你也可以使用这种语法,对类型进行强制转换: |
|
| 1184 | + |
|
| 1185 | +``` |
|
| 1186 | +CAST(x AS typename) |
|
| 1187 | + |
|
| 1188 | + or |
|
| 1189 | + |
|
| 1190 | +x::typename |
|
| 1191 | +``` |
|
| 1192 | + |
|
| 1193 | + |
|
| 1194 | + |
|
| 1195 | +## 如何自定义类型转换(CAST) |
|
| 1196 | + |
|
| 1197 | + |
|
| 1198 | + |
|
| 1199 | +自定义CAST的语法如下: |
|
| 1200 | + |
|
| 1201 | +``` |
|
| 1202 | +CREATE CAST (source_type AS target_type) |
|
| 1203 | + WITH FUNCTION function_name [ (argument_type [, ...]) ] |
|
| 1204 | + [ AS ASSIGNMENT | AS IMPLICIT ] |
|
| 1205 | + |
|
| 1206 | +CREATE CAST (source_type AS target_type) |
|
| 1207 | + WITHOUT FUNCTION |
|
| 1208 | + [ AS ASSIGNMENT | AS IMPLICIT ] |
|
| 1209 | + |
|
| 1210 | +CREATE CAST (source_type AS target_type) |
|
| 1211 | + WITH INOUT |
|
| 1212 | + [ AS ASSIGNMENT | AS IMPLICIT ] |
|
| 1213 | +``` |
|
| 1214 | + |
|
| 1215 | + |
|
| 1216 | + |
|
| 1217 | +解释: |
|
| 1218 | + |
|
| 1219 | +1、WITH FUNCTION,表示转换需要用到什么函数。 |
|
| 1220 | + |
|
| 1221 | +2、WITHOUT FUNCTION,表示被转换的两个类型,在数据库的存储中一致,即物理存储一致。例如text和varchar的物理存储一致。不需要转换函数。 |
|
| 1222 | + |
|
| 1223 | +``` |
|
| 1224 | +Two types can be binary coercible, |
|
| 1225 | +which means that the conversion can be performed “for free” without invoking any function. |
|
| 1226 | + |
|
| 1227 | +This requires that corresponding values use the same internal representation. |
|
| 1228 | + |
|
| 1229 | +For instance, the types text and varchar are binary coercible both ways. |
|
| 1230 | + |
|
| 1231 | +Binary coercibility is not necessarily a symmetric relationship. |
|
| 1232 | + |
|
| 1233 | +For example, the cast from xml to text can be performed for free in the present implementation, |
|
| 1234 | +but the reverse direction requires a function that performs at least a syntax check. |
|
| 1235 | + |
|
| 1236 | +(Two types that are binary coercible both ways are also referred to as binary compatible.) |
|
| 1237 | +``` |
|
| 1238 | + |
|
| 1239 | + |
|
| 1240 | + |
|
| 1241 | +3、WITH INOUT,表示使用内置的IO函数进行转换。每一种类型,都有INPUT 和OUTPUT函数。使用这种方法,好处是不需要重新写转换函数。 |
|
| 1242 | + |
|
| 1243 | +除非有特殊需求,我们建议直接使用IO函数来进行转换。 |
|
| 1244 | + |
|
| 1245 | +``` |
|
| 1246 | + List of functions |
|
| 1247 | + Schema | Name | Result data type | Argument data types | Type |
|
| 1248 | +------------+-----------------+------------------+---------------------+-------- |
|
| 1249 | + pg_catalog | textin | text | cstring | normal |
|
| 1250 | + pg_catalog | textout | cstring | text | normal |
|
| 1251 | + pg_catalog | date_in | date | cstring | normal |
|
| 1252 | + pg_catalog | date_out | cstring | date | normal |
|
| 1253 | +``` |
|
| 1254 | + |
|
| 1255 | + |
|
| 1256 | + |
|
| 1257 | +``` |
|
| 1258 | +You can define a cast as an I/O conversion cast by using the WITH INOUT syntax. |
|
| 1259 | + |
|
| 1260 | +An I/O conversion cast is performed by invoking the output function of the source data type, |
|
| 1261 | +and passing the resulting string to the input function of the target data type. |
|
| 1262 | + |
|
| 1263 | +In many common cases, this feature avoids the need to write a separate cast function for conversion. |
|
| 1264 | + |
|
| 1265 | +An I/O conversion cast acts the same as a regular function-based cast; only the implementation is different. |
|
| 1266 | +``` |
|
| 1267 | + |
|
| 1268 | + |
|
| 1269 | + |
|
| 1270 | +4、AS ASSIGNMENT,表示在赋值时,自动对类型进行转换。例如字段类型为TEXT,输入的类型为INT,那么可以创建一个 cast(int as text) as ASSIGNMENT。 |
|
| 1271 | + |
|
| 1272 | +``` |
|
| 1273 | +If the cast is marked AS ASSIGNMENT then it can be invoked implicitly when assigning a value to a column of the target data type. |
|
| 1274 | + |
|
| 1275 | +For example, supposing that foo.f1 is a column of type text, then: |
|
| 1276 | + |
|
| 1277 | +INSERT INTO foo (f1) VALUES (42); |
|
| 1278 | + |
|
| 1279 | +will be allowed if the cast from type integer to type text is marked AS ASSIGNMENT, |
|
| 1280 | +otherwise not. |
|
| 1281 | + |
|
| 1282 | +(We generally use the term assignment cast to describe this kind of cast.) |
|
| 1283 | +``` |
|
| 1284 | + |
|
| 1285 | + |
|
| 1286 | + |
|
| 1287 | +5、AS IMPLICIT,表示在表达式中,或者在赋值操作中,都对类型进行自动转换。(包含了AS ASSIGNMENT,它只对赋值进行转换) |
|
| 1288 | + |
|
| 1289 | +``` |
|
| 1290 | +If the cast is marked AS IMPLICIT then it can be invoked implicitly in any context, |
|
| 1291 | +whether assignment or internally in an expression. |
|
| 1292 | + |
|
| 1293 | +(We generally use the term implicit cast to describe this kind of cast.) |
|
| 1294 | + |
|
| 1295 | +For example, consider this query: |
|
| 1296 | + |
|
| 1297 | +SELECT 2 + 4.0; |
|
| 1298 | + |
|
| 1299 | +The parser initially marks the constants as being of type integer and numeric respectively. |
|
| 1300 | + |
|
| 1301 | +There is no integer + numeric operator in the system catalogs, but there is a numeric + numeric operator. |
|
| 1302 | + |
|
| 1303 | +The query will therefore succeed if a cast from integer to numeric is available and is marked AS IMPLICIT — |
|
| 1304 | +which in fact it is. |
|
| 1305 | + |
|
| 1306 | +The parser will apply the implicit cast and resolve the query as if it had been written |
|
| 1307 | + |
|
| 1308 | +SELECT CAST ( 2 AS numeric ) + 4.0; |
|
| 1309 | +``` |
|
| 1310 | + |
|
| 1311 | + |
|
| 1312 | + |
|
| 1313 | +6、注意,AS IMPLICIT需要谨慎使用,为什么呢?因为操作符会涉及到多个算子,如果有多个转换,目前数据库并不知道应该选择哪个? |
|
| 1314 | + |
|
| 1315 | +``` |
|
| 1316 | +Now, the catalogs also provide a cast from numeric to integer. |
|
| 1317 | + |
|
| 1318 | +If that cast were marked AS IMPLICIT — (which it is not — ) |
|
| 1319 | + |
|
| 1320 | +then the parser would be faced with choosing between the above interpretation and |
|
| 1321 | +the alternative of casting the numeric constant to integer and applying the integer + integer operator. |
|
| 1322 | + |
|
| 1323 | +Lacking any knowledge of which choice to prefer, it would give up and declare the query ambiguous. |
|
| 1324 | + |
|
| 1325 | +The fact that only one of the two casts is implicit is the way in which we teach the parser to prefer resolution of |
|
| 1326 | +a mixed numeric-and-integer expression as numeric; |
|
| 1327 | + |
|
| 1328 | +there is no built-in knowledge about that. |
|
| 1329 | +``` |
|
| 1330 | + |
|
| 1331 | + |
|
| 1332 | + |
|
| 1333 | +因此,建议谨慎使用AS IMPLICIT。建议使用AS IMPLICIT的CAST应该是非失真转换转换,例如从INT转换为TEXT,或者int转换为numeric。 |
|
| 1334 | + |
|
| 1335 | +而失真转换,不建议使用as implicit,例如numeric转换为int。 |
|
| 1336 | + |
|
| 1337 | +``` |
|
| 1338 | +It is wise to be conservative about marking casts as implicit. |
|
| 1339 | + |
|
| 1340 | +An overabundance of implicit casting paths can cause PostgreSQL to choose surprising interpretations of commands, |
|
| 1341 | +or to be unable to resolve commands at all because there are multiple possible interpretations. |
|
| 1342 | + |
|
| 1343 | +A good rule of thumb is to make a cast implicitly invokable only for information-preserving |
|
| 1344 | +transformations between types in the same general type category. |
|
| 1345 | + |
|
| 1346 | +For example, the cast from int2 to int4 can reasonably be implicit, |
|
| 1347 | +but the cast from float8 to int4 should probably be assignment-only. |
|
| 1348 | + |
|
| 1349 | +Cross-type-category casts, such as text to int4, are best made explicit-only. |
|
| 1350 | +``` |
|
| 1351 | + |
|
| 1352 | + |
|
| 1353 | + |
|
| 1354 | +## 注意事项 + 例子 |
|
| 1355 | + |
|
| 1356 | + |
|
| 1357 | + |
|
| 1358 | +不能嵌套转换。例子 |
|
| 1359 | + |
|
| 1360 | +1、将text转换为date |
|
| 1361 | + |
|
| 1362 | +错误方法 |
|
| 1363 | + |
|
| 1364 | +``` |
|
| 1365 | +create or replace function text_to_date(text) returns date as $$ |
|
| 1366 | + select cast($1 as date); |
|
| 1367 | +$$ language sql strict; |
|
| 1368 | + |
|
| 1369 | +create cast (text as date) with function text_to_date(text) as implicit; |
|
| 1370 | +``` |
|
| 1371 | + |
|
| 1372 | + |
|
| 1373 | + |
|
| 1374 | +嵌套转换后出现死循环 |
|
| 1375 | + |
|
| 1376 | +``` |
|
| 1377 | +postgres=# select text '2017-01-01' + 1; |
|
| 1378 | +ERROR: stack depth limit exceeded |
|
| 1379 | +HINT: Increase the configuration parameter "max_stack_depth" (currently 2048kB), after ensuring the platform's stack depth limit is adequate. |
|
| 1380 | +CONTEXT: SQL function "text_to_date" during startup |
|
| 1381 | +SQL function "text_to_date" statement 1 |
|
| 1382 | +SQL function "text_to_date" statement 1 |
|
| 1383 | +SQL function "text_to_date" statement 1 |
|
| 1384 | +...... |
|
| 1385 | +``` |
|
| 1386 | + |
|
| 1387 | + |
|
| 1388 | + |
|
| 1389 | +正确方法 |
|
| 1390 | + |
|
| 1391 | +``` |
|
| 1392 | +create or replace function text_to_date(text) returns date as $$ |
|
| 1393 | + select to_date($1,'yyyy-mm-dd'); |
|
| 1394 | +$$ language sql strict; |
|
| 1395 | + |
|
| 1396 | +create cast (text as date) with function text_to_date(text) as implicit; |
|
| 1397 | +``` |
|
| 1398 | + |
|
| 1399 | + |
|
| 1400 | + |
|
| 1401 | +``` |
|
| 1402 | +postgres=# select text '2017-01-01' + 1; |
|
| 1403 | + ?column? |
|
| 1404 | +------------ |
|
| 1405 | + 2017-01-02 |
|
| 1406 | +(1 row) |
|
| 1407 | +``` |
|
| 1408 | + |
|
| 1409 | + |
|
| 1410 | + |
|
| 1411 | +我们还可以直接使用IO函数来转换: |
|
| 1412 | + |
|
| 1413 | +``` |
|
| 1414 | +postgres=# create cast (text as date) with inout as implicit; |
|
| 1415 | +CREATE CAST |
|
| 1416 | + |
|
| 1417 | +postgres=# select text '2017-01-01' + 1; |
|
| 1418 | + ?column? |
|
| 1419 | +------------ |
|
| 1420 | + 2017-01-02 |
|
| 1421 | +(1 row) |
|
| 1422 | +``` |
|
| 1423 | + |
|
| 1424 | + |
|
| 1425 | + |
|
| 1426 | +## 参考 |
|
| 1427 | + |
|
| 1428 | + |
|
| 1429 | + |
|
| 1430 | +https://www.postgresql.org/docs/10/static/sql-createcast.html |
|
| 1431 | + |
|
| 1432 | +[《PostgreSQL 整型int与布尔boolean的自动转换设置》](https://github.com/digoal/blog/blob/master/201801/20180131_01.md) |
|
| 1433 | + |
|
| 1434 | + |
|
| 1435 | + |
|
| 1436 | + |
|
| 1437 | + |
|
| 1438 | +# 16 不兼容项 |
|
| 1439 | + |
|
| 1440 | + |
|
| 1441 | + |
|
| 1442 | +```json |
|
| 1443 | +{ |
|
| 1444 | + "id": "guid", |
|
| 1445 | + "jsonrpc": "2.0", |
|
| 1446 | + "method": "service", |
|
| 1447 | + "params": { |
|
| 1448 | + "args": { |
|
| 1449 | + "ids": [ |
|
| 1450 | + "rbac_test_2" |
|
| 1451 | + ], |
|
| 1452 | + "values": { |
|
| 1453 | + "a": "3", |
|
| 1454 | + "lastName": "1", |
|
| 1455 | + "b": 21, |
|
| 1456 | + "c": 7.2, |
|
| 1457 | + "d": "2023-01-16", |
|
| 1458 | + "e": "abc", |
|
| 1459 | + "html": "1", |
|
| 1460 | + "testUser": "rbac_role_admin", |
|
| 1461 | + "d2": "2023-01-20 09:10:10", |
|
| 1462 | + "id": "rbac_test_2" |
|
| 1463 | + }, |
|
| 1464 | + "useDisplayForModel": true |
|
| 1465 | + }, |
|
| 1466 | + "context": { |
|
| 1467 | + "uid": "", |
|
| 1468 | + "timeZone": "UTC+8", |
|
| 1469 | + "lang": "zh-CN" |
|
| 1470 | + }, |
|
| 1471 | + "model": "TestTest1", |
|
| 1472 | + "tag": "master", |
|
| 1473 | + "service": "newSdkApp.TestTest1.TestTest_form.form:master#update", |
|
| 1474 | + "app": "newSdkApp" |
|
| 1475 | + } |
|
| 1476 | +} |
|
| 1477 | +``` |
|
| 1478 | + |
|
| 1479 | +aused by: org.postgresql.util.PSQLException: 错误: 当前事务被终止, 事务块结束之前的查询被忽略 |
|
| 1480 | + |
|
| 1481 | + at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2674) |
|
| 1482 | + |
|
| 1483 | + at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2364) |
|
| 1484 | + |
|
| 1485 | + at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:354) |
|
| 1486 | + |
|
| 1487 | + at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:484) |
|
| 1488 | + |
|
| 1489 | + at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:404) |
|
| 1490 | + |
|
| 1491 | + at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:162) |
|
| 1492 | + |
|
| 1493 | + at org.postgresql.jdbc.PgPreparedStatement.execute(PgPreparedStatement.java:151) |
|
| 1494 | + |
|
| 1495 | + at com.alibaba.druid.filter.FilterChainImpl.preparedStatement_execute(FilterChainImpl.java:3461) |
|
| 1496 | + |
|
| 1497 | + at com.alibaba.druid.filter.FilterEventAdapter.preparedStatement_execute(FilterEventAdapter.java:440) |
|
| 1498 | + |
|
| 1499 | + at com.alibaba.druid.filter.FilterChainImpl.preparedStatement_execute(FilterChainImpl.java:3459) |
|
| 1500 | + |
|
| 1501 | + at com.alibaba.druid.proxy.jdbc.PreparedStatementProxyImpl.execute(PreparedStatementProxyImpl.java:167) |
|
| 1502 | + |
|
| 1503 | + at com.alibaba.druid.pool.DruidPooledPreparedStatement.execute(DruidPooledPreparedStatement.java:497) |
|
| 1504 | + |
|
| 1505 | + at com.sie.snest.engine.db.relationdb.RelationDBAccessor.doExecute(RelationDBAccessor.java:614) |
|
| 1506 | + |
|
| 1507 | + ... 82 common frames omitted |
|
| 1508 | + |
|
| 1509 | +Caused by: org.postgresql.util.PSQLException: 错误: 字段 "age" 的类型为 integer, 但表达式的类型为 character varying |
|
| 1510 | + |
|
| 1511 | + 建议:你需要重写或转换表达式 |
|
| 1512 | + |
|
| 1513 | + 位置:245 |
|
| 1514 | + |
|
| 1515 | + |
|
| 1516 | + |
|
| 1517 | + |
|
| 1518 | + |
|
| 1519 | +## 16.1 boolean值问题 |
|
| 1520 | + |
|
| 1521 | +pg支持boolean,但是我们引擎是使用char(1)代替boolean |
|
| 1522 | + |
|
| 1523 | +## 16.2 索引问题,索引 |
|
| 1524 | + |
|
| 1525 | +2. 在 PostgreSQL 中,索引名称(Index Name)的最大长度受限于标识符(Identifier)的最大长度。根据 PostgreSQL 的官方文档,标识符的最大长度为 **63 字节**。 |
|
| 1526 | + |
|
| 1527 | + ------ |
|
| 1528 | + |
|
| 1529 | + ## 1. **标识符的最大长度** |
|
| 1530 | + |
|
| 1531 | + - PostgreSQL 中的标识符(如表名、列名、索引名等)最大长度为 **63 字节**。 |
|
| 1532 | + - 如果标识符的长度超过 63 字节,PostgreSQL 会自动截断到 63 字节,并发出警告。 |
|
| 1533 | + |
|
| 1534 | + |
|
| 1535 | + |
|
| 1536 | +在 PostgreSQL 中,索引名称(Index Name)的命名规则和限制与其他数据库对象(如表、列、约束等)类似。以下是 PostgreSQL 索引命名的详细限制和最佳实践: |
|
| 1537 | + |
|
| 1538 | +------ |
|
| 1539 | + |
|
| 1540 | +## 1. **索引命名的基本规则** |
|
| 1541 | + |
|
| 1542 | +- **长度限制**:索引名称的最大长度为 **63 字节**。如果名称超过 63 字节,PostgreSQL 会自动截断并发出警告。 |
|
| 1543 | +- **字符集**:索引名称可以包含字母、数字、下划线(`_`)和其他特殊字符(如 `$`),但通常建议使用字母、数字和下划线。 |
|
| 1544 | +- **大小写敏感性**:索引名称是大小写不敏感的,但 PostgreSQL 会将名称转换为小写存储。如果需要保留大小写,可以使用双引号(`"`)包裹名称。 |
|
| 1545 | +- **唯一性**:在同一个 schema 中,索引名称必须是唯一的。 |
|
| 1546 | + |
|
| 1547 | +------ |
|
| 1548 | + |
|
| 1549 | +## 2. **索引命名的限制** |
|
| 1550 | + |
|
| 1551 | +### (1) **长度限制** |
|
| 1552 | + |
|
| 1553 | +索引名称的最大长度为 63 字节。如果名称超过 63 字节,PostgreSQL 会截断名称并发出警告。 |
|
| 1554 | + |
|
| 1555 | +例如: |
|
| 1556 | + |
|
| 1557 | +sql |
|
| 1558 | + |
|
| 1559 | +复制 |
|
| 1560 | + |
|
| 1561 | +``` |
|
| 1562 | +CREATE INDEX this_is_a_very_long_index_name_that_exceeds_the_maximum_length_of_sixty_three_bytes ON my_table(my_column); |
|
| 1563 | +``` |
|
| 1564 | + |
|
| 1565 | +PostgreSQL 会将其截断为: |
|
| 1566 | + |
|
| 1567 | +复制 |
|
| 1568 | + |
|
| 1569 | +``` |
|
| 1570 | +this_is_a_very_long_index_name_that_exceeds_the_maximum_length_of_sixty_three |
|
| 1571 | +``` |
|
| 1572 | + |
|
| 1573 | +### (2) **大小写敏感性** |
|
| 1574 | + |
|
| 1575 | +默认情况下,索引名称会被转换为小写存储。如果需要保留大小写,可以使用双引号包裹名称。 |
|
| 1576 | + |
|
| 1577 | +例如: |
|
| 1578 | + |
|
| 1579 | +sql |
|
| 1580 | + |
|
| 1581 | +复制 |
|
| 1582 | + |
|
| 1583 | +``` |
|
| 1584 | +CREATE INDEX "MyIndex" ON my_table(my_column); |
|
| 1585 | +``` |
|
| 1586 | + |
|
| 1587 | +此时,索引名称会保留为 `MyIndex`。 |
|
| 1588 | + |
|
| 1589 | +### (3) **特殊字符** |
|
| 1590 | + |
|
| 1591 | +索引名称可以包含特殊字符(如 `$`、`#` 等),但通常不建议使用,因为可能会导致兼容性问题。 |
|
| 1592 | + |
|
| 1593 | +例如: |
|
| 1594 | + |
|
| 1595 | +sql |
|
| 1596 | + |
|
| 1597 | +复制 |
|
| 1598 | + |
|
| 1599 | +``` |
|
| 1600 | +CREATE INDEX "my_index$1" ON my_table(my_column); |
|
| 1601 | +``` |
|
| 1602 | + |
|
| 1603 | +### (4) **唯一性** |
|
| 1604 | + |
|
| 1605 | +在同一个 schema 中,索引名称必须是唯一的。如果尝试创建同名的索引,PostgreSQL 会抛出错误。 |
|
| 1606 | + |
|
| 1607 | +例如: |
|
| 1608 | + |
|
| 1609 | +sql |
|
| 1610 | + |
|
| 1611 | +复制 |
|
| 1612 | + |
|
| 1613 | +``` |
|
| 1614 | +CREATE INDEX my_index ON my_table(my_column); |
|
| 1615 | +CREATE INDEX my_index ON my_table(another_column); -- 错误:索引名称冲突 |
|
| 1616 | +``` |
|
| 1617 | + |
|
| 1618 | +------ |
|
| 1619 | + |
|
| 1620 | +## 3. **索引命名的最佳实践** |
|
| 1621 | + |
|
| 1622 | +### (1) **使用有意义的名称** |
|
| 1623 | + |
|
| 1624 | +索引名称应反映其用途,例如: |
|
| 1625 | + |
|
| 1626 | +- `idx_table_column`:表示在 `table` 表的 `column` 列上创建的索引。 |
|
| 1627 | +- `uk_table_column`:表示在 `table` 表的 `column` 列上创建的唯一约束索引。 |
|
| 1628 | + |
|
| 1629 | +### (2) **避免使用保留字** |
|
| 1630 | + |
|
| 1631 | +避免使用 PostgreSQL 的保留字(如 `SELECT`、`INSERT` 等)作为索引名称。 |
|
| 1632 | + |
|
| 1633 | +### (3) **使用一致的命名规则** |
|
| 1634 | + |
|
| 1635 | +为索引名称定义一致的命名规则,例如: |
|
| 1636 | + |
|
| 1637 | +- 前缀:使用 `idx_` 表示普通索引,`uk_` 表示唯一索引,`pk_` 表示主键索引。 |
|
| 1638 | +- 表名和列名:在索引名称中包含表名和列名。 |
|
| 1639 | + |
|
| 1640 | +例如: |
|
| 1641 | + |
|
| 1642 | +sql |
|
| 1643 | + |
|
| 1644 | +复制 |
|
| 1645 | + |
|
| 1646 | +``` |
|
| 1647 | +CREATE INDEX idx_users_email ON users(email); |
|
| 1648 | +CREATE UNIQUE INDEX uk_users_username ON users(username); |
|
| 1649 | +``` |
|
| 1650 | + |
|
| 1651 | +### (4) **避免过长的名称** |
|
| 1652 | + |
|
| 1653 | +尽量保持索引名称简洁,避免超过 63 字节的限制。 |
|
| 1654 | + |
|
| 1655 | +------ |
|
| 1656 | + |
|
| 1657 | +## 4. **修改索引名称** |
|
| 1658 | + |
|
| 1659 | +如果索引名称不符合要求,可以使用 `ALTER INDEX` 修改索引名称。 |
|
| 1660 | + |
|
| 1661 | +例如: |
|
| 1662 | + |
|
| 1663 | +sql |
|
| 1664 | + |
|
| 1665 | +复制 |
|
| 1666 | + |
|
| 1667 | +``` |
|
| 1668 | +ALTER INDEX old_index_name RENAME TO new_index_name; |
|
| 1669 | +``` |
|
| 1670 | + |
|
| 1671 | +------ |
|
| 1672 | + |
|
| 1673 | +## 5. **查看索引名称** |
|
| 1674 | + |
|
| 1675 | +可以通过以下 SQL 查询当前数据库中的所有索引及其名称: |
|
| 1676 | + |
|
| 1677 | +sql |
|
| 1678 | + |
|
| 1679 | +复制 |
|
| 1680 | + |
|
| 1681 | +``` |
|
| 1682 | +SELECT schemaname, tablename, indexname |
|
| 1683 | +FROM pg_indexes |
|
| 1684 | +WHERE schemaname NOT LIKE 'pg_%' -- 排除系统 schema |
|
| 1685 | +ORDER BY schemaname, tablename, indexname; |
|
| 1686 | +``` |
|
| 1687 | + |
|
| 1688 | +------ |
|
| 1689 | + |
|
| 1690 | +## 6. **总结** |
|
| 1691 | + |
|
| 1692 | +- PostgreSQL 索引名称的最大长度为 **63 字节**。 |
|
| 1693 | +- 索引名称在同一个 schema 中必须是唯一的。 |
|
| 1694 | +- 默认情况下,索引名称会被转换为小写存储,可以使用双引号保留大小写。 |
|
| 1695 | +- 建议使用有意义的命名规则,并避免使用特殊字符和保留字。 |
|
| 1696 | + |
|
| 1697 | +通过遵循这些规则和最佳实践,可以确保索引名称的可读性和一致性。 |
|
| 1698 | + |
|
| 1699 | + |
|
| 1700 | + |
|
| 1701 | +## 16.3. 事物问题 |
|
| 1702 | + |
|
| 1703 | +- PostgreSQL支持Transactional DDL |
|
| 1704 | + |
|
| 1705 | +- Transactional DDL |
|
| 1706 | + |
|
| 1707 | + PostgreSQL supports transactional DDL ([Data Definition Language](https://en.wikipedia.org/wiki/Data_definition_language)) operations, such as `CREATE`, `ALTER`, and `DROP` statements. This means that schema-altering operations can be included within a transaction block and rolled back if required. In MySQL, DDL statements [are typically not transactional](https://dev.mysql.com/doc/refman/8.0/en/cannot-roll-back.html). Thus, when an error occurs during a schema-altering operation, it cannot be rolled back. |
|
| 1708 | + |
|
| 1709 | + https://wiki.postgresql.org/wiki/Transactional_DDL_in_PostgreSQL:_A_Competitive_Analysis |
|
| 1710 | + |
|
| 1711 | + |
|
| 1712 | + |
|
| 1713 | + # PostgreSQL支持Transactional DDL |
|
| 1714 | + |
|
| 1715 | + [PostgreSQL运维技术](https://www.modb.pro/u/431686)2022-08-20 |
|
| 1716 | + |
|
| 1717 | + 1228 |
|
| 1718 | + |
|
| 1719 | + ## 什么是Transactional DDL? |
|
| 1720 | + |
|
| 1721 | + Transactional(事务)在关系型数据库是指一组SQL语句,要么提交,要么全部回滚。事务中包含的语句通常是DML语句,如INSERT、UPDATE、DELETE等。但是对于DDL语句呢?是否可以在事务中包含诸如CREATE、ALTER、DROP等DDL命令? |
|
| 1722 | + |
|
| 1723 | + 所谓Transactional DDL就是我们可以把ddl放到事务中,做到事务中的ddl语句要么全部提交,要么全部**回滚**。 |
|
| 1724 | + |
|
| 1725 | + ## PG是否支持Transactional DDL? |
|
| 1726 | + |
|
| 1727 | + 看个pg的例子 |
|
| 1728 | + |
|
| 1729 | + ``` |
|
| 1730 | + postgres=# begin; |
|
| 1731 | + BEGIN |
|
| 1732 | + postgres=*# create table a_test(id int); |
|
| 1733 | + CREATE TABLE |
|
| 1734 | + postgres=*# insert into a_test values(1); |
|
| 1735 | + INSERT 0 1 |
|
| 1736 | + postgres=*# rollback; |
|
| 1737 | + ROLLBACK |
|
| 1738 | + postgres=# select * from a_test; |
|
| 1739 | + ERROR: relation "a_test" does not exist |
|
| 1740 | + LINE 1: select * from a_test; |
|
| 1741 | + ^ |
|
| 1742 | + postgres=# |
|
| 1743 | + ``` |
|
| 1744 | + |
|
| 1745 | + 可见,在postgresql中,是支持transactional ddl的,在上例中,create table语句被回滚掉了。 |
|
| 1746 | + |
|
| 1747 | + 并不是所有数据库都支持Transactional ddl,比如mysql。 |
|
| 1748 | + |
|
| 1749 | + 看个mysql的例子: |
|
| 1750 | + |
|
| 1751 | + ``` |
|
| 1752 | + mysql> begin; |
|
| 1753 | + Query OK, 0 rows affected (0.00 sec) |
|
| 1754 | + |
|
| 1755 | + mysql> create table a_test (id int); |
|
| 1756 | + Query OK, 0 rows affected (0.04 sec) |
|
| 1757 | + |
|
| 1758 | + mysql> insert into a_test values(1); |
|
| 1759 | + Query OK, 1 row affected (0.01 sec) |
|
| 1760 | + |
|
| 1761 | + mysql> rollback; |
|
| 1762 | + Query OK, 0 rows affected (0.00 sec) |
|
| 1763 | + |
|
| 1764 | + mysql> select * from a_test; |
|
| 1765 | + +------+ |
|
| 1766 | + | id | |
|
| 1767 | + +------+ |
|
| 1768 | + | 1 | |
|
| 1769 | + +------+ |
|
| 1770 | + 1 row in set (0.00 sec) |
|
| 1771 | + ``` |
|
| 1772 | + |
|
| 1773 | + 可以看到mysql这个例子里,不仅create语句没有回滚掉,insert语句也没有回滚掉。这是因为:在mysql中,当执行ddl语句时,会隐式地将当前会话的事务进行一次commit操作。 所以我们应该**严格地将DDL和DML完全分开**,不能混合在一起执行。 |
|
| 1774 | + |
|
| 1775 | + ## 一些特例 |
|
| 1776 | + |
|
| 1777 | + 需要注意的是在pg中并不是所有的ddl都支持Transactional ddl。比如CREATE INDEX CONCURRENTLY、CREATE DATABAE 、CREATE TABLESPACE等等。 |
|
| 1778 | + |
|
| 1779 | + ``` |
|
| 1780 | + postgres=# begin |
|
| 1781 | + postgres-# ; |
|
| 1782 | + BEGIN |
|
| 1783 | + postgres=*# CREATE INDEX CONCURRENTLY idx_id ON a_test (id); |
|
| 1784 | + ERROR: CREATE INDEX CONCURRENTLY cannot run inside a transaction block |
|
| 1785 | + ``` |
|
| 1786 | + |
|
| 1787 | + ## Transactional DDL的好处 |
|
| 1788 | + |
|
| 1789 | + 在进行一些模式升级等复杂工作时,可以利用此功能保护数据库。我们可以将所有更改都放入事务块中,确保它们都以原子方式应用,或者根本不应用。这大大降低了数据库因模式更改中的输入错误或其他此类错误而损坏数据库的可能性。 |
|
| 1790 | + |
|
| 1791 | + ## 总结 |
|
| 1792 | + |
|
| 1793 | + transactional ddl是指可以把ddl放到事务中,做到事务中的ddl语句要么全部提交,要么全部回滚。 |
|
| 1794 | + |
|
| 1795 | + PG大部分ddl都支持Transactional ddl,除了一些CREATE INDEX CONCURRENTLY、CREATE DATABAE 、CREATE TABLESPACE等语句。 |
|
| 1796 | + |
|
| 1797 | + |
|
| 1798 | + |
|
| 1799 | + |
|
| 1800 | + |
|
| 1801 | + 参考: |
|
| 1802 | + |
|
| 1803 | + https://wiki.postgresql.org/wiki/Transactional_DDL_in_PostgreSQL:_A_Competitive_Analysis |
|
| 1804 | + |
|
| 1805 | + https://www.cybertec-postgresql.com/en/transactional-ddls/ |
|
| 1806 | + |
|
| 1807 | + |
|
| 1808 | + |
|
| 1809 | +## 16.4 数据类型转换 |
|
| 1810 | + |
|
| 1811 | +- PostgreSQL必须显示的进行数据类型转换 |
|
| 1812 | + |
|
| 1813 | +- 0 1 字符串,字符串不会显示的转换成boolean,不会显示的转换成数字 |
|
| 1814 | + |
|
| 1815 | +- caused by: org.postgresql.util.PSQLException: 错误: 字段 "age" 的类型为 integer, 但表达式的类型为 character varying |
|
| 1816 | + |
|
| 1817 | + 建议:你需要重写或转换表达式 |
|
| 1818 | + |
|
| 1819 | + 位置:245 |
|
| 1820 | + |
|
| 1821 | + |
|
| 1822 | + |
|
| 1823 | + |
|
| ... | ... | \ No newline at end of file |