Java Swing, Membaca Kode Warna Heksadesimal

HexColors

Bagi Anda yang pernah bermain dengan grafik digital, misalnya Web Design, pasti pernah melihat kode warna dalam format misalnya #FFFFFF bukan? Format tersebut adalah representasi warna dalam bilangan Heksadesimal, yang mana terdiri dari 6 digit (di CSS, dapat hanya menggunakan 3 digit) , dengan mencakup 3 Warna Dasar, yaitu Red, Green, dan Blue.

Masing masing warna tersebut direpresentasikan oleh 2 digit bilangan heksadesimal, yang berarti nilai tertinggi dari bilang tersebut adalah FF atau 255, kunjungi tautan berikut untuk proses konversinya.

Dalam post kali ini, Saya ingin memberikan contoh implementasi kode warna Heksadesimal tersebut dalam bahasa pemrograman Java, khususnya dengan menggunakan komponen Swing. Class yang dapat digunakan adalah java.awt.Color. Yang mana, class tersebut dapat di-construct dengan parameter integer, yang mana dapat merupakan representasi nilai bilangan dari huruf heksadesimal.

Sedangkan untuk mengkonversi String Heksadesimal menjadi integer, dapat menggunakan fungsi bawaan Integer.parseInt(String, int Radix), dimana radix disini adalah nilai basis bilangan, dalam hal heksadesimal ini, adalah 16. Iya, heksadesimal adalah bilangan berbasis 16.

Proses konversi String Heksadesimal menjadi integer, dapat dilakukan, misalnya dengan kode berikut

1
2
3
4
5
6
7
8
9
Static Color parseColor(String hex){
    try {
        int hexValue = Integer.parseInt(hex, 16);
        return new Color(hexValue);
    } catch (Exception e){
        e.printStackTrace();
        return null;
    }
}

Dan berikut saya berikan contoh kode untuk diaplikasikan dalam aplikasi Java, tentu saja, kode berikut hanya mendemonstrasikan bagaimana proses konversi dan penggunaan Color tersebut. Berbagai error handling mungkin harus diterapkan dalam aplikasi sebenarnya.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package com.kurungkurawal.spo;
 
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
 
/**
 * Created by konglie on 3/7/16.
 */
public class HexColors extends JFrame {
    public static void main(String[] args){
        new HexColors();
    }
 
    JPanel body, headerPanel, colorPanel;
    JTextField inputColor;
    JButton btnProcess;
    public HexColors(){
        setTitle("Contoh Warna dari String Heksadesimal");
        setResizable(false);
        setPreferredSize(new Dimension(640, 480));
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 
        body = new JPanel(new BorderLayout());
 
        inputColor = new JTextField("DD3333");
        btnProcess = new JButton("Proses");
        btnProcess.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                processInput();
            }
        });
 
        headerPanel = new JPanel(new BorderLayout());
        headerPanel.setBorder(BorderFactory.createEmptyBorder(5,15,5,15));
        headerPanel.add(new JLabel("Kode Warna Heksadesimal, 6 Digit"), BorderLayout.WEST);
        headerPanel.add(inputColor, BorderLayout.CENTER);
        headerPanel.add(btnProcess, BorderLayout.EAST);
        body.add(headerPanel, BorderLayout.NORTH);
 
        colorPanel = new JPanel();
        colorPanel.setOpaque(true);
        body.add(colorPanel, BorderLayout.CENTER);
 
        getContentPane().add(body, BorderLayout.CENTER);
        pack();
        setLocationRelativeTo(null);
        setVisible(true);
 
        processInput();
    }
 
    void processInput(){
        final Color color = parseColor();
        if(color == null){
            JOptionPane.showMessageDialog(null, "Gagal membaca kode warna");
        } else {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    colorPanel.setBackground(color);
                }
            });
        }
    }
 
    Color parseColor(){
        try {
            String hex = inputColor.getText();
            if(hex.startsWith("#")){
                hex = hex.replaceAll("^#", "");
            }
            int hexValue = Integer.parseInt(hex, 16);
 
            return new Color(hexValue);
        } catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }
}

Demikian, semoga berguna untuk kita semua.

Javascript Menghapus atau mengganti data di Array dengan splice()

Kadang kita butuh untuk menghapus atau mengganti data ke n di dalam array javascript. Misalnya,

