MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Android开发:查询SQLite数据库表数据

2022-03-294.5k 阅读

数据库基础知识在 Android SQLite 查询中的应用

关系型数据库概念回顾

在深入探讨 Android 中 SQLite 数据库表数据查询之前,有必要回顾一下关系型数据库的基本概念。关系型数据库以二维表的形式组织数据,每个表由行(记录)和列(字段)组成。例如,一个“用户”表可能包含“用户ID”“用户名”“密码”等列,每一行代表一个具体的用户记录。这种结构使得数据的组织和管理具有高度的逻辑性和规范性。

在关系型数据库中,表与表之间可以通过外键建立关联关系。比如,有一个“订单”表,其中的“用户ID”字段可以作为外键关联到“用户”表的“用户ID”,这样就能明确每个订单是由哪个用户创建的。这种关联关系在数据查询中起着至关重要的作用,它使得我们可以从多个相关表中获取有意义的信息。

SQL 基本语法概述

SQL(Structured Query Language)即结构化查询语言,是用于数据库查询、操作和管理的标准语言。在 Android 开发中查询 SQLite 数据库主要使用 SQL 语句。

SELECT 语句基础

SELECT 语句是 SQL 中用于查询数据的核心语句。其基本语法结构为:SELECT 列名 FROM 表名;。例如,要从名为“users”的表中查询所有用户的“用户名”,可以使用语句:SELECT username FROM users;。如果要查询多个列,列名之间用逗号分隔,如SELECT username, email FROM users;。若要查询表中的所有列,可以使用通配符*,即SELECT * FROM users;

WHERE 子句用于条件过滤

WHERE 子句用于在查询中添加条件,只返回满足特定条件的数据。语法为SELECT 列名 FROM 表名 WHERE 条件;。条件可以是各种比较表达式,例如,要查询年龄大于 30 岁的用户,可以写为SELECT * FROM users WHERE age > 30;。条件中还可以使用逻辑运算符ANDORNOT进行组合。比如,查询年龄大于 30 岁且性别为“男”的用户:SELECT * FROM users WHERE age > 30 AND gender = '男';

ORDER BY 子句用于排序

ORDER BY 子句用于对查询结果进行排序。可以按升序(ASC,默认)或降序(DESC)排列。例如,要按用户年龄从大到小查询用户:SELECT * FROM users ORDER BY age DESC;。如果要按多个列排序,列名之间用逗号分隔,如SELECT * FROM users ORDER BY age DESC, username ASC;

Android 开发中 SQLiteOpenHelper 类

SQLiteOpenHelper 类简介

在 Android 开发中,SQLiteOpenHelper 类是一个非常重要的辅助类,用于管理 SQLite 数据库的创建、版本升级和降级等操作。它提供了一种方便的方式来处理数据库的生命周期。

继承 SQLiteOpenHelper 类

要使用 SQLiteOpenHelper,首先需要创建一个继承自该类的自定义类。例如:

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

public class MyDatabaseHelper extends SQLiteOpenHelper {
    private static final String TAG = "MyDatabaseHelper";
    private static final String DATABASE_NAME = "my_database.db";
    private static final int DATABASE_VERSION = 1;

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

    @Override
    public void onCreate(SQLiteDatabase db) {
        String createTableQuery = "CREATE TABLE users (" +
                "id INTEGER PRIMARY KEY AUTOINCREMENT," +
                "username TEXT," +
                "password TEXT," +
                "age INTEGER," +
                "gender TEXT)";
        db.execSQL(createTableQuery);
        Log.d(TAG, "Table 'users' created successfully");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        if (oldVersion < newVersion) {
            // 进行数据库版本升级操作,例如添加新列
            String addColumnQuery = "ALTER TABLE users ADD COLUMN email TEXT";
            db.execSQL(addColumnQuery);
            Log.d(TAG, "Column 'email' added to 'users' table during upgrade");
        }
    }
}

