Foto oleh panumas nikhomkhai via Pexels

Secara default, SQL Server tidak menjamin urutan baris yang dikembalikan oleh sebuah query kecuali kita menentukannya secara eksplisit menggunakan ORDER BY. Asumsi bahwa data 'pasti' terurut sesuai urutan insert adalah kesalahan umum yang bisa menyebabkan bug tidak konsisten, terutama setelah data dipindahkan, di-restore dari backup, atau tabel mengalami rebuild index.

Artikel ini membahas cara mengurutkan hasil query baik secara menaik maupun menurun, serta mengurutkan berdasarkan lebih dari satu kolom sekaligus, lengkap dengan penjelasan mengapa mengandalkan urutan implisit itu berisiko.

Mengurutkan Data Secara Dasar

ASC vs DESC

-- Menaik (default), dari harga termurah SELECT ProductName, Price FROM Products ORDER BY Price ASC; -- Menurun, dari harga termahal SELECT ProductName, Price FROM Products ORDER BY Price DESC; -- Expected ASC: produk termurah muncul di baris pertama -- Expected DESC: produk termahal muncul di baris pertama

ASC adalah urutan default jika tidak disebutkan secara eksplisit, sehingga ORDER BY Price dan ORDER BY Price ASC menghasilkan hasil yang identik — namun menulis ASC secara eksplisit tetap disarankan agar maksud query lebih jelas dibaca developer lain.

Mengurutkan Berdasarkan Banyak Kolom

Urutan nama pelanggan A-Z, lalu tanggal terbaru dulu

SELECT CustomerName, OrderDate, TotalAmount FROM Orders ORDER BY CustomerName ASC, OrderDate DESC; -- Expected: data dikelompokkan per nama pelanggan secara alfabetis, -- dan dalam satu pelanggan yang sama, order terbaru tampil lebih dulu

Ketika beberapa kolom diurutkan sekaligus, SQL Server akan mengurutkan berdasarkan kolom pertama terlebih dahulu, baru menggunakan kolom kedua untuk memecah kesamaan nilai pada kolom pertama, dan seterusnya untuk kolom-kolom berikutnya jika masih ada nilai yang sama.

Mengurutkan Berdasarkan Posisi Kolom

Mengurutkan dengan nomor posisi kolom

SELECT ProductName, Price FROM Products ORDER BY 2 DESC; -- mengacu ke kolom kedua, yaitu Price -- Secara fungsional identik dengan ORDER BY Price DESC

Cara ini berfungsi tapi kurang disarankan dalam kode produksi karena mudah salah ketika urutan kolom di SELECT berubah di kemudian hari. Lebih aman menulis nama kolom secara eksplisit agar maksud query tetap jelas meski struktur SELECT berubah seiring waktu.

Bagaimana ORDER BY Memperlakukan Nilai NULL

Satu hal yang sering luput diperhatikan adalah bagaimana ORDER BY memperlakukan nilai NULL. Di SQL Server, NULL dianggap sebagai nilai 'terkecil', sehingga akan selalu muncul paling awal saat diurutkan ASC dan paling akhir saat diurutkan DESC.

Perilaku NULL pada pengurutan ASC

SELECT CustomerName, ShippedDate FROM Orders ORDER BY ShippedDate ASC; -- Expected: baris dengan ShippedDate NULL (belum dikirim) -- akan muncul di posisi PALING ATAS pada hasil ASC

Jika ingin baris dengan ShippedDate NULL justru ditampilkan di akhir meski mengurutkan ASC, salah satu trik umum adalah menambahkan kolom bantu dengan CASE WHEN yang mengubah NULL menjadi nilai besar secara sementara khusus untuk kebutuhan pengurutan, tanpa mengubah data aslinya.

Memahami perilaku NULL pada ORDER BY sangat penting terutama pada laporan operasional, misalnya daftar pesanan yang belum dikirim — jika tidak diantisipasi, baris-baris penting tersebut bisa tenggelam di posisi yang tidak terduga tergantung arah pengurutan yang dipilih.

Mengurutkan Berdasarkan Banyak Kolom dan Posisi Kolom