1
var arr = ['satu', 'dua', 'apel', 'empat', 'lima'];

Kita akan menghapus element apel dari array di atas.

1
2
3
4
5
var arr = ['satu', 'dua', 'apel', 'empat', 'lima']; 
arr.splice(2,1);
 
// arr sekarang adalah ["satu", "dua", "empat", "lima"]
console.log(arr);

Atau, sebagai salah satu contoh lain fungsi splice() adalah kita mengganti apel menjadi tiga, misalnya

1
2
3
4
5
var arr = ['satu', 'dua', 'apel', 'empat', 'lima']; 
arr.splice(2,1, 'tiga');
 
// arr sekarang adalah ["satu", "dua", "tiga", "empat", "lima"]
console.log(arr);

Lalu apa maksud dari splice(2,1), itu berarti bahwa kita akan menghapus mulai dari index ke 2 sebanyak 1 buah. Jangan lupa, bahwa perhitungan index array adalah dimulai dari 0. Sehingga, di contoh di atas, kata apel berada di index ke 2. Cara lain, bisa juga dengan

1
arr.splice( arr.indexOf('apel'), 1 );

arr.indexOf('apel'); berarti mencari posisi apel pada array arr. Tentu saja akan banyak sekali penggunaan fungsi splice() ini sesuai dengan kebutuhan dan pengembangan pada script yang digunakan. Namun, topik kali ini hanya bertujuan memperkenalkan cara menghapus dan mengganti data di array javascript.

Demikian, semoga berguna untuk kita semua.

Javascript Mengganti kata/karakter dalam String

String di Javascript memiliki method replace(), yang berfungsi untuk mencari sebuah nilai, yang sesuai dengan String atau Regular Expression yang telah ditentukan, dan kemudian mengganti hasil pencarian tersebut dengan String yang telah ditentukan pula.

Misalnya,

1
2
3
4
5
var str = "Selamat datang di Jakarta.";
var strBaru = str.replace('Jakarta', 'Bandung');
 
// strBaru telah menjadi "Selamat datang di Bandung"
console.log(strBaru);

Namun, perlu diingat jika menggunakan sintaks di atas, maka penggantian hanya terjadi 1 kali saja. Misalnya dalam kasus seperti di bawah ini

1
2
3
4
5
var str = "Selamat datang di Jakarta. Kota Jakarta adalah merupakan kota besar.";
var strBaru = str.replace('Jakarta', 'Bandung');
 
// strBaru telah menjadi "Selamat datang di Bandung. Kota Jakarta adalah merupakan kota besar."
console.log(strBaru);

Perhatikan bahwa, kata “Jakarta” hanya tergantikan 1 kali saja. Untuk menggantikan sebuah kata secara menyeluruh, gunakan Regular Expression, dan berikan tanda global atau g, seperti pada contoh di bawah

1
2
3
4
5
var str = "Selamat datang di Jakarta. Kota Jakarta adalah merupakan kota besar.";
var strBaru = str.replace(/Jakarta/g, 'Bandung');
 
// strBaru telah menjadi "Selamat datang di Bandung. Kota Bandung adalah merupakan kota besar."
console.log(strBaru);

Sekedar catatan, jika Anda menggunakan console.log(string), hasilnya dapat dilihat menggunakan Web Developer Tools di Google Chrome, plugin Firebug di Firefox atau tools lainnya. Atau jika bingung, gunakan saja fungsi alert Javascript.

Demikian, semoga berguna untuk kita semua.

PHP Menghitung jumlah data di table MySQL

Ada kalanya aplikasi PHP kita butuh untuk mengetahui jumlah data di dalam suatu table database, dalam contoh kali ini, database MySQL. Misalnya, mengetahui jumlah member, mengetahui jumlah transaksi, dan lain sebagainya.

Untuk keperluan tersebut, MySQL menyediakan fungsi count() untuk mengetahui jumlah data dari sebuah query. Dipahami ya, bahwa jumlah data sebuah query, tidak selalu berarti jumlah data dalam suatu table. Lebih detail mengenai ini akan dibahas lain waktu, semoga ada kesempatan itu.

Kembali ke topik kita, cara kedua adalah, menggunakan fungsi dari driver MySQL di PHP, mysql_num_rows().