在上述代码中,MyDatabaseHelper类继承自SQLiteOpenHelper。构造函数中调用了父类的构造函数,传入了数据库名称、游标工厂(通常为null)和数据库版本。onCreate方法在数据库首次创建时被调用,这里创建了一个名为“users”的表。onUpgrade方法在数据库版本发生变化时被调用,这里演示了添加新列的操作。

获取 SQLiteDatabase 对象

通过MyDatabaseHelper类的实例,可以获取SQLiteDatabase对象,用于执行 SQL 语句。例如:

MyDatabaseHelper helper = new MyDatabaseHelper(context);
SQLiteDatabase db = helper.getWritableDatabase();
// 或者获取只读数据库
// SQLiteDatabase db = helper.getReadableDatabase();

getWritableDatabase方法用于获取可读写的数据库对象,如果数据库不存在,会先调用onCreate方法创建数据库。getReadableDatabase方法用于获取只读的数据库对象,当数据库磁盘空间不足等情况下,可能会返回只读数据库。

简单的单表查询

查询所有列

在 Android 中查询 SQLite 数据库表的所有列数据是最基本的操作之一。假设我们已经有了一个SQLiteDatabase对象db,并且数据库中有一个名为“users”的表,要查询该表的所有数据,可以使用以下代码:

Cursor cursor = db.query("users", null, null, null, null, null, null);
if (cursor.moveToFirst()) {
    do {
        int id = cursor.getInt(cursor.getColumnIndex("id"));
        String username = cursor.getString(cursor.getColumnIndex("username"));
        String password = cursor.getString(cursor.getColumnIndex("password"));
        int age = cursor.getInt(cursor.getColumnIndex("age"));
        String gender = cursor.getString(cursor.getColumnIndex("gender"));
        Log.d("QueryResult", "ID: " + id + ", Username: " + username + ", Password: " + password + ", Age: " + age + ", Gender: " + gender);
    } while (cursor.moveToNext());
}
cursor.close();

在上述代码中,db.query方法用于执行查询操作。第一个参数是表名“users”,第二个参数null表示查询所有列。后面几个null参数分别用于指定筛选条件、分组、分组条件、排序等,这里都未使用。query方法返回一个Cursor对象,它指向查询结果集。通过cursor.moveToFirst方法将游标移动到结果集的第一行,如果存在数据,则通过do - while循环遍历结果集。在循环中,使用cursor.getColumnIndex方法获取列的索引,然后通过cursor.getIntcursor.getString等方法获取对应列的数据。最后,关闭游标以释放资源。

查询指定列

如果只需要查询表中的某些特定列,可以在db.query方法的第二个参数中指定列名数组。例如,只查询“用户名”和“年龄”列:

String[] columns = {"username", "age"};
Cursor cursor = db.query("users", columns, null, null, null, null, null);
if (cursor.moveToFirst()) {
    do {
        String username = cursor.getString(cursor.getColumnIndex("username"));
        int age = cursor.getInt(cursor.getColumnIndex("age"));
        Log.d("QueryResult", "Username: " + username + ", Age: " + age);
    } while (cursor.moveToNext());
}
cursor.close();

这里将需要查询的列名“username”和“age”组成数组作为db.query方法的第二个参数,其他操作与查询所有列类似。

使用 WHERE 子句进行条件查询

简单条件查询

使用WHERE子句可以根据特定条件查询数据。例如,要查询年龄大于 30 岁的用户:

String selection = "age >?";
String[] selectionArgs = {"30"};
Cursor cursor = db.query("users", null, selection, selectionArgs, null, null, null);
if (cursor.moveToFirst()) {
    do {
        int id = cursor.getInt(cursor.getColumnIndex("id"));
        String username = cursor.getString(cursor.getColumnIndex("username"));
        String password = cursor.getString(cursor.getColumnIndex("password"));
        int age = cursor.getInt(cursor.getColumnIndex("age"));
        String gender = cursor.getString(cursor.getColumnIndex("gender"));
        Log.d("QueryResult", "ID: " + id + ", Username: " + username + ", Password: " + password + ", Age: " + age + ", Gender: " + gender);
    } while (cursor.moveToNext());
}
cursor.close();

