PostgreSQL: 複合プライマリーキー

次のように、複合プライマリーキー（複合主キー）があるテーブルの取り扱いです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/96777d9c-a8b0-549d-0065-2397985d63e4.png)

## Create

```sql:create.sql
create table cities (id_pref varchar(10) not null, id_city varchar(10) not null,
	name text, population int, date_mod date,
	primary key (id_pref,id_city));
```

実行結果

```text
city=# \d cities
                        Table "public.cities"
   Column   |         Type          | Collation | Nullable | Default 
------------+-----------------------+-----------+----------+---------
 id_pref    | character varying(10) |           | not null | 
 id_city    | character varying(10) |           | not null | 
 name       | text                  |           |          | 
 population | integer               |           |          | 
 date_mod   | date                  |           |          | 
Indexes:
    "cities_pkey" PRIMARY KEY, btree (id_pref, id_city)
```

## Insert

```sql:insert.sql
insert into cities values ('p34','c01','広島',72814,'2001-9-14');
insert into cities values ('p34','c02','福山',41738,'2001-7-21');
insert into cities values ('p34','c03','東広島',92513,'2001-6-12');
```

実行結果

```text
city=# select * from cities;
 id_pref | id_city |  name  | population |  date_mod  
---------+---------+--------+------------+------------
 p34     | c01     | 広島   |      72814 | 2001-09-14
 p34     | c02     | 福山   |      41738 | 2001-07-21
 p34     | c03     | 東広島 |      92513 | 2001-06-12
(3 rows)
```

## Upsert

```sql:upsert01.sql
INSERT INTO cities (id_pref,id_city,name,population,date_mod)
    VALUES ('p35','c01','山口',23541,'2022-5-10'),
	('p35','c02','下関',83762,'2022-5-10'),
	('p35','c03','宇部',31725,'2022-5-10')
    ON CONFLICT (id_pref,id_city) DO UPDATE SET name = EXCLUDED.name,
	 population = EXCLUDED.population,
	 date_mod = EXCLUDED.date_mod;
```

実行結果

```text
city=# select * from cities;
 id_pref | id_city |  name  | population |  date_mod  
---------+---------+--------+------------+------------
 p34     | c01     | 広島   |      72814 | 2001-09-14
 p34     | c02     | 福山   |      41738 | 2001-07-21
 p34     | c03     | 東広島 |      92513 | 2001-06-12
 p35     | c01     | 山口   |      23541 | 2022-05-10
 p35     | c02     | 下関   |      83762 | 2022-05-10
 p35     | c03     | 宇部   |      31725 | 2022-05-10
(6 rows)
```

```sql:upsert02.sql
INSERT INTO cities (id_pref,id_city,name,population,date_mod)
    VALUES ('p35','c01','山口',9923541,'2024-7-11'),
	('p35','c02','下関',9983762,'2024-7-11'),
	('p35','c03','宇部',9931725,'2024-7-11')
    ON CONFLICT (id_pref,id_city) DO UPDATE SET name = EXCLUDED.name,
	 population = EXCLUDED.population,
	 date_mod = EXCLUDED.date_mod;
```

実行結果

```text
city=# select * from cities;
 id_pref | id_city |  name  | population |  date_mod  
---------+---------+--------+------------+------------
 p34     | c01     | 広島   |      72814 | 2001-09-14
 p34     | c02     | 福山   |      41738 | 2001-07-21
 p34     | c03     | 東広島 |      92513 | 2001-06-12
 p35     | c01     | 山口   |    9923541 | 2024-07-11
 p35     | c02     | 下関   |    9983762 | 2024-07-11
 p35     | c03     | 宇部   |    9931725 | 2024-07-11
(6 rows)
```

## Python で実装した例

```py:composite_upsert.py
#! /usr/bin/python
#
#	composite_upsert.py
#
#				Aug/20/2024
#
# --------------------------------------------------------
import sys
import os
import psycopg2
from dotenv import load_dotenv
#
#
# --------------------------------------------------------
def sql_upsert_string_gen_proc ():
	insert_sql = '''
	INSERT INTO cities (id_pref, id_city, name, population, date_mod)
	VALUES (%s, %s, %s, %s, %s)
	ON CONFLICT (id_pref, id_city) DO UPDATE SET
	(name, population, date_mod) = (EXCLUDED.name, EXCLUDED.population, EXCLUDED.date_mod);
'''
	return	insert_sql
# --------------------------------------------------------
sys.stderr.write ("*** 開始 ***\n")
#
dotenv_path = '.env'
load_dotenv(dotenv_path)
USER=os.environ.get("user")
PASSWORD=os.environ.get("password")
DATA_BASE=os.environ.get("data_base")
#
conn = psycopg2.connect("dbname=" + DATA_BASE + " user=" + USER + " password=" + PASSWORD)
#
cur = conn.cursor()
#
sql_str=sql_upsert_string_gen_proc()
print(sql_str)
cur.execute(sql_str,('p35','c01','山口',113549,'2024-8-17'))
cur.execute(sql_str,('p35', 'c02','下関',113762,'2024-8-17'))
cur.execute(sql_str,('p35', 'c03','宇部',111725,'2024-8-17'))
conn.commit ()
#
cur.close ()
conn.close ()
#
sys.stderr.write ("*** 終了 ***\n")
#
# --------------------------------------------------------
```