第19节-非规范化数据类型-Composite-types

总结 :在本教程中,您将学习如何使用 PostgreSQL 复合类型并有效地作复合列。

PostgreSQL 复合类型简介

PostgreSQL 中,复合类型表示行或记录的结构。复合类型是字段名称及其数据类型的列表。

PostgreSQL 允许您以与简单类型相同的方式使用复合类型。

例如,可以将复合类型用于表列、函数参数和函数返回类型。

创建复合类型

要创建新的复合类型,请使用 CREATE TYPE 语句,如下所示:

sql 复制代码
CREATE TYPE type_name AS (
    field1 data_type,
    field2 data_type,
    ...
);

在此语法中:

  • 首先,在 CREATE TYPE 关键字后提供复合类型名称。
  • 其次,在 AS 关键字后面的括号内列出字段名称及其相应的数据类型。

例如,以下语句创建名为 coordinate 的复合类型,其中包括 latitude 度和 longitude

sql 复制代码
CREATE TYPE coordinate AS (
    latitude DEC,
    longitude DEC
);

使用复合类型

创建复合类型后,您可以像简单类型一样使用它。例如,您可以使用复合类型作为表列的类型。

以下语句创建一个名为 warehouse_locations 的表,该表使用 coordinate 复合类型:

sql 复制代码
CREATE TABLE warehouse_locations(
    warehouse_id INT PRIMARY KEY,
    location coordinate
);

构造复合值

要将复合值创建为文本常量,可以使用 ROW 关键字并将逗号分隔的字段值括在括号中,如下所示:

sql 复制代码
ROW(value1, value2, ...)

例如,您可以像这样构造 coordinate 复合类型的值:

sql 复制代码
ROW(37.318686, -121.871019)

以下语句将新行插入 warehouse_locations 表中:

sql 复制代码
INSERT INTO warehouse_locations(warehouse_id, location)
VALUES(1, ROW(37.318686, -121.871019))
RETURNING *;

输出:

sql 复制代码
 warehouse_id |        location
--------------+-------------------------
            1 | (37.318686,-121.871019)

可以使用点表示法显式指定要插入的复合列的字段:

sql 复制代码
composite_column.field_name

例如,以下语句将新行插入到 warehouse_locations 表中:

sql 复制代码
INSERT INTO
  warehouse_locations (
    warehouse_id,
    location.latitude,
    location.longitude
  )
VALUES
  (2, 37.650972, -122.398659)
RETURNING
  *;

输出:

sql 复制代码
 warehouse_id |        location
--------------+-------------------------
            2 | (37.650972,-122.398659)

访问复合类型的字段

您可以使用点表示法访问复合列的字段:

sql 复制代码
composite_column.field_name

有时,您需要将复合列放在括号中,如下所示:

sql 复制代码
(composite_column).field_name

要访问复合列的所有字段,您可以使用星号简写 (*)

sql 复制代码
(composite_column).*

例如,以下语句从 warehouse_locations 表中检索 latitude 度和 longitude 度以及 warehouse_id

sql 复制代码
SELECT
  warehouse_id,
  (location).latitude,
  (location).longitude
FROM
  warehouse_locations;

输出:

sql 复制代码
 warehouse_id | latitude  |  longitude
--------------+-----------+-------------
            1 | 37.318686 | -121.871019
            2 | 37.650972 | -122.398659

SELECT 语句中,如果不使用括号,PostgreSQL 可能会将 location 类型误解为表名并发出错误。

上述查询等效于以下使用简写 (*) 的查询:

sql 复制代码
SELECT
    warehouse_id,
    (location).*
FROM
    warehouse_locations;

输出:

sql 复制代码
 warehouse_id | latitude  |  longitude
--------------+-----------+-------------
            1 | 37.318686 | -121.871019
            2 | 37.650972 | -122.398659

更新复合值

以下语句更新 id1 的仓库位置的 latitudelongitude

sql 复制代码
UPDATE warehouse_locations
SET
  location.latitude = 37.650971,
  location.longitude = -122.398658
WHERE
  warehouse_id = 1
RETURNING *;

输出:

sql 复制代码
 warehouse_id |        location
--------------+-------------------------
            1 | (37.650971,-122.398658)

访问 UPDATE 语句的 SET 子句中的字段时,不要使用括号,因为点表示法足以标识复合类型中的各个字段。在这里使用括号会导致错误。

要一次更新 location 列的所有字段,您还可以使用以下语法:

sql 复制代码
UPDATE warehouse_locations
SET
  location = ROW (37.650971, -122.398658)
