Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Android

วิธีใช้ DISTINCT ใน SQLite Android เพื่อกำจัดข้อมูลซ้ำ

ภาพรวม

SQLite เป็นฐานข้อมูล SQL แบบ Open Source ที่เก็บข้อมูลในไฟล์ข้อความบนอุปกรณ์ Android มี SQLite ในตัว รองรับฟีเจอร์ฐานข้อมูลเชิงสัมพันธ์ครบถ้วน

ตัวอย่างนี้สาธิตการใช้ DISTINCT ในคำสั่ง SQLite เพื่อกำจัดแถวข้อมูลที่ซ้ำกัน (Duplicate Rows) เมื่อดึงข้อมูลมาแสดง

ขั้นตอนการสร้าง

ขั้นตอนที่ 1: สร้างโปรเจกต์ใหม่

Android Studio → File → New Project → กรอกรายละเอียด

ขั้นตอนที่ 2: จัดรูปแบบหน้าจอ (res/layout/activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical">

    <EditText
        android:id="@+id/name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="ป้อนชื่อ" />

    <EditText
        android:id="@+id/salary"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="numberDecimal"
        android:hint="ป้อนเงินเดือน" />

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:id="@+id/save"
            android:text="บันทึก"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <Button
            android:id="@+id/refresh"
            android:text="รีเฟรช"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <Button
            android:id="@+id/update"
            android:text="อัปเดต"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

    </LinearLayout>

    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

ขั้นตอนที่ 3: เขียนโค้ด MainActivity (src/MainActivity.java)

package com.example.andy.myapplication;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;
import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {
    Button save, refresh;
    EditText name, salary;
    private ListView listView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final DatabaseHelper helper = new DatabaseHelper(this);
        final ArrayList<String> arrayList = helper.getAllContacts();

        name = findViewById(R.id.name);
        salary = findViewById(R.id.salary);
        listView = findViewById(R.id.listView);

        final ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(
            this, android.R.layout.simple_list_item_1, arrayList);
        listView.setAdapter(arrayAdapter);

        findViewById(R.id.refresh).setOnClickListener(v -> {
            arrayList.clear();
            arrayList.addAll(helper.getAllContacts());
            arrayAdapter.notifyDataSetChanged();
            listView.invalidateViews();
            listView.refreshDrawableState();
        });

        findViewById(R.id.save).setOnClickListener(v -> {
            String nameStr = name.getText().toString().trim();
            String salaryStr = salary.getText().toString().trim();

            if (!nameStr.isEmpty() && !salaryStr.isEmpty()) {
                if (helper.insert(nameStr, salaryStr)) {
                    Toast.makeText(this, "บันทึกสำเร็จ", Toast.LENGTH_LONG).show();
                } else {
                    Toast.makeText(this, "บันทึกไม่สำเร็จ", Toast.LENGTH_LONG).show();
                }
            } else {
                name.setError("กรุณาป้อนชื่อ");
                salary.setError("กรุณาป้อนเงินเดือน");
            }
        });
    }
}

ขั้นตอนที่ 4: สร้าง DatabaseHelper (src/DatabaseHelper.java)

package com.example.andy.myapplication;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import java.io.IOException;
import java.util.ArrayList;

class DatabaseHelper extends SQLiteOpenHelper {
    public static final String DATABASE_NAME = "salaryDatabase5";
    public static final String CONTACTS_TABLE_NAME = "SalaryDetails";

