《PostgreSQL 開發指南》 第 22 篇 DML 語句

本篇介紹如何對錶中的數據進行修改操作,包括插入數據的INSERT語句、更新數據的UPDATE語句、刪除數據的DELETE語句,以及合併數據的INSERT ON CONFLICT語句。

我們首先創建創建兩個示例表:

CREATE TABLE dept (
	department_id int NOT NULL,
	department_name varchar(30) NOT NULL,
	CONSTRAINT dept_pkey PRIMARY KEY (department_id)
);

CREATE TABLE emp (
	employee_id int NOT NULL,
	first_name varchar(20) NULL,
	last_name varchar(25) NOT NULL,
	hire_date date not null default current_date,
	salary numeric(8,2) NULL,
	manager_id int NULL,
	department_id int NULL,
	CONSTRAINT emp_pkey PRIMARY KEY (employee_id),
	CONSTRAINT fk_emp_dept FOREIGN KEY (department_id) REFERENCES dept(department_id) ON DELETE CASCADE,
	CONSTRAINT fk_emp_manager FOREIGN KEY (manager_id) REFERENCES emp(employee_id)
);

關於創建表的CREATE TABLE語句,可以參考第 06 篇 管理數據表

插入數據

PostgreSQL 提供了INSERT語句,可以用於插入一行或者多行數據。

插入單行數據

INSERT語句的簡單形式如下:

INSERT INTO table_name(column1, column2, ...)
VALUES (value1, value2, ...);

其中,value1 是 column1 的值,value2 是 column2 的值。例如:

INSERT INTO dept(department_id, department_name) VALUES ( 10, 'Administration');

select * from dept;
department_id|department_name|
-------------|---------------|
           10|Administration |

如果VALUES列表爲所有字段都指定了值,並且按照表的字段順序出現,可以省略表名後的字段列表。因此,我們也可以使用以下插入語句:

INSERT INTO dept VALUES ( 20, 'Marketing');

select * from dept;
department_id|department_name|
-------------|---------------|
           10|Administration |
           20|Marketing      |

指定字段的值也可以使用 DEFAULT,表示使用定義字段時的默認值;如果沒有指定默認值使用 NULL 值。

插入多行數據

PostgreSQL 中的INSERT語句支持一次插入多行數據,在VALUES之後使用逗號進行分隔。例如:

INSERT INTO emp
VALUES (200, 'Jennifer', 'Whalen', '2020-01-01', 4400.00, NULL, 10),
       (201, 'Michael', 'Hartstein', '2020-02-02', 13000.00, NULL, 20),
       (202, 'Pat', 'Fay', default, 6000.00, 201, 20);

select * from emp;
employee_id|first_name|last_name|hire_date |salary  |manager_id|department_id|
-----------|----------|---------|----------|--------|----------|-------------|
        200|Jennifer  |Whalen   |2020-01-01| 4400.00|          |           10|
        201|Michael   |Hartstein|2020-02-02|13000.00|          |           20|
        202|Pat       |Fay      |2020-04-14| 6000.00|       201|           20|

以上語句一次增加了 3 名員工信息,日期可以使用字符形式的字面值(‘2020-01-01’),default 表示使用默認的當前日期。

複製數據

INSERT INTO SELECT語句可以將一個查詢語句的結果插入表中。例如:

create table emp1 (like emp);

insert into emp1
select * from emp 
where department_id = 20;

select * from emp1;
employee_id|first_name|last_name|hire_date |salary  |manager_id|department_id|
-----------|----------|---------|----------|--------|----------|-------------|
        201|Michael   |Hartstein|2020-02-02|13000.00|          |           20|
        202|Pat       |Fay      |2020-04-14| 6000.00|       201|           20|

我們首先基於 emp 創建了一個新表 emp1,然後通過查詢語句將 emp 中的部分數據複製到 emp1 中。

返回插入的數據

PostgreSQL 對 SQL進行了擴展,可以在INSERT語句之後使用RETURNING返回插入的數據值。例如:

insert into dept
values (30, 'Purchasing')
returning department_id;
department_id|
-------------|
           30|

以上語句除了插入一條數據到 dept 表中,同時還返回了該數據的 department_id。

更新數據

單表更新

PostgreSQL 使用UPDATE語句更新表中已有的數據,基本的語法如下:

UPDATE table_name
   SET column1 = value1,
       column2 = value2,
       ...
WHERE conditions;

其中,WHERE決定了需要更新的數據行,只有滿足條件的數據纔會更新;如果省略WHERE條件,將會更新表中的所有數據,需要謹慎使用。

以下語句將編號爲 200 的員工從原部門調動到 Marketing,並且漲薪 1000:

update emp 
set salary = salary + 1000,
    department_id = 20
where employee_id = 200;

跨表更新

除了以上形式的更新語句之外,PostgreSQL 還支持通過關聯其他表中的數據進行更新。以下語句利用 emp 中的數據更新 emp1 表:

update emp1
set salary = emp.salary,
    department_id = emp.department_id,
    manager_id = emp.manager_id 
from emp
where emp1.employee_id = emp.employee_id;

