Tag: MySQL

  • Mencari Nilai Maksimum Per Kategori SQL

    Salah satu masalah paling umum yang melibatkan query database adalah mencari nilai terbesar/terkecil pada tiap kategori/tipe. Sebagai contoh adalah mencari pencetak gol terbanyak pada masing-masing dari 4 klub teratas sepak bola Liga Inggris berikut ini.

    Nama                    Klub           Gol
    ------------------------------------------
    Sergio Agüero           Man City       23
    Edin Džeko              Man City       14
    Mario Balotelli         Man City       13
    Wayne Rooney	        Man United     27
    Javier Hernández        Man United     10
    Robin Van Persie        Arsenal        30
    Emmanuel Adebayor       Tottenham      17
    Jermain Defoe           Tottenham      11
    Rafael Van der Vaart    Tottenham      11
    

    Berikut ini adalah hasil yang diinginkan.

    Nama                    Klub           Gol
    ------------------------------------------
    Robin Van Persie        Arsenal        30
    Wayne Rooney	        Man United     27
    Sergio Agüero           Man City       23
    Emmanuel Adebayor       Tottenham      17
    

    Untuk menghasilkan keluaran seperti di atas, paling tidak kita membutuhkan dua langkah. Mencari jumlah gol yang diinginkan, kemudian tampilkan field lainnya berdasarkan gol yang sudah dicari tadi.

    Langkah pertama adalah mencari gol terbanyak pada masing-masing klub tanpa melihat siapa pencetak golnya.

    SELECT Klub, MAX(Gol) as maxGol 
    FROM `skorer` 
    GROUP BY Klub
    
    Klub           maxGol
    ---------------------
    Arsenal        30
    Man City       23
    Man United     27
    Tottenham      17
    

    Hasil bisa berbeda tergantung teknik penyortingan yang digunakan. Pada contoh di atas penyortingan menaik (ascending) berdasarkan field “Klub”.

    Langkah kedua adalah menampilkan field sisanya (Nama) dengan cara menggabungkan table hasil query pertama dengan table mula-mula (skorer).

    SELECT b.Nama, b.Klub, b.Gol
    FROM (
      SELECT Klub, MAX(Gol) AS maxGol
      FROM `skorer` 
      GROUP BY Klub
    ) AS a
    INNER JOIN `skorer` AS b ON 
      a.Klub = b.Klub AND a.maxGol = b.Gol
    
    Nama                    Klub           Gol
    ------------------------------------------
    Sergio Agüero           Man City       23
    Wayne Rooney	        Man United     27
    Robin Van Persie        Arsenal        30
    Emmanuel Adebayor       Tottenham      17
    

    Jika ingin diurutkan sesuai contoh, tinggal tambahkan ORDER BY Gol DESC pada akhir query.

    Sebenarnya ada query yang lebih singkat untuk menampilkan hasil seperti di atas, yaitu dengan menggunakan sub-query untuk mencari gol terbanyak pada masing-masing klub.

    SELECT * 
    FROM `skorer`
    WHERE Gol = (
      SELECT MAX(Gol) 
      FROM `skorer` AS a 
      WHERE a.Klub = skorer.Klub
    )
    
  • Akses Database Menggunakan PDO pada PHP

    Sebagian besar programer PHP pasti pernah membuat aplikasi yang harus berinteraksi dengan database. Sampai pada PHP versi 5.1, pilihan yang direkomendasikan adalah menggunakan native drivers seperti Mysql Extensions.

    Sayangnya, pada PHP versi 5.4 penggunaan Mysql Extensions mulai ditinggalkan, dan akan dihapus secara keseluruhan pada PHP versi 5.5. Itu artinya, penggunaan fungsi mysql_connect() atau mysql_query(), seperti pada postingan yang lalu, harus diganti. Pilihannya adalah menggunakan Mysqli Extensions atau yang akan dibahas berikut ini, PDO.

    PDO (PHP Data Object), yang diperkenalkan sejak PHP versi 5.1, menyediakan antarmuka untuk berinteraksi dengan berbagai jenis database secara seragam. Gampangnya, kita bisa mengakses database MySQL, Firebird, atau IBM dengan syntax yang sama. Namun tidak semua driver otomatis tersedia pada sistem Anda. Untuk memeriksanya kita gunakan:

    <?php
    print_r(PDO::getAvailableDrivers()); 
    

    Pada artikel ini menggunakan database MySQL, jadi pastikan tulisan berikut ini yang muncul.

    Array ( [0] => mysql )

    Berikut ini adalah syntax untuk koneksi awal dengan database dan query SELECT MySQL.

    <?php
    $pdo = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
    $pdo->query('SELECT nama FROM tabel_pengguna WHERE id = ' . $_GET['id']);
    

    Kode di atas adalah contoh yang buruk karena data dari pengguna langsung dimasukan ke dalam query SQL tanpa difilter, sehingga rentan disusupi kode dari luar (SQL Injection). Jika dahulu kita biasa menggunakan fungsi mysql_real_escape_string(), sekarang dapat menggunakan PDO statement prepare() dan bindParam().

    <?php
    $pdo = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
    $stmt = $pdo->prepare('SELECT nama FROM tabel_pengguna WHERE id = :id');
    $stmt->bindParam(':id', filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT), PDO::PARAM_INT);
    $stmt->execute();
    

    Pada baris ke-3, masukan dari pengguna diganti dengan sebuah placeholder. Nama untuk placeholder dimulai dengan tanda titik dua (:). Fungsi filter_input() pada baris ke-4 digunakan untuk memastikan masukan dari luar, dalam hal ini $_GET['id'], benar-benar berupa angka integer. Dengan PDO statement dan bindParam() di atas, kode Anda akan aman dari serangan SQL Injection.

    Meskipun kode di atas dapat berjalan dengan baik, namun Anda akan mendapat pesan peringatan (notice):

    Only variables should be passed by reference bla.. bla..

    Itu terjadi karena parameter kedua dari bindParam() harus berupa variabel. Jadi kode di atas diubah menjadi seperti ini:

    <?php
    $pdo = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
    $stmt = $pdo->prepare('SELECT nama FROM tabel_pengguna WHERE id = :id');
    
    $id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT);
    $stmt->bindParam(':id', $id, PDO::PARAM_INT);
    $stmt->execute();
    

    Untuk contoh berikutnya, akan kita coba query INSERT dan UPDATE.

    <?php
    $pdo = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
    $stmt = $pdo->prepare('INSERT INTO tabel_pengguna (id, nama) VALUES (:id, :nama)');
    
    $id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT);
    $nama = filter_input(INPUT_GET, 'nama', FILTER_SANITIZE_STRING)
    $stmt->bindParam(':id', $id, PDO::PARAM_INT);
    $stmt->bindParam(':nama', $nama, PDO::PARAM_STR);
    $stmt->execute();
    
    <?php
    $pdo = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
    $stmt = $pdo->prepare('UPDATE tabel_pengguna SET nama = :nama WHERE id = :id');
    
    $id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT);
    $nama = filter_input(INPUT_GET, 'nama', FILTER_SANITIZE_STRING)
    $stmt->bindParam(':id', $id, PDO::PARAM_INT);
    $stmt->bindParam(':nama', $nama, PDO::PARAM_STR);
    $stmt->execute();
    

    Cara mencoba kode-kode di atas, langsung ketik saja di address bar. Contoh: localhost/namafile.php?id=10&nama=ronaldo. Jangan lupa isi variabel-variabel $host, $dbname, $user, dan $pass.

  • Menyesuaikan Zona Waktu di MySQL dan PHP

    Ketika membuat sebuah aplikasi website, seringkali waktu yang ditunjukkan oleh server tidak sama dengan waktu client. Ini terjadi karena server terletak di zona waktu yang berbeda dengan client. Misalnya server yang berlokasi di USA, jelas berada di zona waktu yang berbeda dengan client di Indonesia.

    echo date('d-m-Y H:i:s');
    

    Kode PHP di atas akan menampilkan waktu aktual pada server. Jika dicoba di localhost tentu saja waktunya akan sama seperti komputer lokal, namun hasilnya bisa berbeda jika dicoba pada server web Anda. Agar hasilnya sesuai dengan waktu di komputer kita, tambahkan pengaturan zona waktu.

    date_default_timezone_set('Asia/Jakarta');
    echo date('d-m-Y H:i:s');
    

    Untuk zona waktu yang lain bisa menyesuaikan.

    Namun cara di atas memiliki beberapa kelemahan, terutama pada aplikasi yang menggunakan database. Selain kemungkinan terkena bug Y2K38, MySQL dan PHP memiliki pengaturan zona waktu yang terpisah. Misal kita menjalankan fungsi NOW() pada MySQL, waktu yang ditampilkan tidak sesuai dengan zona waktu kita. Selain itu, aplikasi juga membutuhkan tambahan proses setiap kali mengolah data dari field waktu pada MySQL agar sesuai dengan zona waktu kita.

    Agar proses penyesuaian waktu bisa berjalan secara otomatis, kita harus mengetahui berapa selisih waktu client dengan UTC, kemudian beritahukan kepada MySQL. Pertama-tama dengan bantuan class Datetime, kita dapatkan offset (untuk menutupi selisih yang ada) dalam detik, kemudian mudah saja kita konversikan ke dalam menit dan jam.

    $sekarang = new DateTime();
    $menit = $sekarang -> getOffset() / 60;
    

    Kemudian cari tau apakah offset bernilai positif atau negatif, lalu konversikan saja ke bilangan positif untuk memudahkan penghitungan.

    $tanda = ($menit < 0 ? -1 : 1);
    $menit = abs($menit);
    $jam = floor($menit / 60);
    $menit -= $jam * 60;
    

    Gunakan fungsi sprintf untuk memformat string agar bisa diterima MySQL (format yang bisa diterima misalnya: +07:00).

    $offset = sprintf('%+d:%02d', $tanda * $jam, $menit);
    

    Terakhir jalankan query SET time_zone setelah mengkoneksikan aplikasi dengan database. Silakan ganti dengan koneksi database yang Anda gunakan.

    mysql_connect($server, $username, $password);
    mysql_select_db($database);
    
    mysql_query("SET time_zone = '$offset'");
    

    Akhirnya pengaturan zona waktu antara PHP dan MySQL telah sesuai. Berikut ini adalah gabungan kode-kode snippet di atas.

    <?php
    date_default_timezone_set('Asia/Jakarta');
    
    $sekarang = new DateTime();
    $menit = $sekarang -> getOffset() / 60;
    
    $tanda = ($menit < 0 ? -1 : 1);
    $menit = abs($menit);
    $jam = floor($menit / 60);
    $menit -= $jam * 60;
    
    $offset = sprintf('%+d:%02d', $tanda * $jam, $menit);
    
    mysql_connect($server, $username, $password);
    mysql_select_db($database);
    
    mysql_query("SET time_zone = '$offset'");