Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Android

Cách sử dụng ORDER BY trong SQLite Android

ORDER BY trong SQLite - Sắp xếp kết quả truy vấn

Mệnh đề ORDER BY sắp xếp các hàng kết quả theo một hoặc nhiều cột. Mặc định là ASC (tăng dần), có thể dùng DESC (giảm dần).

Bước 1: Tạo dự án

Android Studio → File → New Project.

Bước 2: Giao diện (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"
    android:padding="16dp">

    <EditText android:id="@+id/name" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Nhập tên" />
    <EditText android:id="@+id/salary" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Nhập lương" android:inputType="numberDecimal" />

    <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_marginTop="8dp">
        <Button android:id="@+id/save" android:text="Lưu" android:layout_width="wrap_content" android:layout_height="wrap_content" />
        <Button android:id="@+id/refresh" android:text="Làm mới" android:layout_width="wrap_content" android:layout_height="wrap_content" />
        <Button android:id="@+id/update" android:text="Cập nhật" 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" android:layout_marginTop="16dp" />
</LinearLayout>

Bước 3: MainActivity.java

package com.example.sqliteorderby;

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 btnSave, btnRefresh, btnUpdate;
    EditText edtName, edtSalary;
    ListView listView;
    DatabaseHelper dbHelper;
    ArrayList dataList;
    ArrayAdapter adapter;

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

        dbHelper = new DatabaseHelper(this);
        dataList = new ArrayList<>(dbHelper.getAllContacts());
        
        edtName = findViewById(R.id.name);
        edtSalary = findViewById(R.id.salary);
        listView = findViewById(R.id.listView);
        btnSave = findViewById(R.id.save);
        btnRefresh = findViewById(R.id.refresh);
        btnUpdate = findViewById(R.id.update);

        adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, dataList);
        listView.setAdapter(adapter);

        btnRefresh.setOnClickListener(v -> refreshList());
        
        btnSave.setOnClickListener(v -> {
            String name = edtName.getText().toString().trim();
            String salary = edtSalary.getText().toString().trim();
            if (name.isEmpty() || salary.isEmpty()) {
                showError(name, salary);
                return;
            }
            if (dbHelper.insert(name, salary)) {
                Toast.makeText(this, "Đã thêm", Toast.LENGTH_SHORT).show();
                refreshList();
            } else {
                Toast.makeText(this, "Thêm thất bại", Toast.LENGTH_SHORT).show();
            }
        });

        btnUpdate.setOnClickListener(v -> {
            String name = edtName.getText().toString().trim();
            String salary = edtSalary.getText().toString().trim();
            if (name.isEmpty() || salary.isEmpty()) {
                showError(name, salary);
                return;
            }
            if (dbHelper.update(name, salary)) {
                Toast.makeText(this, "Đã cập nhật", Toast.LENGTH_SHORT).show();
                refreshList();
            } else {
                Toast.makeText(this, "Cập nhật thất bại", Toast.LENGTH_SHORT).show();
            }
        });
    }

    private void refreshList() {
        dataList.clear();
        dataList.addAll(dbHelper.getAllContacts());
        adapter.notifyDataSetChanged();
    }

    private void showError(String name, String salary) {
        if (name.isEmpty()) edtName.setError("Nhập tên");
        if (salary.isEmpty()) edtSalary.setError("Nhập lương");
    }
}

Bước 4: DatabaseHelper.java - ORDER BY name ASC

package com.example.sqliteorderby;

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;

