Giới thiệu về SQLite trong Android
SQLite là cơ sở dữ liệu SQL mã nguồn mở, lưu trữ dữ liệu trong file văn bản trên thiết bị. Android tích hợp sẵn SQLite, hỗ trợ đầy đủ tính năng cơ sở dữ liệu quan hệ. Không cần thiết lập kết nối như JDBC/ODBC để truy cập.
Ví dụ này minh họa cách sử dụng hàm tổng hợp COUNT() trong SQLite Android để đếm số bản ghi.
Bước 1: Tạo dự án mới
Mở Android Studio, chọn File → New Project và điền đầy đủ thông tin cần thiết.
Bước 2: Thiết kế giao diện (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"
android:orientation="vertical"
tools:context=".MainActivity">
<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">
<Button
android:id="@+id/save"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Lưu" />
<Button
android:id="@+id/refresh"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Đếm (Refresh)" />
<Button
android:id="@+id/update"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Cập nhật" />
<Button
android:id="@+id/delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Xóa tất cả" />
</LinearLayout>
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
Bước 3: MainActivity.java
package com.example.andy.myapplication;
import android.os.Bundle;
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 androidx.appcompat.app.AppCompatActivity;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
Button save, refresh, update, delete;
EditText name, salary;
ListView listView;
DatabaseHelper helper;
ArrayAdapter arrayAdapter;
ArrayList arrayList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
helper = new DatabaseHelper(this);
arrayList = new ArrayList<>(helper.getAllContacts());
name = findViewById(R.id.name);
salary = findViewById(R.id.salary);
listView = findViewById(R.id.listView);
save = findViewById(R.id.save);
refresh = findViewById(R.id.refresh);
update = findViewById(R.id.update);
delete = findViewById(R.id.delete);
arrayAdapter = new ArrayAdapter<>(this,
android.R.layout.simple_list_item_1, arrayList);
listView.setAdapter(arrayAdapter);
delete.setOnClickListener(v -> {
if (helper.deleteAll()) {
Toast.makeText(this, "Đã xóa", Toast.LENGTH_LONG).show();
refreshList();
} else {
Toast.makeText(this, "Xóa thất bại", Toast.LENGTH_LONG).show();
}
});
update.setOnClickListener(v -> {
String n = name.getText().toString().trim();
String s = salary.getText().toString().trim();
if (!n.isEmpty() && !s.isEmpty()) {
if (helper.update(n, s)) {
Toast.makeText(this, "Đã cập nhật", Toast.LENGTH_LONG).show();
refreshList();
} else {
Toast.makeText(this, "Cập nhật thất bại", Toast.LENGTH_LONG).show();
}
} else {
name.setError("Nhập tên");
salary.setError("Nhập lương");
}
});
refresh.setOnClickListener(v -> refreshList());
save.setOnClickListener(v -> {
String n = name.getText().toString().trim();
String s = salary.getText().toString().trim();
if (!n.isEmpty() && !s.isEmpty()) {
if (helper.insert(n, s)) {
Toast.makeText(this, "Đã thêm", Toast.LENGTH_LONG).show();
refreshList();
name.setText("");
salary.setText("");
} else {
Toast.makeText(this, "Thêm thất bại", Toast.LENGTH_LONG).show();
}
} else {
name.setError("Nhập tên");
salary.setError("Nhập lương");
}
});
}
private void refreshList() {
arrayList.clear();
arrayList.addAll(helper.getAllContacts());
arrayAdapter.notifyDataSetChanged();
}
}
Bước 4: 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.SQLiteOpenHelper;
import java.util.ArrayList;
class DatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "salaryDatabase5";
private static final String TABLE_NAME = "SalaryDetails";
private static final int DATABASE_VERSION = 1;
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
String sql = "CREATE TABLE " + TABLE_NAME + " (" +
"id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"name TEXT, " +
"salary TEXT, " + -- hoặc REAL cho số
"datetime DEFAULT CURRENT_TIMESTAMP)";
db.execSQL(sql);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
onCreate(db);
}
boolean insert(String name, String salary) {
SQLiteDatabase db = getWritableDatabase();
ContentValues cv = new ContentValues();
cv.put("name", name);
cv.put("salary", salary);
long result = db.replace(TABLE_NAME, null, cv);
return result != -1;
}
// QUAN TRỌNG: Sử dụng COUNT(*) hoặc COUNT(cột)
ArrayList getAllContacts() {
SQLiteDatabase db = getReadableDatabase();
ArrayList list = new ArrayList<>();
// COUNT(*) đếm tất cả hàng, COUNT(name) đếm hàng có name không null
String query = "SELECT COUNT(*) AS total_count FROM " + TABLE_NAME;
Cursor res = db.rawQuery(query, null);
if (res.moveToFirst()) {
// Chỉ có 1 hàng kết quả
int count = res.getInt(res.getColumnIndexOrThrow("total_count"));
list.add("Tổng số bản ghi: " + count);
}
res.close();
return list;
}
boolean update(String name, String salary) {
SQLiteDatabase db = getWritableDatabase();
ContentValues cv = new ContentValues();
cv.put("name", name);
cv.put("salary", salary);
// CẢNH BÁO: UPDATE không có WHERE sẽ cập nhật TẤT CẢ hàng
// Nên dùng: db.update(TABLE_NAME, cv, "id=?", new String[]{id})
int rows = db.update(TABLE_NAME, cv, null, null);
return rows > 0;
}
boolean deleteAll() {
SQLiteDatabase db = getWritableDatabase();
int rows = db.delete(TABLE_NAME, null, null);
return rows > 0;
}
}
Chạy ứng dụng
Kết nối thiết bị, nhấn Run. Nhập vài bản ghi (Lưu), sau đó nhấn Đếm (Refresh).
Kết quả
ListView hiển thị: "Tổng số bản ghi: 5" (giả sử đã insert 5 bản ghi)
Giải thích COUNT() trong SQLite
| Cú pháp | Ý nghĩa |
|---|---|
COUNT(*) | Đếm tất cả hàng (kể cả NULL) |
COUNT(cot) | Đếm hàng có cot không NULL |
COUNT(DISTINCT cot) | Đếm giá trị duy nhất của cột |
Ví dụ nâng cao:
-- Đếm theo nhóm
SELECT name, COUNT(*) as cnt FROM SalaryDetails GROUP BY name;
-- Có điều kiện
SELECT COUNT(*) FROM SalaryDetails WHERE salary > 1000;
Lưu ý: Code gốc dùng res.isAfterLast() == false (logic ngược, dễ sai). Đã sửa dùng moveToFirst() + do-while hoặc chỉ moveToFirst() vì COUNT trả về 1 hàng. Cũng sửa UPDATE an toàn dùng ContentValues. Tên phương thức getAllCotacts → getAllContacts.