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

วิธีกรองข้อมูล SQLite ด้วย WHERE, NOT IN ใน Android

Query SQLite ด้วย WHERE ... NOT IN ใน Android

บทความนี้สาธิตการใช้ SQLite WHERE Clause ร่วมกับ NOT IN Operator เพื่อกรองข้อมูลที่ ไม่อยู่ในรายการที่กำหนด ใช้งานได้กับทุกเวอร์ชัน Android

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

Android Studio → File → New Project → Empty Activity

ขั้นตอนที่ 2: Layout - activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:orientation="vertical"
        android:padding="16dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="จัดการข้อมูลพนักงาน (NOT IN Demo)"
            android:textSize="18sp"
            android:textStyle="bold"
            android:layout_marginBottom="16dp" />

        <EditText
            android:id="@+id/etName"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="ชื่อพนักงาน"
            android:layout_marginBottom="8dp" />

        <EditText
            android:id="@+id/etSalary"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="เงินเดือน"
            android:inputType="numberDecimal"
            android:layout_marginBottom="16dp" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:layout_marginBottom="16dp">

            <Button
                android:id="@+id/btnSave"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:layout_marginEnd="4dp"
                android:text="บันทึก" />

            <Button
                android:id="@+id/btnRefresh"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:layout_marginStart="4dp"
                android:text="รีเฟรช (NOT IN)" />

        </LinearLayout>

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/rvEmployees"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1" />

    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

ขั้นตอนที่ 3: DatabaseHelper.java

package com.example.sqlitenotindemo;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import java.util.ArrayList;
import java.util.List;

public class DatabaseHelper extends SQLiteOpenHelper {

    private static final String DATABASE_NAME = "SalaryDB";
    private static final int DATABASE_VERSION = 1;
    private static final String TABLE_EMPLOYEES = "Employees";
    private static final String COL_ID = "id";
    private static final String COL_NAME = "name";
    private static final String COL_SALARY = "salary";

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

    @Override
    public void onCreate(SQLiteDatabase db) {
        String createTable = "CREATE TABLE " + TABLE_EMPLOYEES + " (" +
                COL_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
                COL_NAME + " TEXT NOT NULL, " +
                COL_SALARY + " REAL NOT NULL" +
                ")";
        db.execSQL(createTable);
    }

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

    public boolean insertEmployee(String name, double salary) {
        SQLiteDatabase db = this.getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put(COL_NAME, name);
        values.put(COL_SALARY, salary);
        long result = db.insert(TABLE_EMPLOYEES, null, values);
        return result != -1;
    }

    /**
     * ดึงข้อมูลพนักงานที่เงินเดือนไม่อยู่ในรายการที่กำหนด
     * @param excludedSalaries รายการเงินเดือนที่ต้องการยกเว้น
     */
    public List<String> getEmployeesNotInSalaries(List<Double> excludedSalaries) {
        List<String> result = new ArrayList<>();
        SQLiteDatabase db = this.getReadableDatabase();

        // สร้าง Placeholder สำหรับ NOT IN (?, ?, ?)
        StringBuilder placeholders = new StringBuilder();
        for (int i = 0; i < excludedSalaries.size(); i++) {
            if (i > 0) placeholders.append(",");
            placeholders.append("?");
        }

        String query = "SELECT " + COL_NAME + " FROM " + TABLE_EMPLOYEES +
                " WHERE " + COL_SALARY + " NOT IN (" + placeholders + ")";

        // แปลง List<Double> เป็น String[] สำหรับ selectionArgs
        String[] args = new String[excludedSalaries.size()];
        for (int i = 0; i < excludedSalaries.size(); i++) {
            args[i] = String.valueOf(excludedSalaries.get(i));
        }

        Cursor cursor = db.rawQuery(query, args);
        if (cursor.moveToFirst()) {
            do {
                result.add(cursor.getString(cursor.getColumnIndexOrThrow(COL_NAME)));
            } while (cursor.moveToNext());
        }
        cursor.close();
        return result;
    }

    // เมธอดเดิมสำหรับแสดงทั้งหมด
    public List<String> getAllEmployees() {
        List<String> result = new ArrayList<>();
        SQLiteDatabase db = this.getReadableDatabase();
        Cursor cursor = db.rawQuery("SELECT " + COL_NAME + " FROM " + TABLE_EMPLOYEES, null);
        if (cursor.moveToFirst()) {
            do {
                result.add(cursor.getString(cursor.getColumnIndexOrThrow(COL_NAME)));
            } while (cursor.moveToNext());
        }
        cursor.close();
        return result;
    }
}

ขั้นตอนที่ 4: MainActivity.java