在上述代码中,selection变量定义了WHERE子句的条件“age >?”,其中?是占位符。selectionArgs数组用于替换占位符的值,这里是“30”。这样就实现了查询年龄大于 30 岁的用户数据。

复杂条件查询

可以使用逻辑运算符组合多个条件。例如,查询年龄大于 30 岁且性别为“男”的用户:

String selection = "age >? AND gender =?";
String[] selectionArgs = {"30", "男"};
Cursor cursor = db.query("users", null, selection, selectionArgs, null, null, null);
if (cursor.moveToFirst()) {
    do {
        int id = cursor.getInt(cursor.getColumnIndex("id"));
        String username = cursor.getString(cursor.getColumnIndex("username"));
        String password = cursor.getString(cursor.getColumnIndex("password"));
        int age = cursor.getInt(cursor.getColumnIndex("age"));
        String gender = cursor.getString(cursor.getColumnIndex("gender"));
        Log.d("QueryResult", "ID: " + id + ", Username: " + username + ", Password: " + password + ", Age: " + age + ", Gender: " + gender);
    } while (cursor.moveToNext());
}
cursor.close();

这里的selection条件使用了AND逻辑运算符组合了两个条件,selectionArgs数组依次为两个占位符提供值。

使用 ORDER BY 子句进行排序查询

按单个列排序

要对查询结果按某个列进行排序,可以使用ORDER BY子句。例如,按年龄从大到小查询用户:

String orderBy = "age DESC";
Cursor cursor = db.query("users", null, null, null, null, null, orderBy);
if (cursor.moveToFirst()) {
    do {
        int id = cursor.getInt(cursor.getColumnIndex("id"));
        String username = cursor.getString(cursor.getColumnIndex("username"));
        String password = cursor.getString(cursor.getColumnIndex("password"));
        int age = cursor.getInt(cursor.getColumnIndex("age"));
        String gender = cursor.getString(cursor.getColumnIndex("gender"));
        Log.d("QueryResult", "ID: " + id + ", Username: " + username + ", Password: " + password + ", Age: " + age + ", Gender: " + gender);
    } while (cursor.moveToNext());
}
cursor.close();

db.query方法中,通过orderBy参数指定按“age”列降序排列(DESC表示降序,若要升序则使用ASC,默认是ASC)。

按多个列排序

也可以按多个列进行排序。例如,先按年龄从大到小,年龄相同的再按用户名升序排序:

String orderBy = "age DESC, username ASC";
Cursor cursor = db.query("users", null, null, null, null, null, orderBy);
if (cursor.moveToFirst()) {
    do {
        int id = cursor.getInt(cursor.getColumnIndex("id"));
        String username = cursor.getString(cursor.getColumnIndex("username"));
        String password = cursor.getString(cursor.getColumnIndex("password"));
        int age = cursor.getInt(cursor.getColumnIndex("age"));
        String gender = cursor.getString(cursor.getColumnIndex("gender"));
        Log.d("QueryResult", "ID: " + id + ", Username: " + username + ", Password: " + password + ", Age: " + age + ", Gender: " + gender);
    } while (cursor.moveToNext());
}
cursor.close();

这里的orderBy参数中,多个列名之间用逗号分隔,分别指定了排序方式。

聚合函数与分组查询

聚合函数的使用

COUNT 函数

COUNT函数用于统计行数。例如,要统计“users”表中的用户数量:

String[] columns = {"COUNT(*)"};
Cursor cursor = db.query("users", columns, null, null, null, null, null);
if (cursor.moveToFirst()) {
    int count = cursor.getInt(0);
    Log.d("QueryResult", "User count: " + count);
}
cursor.close();

db.query方法中,将"COUNT(*)"作为查询列,执行查询后,从结果集中获取第一列的值,即为用户数量。

SUM 函数

假设“users”表中有一个“score”列表示用户的成绩,要计算所有用户成绩的总和,可以使用SUM函数:

String[] columns = {"SUM(score)"};
Cursor cursor = db.query("users", columns, null, null, null, null, null);
if (cursor.moveToFirst()) {
    int sumScore = cursor.getInt(0);
    Log.d("QueryResult", "Total score: " + sumScore);
}
cursor.close();

这里以"SUM(score)"作为查询列,获取的结果就是成绩总和。

AVG 函数

计算“users”表中用户成绩的平均值,使用AVG函数:

String[] columns = {"AVG(score)"};
Cursor cursor = db.query("users", columns, null, null, null, null, null);
if (cursor.moveToFirst()) {
    double avgScore = cursor.getDouble(0);
    Log.d("QueryResult", "Average score: " + avgScore);
}
cursor.close();

同样,将"AVG(score)"作为查询列获取平均成绩。

MAX 和 MIN 函数

要获取“users”表中成绩的最大值和最小值,分别使用MAXMIN函数:

// 获取最大成绩
String[] maxColumns = {"MAX(score)"};
Cursor maxCursor = db.query("users", maxColumns, null, null, null, null, null);
if (maxCursor.moveToFirst()) {
    int maxScore = maxCursor.getInt(0);
    Log.d("QueryResult", "Max score: " + maxScore);
}
maxCursor.close();

// 获取最小成绩
String[] minColumns = {"MIN(score)"};
Cursor minCursor = db.query("users", minColumns, null, null, null, null, null);
if (minCursor.moveToFirst()) {
    int minScore = minCursor.getInt(0);
    Log.d("QueryResult", "Min score: " + minScore);
}
minCursor.close();

分别以"MAX(score)""MIN(score)"作为查询列获取最大和最小成绩。

分组查询

GROUP BY 基本使用

分组查询可以将数据按某个或某些列进行分组,并对每个组应用聚合函数。例如,要按性别统计用户数量:

String[] columns = {"gender", "COUNT(*)"};
String groupBy = "gender";
Cursor cursor = db.query("users", columns, null, null, groupBy, null, null);
if (cursor.moveToFirst()) {
    do {
        String gender = cursor.getString(cursor.getColumnIndex("gender"));
        int count = cursor.getInt(cursor.getColumnIndex("COUNT(*)"));
        Log.d("QueryResult", "Gender: " + gender + ", Count: " + count);
    } while (cursor.moveToNext());
}
cursor.close();

db.query方法中,通过groupBy参数指定按“gender”列进行分组,查询列包括“gender”和每组的用户数量(使用COUNT(*))。

HAVING 子句用于分组后筛选

HAVING子句用于对分组后的结果进行筛选。例如,只显示用户数量大于 10 的性别分组:

String[] columns = {"gender", "COUNT(*)"};
String groupBy = "gender";
String having = "COUNT(*) > 10";
Cursor cursor = db.query("users", columns, null, null, groupBy, having, null);
if (cursor.moveToFirst()) {
    do {
        String gender = cursor.getString(cursor.getColumnIndex("gender"));
        int count = cursor.getInt(cursor.getColumnIndex("COUNT(*)"));
        Log.d("QueryResult", "Gender: " + gender + ", Count: " + count);
    } while (cursor.moveToNext());
}
cursor.close();

这里通过having参数指定了分组后筛选的条件“COUNT(*) > 10”,只有满足该条件的分组才会出现在查询结果中。

多表关联查询

内连接(INNER JOIN)

假设我们有两个表,“users”表和“orders”表,“orders”表中有一个“user_id”外键关联到“users”表的“id”。要查询每个用户及其对应的订单信息,可以使用内连接:

String query = "SELECT users.username, orders.order_id, orders.order_amount " +
        "FROM users " +
        "INNER JOIN orders ON users.id = orders.user_id";
Cursor cursor = db.rawQuery(query, null);
if (cursor.moveToFirst()) {
    do {
        String username = cursor.getString(cursor.getColumnIndex("username"));
        int orderId = cursor.getInt(cursor.getColumnIndex("order_id"));
        double orderAmount = cursor.getDouble(cursor.getColumnIndex("order_amount"));
        Log.d("QueryResult", "Username: " + username + ", Order ID: " + orderId + ", Order Amount: " + orderAmount);
    } while (cursor.moveToNext());
}
cursor.close();