我們使用FROM子句訪問 emp 中的數據,並且在WHERE子句中指定了兩個表的關聯條件。這種語句與多表連接查詢(join)類似,有時候也稱爲多表連接更新(UPDATE JOIN)。

返回更新後的數據

PostgreSQL 同樣對UPDATE語句進行了擴展,支持使用RETURNING返回更新後的數據值。例如:

update emp 
set salary = salary + 1000,
    department_id = 20
where employee_id = 200
returning first_name, last_name, salary;
first_name|last_name|salary |
----------|---------|-------|
Jennifer  |Whalen   |6400.00|

我們再次更新編號爲 200 的員工的信息,並且返回了更新之後的記錄。

刪除數據

刪除數據可以使用DELETE語句:

DELETE FROM table_name
WHERE conditions;

同樣,只有滿足WHERE條件的數據纔會被刪除;如果省略,將會刪除表中所有的數據。

單表刪除

以下語句用於刪除 emp1 中員工編號爲 201 的數據:

delete
from emp1
where employee_id = 201;

如果沒有編號爲 201 的數據,不會刪除任何數據。

跨表刪除

PostgreSQL 同樣支持通過關聯其他表進行數據刪除。以下語句利用 emp 表刪除 emp1 表中的數據:

delete
from emp1
using emp
where emp1.employee_id = emp.employee_id;

注意,跨表刪除使用USING關鍵字引用其他的表,而不是JOIN。以上語句了 emp1 中員工編號存在於 emp 表中的數據,等價於以下子查詢實現:

delete
from emp1
where emp1.employee_id in (select employee_id from emp);

返回被刪除的數據

PostgreSQL 中的DELETE語句也可以使用RETURNING返回被刪除的數據。例如:

-- 先插入一些數據
insert into emp1
select * from emp 
where department_id = 20;

delete
from emp1
returning *;
employee_id|first_name|last_name|hire_date |salary  |manager_id|department_id|
-----------|----------|---------|----------|--------|----------|-------------|
        201|Michael   |Hartstein|2020-02-02|13000.00|          |           20|
        202|Pat       |Fay      |2020-04-14| 6000.00|       201|           20|
        200|Jennifer  |Whalen   |2020-01-01| 6400.00|          |           20|

我們先從 emp 複製了一些數據到 emp1 中,然後刪除所有數據並且返回這些記錄。

合併數據

在 SQL 標準中還定義了一個合併數據的語句:MERGE。PostgreSQL 沒有實現該語句,但是可以通過INSERT INTO ON CONFLICT實現數據合併的功能。

INSERT INTO table_name(column1, column2, ...)
{VALUES (value1, value2, ...) | SELECT ...}
ON CONFLICT conflict_target conflict_action;

其中,conflict_target 是判斷數據是否已經存在的條件:

  • ( { index_column_name | ( index_expression ) } ) ,基於某個具有索引的字段或者表達式進行判斷;
  • ON CONSTRAINT constraint_name,基於某個唯一約束進行判斷。

conflict_action 表示衝突時採取的操作:

  • DO NOTHING,如果數據已經存在,不做任何操作;
  • DO UPDATE SET,如果數據已經存在,更新該數據;可以使用WHERE子句進一步限制需要更新的數據。

這種語句通過爲INSERT語句增加ON CONFLICT選項,組合了INSERTUPDATE語句的功能,因此也被稱爲UPSERT語句。

衝突時不做任何操作

emp 表中已經存在編號爲 200 的員工,如果我們再次插入該編號將會提示主鍵衝突:

insert into emp
values (200, 'Jennifer', 'Whalen', '2020-01-01', 4400.00, NULL, 10)
SQL Error [23505]: ERROR: duplicate key value violates unique constraint "emp_pkey"
  Detail: Key (employee_id)=(200) already exists.

此時,我們可以增加衝突處理,從而避免語句出錯:

insert into emp
values (200, 'Jennifer', 'Whalen', '2020-01-01', 4400.00, NULL, 10)
on conflict (employee_id)
do nothing;

以上語句基於 employee_id 字段是否重複進行判斷,衝突時不做任何處理。

衝突時更新目標數據

另一種處理衝突的方式就是進行數據更新:

select department_id 
from emp
where employee_id = 200;
department_id|
-------------|
           20|

insert into emp
values (200, 'Jennifer', 'Whalen', '2020-01-01', 4400.00, NULL, 10)
on conflict on constraint emp_pkey
do update
set first_name = EXCLUDED.first_name,
    last_name = EXCLUDED.last_name,
    hire_date = EXCLUDED.hire_date,
    salary = EXCLUDED.salary,
    manager_id =EXCLUDED.manager_id,
    department_id = EXCLUDED.department_id;

select *
from emp
where employee_id = 200;
employee_id|first_name|last_name|hire_date |salary |manager_id|department_id|
-----------|----------|---------|----------|-------|----------|-------------|
        200|Jennifer  |Whalen   |2020-01-01|4400.00|          |           10|

該員工的部門編號在前面被修改爲 20;我們通過主鍵約束 emp_pkey 進行重複數據的判斷,然後更新該員工的數據;EXCLUDED 是一個特殊的表,代表了原本應該插入的數據行;最終該員工的部門編號被更新爲 10。

歡迎點贊👍、評論📝、收藏❤️!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章