Dan cara ketiga, adalah cara yang paling tradisional dan, seringnya, tidak efektif, yaitu mengambil seluruh data dari query ke dalam sebuah variable penampung, misalnya Array(), dan kemudian menghitung jumlah isi dari Array tersebut.

Kenapa cara ketiga ini (sering kali) tidak efektif ? Bayangkan jika query yang ingin kita hitung jumlah hasilnya tersebut ternyata memberikan hasil 1 juta record. Berapa banyak memory yang diperlukan untuk menampung data data tersebut ? Jawabannya tentu saja tergantung dari bagaimana struktur data per-record. Yang pasti, besar.

Tentu saja, tergantung kebutuhan Anda, saya sajikan contoh dari 3 cara di atas. Anda ada cara lain? Silakan dishare.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php
mysql_connect('localhost', 'username', 'password');
mysql_select_db('dbname');
 
$table = "tablename";
 
// Cara 1
$sql = "SELECT count(*) AS jumlah FROM $table";
$query = mysql_query($sql);
$result = mysql_fetch_array($query);
echo "Jumlah data dengan fungsi MySQL count(): {$result['jumlah']} <br/>";
 
// Cara 2
$sql = "SELECT * FROM $table";
$query = mysql_query($sql);
$count = mysql_num_rows($query);
echo "Jumlah data dengan mysql_num_rows: $count <br/>";
 
// Cara 3
$sql = "SELECT * FROM $table";
$query = mysql_query($sql);
$data = array();
while(($row = mysql_fetch_array($query)) != null){
    $data[] = $row;
}
$count = count($data);
echo "Jumlah data dari array PHP: $count";

Jangan lupa, seluruh contoh di atas adalah menghitung sebenarnya adalah menghitung jumlah data dari hasil query. Karena querynya adalah mengambil seluruh data dari $table, maka bisa dibilang contoh di atas adalah menampilkan jumlah data di dalam $table.

Fungsi-fungsi di atas dapat dikembangkan menjadi lebih kompleks, misalnya menghitung jumlah member yang hanya mendaftar tapi belum aktif, atau menghitung jumlah mahasiswa yang sudah lebih 6 tahun betah di kampus, atau menghitung jumlah transaksi yang belum lunas dan atau-atau lainnya.

Demikian semoga berguna untuk kita semua.

Format Baris Teks untuk dokumen menggunakan Java

Banyak library Java yang memberikan kemudahan untuk menciptakan dokumen dalam format Excel, PDF, dan format modern (dan canggih) lainnya. Namun bagaimana jika yang dibutuhkan hanya untuk membuat dokumen teks sederhana saja, namun membutuhkan pengaturan baris sederhana, misalnya kolom, dan rata kiri-kanan-tengah.

Kasus yang umum terjadi misalnya, ketika membuat dokumen yang akan dicetak dengan menggunakan printer Dot Matrix, misalnya membuat nota penjualan. Meskipun, tentu saja, bisa juga mencetak PDF langsung di printer tersebut, namun umumnya akan memakan waktu lebih lama dan hasilnya mungkin tidak sebagus jika mencetak file *.txt saja.

Misalnya, ingin membuat dokumen teks seperti berikut

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
            Toko Terang Gelap           
         Jln. Lampu Senter No 33        
               Kota Senja               
========================================
Nota No. 778899      29/10/2015 11:23:55
----------------------------------------
 Qty        Nama Barang          Total  
----------------------------------------
  1  Item 1                           10
  2  Item 2                          200
  3  Item 3                         3000
  4  Item 4                        40000
  5  Item 5                       500000
----------------------------------------
              Terima Kasih              
           Atas Kunjungan Anda          
========================================

Perhatikan bahwa dokumen tersebut hanya berupa teks saja, tanpa format yang Rich. Format yang ada hanyalah rata kiri, kanan, dan tengah. Namun format tersebut dapat pula diaplikasikan di dalam 1 baris, dengan kolom-kolom seperti tabel.

Format rata kiri, kanan, tengah tersebut sebenarnya hanya dihasilkan dengan jumlah spasi yang tepat agar dalam 1 baris, teks yang diinginkan dapat diletakkan di bagian yang sesuai.