在上述代码中,使用db.rawQuery方法执行原生 SQL 查询。查询语句通过INNER JOIN关键字将“users”表和“orders”表连接起来,连接条件是“users.id = orders.user_id”。查询结果中选择了“users”表的“username”列和“orders”表的“order_id”、“order_amount”列。

左连接(LEFT JOIN)

左连接会返回左表(LEFT JOIN关键字左边的表)中的所有行,以及右表中满足连接条件的行。例如,要查询所有用户及其订单信息,即使某个用户没有订单也显示该用户信息:

String query = "SELECT users.username, orders.order_id, orders.order_amount " +
        "FROM users " +
        "LEFT JOIN orders ON users.id = orders.user_id";
Cursor cursor = db.rawQuery(query, null);
if (cursor.moveToFirst()) {
    do {
        String username = cursor.getString(cursor.getColumnIndex("username"));
        Integer orderId = cursor.isNull(cursor.getColumnIndex("order_id"))? null : cursor.getInt(cursor.getColumnIndex("order_id"));
        Double orderAmount = cursor.isNull(cursor.getColumnIndex("order_amount"))? null : cursor.getDouble(cursor.getColumnIndex("order_amount"));
        Log.d("QueryResult", "Username: " + username + ", Order ID: " + orderId + ", Order Amount: " + orderAmount);
    } while (cursor.moveToNext());
}
cursor.close();

这里使用LEFT JOIN进行连接,对于没有订单的用户,其对应的“order_id”和“order_amount”列值为null,在代码中通过cursor.isNull方法进行判断并处理。

右连接(RIGHT JOIN)

右连接与左连接相反,它会返回右表中的所有行,以及左表中满足连接条件的行。例如:

String query = "SELECT users.username, orders.order_id, orders.order_amount " +
        "FROM users " +
        "RIGHT JOIN orders ON users.id = orders.user_id";
Cursor cursor = db.rawQuery(query, null);
if (cursor.moveToFirst()) {
    do {
        String username = cursor.isNull(cursor.getColumnIndex("username"))? null : cursor.getString(cursor.getColumnIndex("username"));
        int orderId = cursor.getInt(cursor.getColumnIndex("order_id"));
        double orderAmount = cursor.getDouble(cursor.getColumnIndex("order_amount"));
        Log.d("QueryResult", "Username: " + username + ", Order ID: " + orderId + ", Order Amount: " + orderAmount);
    } while (cursor.moveToNext());
}
cursor.close();

此代码使用RIGHT JOIN,对于没有匹配用户的订单,“username”列值为null,同样通过cursor.isNull方法处理。

全外连接(FULL OUTER JOIN)

在 SQLite 中没有直接支持全外连接,但可以通过左连接和右连接的并集来模拟。假设要查询所有用户和所有订单,无论是否有匹配关系:

// 左连接部分
String leftJoinQuery = "SELECT users.username, orders.order_id, orders.order_amount " +
        "FROM users " +
        "LEFT JOIN orders ON users.id = orders.user_id";
Cursor leftCursor = db.rawQuery(leftJoinQuery, null);
// 右连接部分
String rightJoinQuery = "SELECT users.username, orders.order_id, orders.order_amount " +
        "FROM users " +
        "RIGHT JOIN orders ON users.id = orders.user_id";
Cursor rightCursor = db.rawQuery(rightJoinQuery, null);

// 合并结果(这里简单打印,实际应用中可能需要更复杂处理)
if (leftCursor.moveToFirst()) {
    do {
        String username = leftCursor.getString(leftCursor.getColumnIndex("username"));
        Integer orderId = leftCursor.isNull(leftCursor.getColumnIndex("order_id"))? null : leftCursor.getInt(leftCursor.getColumnIndex("order_id"));
        Double orderAmount = leftCursor.isNull(leftCursor.getColumnIndex("order_amount"))? null : leftCursor.getDouble(leftCursor.getColumnIndex("order_amount"));
        Log.d("LeftJoinResult", "Username: " + username + ", Order ID: " + orderId + ", Order Amount: " + orderAmount);
    } while (leftCursor.moveToNext());
}
leftCursor.close();