ORDER BY dengan dua kolom dan arah urutan berbeda

-- Urutkan dulu berdasarkan Kota (A-Z), lalu dalam kota -- yang sama urutkan berdasarkan TotalBelanja (terbesar dulu) SELECT CustomerName, City, TotalBelanja FROM Customers ORDER BY City ASC, TotalBelanja DESC; -- Hasil, contoh: -- Andi Bandung 2500000 -- Sari Bandung 1800000 -- Budi Jakarta 3000000

ORDER BY dapat menerima lebih dari satu kolom yang dipisahkan koma, dan urutan penulisan kolom menentukan prioritas pengurutan: kolom pertama menjadi kriteria utama, lalu jika ada baris dengan nilai sama pada kolom pertama, barulah kolom kedua dipakai untuk menentukan urutan di antara baris-baris tersebut. Setiap kolom juga bisa memiliki arah urutan berbeda, seperti City secara ascending namun TotalBelanja secara descending pada contoh di atas.

Selain menyebut nama kolom secara eksplisit, ORDER BY juga menerima nomor posisi kolom sebagaimana ia muncul pada daftar SELECT, misalnya ORDER BY 2, 3 berarti mengurutkan berdasarkan kolom kedua lalu ketiga pada hasil SELECT. Cara ini mempersingkat penulisan namun membuat query lebih rapuh, karena mengubah urutan kolom pada SELECT akan diam-diam mengubah makna ORDER BY tanpa pesan error apa pun.

Satu hal yang perlu diingat adalah ORDER BY selalu dieksekusi paling akhir dalam urutan logis pemrosesan query, setelah WHERE, GROUP BY, dan HAVING, sehingga ORDER BY boleh merujuk ke alias kolom yang didefinisikan di klausa SELECT, sesuatu yang tidak diperbolehkan pada klausa WHERE karena WHERE diproses sebelum alias tersebut terbentuk.

Perilaku Pengurutan NULL dan Paging dengan OFFSET-FETCH

Posisi NULL pada ORDER BY dan paging dengan OFFSET-FETCH

-- NULL selalu dianggap nilai terkecil pada SQL Server SELECT NamaPelanggan, TanggalBerhenti FROM dbo.Pelanggan ORDER BY TanggalBerhenti ASC; -- baris dengan TanggalBerhenti NULL muncul paling awal -- Paging data memakai OFFSET-FETCH, mengambil halaman ke-3 (10 baris per halaman) SELECT NamaPelanggan, TanggalBerhenti FROM dbo.Pelanggan ORDER BY NamaPelanggan ASC OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY; -- Hasil ORDER BY ASC, contoh: -- NamaPelanggan TanggalBerhenti -- Budi NULL -- Citra NULL -- Andi 2025-01-15

Berbeda dengan beberapa database lain yang memungkinkan pengaturan NULLS FIRST atau NULLS LAST secara eksplisit, SQL Server memiliki aturan tetap: nilai NULL selalu diperlakukan sebagai nilai terkecil, sehingga pada pengurutan ASC, baris dengan NULL akan muncul paling awal, dan pada DESC baris tersebut muncul paling akhir. Perilaku ini tidak dapat diubah lewat opsi session atau konfigurasi server, dan satu-satunya cara mengatur posisi NULL secara kustom adalah memakai ekspresi tambahan seperti CASE WHEN kolom IS NULL THEN 1 ELSE 0 END pada klausa ORDER BY.

Klausa OFFSET-FETCH, yang diperkenalkan sejak SQL Server 2012, memberi cara standar ANSI untuk melakukan paging data tanpa bergantung pada trik lama seperti subquery dengan TOP dan ROW_NUMBER. OFFSET menentukan berapa baris yang dilewati dari awal hasil yang sudah diurutkan, sedangkan FETCH NEXT menentukan jumlah baris yang diambil setelahnya, dan keduanya wajib didahului klausa ORDER BY karena tanpa urutan yang pasti, hasil paging antar-eksekusi bisa tidak konsisten.