Kembali ke topik tentang penggunaan Java untuk membuat dokumen seperti ini. Dokumen diatas dihasilkan dengan script Java berikut ini

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public static void main(String[] args){
 
        LineFormatter lf = new LineFormatter(40);
 
        lf.addLine("Toko Terang Gelap", LineFormatter.CENTER_ALIGN);
        lf.addLine("Jln. Lampu Senter No 33", LineFormatter.CENTER_ALIGN);
        lf.addLine("Kota Senja", LineFormatter.CENTER_ALIGN);
        lf.addDivider("=");
        lf.addLine(
                new LineColumn("Nota No. 778899", 20)
                        .addColumn("29/10/2015 11:23:55", 20, LineFormatter.RIGHT_ALIGN)
        );
        lf.addDivider("-");
 
        lf.addLine(
                new LineColumn("Qty", 5, LineFormatter.CENTER_ALIGN)
                        .addColumn("Nama Barang", 25, LineFormatter.CENTER_ALIGN)
                        .addColumn("Total", 10, LineFormatter.CENTER_ALIGN)
        );
        lf.addDivider("-");
 
        for(int i = 1; i <= 5; i++){
            lf.addLine(
                    new LineColumn(i + " ", 5, LineFormatter.CENTER_ALIGN)
                            .addColumn("Item " + i, 25)
                            .addColumn("" + (i * ((int)Math.pow(10, i))), 10, LineFormatter.RIGHT_ALIGN)
            );
        }
 
        lf.addDivider("-");
        lf.addLine("Terima Kasih", LineFormatter.CENTER_ALIGN);
        lf.addLine("Atas Kunjungan Anda", LineFormatter.CENTER_ALIGN);
        lf.addDivider("=");
 
        System.out.println( lf.render() );
}

Disini, yang saya gunakan adalah class LineFormatter dan LineColumn.

LineFormatter.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package gaisma.components.plaintext;
 
import gaisma.Params;
 
import java.util.ArrayList;
import java.util.List;
 
/**
 * Created with IntelliJ IDEA.
 * User: lee
 * Date: 11/29/13
 * Time: 9:28 PM
 * <p/>
 * Java Plain Text Line Formatter
 */
public class LineFormatter {
    public static final int LEFT_ALIGN = -1;
    public static final int CENTER_ALIGN = 0;
    public static final int RIGHT_ALIGN = 1;
 
    private int lineWidth;
    private List<String> lines;
    private String newLine = System.getProperty("line.separator");
 
    public static String LEFT_MARGIN = ""; // nothing
 
    /**
     * Initialize LineFormatter with line width initialized as 40 (epson POS printer)
     */
    public LineFormatter() {
        init(40);
    }
 
    /**
     * Initialize LineFormatter with specified line width
     *
     * @param l : line width
     */
    public LineFormatter(int l) {
        init(l);
    }
 
    /**
     * Set new line character, this is not necessary as LineFormatter automatically detect System new line character
     *
     * @param n : new line character
     * @return current LineFormatter instance
     */
    public LineFormatter setNewLine(String n) {
        this.newLine = n;
        return this;
    }
 
    private void init(int l) {
        this.lineWidth = l;
        lines = new ArrayList<String>();
    }
 
    /**
     * add new line
     *
     * @param cols LineColumn Class instance from whose columns constructed as line
     * @return current LineFormatter instance
     */
    public LineFormatter addLine(LineColumn cols) {
        return addLine(cols.getLine());
    }
 
    /**
     * add new line, with LineFormatter.LEFT_ALIGN
     *
     * @param s line to be added
     * @return current LineFormatter instance
     */
    public LineFormatter addLine(String s) {
        return addLine(s, LEFT_ALIGN);
    }
 
    /**
     * add new line, with specified alignment
     *
     * @param s     line to be added
     * @param align
     * @return current LineFormatter instance
     */
    public LineFormatter addLine(String s, int align) {
        switch (align) {
            case LEFT_ALIGN:
                return saveLine(String.format("%-" + this.lineWidth + "s", s));
            case RIGHT_ALIGN:
                return saveLine(String.format("%" + this.lineWidth + "s", s));
            case CENTER_ALIGN:
                int sw = s.length();
                int pad = (this.lineWidth - sw) / 2;
                String l = String.format("%" + pad + "s%s%" + pad + "s", "", s, "");
                return addLine(l, RIGHT_ALIGN);
        }
        return null;
    }
 