if (rightCursor.moveToFirst()) {
    do {
        String username = rightCursor.isNull(rightCursor.getColumnIndex("username"))? null : rightCursor.getString(rightCursor.getColumnIndex("username"));
        int orderId = rightCursor.getInt(rightCursor.getColumnIndex("order_id"));
        double orderAmount = rightCursor.getDouble(rightCursor.getColumnIndex("order_amount"));
        Log.d("RightJoinResult", "Username: " + username + ", Order ID: " + orderId + ", Order Amount: " + orderAmount);
    } while (rightCursor.moveToNext());
}
rightCursor.close();

这里分别执行左连接和右连接查询,并分别处理结果,模拟了全外连接的效果。

使用 ContentProvider 查询 SQLite 数据库

ContentProvider 简介

ContentProvider 是 Android 四大组件之一,它主要用于在不同的应用程序之间共享数据。通过 ContentProvider,可以将 SQLite 数据库的数据暴露给其他应用,实现数据的共享和交换。

创建自定义 ContentProvider

首先需要创建一个继承自ContentProvider的类。例如:

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.util.Log;

public class MyContentProvider extends ContentProvider {
    private static final String TAG = "MyContentProvider";
    private static final String AUTHORITY = "com.example.mycontentprovider";
    private static final String PATH_USERS = "users";
    public static final Uri CONTENT_URI_USERS = Uri.parse("content://" + AUTHORITY + "/" + PATH_USERS);
    private static final int USERS = 1;

    private static final UriMatcher uriMatcher;

    static {
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        uriMatcher.addURI(AUTHORITY, PATH_USERS, USERS);
    }

    private MyDatabaseHelper dbHelper;

    @Override
    public boolean onCreate() {
        dbHelper = new MyDatabaseHelper(getContext());
        return true;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        Cursor cursor;
        switch (uriMatcher.match(uri)) {
            case USERS:
                cursor = db.query("users", projection, selection, selectionArgs, null, null, sortOrder);
                break;
            default:
                throw new IllegalArgumentException("Unknown URI: " + uri);
        }
        cursor.setNotificationUri(getContext().getContentResolver(), uri);
        return cursor;
    }

    // 其他未实现的方法(insert、update、delete等)
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        // 实现插入逻辑
        return null;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        // 实现更新逻辑
        return 0;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // 实现删除逻辑
        return 0;
    }

    @Override
    public String getType(Uri uri) {
        // 返回 MIME 类型
        return null;
    }
}

在上述代码中,MyContentProvider类继承自ContentProviderAUTHORITY定义了内容提供者的权限,PATH_USERS定义了与“users”表相关的路径。uriMatcher用于匹配不同的 URI。onCreate方法初始化MyDatabaseHelperquery方法根据匹配的 URI 执行相应的查询操作,并设置通知 URI。

在 AndroidManifest.xml 中注册 ContentProvider

要使ContentProvider生效,需要在AndroidManifest.xml中注册:

<provider
    android:name=".MyContentProvider"
    android:authorities="com.example.mycontentprovider"
    android:exported="true" />

这里android:name指定了ContentProvider类的路径,android:authorities与代码中的AUTHORITY一致,android:exported设置为true表示可以被其他应用访问。

通过 ContentResolver 查询数据

在其他应用中,可以通过ContentResolver来查询MyContentProvider暴露的数据。例如:

ContentResolver resolver = getContentResolver();
Cursor cursor = resolver.query(MyContentProvider.CONTENT_URI_USERS, null, null, null, null);
if (cursor.moveToFirst()) {
    do {
        int id = cursor.getInt(cursor.getColumnIndex("id"));
        String username = cursor.getString(cursor.getColumnIndex("username"));
        Log.d("QueryResult", "ID: " + id + ", Username: " + username);
    } while (cursor.moveToNext());
}
cursor.close();