    public DatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, 1);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        try {
            db.execSQL("CREATE TABLE " + CONTACTS_TABLE_NAME + 
                "(id INTEGER PRIMARY KEY, name TEXT, salary TEXT, datetime DEFAULT CURRENT_TIMESTAMP)");
        } catch (SQLiteException e) {
            try { throw new IOException(e); } 
            catch (IOException e1) { e1.printStackTrace(); }
        }
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("DROP TABLE IF EXISTS " + CONTACTS_TABLE_NAME);
        onCreate(db);
    }

    public boolean insert(String name, String salary) {
        SQLiteDatabase db = this.getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put("name", name);
        values.put("salary", salary);
        // ใช้ REPLACE: ถ้ามี PRIMARY KEY ซ้ำ จะแทนที่แถวเดิม
        db.replace(CONTACTS_TABLE_NAME, null, values);
        return true;
    }

    public ArrayList<String> getAllContacts() {
        SQLiteDatabase db = this.getReadableDatabase();
        ArrayList<String> arrayList = new ArrayList<>();
        
        // ใช้ DISTINCT เพื่อกำจัดชื่อซ้ำ และ ORDER BY เรียงตามชื่อ แล้วเงินเดือน
        Cursor res = db.rawQuery(
            "SELECT DISTINCT name FROM " + CONTACTS_TABLE_NAME + 
            " ORDER BY name, salary", null);
        
        res.moveToFirst();
        while (!res.isAfterLast()) {
            arrayList.add(res.getString(res.getColumnIndex("name")));
            res.moveToNext();
        }
        res.close();
        return arrayList;
    }

    public boolean update(String name, String salary) {
        SQLiteDatabase db = this.getWritableDatabase();
        db.execSQL("UPDATE " + CONTACTS_TABLE_NAME + 
            " SET name = '" + name + "', salary = '" + salary + "'");
        return true;
    }

    public boolean delete() {
        SQLiteDatabase db = this.getWritableDatabase();
        db.execSQL("DELETE FROM " + CONTACTS_TABLE_NAME);
        return true;
    }
}

การทดสอบ

รันแอปพลิเคชัน เพิ่มข้อมูลซ้ำกันหลายรายการ แล้วกด Refresh

วิธีใช้ DISTINCT ใน SQLite Android เพื่อกำจัดข้อมูลซ้ำ

ก่อนใช้ DISTINCT จะแสดงข้อมูลซ้ำ

วิธีใช้ DISTINCT ใน SQLite Android เพื่อกำจัดข้อมูลซ้ำ

หลังบันทึกข้อมูลแล้วกด Refresh จะแสดงชื่อเรียงลำดับ ASC

วิธีใช้ DISTINCT ใน SQLite Android เพื่อกำจัดข้อมูลซ้ำ

ความรู้เพิ่มเติมเกี่ยวกับ DISTINCT

การทำงานของ DISTINCT

-- กำจัดแถวที่ซ้ำกันทุกคอลัมน์
SELECT DISTINCT * FROM table;

-- กำจัดแถวที่ซ้ำกันเฉพาะคอลัมน์ที่ระบุ
SELECT DISTINCT name, salary FROM table;

-- นับค่าที่ไม่ซ้ำกัน
SELECT COUNT(DISTINCT name) FROM table;

DISTINCT vs GROUP BY

คุณสมบัติ DISTINCT GROUP BY
วัตถุประสงค์ กำจัดแถวซ้ำ จัดกลุ่มข้อมูลเพื่อ Aggregate
Performance เร็วกว่าสำหรับการกำจัดซ้ำอย่างเดียว ช้ากว่า แต่ยืดหยุ่นกว่า
ใช้กับ Aggregate ได้ (COUNT DISTINCT) ได้ (SUM, AVG, COUNT ต่อกลุ่ม)

การป้องกันข้อมูลซ้ำที่ระดับ Database (ดีกว่า DISTINCT)

-- 1. UNIQUE Constraint
CREATE TABLE users (
    id INTEGER PRIMARY KEY,
    email TEXT UNIQUE NOT NULL
);

-- 2. PRIMARY KEY บนหลายคอลัมน์ (Composite Key)
CREATE TABLE user_roles (
    user_id INTEGER,
    role_id INTEGER,
    PRIMARY KEY (user_id, role_id)
);

-- 3. INSERT OR IGNORE / INSERT OR REPLACE
INSERT OR IGNORE INTO users (email) VALUES ('test@example.com');
INSERT OR REPLACE INTO users (email, name) VALUES ('test@example.com', 'New Name');

Best Practices

  • ใช้ UNIQUE Constraint ป้องกันข้อมูลซ้ำตั้งแต่ต้นทาง (Database Level)
  • DISTINCT ใช้ตอน Query เมื่อไม่สามารถควบคุมข้อมูลนำเข้าได้
  • หลีกเลี่ยง SELECT DISTINCT * บนตารางใหญ่ (Performance)
  • ปิด Cursor เสมอหลังใช้งาน
  • พิจารณาใช้ Room Database พร้อม @Entity(indices = @Index(value = "name", unique = true))