# ORM vs Raw SQL: The Winner is…

## Intro

Kalau di dunia sepakbola, mungkin udah ga asing di telinga kita soal perdebatan yang masih belum jelas kapan selesainya: CR7 atau Messi? Menurut saya sendiri sih, ini betul-betul murni preferensi pribadi aja. Kehebatan mereka berdua sudah diakui oleh dunia, dibuktikan dengan deretan trofi kejuaraan dan penghargaan pribadi yang mereka dapatkan. Cuma mungkin ada yang lebih suka gaya permainan CR7, ada yang lebih suka gaya permainan Messi.

Nah pertanyaannya, apakah di dunia teknologi ada juga perdebatan kayak gitu? Perdebatan yang sampai bisa bikin netizen saling “baku hantam”?

Sebetulnya, namanya perdebatan di bidang apapun pasti bakal selalu ada. Termasuk di dunia teknologi pun ada aja silang pendapat yg berujung ke “baku hantam”. Dan salah satu perdebatan yang selalu menarik dibicarakan adalah: *apakah lebih baik menggunakan ORM atau raw SQL?*

Di tulisan kali ini saya akan coba bedah sedikit kenapa perdebatan itu bisa muncul, dan kenapa topik ini selalu menarik buat dijadikan bahan “baku hantam”.

## Pengertian ORM

ORM, singkatan dari *Object Relational Mapping*, merupakan salah satu metode yang lumrah digunakan untuk manajemen data di database. ORM bertujuan melakukan “konversi” terhadap data menjadi objek, sehingga memungkinkan kita untuk manipulasi data menggunakan paradigma pemrograman berorientasi objek.

Tujuan dari ORM adalah menjadi layer abstaksi antara aplikasi dengan database sehingga memungkinkan kita melakukan manipulasi data tanpa harus menulis SQL, karena ORM yang akan menuliskan SQL-nya untuk kita.

Umumnya jika kita menggunakan ORM, kita menuliskan *class* yang akan menjadi representasi dari suatu tabel di database. Kemudian, objek dari *class* tersebut merupakan representasi dari satu atau lebih baris data di database. ORM akan menyediakan *library* yang memungkinkan kita untuk melakukan operasi *create, read, update* dan *delete* terhadap objek data, yang akan diterjemahkan oleh ORM menjadi operasi SQL INSERT, SELECT, UPDATE dan DELETE di balik layar.

## Contoh ORM

Beberapa contoh *library* ORM yang cukup populer diantaranya:

* [Django ORM (Python)](https://docs.djangoproject.com/en/5.0/topics/db/models/)
    
* [Entity Framework (C#)](https://learn.microsoft.com/en-us/ef/core/)
    
* Active Record ([PHP](https://www.phpactiverecord.org), [Ruby](https://guides.rubyonrails.org/active_record_basics.html))
    
* [Gorm (Go)](https://gorm.io/index.html)
    
* dan lain-lain..
    

## ORM vs Raw SQL: sintaks

Jika tadi kita masih membahas kulitnya, sekarang kita mulai masuk ke topik yang lebih dalam yaitu perbedaan sintaks penulisan antara ORM dengan raw SQL. Di bawah ini kita bisa melihat beberapa contoh sintaks penulisan ORM dan sintaks SQL yang ekivalen.

**Django ORM:**

```python
books = Book.objects.all()
```

**Ekivalen dengan SQL:**

```sql
SELECT * FROM books;
```

**Django ORM**

```python
from django.db.models import Sum

expensive_customers = Customer.objects.annotate(
    total_order_cost=Sum('order__total_cost')
).filter(
    total_order_cost__gt=1000
)
```

**Ekivalen dengan SQL:**

```sql
SELECT c.name, SUM(o.total_cost) as total_order_cost
FROM customers c
JOIN orders o ON c.id = o.customer_id
GROUP BY c.name
HAVING SUM(o.total_cost) > 1000;
```

**Django ORM**

```python
from django.db.models import Sum

ordered_products = Product.objects.filter(
    order_items__order__customer__state__in=['NY', 'CA']
).annotate(
    total_quantity=Sum('order_items__quantity')
)
```

**Ekivalen dengan SQL:**

```sql
SELECT p.name, SUM(oi.quantity) as total_quantity
FROM products p
JOIN order_items oi ON p.id = oi.product_id
JOIN orders o ON oi.order_id = o.id
JOIN customers c ON o.customer_id = c.id
WHERE c.state IN ('NY', 'CA')
GROUP BY p.name;
```

Catatan: seluruh ORM menggunakan sintaks Django ORM. Untuk *library* ORM yang lain tentu saja akan ada perbedaan, tapi ide dasarnya tetap sama. *Class* merepresentasikan tabel di database, dan objek merepresentasikan satu atau lebih baris data. Dari contoh di atas, `Book` , `Customer` dan `Product` adalah class yang merepresentasikan tabel `books`, `customers` dan `products`. Sementara itu, `books`, `expensive_customers`, dan `ordered_products` merupakan objek yang merepresentasikan data yang didapatkan dari database. Dari objek yang dihasilkan tadi, layaknya objek pada pemrograman berorientasi objek, kita dapat melakukan manipulasi data dengan menggunakan fungsi-fungsi yang terdaftar pada *class* masing-masing.

**Django ORM**

```python
product = Product.objects.filter(price__lt=10)
product.update(price=12.99)
```

**Ekivalen dengan SQL:**

```sql
UPDATE products SET price = 12.99 WHERE price < 10;
```

Pada contoh di atas, objek `product` menggunakan fungsi `update()` yang terdapat pada *class*`Product` untuk melakukan operasi SQL *update.*

Jika dilihat dari sintaks, kita sebagai programmer tentunya akan "dimanjakan" dengan ORM ini. Selain sintaks penulisan ORM yang relatif lebih sederhana, penggunaan objek sebagai representasi data sangat memudahkan kita apabila kita ingin melakukan manipulasi data. Selain itu, ada satu lagi daya tarik ORM yang mungkin membuat orang menggunakan ORM pada *software* yang dibuat. Karena ORM bertugas melakukan penulisan raw SQL yang ekivalen, ORM dapat memberikan sintaks SQL yang sesuai dengan *engine* database yang digunakan. Fitur ini sangat bermanfaat apabila *software* yang kita buat menggunakan lebih dari beberapa *engine* database yang berbeda (misal, SQL Server dan MySQL).

**Django ORM**

```python
# ambil 5 produk dari database
products = Product.objects.all()[:5]
```

**Sintaks SQL Server yang ekivalen:**

```sql
SELECT TOP 5 * FROM products;
```

**Sintaks MySQL yang ekivalen**

```sql
SELECT * FROM products LIMIT 5;
```

Walaupun terdapat perbedaan dari sintaks SQL Server dan MySQL di atas, sintaks ORM tetaplah sama. ORM yang akan mendeteksi *engine* database yang digunakan, untuk kemudian menghasilkan SQL yang sesuai. Sehingga kita sebagai *software developer* tidak perlu menghafal sintaks SQL untuk masing-masing *engine* database, dan serahkan ke ORM untuk melakukan tugasnya.

## ORM vs Raw SQL: performance

Apabila sebelumnya kita sudah membahas sintaks ORM dan raw SQL yang ekivalen, bagaimana dengan performancenya?

Sebetulnya dengan segala fitur yang ditawarkan ORM, sekilas terdengar *too good to be true*. Sintaks yang relatif lebih sederhana, dapat melakukan manipulasi data menggunakan pendekatan objek, dapat mendeteksi engine database yang digunakan, lalu apakah ini artinya kita betul-betul sudah tidak membutuhkan raw SQL?

Layaknya peribahasa "tak ada gading yang tak retak" maka begitu juga dengan ORM. Karena ORM berfungsi sebagai layer abstraksi **tambahan** antara *software* dengan database, tentu saja akan ada *overhead* yang dihasilkan oleh ORM.

Berikut merupakan kode sederhana yang saya gunakan untuk melakukan perbandingan *performance* antara ORM dengan raw SQL. Kode berikut ditulis dalam bahasa Python dan menggunakan *framework* Django, namun hasilnya harusnya kurang lebih akan mirip jika menggunakan ORM dalam bahasa lain.

```python
from django.db import connection
from django.shortcuts import render
from django.http import HttpResponse
from memory_profiler import memory_usage
from memory_profiler import profile
from .models import User
import time

# Function to reset the query log
def reset_queries():
    connection.queries_log.clear()

# Function to measure execution time
def measure_time(func):
    start = time.time()
    result = func()
    end = time.time()
    return result, end - start

# Django ORM query
@profile
def orm_query():
    return list(User.objects.all()[:1000000])

# Raw SQL query
@profile
def raw_sql_query():
    with connection.cursor() as cursor:
        cursor.execute("SELECT * FROM user LIMIT 1000000")
        return list(cursor.fetchall())

# Create your views here.
def index(request):
   # loop 10 times to get the average time
    total = 0
    for x in range(10):
        reset_queries()
        # measure execution time
        raw_result, raw_time = measure_time(orm_query)
        total += raw_time

        # measure memory consumption 
        mem_usage = memory_usage(orm_query)
        max_usage = max(mem_usage)
        total += max_usage

    # calculate the average time
    average = total / 10

    return HttpResponse(average)
```

Kode tersebut membandingkan dua hal: waktu eksekusi (dalam detik) dan penggunaan memory (dalam MiB). Dalam melakukan perbandingan tersebut, saya menggunakan database SQLite. Mesin yang digunakan adalah laptop dengan CPU Core i7 1255U dan RAM 16GB. Berikut hasil yang didapatkan disajikan dalam grafik:

## ORM vs Raw SQL: Waktu Eksekusi (dalam detik)

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1719710223563/b13b4c2f-87cb-496c-964b-c2eadab1be2a.png align="center")

| Jumlah Row | Django ORM | Raw SQL |
| --- | --- | --- |
| 100 | 0.00782 | 0.00238 |
| 1000 | 0.05666 | 0.00313 |
| 10000 | 0.50069 | 0.01425 |
| 100000 | 5.52776 | 0.11043 |
| 1000000 | 53.64550 | 1.11739 |

## ORM vs Raw SQL: Konsumsi Memory (dalam MiB)

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1719710296947/860861b9-a4c4-4753-a8dd-d36a20239055.png align="center")

| Jumlah Row | Django ORM | Raw SQL |
| --- | --- | --- |
| 100 | 43.33359 | 43.28085 |
| 1000 | 45.19882 | 44.42343 |
| 10000 | 53.18281 | 54.51219 |
| 100000 | 132.36835 | 142.86562 |
| 1000000 | 913.72148 | 567.93789 |

> Disclaimer: hasil bisa berbeda-beda, karena banyak variabel yang mempengaruhi seperti mesin yang digunakan, ORM/bahasa yang digunakan, engine database yang digunakan dll.

## Kesimpulan: Kapan harus pakai yang mana?

Dari hasil perbandingan di atas, terbukti bahwa ada harga yang harus dibayar sebagai kompensasi atas bermacam fitur tambahan yang disematkan dalam ORM. Pada jumlah data yang masih kecil, perbedaan masih tidak terasa. Namun jika aplikasi sudah berurusan dengan jumlah data yang besar, perbedaan tersebut akan sangat terasa. Jadi, kapan kita dapat menggunakan ORM, dan kapan kita menggunakan raw SQL?

## Gunakan ORM jika...

* Aplikasi masih dalam tahap pengembangan, karena kesederhanaan sintaks ORM dapat mempercepat pengembangan
    
* Aplikasi masih dalam tahap *early stage,* dimana data yang digunakan / jumlah user masih sedikit
    
* *Performance* bukan metric utama dari aplikasi, karena memang ada skenario dimana metric lain lebih penting dari *performance*
    
* Aplikasi menggunakan beberapa *engine* database yang berbeda, yang berpotensi banyak terjadi perbedaan sintaks SQL. Umumnya ORM cukup *portable* sehingga dengan kode ORM yang sama dapat memberikan sintaks SQL yang sesuai dengan engine database yang digunakan
    

## Gunakan SQL jika...

* Aplikasi sudah memiliki data besar, atau jumlah data relatif sedikit namun basis user yang besar. Dari perbandingan di atas, dapat kita lihat pada saat jumlah data sedikit sebenarnya perbedaan yang ada tidaklah terasa. Namun jika dihadapkan dengan jumlah user yang besar, perbedaan sekecil apapun dapat terakumulasi sehingga cepat menghabiskan *resource*
    
* *Performance* merupakan metric yang *critical* dari aplikasi
    
* Aplikasi hanya menggunakan satu engine database, atau menggunakan beberapa *engine* database namun potensi perbedaan sintaks sangat minim
    

Jadi, mana yang lebih baik, ORM atau raw SQL? 😁