这里通过getContentResolver获取ContentResolver对象,然后使用resolver.query方法根据MyContentProvider.CONTENT_URI_USERS进行查询,操作方式与直接使用SQLiteDatabase查询类似,但数据来自ContentProvider提供的共享数据。

事务在查询中的应用

事务的概念

事务是数据库操作的一个逻辑单元,它由一组相关的数据库操作组成,这些操作要么全部成功执行,要么全部不执行,以保证数据的一致性和完整性。在 Android SQLite 数据库中,事务对于涉及多个查询操作且需要保证数据一致性的场景非常重要。

使用事务进行查询操作

例如,假设我们有两个表“accounts”(包含账户余额信息)和“transactions”(记录交易信息)。现在要进行一次转账操作,从一个账户扣除金额并在另一个账户增加金额,同时记录交易信息。这就需要使用事务来确保这一系列操作的原子性。

SQLiteDatabase db = dbHelper.getWritableDatabase();
try {
    db.beginTransaction();
    // 从转出账户扣除金额
    String updateFromAccountQuery = "UPDATE accounts SET balance = balance -? WHERE account_id =?";
    String[] fromArgs = {"100", "1"};
    db.execSQL(updateFromAccountQuery, fromArgs);

    // 向转入账户增加金额
    String updateToAccountQuery = "UPDATE accounts SET balance = balance +? WHERE account_id =?";
    String[] toArgs = {"100", "2"};
    db.execSQL(updateToAccountQuery, toArgs);

    // 记录交易信息
    ContentValues values = new ContentValues();
    values.put("from_account_id", 1);
    values.put("to_account_id", 2);
    values.put("amount", 100);
    db.insert("transactions", null, values);

    db.setTransactionSuccessful();
} catch (Exception e) {
    Log.e("TransactionError", "Transaction failed", e);
} finally {
    db.endTransaction();
}

在上述代码中,首先调用db.beginTransaction开始事务。然后依次执行从转出账户扣除金额、向转入账户增加金额以及记录交易信息的操作。如果所有操作都成功,调用db.setTransactionSuccessful标记事务成功。如果在事务执行过程中出现异常,catch块捕获异常并记录错误信息。最后,无论事务是否成功,都通过db.endTransaction结束事务。如果事务未标记成功,endTransaction会回滚所有操作,保证数据的一致性。

查询性能优化

索引的使用

创建索引

索引可以显著提高查询性能。在 SQLite 中,可以使用CREATE INDEX语句创建索引。例如,在“users”表的“username”列上创建索引:

String createIndexQuery = "CREATE INDEX idx_username ON users (username)";
db.execSQL(createIndexQuery);

这样在查询“username”列相关条件时,数据库可以更快地定位数据。

复合索引

也可以创建复合索引,用于多个列的联合查询。例如,在“users”表的“age”和“gender”列上创建复合索引:

String createCompositeIndexQuery = "CREATE INDEX idx_age_gender ON users (age, gender)";
db.execSQL(createCompositeIndexQuery);

当查询条件涉及“age”和“gender”两个列时,复合索引能提高查询效率。

避免不必要的查询

在代码中,要避免进行不必要的数据库查询。例如,如果某些数据已经在内存中,就不需要再次从数据库查询。可以使用缓存机制来存储经常查询的数据,减少数据库的负载。

优化查询语句

尽量简化查询语句,避免复杂的子查询和嵌套查询。例如,能用连接查询解决的问题,尽量不使用子查询,因为连接查询通常在性能上更优。同时,合理使用WHERE子句、ORDER BY子句等,确保查询条件准确,避免全表扫描。

通过以上对 Android 开发中 SQLite 数据库表数据查询的详细介绍,包括从基础的单表查询到复杂的多表关联查询,以及性能优化等方面,开发者可以全面掌握 SQLite 查询的相关知识和技巧,开发出高效、稳定的数据查询功能。