OFFSET-FETCH sangat relevan untuk fitur pagination pada aplikasi web, misalnya menampilkan 10 baris per halaman, namun perlu diwaspadai bahwa performanya menurun pada OFFSET bernilai besar karena SQL Server tetap harus memindai dan mengurutkan seluruh baris sebelum titik offset tersebut sebelum membuang baris-baris yang dilewati, sehingga untuk dataset sangat besar, pendekatan keyset pagination berbasis kolom kunci biasanya lebih efisien dibanding OFFSET-FETCH murni.

Pengaruh Collation terhadap Urutan ORDER BY pada Kolom Teks

Collation memengaruhi hasil ORDER BY

-- Collation case-insensitive: huruf besar dan kecil dianggap setara SELECT NamaProduk FROM dbo.Produk ORDER BY NamaProduk COLLATE SQL_Latin1_General_CP1_CI_AS; -- Collation case-sensitive: huruf besar diurutkan lebih dulu dari kecil SELECT NamaProduk FROM dbo.Produk ORDER BY NamaProduk COLLATE SQL_Latin1_General_CP1_CS_AS; -- Hasil CS: 'Apel' muncul sebelum 'apel' -- Hasil CI: 'Apel' dan 'apel' dianggap setara, urutan di antaranya tidak pasti

Hasil pengurutan ORDER BY pada kolom bertipe teks tidak hanya bergantung pada nilai karakter itu sendiri, melainkan juga pada aturan collation yang berlaku pada kolom tersebut. Collation case-insensitive (CI) memperlakukan huruf besar dan kecil sebagai nilai yang setara saat dibandingkan, sehingga 'Apel' dan 'apel' dianggap sama persis dalam urutan, sedangkan collation case-sensitive (CS) membedakan keduanya dan biasanya menempatkan huruf kapital lebih dulu.

Perbedaan ini sering baru terlihat ketika aplikasi dipindahkan ke server dengan collation default berbeda dari server sebelumnya, membuat urutan hasil laporan tiba-tiba berubah meski query dan datanya persis sama. Menentukan collation secara eksplisit pada ORDER BY lewat klausa COLLATE adalah cara paling pasti untuk menjamin urutan hasil tetap konsisten terlepas dari collation default server tempat query dijalankan.

Kesalahan Umum yang Sering Terjadi

Mengasumsikan data terurut tanpa ORDER BY eksplisit

SQL Server tidak menjamin urutan baris tanpa ORDER BY, meski pada praktiknya sering terlihat terurut secara kebetulan sesuai urutan fisik penyimpanan. Asumsi ini bisa gagal kapan saja setelah operasi seperti rebuild index atau migrasi data ke server baru.

Memakai nomor posisi kolom pada query yang sering berubah

ORDER BY 2 akan tiba-tiba mengurutkan kolom yang salah jika urutan kolom di SELECT diubah, menyebabkan bug yang sulit terdeteksi karena query tetap berjalan tanpa error apa pun.

Kapan Pendekatan Ini Tidak Cocok Dipakai

Mengurutkan data pada hasil query yang sangat besar (misalnya jutaan baris tanpa TOP atau paging) membutuhkan operasi sort yang cukup berat secara komputasi, kadang bahkan memaksa SQL Server menggunakan tempdb sebagai penyimpanan sementara jika data yang diurutkan tidak muat di memori.

Jika hasil akhir hanya butuh sebagian kecil data teratas, menggabungkan ORDER BY dengan TOP (dibahas pada materi berikutnya) jauh lebih efisien dibanding mengurutkan seluruh dataset lalu memotongnya di sisi aplikasi, karena optimizer bisa menghentikan proses lebih awal begitu jumlah baris yang diminta terpenuhi.

Ringkasan

ORDER BY memastikan hasil query tampil dalam urutan yang bisa diprediksi, yang sangat penting terutama untuk laporan dan tampilan data berpaginasi yang akan dibahas pada materi TOP dan paging selanjutnya.

Seri Belajar SQL Server:

← Sebelumnya: Filtering Data dengan WHERE di SQL Server

→ Selanjutnya: Membatasi Hasil Query dengan TOP