public class DatabaseHelper extends SQLiteOpenHelper {
    private static final String DATABASE_NAME = "salaryDatabase";
    private static final String TABLE_NAME = "SalaryDetails";
    private static final int DB_VERSION = 1;

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

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE " + TABLE_NAME + 
            "(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, salary TEXT, datetime DEFAULT CURRENT_TIMESTAMP)");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldV, int newV) {
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
        onCreate(db);
    }

    public boolean insert(String name, String salary) {
        SQLiteDatabase db = getWritableDatabase();
        ContentValues cv = new ContentValues();
        cv.put("name", name);
        cv.put("salary", salary);
        return db.insert(TABLE_NAME, null, cv) != -1;
    }

    // ORDER BY name ASC (mặc định) - A → Z
    public ArrayList getAllContacts() {
        ArrayList list = new ArrayList<>();
        SQLiteDatabase db = getReadableDatabase();
        
        Cursor cursor = db.rawQuery(
            "SELECT name FROM " + TABLE_NAME + " ORDER BY name ASC", null);

        if (cursor.moveToFirst()) {
            do {
                list.add(cursor.getString(cursor.getColumnIndex("name")));
            } while (cursor.moveToNext());
        }
        cursor.close();
        return list;
    }

    public boolean update(String name, String salary) {
        SQLiteDatabase db = getWritableDatabase();
        ContentValues cv = new ContentValues();
        cv.put("name", name);
        cv.put("salary", salary);
        return db.update(TABLE_NAME, cv, "name = ?", new String[]{name}) > 0;
    }

    public boolean deleteAll() {
        return getWritableDatabase().delete(TABLE_NAME, null, null) > 0;
    }
}

Các biến thể ORDER BY


-- 1. Một cột
ORDER BY name ASC           -- A, B, C... (mặc định)
ORDER BY name DESC          -- Z, Y, X...
ORDER BY salary DESC        -- Lương cao → thấp
ORDER BY datetime DESC      -- Mới nhất → Cũ nhất

-- 2. Nhiều cột (ưu tiên trái → phải)
ORDER BY department ASC, salary DESC
-- Sắp xếp phòng ban A→Z, trong mỗi phòng lương cao→thấp

ORDER BY is_manager DESC, hire_date ASC
-- Quản lý lên đầu, cùng cấp thì người cũ lên đầu

-- 3. Sắp xếp theo biểu thức/hàm
ORDER BY LENGTH(name) DESC          -- Tên dài nhất trước
ORDER BY UPPER(name) ASC            -- Không phân biệt hoa thường
ORDER BY (salary + bonus) DESC      -- Tổng thu nhập
ORDER BY CASE status WHEN 'VIP' THEN 1 ELSE 0 END DESC  -- VIP lên đầu

-- 4. Vị trí cột (không khuyến nghị)
ORDER BY 2 DESC  -- Cột thứ 2 trong SELECT

-- 5. Kết hợp với NULL
ORDER BY name ASC NULLS FIRST   -- NULL lên đầu (SQLite mặc định)
ORDER BY name ASC NULLS LAST    -- NULL cuối (cần workaround)
-- SQLite workaround:
ORDER BY (name IS NULL), name ASC  -- NULL cuối

Tối ưu hiệu năng với INDEX


-- Tạo index hỗ trợ ORDER BY
CREATE INDEX idx_name ON SalaryDetails(name);
CREATE INDEX idx_salary ON SalaryDetails(salary);
CREATE INDEX idx_dept_salary ON SalaryDetails(department, salary DESC);

-- Query dùng index:
SELECT * FROM SalaryDetails ORDER BY name;           -- Dùng idx_name
SELECT * FROM SalaryDetails WHERE dept='IT' ORDER BY salary DESC;  -- Dùng idx_dept_salary

Phân trang (Pagination) với ORDER BY


// Trang 1: 20 bản ghi mới nhất
SELECT * FROM news ORDER BY created_at DESC LIMIT 20 OFFSET 0;

// Trang 2:
SELECT * FROM news ORDER BY created_at DESC LIMIT 20 OFFSET 20;

// Công thức: OFFSET = (page - 1) * pageSize

Lưu ý quan trọng

  • Không có ORDER BY → thứ tự không xác định (thường theo INSERT order nhưng không đảm bảo).
  • ORDER BY trên cột không có index → full table scan + sort (chậm trên bảng lớn).
  • LIMIT không có ORDER BY vô nghĩa.
  • COLLATE: ORDER BY name COLLATE NOCASE ASC để sắp xếp không phân biệt hoa/thường.