package com.example.sqlitenotindemo;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.Arrays;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private DatabaseHelper dbHelper;
    private EditText etName, etSalary;
    private Button btnSave, btnRefresh;
    private RecyclerView rvEmployees;
    private EmployeeAdapter adapter;

    // เงินเดือนที่ต้องการยกเว้น (NOT IN)
    private final List<Double> excludedSalaries = Arrays.asList(10000.0, 20000.0, 50000.0);

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

        dbHelper = new DatabaseHelper(this);

        etName = findViewById(R.id.etName);
        etSalary = findViewById(R.id.etSalary);
        btnSave = findViewById(R.id.btnSave);
        btnRefresh = findViewById(R.id.btnRefresh);
        rvEmployees = findViewById(R.id.rvEmployees);
        rvEmployees.setLayoutManager(new LinearLayoutManager(this));

        adapter = new EmployeeAdapter();
        rvEmployees.setAdapter(adapter);

        // โหลดข้อมูลทั้งหมดตอนเริ่มต้น
        loadAllEmployees();

        btnSave.setOnClickListener(v -> saveEmployee());
        btnRefresh.setOnClickListener(v -> loadFilteredEmployees());
    }

    private void saveEmployee() {
        String name = etName.getText().toString().trim();
        String salaryStr = etSalary.getText().toString().trim();

        if (name.isEmpty() || salaryStr.isEmpty()) {
            Toast.makeText(this, "กรุณากรอกข้อมูลให้ครบ", Toast.LENGTH_SHORT).show();
            return;
        }

        double salary = Double.parseDouble(salaryStr);
        if (dbHelper.insertEmployee(name, salary)) {
            Toast.makeText(this, "บันทึกสำเร็จ", Toast.LENGTH_SHORT).show();
            etName.setText("");
            etSalary.setText("");
            loadAllEmployees();
        } else {
            Toast.makeText(this, "บันทึกล้มเหลว", Toast.LENGTH_SHORT).show();
        }
    }

    private void loadAllEmployees() {
        adapter.setData(dbHelper.getAllEmployees());
    }

    private void loadFilteredEmployees() {
        List<String> filtered = dbHelper.getEmployeesNotInSalaries(excludedSalaries);
        adapter.setData(filtered);
        Toast.makeText(this, "กรองเงินเดือนไม่อยู่ใน: " + excludedSalaries, Toast.LENGTH_SHORT).show();
    }
}

ขั้นตอนที่ 5: EmployeeAdapter.java

package com.example.sqlitenotindemo;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;

public class EmployeeAdapter extends RecyclerView.Adapter<EmployeeAdapter.ViewHolder> {

    private final List<String> items = new ArrayList<>();

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(android.R.layout.simple_list_item_1, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        holder.textView.setText(items.get(position));
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    public void setData(List<String> newData) {
        items.clear();
        items.addAll(newData);
        notifyDataSetChanged();
    }

    static class ViewHolder extends RecyclerView.ViewHolder {
        TextView textView;
        ViewHolder(View itemView) {
            super(itemView);
            textView = itemView.findViewById(android.R.id.text1);
        }
    }
}

คำอธิบาย Query NOT IN

-- SQL ตรงๆ
SELECT name FROM Employees WHERE salary NOT IN (10000, 20000, 50000);

-- ใน Android ใช้ Parameterized Query (ป้องกัน SQL Injection)
String query = "SELECT name FROM Employees WHERE salary NOT IN (?, ?, ?)";
String[] args = {"10000", "20000", "50000"};
Cursor cursor = db.rawQuery(query, args);

จุดสำคัญของ NOT IN

  1. NULL Values: หากคอลัมน์มีค่า NULL, NOT IN (..., NULL) จะคืนค่า 0 แถวเสมอ (เพราะ NULL ไม่เท่ากับอะไรเลย) → ควรเพิ่ม AND salary IS NOT NULL
  2. Performance: NOT IN ช้ากว่า NOT EXISTS ในบางกรณี (Subquery)
  3. Empty List: NOT IN () เป็น Syntax Error → ต้องเช็คว่ารายการไม่ว่างก่อน Query

ตัวเลือก: ใช้ NOT EXISTS (ประสิทธิภาพดีกว่าสำหรับ Subquery)

SELECT name FROM Employees e
WHERE NOT EXISTS (
    SELECT 1 FROM (VALUES (10000), (20000), (50000)) AS excluded(sal)
    WHERE e.salary = excluded.sal
)

สรุป

การใช้ WHERE ... NOT IN ใน SQLite Android ทำได้ง่ายด้วย rawQuery() และ Parameterized Query ควรระวังค่า NULL และ Empty List และพิจารณา NOT EXISTS สำหรับ Subquery ที่ซับซ้อน