WHERE
  warehouse_id = 1
RETURNING *;

输出:

sql 复制代码
 warehouse_id |        location
--------------+-------------------------
            1 | (37.650971,-122.398658)

根据复合值删除行

使用 DELETE 语句删除基于复合类型字段的行时,确实需要在 WHERE 子句中使用括号。原因是 WHERE 子句用表名限定列。如果不使用括号,PostgreSQL 可能会将复合误解为表。

例如,以下语句从 warehouse_locations 表中删除位置纬 latitude 等于 37.650971 的行:

sql 复制代码
DELETE FROM warehouse_locations
WHERE
  (location).latitude = 37.650971
RETURNING *;

输出:

sql 复制代码
 warehouse_id |        location
--------------+-------------------------
            1 | (37.650971,-122.398658)

应用约束

复合类型是灵活的。但是,它们不直接支持单个字段的约束,例如 NOT NULLCHECK

例如,如果将 NOT NULL 约束应用于 warehouse_locations 表:

sql 复制代码
ALTER TABLE warehouse_locations
ALTER COLUMN location
SET NOT NULL;

NOT NULL 约束确保整个复合值为 NOT NULL 而不是单个字段。

因此,以下语句无法在 location 列中插入 NULL

sql 复制代码
INSERT INTO warehouse_locations(warehouse_id, location)
VALUES(3, NULL);

错误:

sql 复制代码
null value in column "location" of relation "warehouse_locations" violates not-null constraint

但是您可以在 ROW 中单独将 NULL 插入到纬 latitudelongitude 中:

sql 复制代码
INSERT INTO
  warehouse_locations (warehouse_id, location)
VALUES
  (3, ROW (NULL, NULL));

若要将 NOT NULLCHECK 等约束应用于复合列的各个字段,可以在复合类型上创建一个域,并将约束应用于该域。例如:

首先,从 warehouse_locations 表中删除 id3 的行(如果存在):

sql 复制代码
DELETE FROM warehouse_locations 
WHERE warehouse_id = 3;

其次,在 coordinate 类型上创建一个域 coordinate_domain

sql 复制代码
CREATE DOMAIN coordinate_domain 
AS coordinate 
CHECK (
  (VALUE).latitude IS NOT NULL
  AND (VALUE).longitude IS NOT NULL
);

第三,将 location 列的类型更改为 coordinate_domain

sql 复制代码
ALTER TABLE warehouse_locations
ALTER COLUMN location TYPE coordinate_domain;

最后,尝试将 NULL 插入到 location 的各个字段中:

sql 复制代码
INSERT INTO
  warehouse_locations (warehouse_id, location)
VALUES
  (3, ROW (NULL, NULL));

这将导致预期的错误:

sql 复制代码
ERROR:  value for domain coordinate_domain violates check constraint "coordinate_domain_check"

隐式复合类型

创建表时,PostgreSQL 会自动创建相应的复合类型。

sql 复制代码
CREATE TABLE product_serials (
  serial_no VARCHAR(25) PRIMARY KEY,
  product_id INT NOT NULL
);

PostgreSQL 会自动创建一个名为 product_serialsproduct_serials 复合类型,其中包含两个字段 serial_noproduct_id 。但是,它不会对类型进行约束。

总结

  • 复合类型表示记录或行。
  • 复合类型包含字段及其相应数据类型的列表。
  • 使用复合类型类似于简单类型。
  • 复合类型不直接支持对单个字段的约束,但您可以在复合类型上创建域并将约束应用于该域。
  • PostgreSQL 会自动为每个表创建相应的隐式复合类型。
相关推荐
best_virtuoso3 小时前
PostgreSQL 常见数组操作函数语法、功能
java·数据结构·postgresql
-雷阵雨-3 小时前
MySQL——桥梁JDBC
数据库·mysql·oracle
亿坊电商3 小时前
在PHP框架里如何进行数据库连接?
数据库·oracle·php
满昕欢喜4 小时前
SQL Server从入门到项目实践(超值版)读书笔记 28
数据库·sql·sqlserver
楚韵天工4 小时前
宠物服务平台(程序+文档)
java·网络·数据库·spring cloud·编辑器·intellij-idea·宠物
李白你好5 小时前
一款专业的多数据库安全评估工具,支持 **PostgreSQL、MySQL、Redis、MSSQL** 等多种数据库的后渗透操作
数据库·mysql·postgresql
恋红尘6 小时前
Mysql
数据库·mysql
paishishaba6 小时前
数据库设计原则
数据库
曹牧7 小时前
oracle:NOT IN
数据库·oracle