# Relasi Dalam Database Mengunakan PostgreSQL: one to one, one to many, dan many to many

Assalamualikum, pada kesempatan kali ini kita akan membuat relasi dalam database:

data terbagi menjadi 3:

* one to one
    
* one to many
    
* many to many
    

## 1\. One to One

### 1.1Masalah

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1755931233615/cbffe459-62c1-4059-865e-455aab9a5a85.png align="center")

* **Banyak** `NULL` untuk role yang tidak butuh biodata → boros, berisik, rawan salah.
    
* **Keamanan & tanggung jawab bercampur**: data login (sensitif) digabung dengan biodata (publik).
    
* **Sulit berkembang**: tambah kolom profil baru akan “mengganggu” semua baris (termasuk admin).
    

### 1.2 Solusi

**Intinya:** bedakan **akun** dari **profil**.

* `users` → khusus autentikasi & otorisasi (username, password\_hash, role).
    
* `profile` → khusus biodata (name, address, no\_hp, jenis\_kelamin), **opsional** tergantung role.
    

Dengan begitu:

* Admin cukup punya akun (tanpa profil).
    
* Member/staff bisa punya profil lengkap.
    
* Data bersih, hemat `NULL`, dan mudah diatur izinnya.
    

```pgsql
CREATE TABLE users (
  id            BIGSERIAL PRIMARY KEY,
  username      VARCHAR(64) NOT NULL UNIQUE,
  password_hash TEXT        NOT NULL,
  role          VARCHAR(16) NOT NULL CHECK (role IN ('admin','member','staff')),
  created_at    TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at    TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE TABLE profile (
  user_id        BIGINT PRIMARY KEY
                 REFERENCES users(id) ON DELETE CASCADE,
  name           VARCHAR(120),
  address        VARCHAR(255),
  no_hp          VARCHAR(32),
  jenis_kelamin  CHAR(1) CHECK (jenis_kelamin IN ('L','P')),
  created_at     TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at     TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
```

Kuncinya pada **table profiles** dimana pada bagian user\_id mereferensikan id users

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1755931835721/fafa0335-e80b-420f-99d7-6cdde0fe9f81.png align="center")

Kemudian membuat data pada users

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1755931867213/479852af-8d03-48d6-979f-d8c5aa0d8dee.png align="center")

Kemudian membuat data untuk profiles

error: banyak typo di berbagai tempat

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1755932014219/1a17de49-28d3-4718-aebf-49f6a563dc95.png align="center")

Kemudian membaca hasil keduanya bersamaan dengan mengunakan join (hanya menampilkan tanpa benar benar mengabungkanya jika di lihat satu persatu seperti ini

error: typo dalam penulisan kata profile

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1755932124141/b10b7f48-cd79-431d-af20-b6e740d2b759.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1755932126151/bfebf2f0-38a5-4045-a1a2-256b298d844f.png align="center")

## 2\. One to Many

### 2.1 Masalah

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1756088773817/d2ae7c20-0b09-4f8c-8ccd-19329929b7c1.png align="center")

* **Batas kolom**: Hanya bisa menyimpan 4 setoran. Saat user menyetor ke-5, harus tambah kolom baru.
    
* **Banyak** `NULL`: Tidak semua user menyetor 4 kali → kolom kosong.
    
* **Sulit analitik**: Agregasi (SUM, AVG), filter per tanggal/metode setoran jadi ribet karena data melebar ke banyak kolom.
    
* **Tidak ada detail waktu/metode** per setoran.
    

### 2.2 Solusi

* Pisahkan **entitas** `users` dan **kejadian** `setoran`.
    
* Satu `user` **punya banyak** baris `setoran`.
    

### 2.3 Praktik

* Ini adalah table yang akan kita buat
    

```pgsql
CREATE TABLE users (
 id       SERIAL PRIMARY KEY,
 username VARCHAR(64) NOT NULL UNIQUE,
 name     VARCHAR(120) NOT NULL
 );

CREATE TABLE transaksi (
 id       BIGSERIAL PRIMARY KEY,
 user_id  BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
 nominal  NUMERIC(12,2) NOT NULL CHECK (nominal > 0),
 tgl      DATE NOT NULL,
 jenis_transaksi   VARCHAR(32)
 );
```

Inilah adalah contoh pemaktrikanya

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1756089220174/e8a59db0-06d5-44ae-8d79-76c37ad35f9f.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1756089283337/49e23c40-a66e-407c-ab77-d46ded31efff.png align="center")

Setelah data dibuat maka kita akan memasukan datanya

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1756089373597/84162949-34a2-4d22-bb19-1d2783f2dad5.png align="center")

### 2.4 Error

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1756089404053/2d457e2e-4f04-40d0-ba86-638978a6bbb6.png align="center")

* Saya salah dalam memberi nama table ke 2 maka saya harus membuat table kembali
    
* Data pada table users, saya hapus entah kenapa saya berpikir untuk mengulangnya maka id 1 dan 2 juga tidak dapat di akses
    

## 3\. Many to Many

### 3.1 Masalah

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1756090477579/083372e1-ec28-42bc-9c57-0d634c4273c1.png align="center")

* **Integritas buruk**: tidak ada FK ke kamus `tag`. Ejaan bisa beda (“VIP”, “Vip”, “vip”).
    
* **Sulit query**: mencari user yang punya **semua** tag tertentu jadi rumit (harus parsing string/ARRAY).
    
* **Duplikasi tak terlihat**: tag yang sama bisa ditulis berulang dengan variasi penulisan.
    

### 3.2 Solusi

Pisahkan menjadi 3 tabel: `users`, `tags`, dan **tabel penghubung** `user_has_tag`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1756090596826/9b710e15-d22c-4638-8750-d00a0b91521a.png align="center")

### 3.3 Praktik

* ini adalah table yang akan kita buat
    

```pgsql
CREATE TABLE users (
  id       BIGSERIAL PRIMARY KEY,
  username VARCHAR(64) NOT NULL UNIQUE,
  name     VARCHAR(120) NOT NULL
);
 
CREATE TABLE tags (
  id   BIGSERIAL PRIMARY KEY,
  name VARCHAR(64) NOT NULL UNIQUE
);
 
CREATE TABLE user_has_tag (
  user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
  tag_id  BIGINT NOT NULL REFERENCES tags(id)  ON DELETE CASCADE,
  PRIMARY KEY (user_id, tag_id)
);
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1756091494942/6153517c-1cd1-4bc7-8dc7-93e72d889fed.png align="center")

setelah membuat table kita akan menambahkan isinya

error: typo pada tabel users\_name\_tags dimana saya menulis tags\_is padahal harusnya tags\_id

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1756092058199/8577fdc0-b00a-4736-9b27-52033c1e9919.png align="center")

error:

* typo penulisan user\_id harusnay users\_id
    
* lupa kalau tags hanya ada 3 ngak sampai 4
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1756092610887/4259307e-c19a-40f2-bb42-2224fd44122c.png align="center")