    private LineFormatter saveLine(String s) {
        if (s.length() > this.lineWidth)
            s = s.substring(0, this.lineWidth);
        this.lines.add(LEFT_MARGIN + s);
        return this;
    }
 
    /**
     * add blank line
     *
     * @return current LineFormatter instance
     */
    public LineFormatter addDivider() {
        return addLine(" ");
    }
 
    /**
     * add line with repeated character
     *
     * @param s single string to be repeated
     * @return current LineFormatter instance
     */
    public LineFormatter addDivider(String s) {
        return addLine(
                String.format("%" + lineWidth + "s", s)
                        .replace(' ', s.charAt(0))
        );
    }
 
    /**
     * produce formatted lines
     *
     * @return rendered string
     */
    public String render() {
        String text = "";
        if (lines.size() > 0) {
            text = lines.get(0);
        }
        for (int i = 1; i < lines.size(); i++) {
            text += this.newLine + lines.get(i);
        }
 
        return text;
    }
 
    /**
     * clear all buffered line
     *
     * @return current LineFormatter instance
     */
    public LineFormatter reset() {
        lines.clear();
        return this;
    }
}

LineColumn.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package gaisma.components.plaintext;
 
import java.util.ArrayList;
import java.util.List;
 
/**
 * Created with IntelliJ IDEA.
 * User: lee
 * Date: 11/29/13
 * Time: 9:39 PM
 */
public class LineColumn {
 
    private List<String> columns = new ArrayList<String>();
    public LineColumn(){
 
    }
 
    public LineColumn(String s, int width){
        this.addColumn(s, width);
    }
 
    public LineColumn addBlankSpace(int width){
        return this.addColumn(" ", width);
    }
 
    public LineColumn(String s, int width, int align){
        addColumn(s, width, align);
    }
 
    public LineColumn addColumn(String s, int width){
        return addColumn(s, width, LineFormatter.LEFT_ALIGN);
    }
 
    public LineColumn addColumn(String s, int width, int align){
        if(s.length() > width)
            s = s.substring(0, width);
 
        switch (align){
            case LineFormatter.LEFT_ALIGN:
                columns.add(String.format("%-" + width + "s", s));
            break;
            case LineFormatter.RIGHT_ALIGN:
                columns.add(String.format("%" + width + "s", s));
            break;
            case LineFormatter.CENTER_ALIGN:
                int sw = s.length();
                int pad = (width - sw) / 2;
                String l = String.format("%" + pad + "s%s%" + pad + "s", "", s, "");
                return addColumn(l, width, LineFormatter.RIGHT_ALIGN);
        }
 
        return this;
    }
 
    public String getLine(){
        String line = "";
        for(int i = 0; i < columns.size(); i++){
            line += columns.get(i);
        }
 
        return line;
    }
}

LineFormatter adalah class utama yang akan membuat format teks dapat diatur agar ditempatkan dalam format rata kiri, kanan, atau tengah. Sedangkan LineColumn, berguna agar dalam 1 baris, dapat dibuat beberapa kolom, yang masing-masing kolom dapat diberikan format sendiri sendiri.

Dua class ini saya buat karena punya kebutuhan membuat dokumen teks sederhana, dan aplikasinya sendiri diusahakan agar dapat berukuran sekecil-kecilnya. Pencarian singkat di Google tidak membuahkan hasil library yang sesuai, dan alasan itulah yang membuat saya menciptakan library ini.

Atau mungkin Anda tahu Library yang lain? Comment ya.
Terima kasih, semoga bisa berguna untuk kita semua.

Menonaktifkan PHP di folder tertentu

Terkadang kita perlu untuk menonaktifkan file dengan ektensi *.php di folder tertentu di website kita. Misalnya saja, pada instalasi WordPress, pada folder wp-content/wp-uploads, terkadang kita perlu memastikan bahwa user tidak dapat mengupload file *.php dan melakukan eksekusi file php tersebut.

Tapi pertanyaannya, bukankah kita tinggal membatasi jenis file yang boleh diupload? Betul, wordpress memang dapat membatasi jenis file yang dianggap “berbahaya” agar tidak dapat diupload.

Tapi bagaimana jika user tetap nakal dan mengupload file tersebut melalui FTP ? Gampang, jangan berikan akses write folder pada user. Namuun, dalam kasus tertentu, hal ini tidak dapat dilakukan.

Namun bagaimanapun juga, apapun alasan penggunaannya, berikut ada cara sederhana menonaktifkan script php dalam suatu folder.

Dalam contoh ini, kita akan menggunakan file .htaccess yang akan diletakan di folder yang ingin dinonaktifkan file php nya. Tentu saja, dengan asumsi Server Apache yang digunakan telah dikonfigurasi untuk membaca file .htaccess ini, yang mana, pada umumnya, memang sudah dikonfigurasi begitu.

Isikan baris berikut pada file .htaccess tersebut,

1
php_flag engine off

Atau jika Anda menggunakan PHP-CGI, bisa dengan mengisikan script berikut di .htaccess

1
2
3
4
<FilesMatch  \.php$>
        SetHandler None
        ForceType text/plain
</FilesMatch>

Maka, file *.php di folder tersebut akan disajikan sebagai teks biasa saja, dan script php di dalamnya tidak akan dieksekusi.

SQL: filter data berdasarkan beberapa baris data

Post kali ini, saya ingin membahas contoh kasus yang ditanyakan di stackoverflow.

Dimana pertanyaan inti-nya adalah, bagaimana menampilkan data post yang memiliki relasi terhadap tag dalam table relasi, namun yang diinginkan adalah melakukan filter post yang memiliki beberapa tag, bukan hanya tag tertentu saja.

Misalnya,

  1. post “Hallo Dunia” memiliki tag “tag1”
  2. post “Apa Kabar” memiliki tag “tag2”
  3. post “Pilih Saya” memiliki tag “tag1” DAN “tag2”

Nah, yang diinginkan adalah menampilkan post yang memiliki 2 tag “tag1” dan “tag2”. Bukan salah satu tag saja, di contoh di atas, akan muncul post “Pilih Saya”.

Saya mendesain ulang contoh tabel yang digunakan seperti di bawah ini.

Table yang digunakan

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
konglie=# \d
             List OF relations
 Schema |      Name       | TYPE  |  Owner  
--------+-----------------+-------+---------
 public | ppost           | TABLE | konglie
 public | ptags           | TABLE | konglie
 public | rel_ppost_ptags | TABLE | konglie
(3 ROWS)
 
konglie=# \d ppost 
            TABLE "public.ppost"
 COLUMN |         TYPE          | Modifiers 
--------+-----------------------+-----------
 pid    | INTEGER               | 
 pname  | CHARACTER VARYING(20) | 
 
konglie=# \d ptags 
            TABLE "public.ptags"
 COLUMN |         TYPE          | Modifiers 
--------+-----------------------+-----------
 tid    | INTEGER               | 
 tname  | CHARACTER VARYING(20) | 
 
konglie=# \d rel_ppost_ptags 
TABLE "public.rel_ppost_ptags"
 COLUMN |  TYPE   | Modifiers 
--------+---------+-----------
 pid    | INTEGER | 
 tid    | INTEGER |

Sample Data

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
konglie=# SELECT * FROM ppost ;
 pid | pname  
-----+--------
 100 | post 1
 101 | post 2
 102 | post 3
(3 ROWS)
 
konglie=# SELECT * FROM ptags ;
 tid  | tname 
------+-------
 1000 | tag1
 1001 | tag2
 1002 | tag3
 1004 | tag5
 1005 | tag6
 1003 | tag4
(6 ROWS)
 
konglie=# SELECT * FROM rel_ppost_ptags ;
 pid | tid  
-----+------
 100 | 1000    *
 100 | 1001    *
 100 | 1003    *
 101 | 1000    **
 101 | 1001    **
 102 | 1001
 102 | 1002
 102 | 1001
 101 | 1001
 101 | 1003    **
 102 | 1001
(11 ROWS)

Data yang diinginkan adalah bagaimana mengambil data atau post, yang memiliki beberapa tag tertentu. Misalnya, tampilkan data post yang memiliki tag tag1,tag2, dan tag4. Data yang hanya memiliki salah satu tag tersebut tidak ikut ditampilkan. Hanya post yang memiliki ketiga tag tersebut saja, yang akan ditampilkan.

Dalam contoh ini, post yang diinginkan adalah post dengan pid 100 dan 101, perhatikan yang diberi tanda * dan ** di atas.

Solusi yang terpikirkan adalah sebagai berikut.

1. Tentukan tid dari tag yang diinginkan, dalam contoh ini, adalah mengambil tid dari table ptags berdasarkan tname.

1
2
3
4
5
6
7
konglie=# SELECT tid FROM ptags WHERE tname IN ('tag1', 'tag2', 'tag4');
 tid  
------
 1000
 1001
 1003
(3 ROWS)

2. Kumpulkan terlebih dahulu seluruh post yang memiliki tag tag yang diinginkan. Sampai tahap ini, post dengan tag yang tidak lengkap pun akan dimunculkan.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
konglie=# SELECT pid, tid FROM rel_ppost_ptags WHERE tid IN ( SELECT tid FROM ptags WHERE tname IN ('tag1', 'tag2', 'tag4' ) );
 pid | tid  
-----+------
 100 | 1000
 100 | 1001
 100 | 1003
 101 | 1000
 101 | 1001
 102 | 1001     *
 102 | 1001     *
 101 | 1001
 101 | 1003
 102 | 1001     *
(10 ROWS)

Perhatikan baris diberi tanda *, yang merupakan data-data yang terduplikasi. Sebenarnya data ini hanya memberi contoh saja, dalam kenyataannya, seharusnya hal ini tidak akan terjadi, dan table rel_ppost_ptags akan lebih baik jika memberikan batasan UNIQUE atau PRIMARY KEY pada 2 field, pid dan tid. Sehingga tidak akan ada data yang menduplikasi pid dan tid secara bersamaan.

Untuk itu, akan kita tambahkan keyword distinct

1
2
3
4
5
6
7
8
9
10
11
konglie=# SELECT DISTINCT pid, tid FROM rel_ppost_ptags WHERE tid IN ( SELECT tid FROM ptags WHERE tname IN ('tag1', 'tag2', 'tag4' ) );
 pid | tid  
-----+------
 100 | 1001
 100 | 1003
 102 | 1001
 101 | 1000
 100 | 1000
 101 | 1001
 101 | 1003
(7 ROWS)

Dengan distinct, baris baris yang terduplikasi akan dihilangkan.

3. Hitung jumlah data berdasarkan pid

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
konglie=# SELECT
        pid, COUNT(*)
    FROM (
        SELECT DISTINCT pid, tid FROM rel_ppost_ptags WHERE tid IN ( 
            SELECT tid FROM ptags WHERE tname IN ('tag1', 'tag2', 'tag4' ) 
        )                                             
    ) tmp
    GROUP BY pid;
 
 pid | COUNT 
-----+-------
 101 |     3
 100 |     3
 102 |     1
(3 ROWS)

4. Pada langkah kedua, kita sudah menampilkan data pid dan tid sesuai dengan tag yang kita inginkan, hanya saja belum menentukan apakah sebuah pid memiliki semua kriteria tag yang kita inginkan. Namun, kita dapat pastikan bahwa, apabila pid tersebut memiliki semua tag yang kita inginkan, maka jumlah pid dari hasil sql tersebut akan ada sebanyak n buah, dimana n adalah jumlah tag yang kita inginkan, dalam contoh ini adalah 3. Nah, pada langkah di atas, kita sudah menghitung jumlah pid yang ada, langkah terakhir ini hanya memfilter semuah pid yang berjumlah n tadi saja, dengan memberikan keyword having.

konglie=# select
        pid, count(*)
    from (
        select distinct pid, tid from rel_ppost_ptags where tid in ( 
            select tid from ptags where tname in ('tag1', 'tag2', 'tag4' ) 
        )                                             
    ) tmp
    group by pid having count(*) = 3;

 pid | count 
-----+-------
 101 |     3
 100 |     3
(2 rows)

Dengan demikian, kita sudah dapat memfilter post yang memiliki tag-tag tertentu secara keseluruhan, bukan hanya memiliki salah satu tag saja.

Sebagai tambahan informasi, sql di atas diujicobakan menggunakan Database PostgreSQL 9.4, namun saya rasa tidak ada fungsi spesific database di atas sehingga besar kemungkinan akan dapat digunakan di database server lainnya.

Semoga bermanfaat.