在android中获取sqlite SQL join的表名

Posted

技术标签:

【中文标题】在android中获取sqlite SQL join的表名【英文标题】:Obtaining table names for sqlite SQL join in android 【发布时间】:2016-12-13 16:34:29 【问题描述】:

我需要使用 SQL 加入标准 android 的表(如联系人和通话记录)。可以使用 SQLiteDatabase 类的 rawQuery 或查询方法。但要让这些方法正常工作,我需要知道可以在原始 SQL 查询中提供的表名。

示例。我想执行这样的查询:

SELECT * FROM Contacts as c INNER JOIN Call_Log as l ON c.number=l.number

我知道如何获取字段名称(如 CallLog.Calls.NUMBER),但我不知道如何获取每个 android 都有的标准表的名称。可以对名称进行硬编码,但使用 CallLog.TABLE_NAME 之类的方式看起来更可靠。那么,我在哪里可以找到 CallLog.TABLE_NAME 的类似物?

【问题讨论】:

【参考方案1】:

您要求提供大量信息,但这是对如何访问联系人表以及如何创建自己的 SQL 表并使用从其他表中获得的信息进行更新的一个很好的总结。

要对联系人提供程序进行任何类型的搜索,您的应用必须具有 READ_CONTACTS 权限。要请求这个,请将此元素添加到您的清单文件中作为子元素:

<uses-permission android:name="android.permission.READ_CONTACTS" />

要对通话记录进行任何类型的搜索,您的应用必须具有 READ_CALL_LOG 权限。要请求这个,请将此元素添加到您的清单文件中作为子元素:

    <uses-permission android:name="android.permission.READ_CALL_LOG" />

以下代码说明如何访问电话历史记录

Uri allCalls = Uri.parse("content://call_log/calls");
Cursor c = managedQuery(allCalls, null, null, null, null);

String num= c.getString(c.getColumnIndex(CallLog.Calls.NUMBER));// for  number
String name= c.getString(c.getColumnIndex(CallLog.Calls.CACHED_NAME));// for name
String duration = c.getString(c.getColumnIndex(CallLog.Calls.DURATION));// for duration
int type = Integer.parseInt(c.getString(c.getColumnIndex(CallLog.Calls.TYPE)));// for call type, Incoming or out going.

此技术尝试将搜索字符串与联系人提供程序的 ContactsContract.Contacts 表中的一个或多个联系人的名称进行匹配。您通常希望在 ListView 中显示结果,以允许用户在匹配的联系人中进行选择。

将数据保存到数据库是重复数据或结构化数据(例如联系信息)的理想选择。本课程假定您熟悉 SQL 数据库,并帮助您开始在 Android 上使用 SQLite 数据库。 android.database.sqlite 包中提供了在 Android 上使用数据库所需的 API。

SQL 数据库的主要原则之一是架构:数据库组织方式的正式声明。架构反映在您用于创建数据库的 SQL 语句中。您可能会发现创建一个称为契约类的伴生类很有帮助,它以系统和自记录的方式明确指定架构的布局。

契约类是定义 URI、表和列名称的常量的容器。契约类允许您在同一个包中的所有其他类中使用相同的常量。这使您可以在一个位置更改列名并使其在整个代码中传播。

组织契约类的一个好方法是将整个数据库的全局定义放在类的根级别。然后为枚举其列的每个表创建一个内部类。

public final class FeedReaderContract 
  // To prevent someone from accidentally instantiating the contract class,
  // make the constructor private.
  private FeedReaderContract() 

  /* Inner class that defines the table contents */
  public static class FeedEntry implements BaseColumns 
      public static final String TABLE_NAME = "entry";
      public static final String COLUMN_NAME_TITLE = "title";
      public static final String COLUMN_NAME_SUBTITLE = "subtitle";
    

一旦您定义了数据库的外观,您应该实现创建和维护数据库和表的方法。以下是一些创建和删除表的典型语句:

 private static final String TEXT_TYPE = " TEXT";
 private static final String COMMA_SEP = ",";
 private static final String SQL_CREATE_ENTRIES =
   "CREATE TABLE " + FeedEntry.TABLE_NAME + " (" +
    FeedEntry._ID + " INTEGER PRIMARY KEY," +
    FeedEntry.COLUMN_NAME_TITLE + TEXT_TYPE + COMMA_SEP +
    FeedEntry.COLUMN_NAME_SUBTITLE + TEXT_TYPE + " )";

  private static final String SQL_DELETE_ENTRIES =
  "DROP TABLE IF EXISTS " + FeedEntry.TABLE_NAME;

就像您保存在设备内部存储中的文件一样,Android 将您的数据库存储在与应用程序相关联的私有磁盘空间中。您的数据是安全的,因为默认情况下其他应用程序无法访问此区域。

SQLiteOpenHelper 类中提供了一组有用的 API。当您使用此类获取对数据库的引用时,系统仅在需要时而不是在应用程序启动期间执行创建和更新数据库的潜在长时间运行操作。您需要做的就是调用 getWritableDatabase() 或 getReadableDatabase()。

要使用 SQLiteOpenHelper,请创建一个覆盖 onCreate()、onUpgrade() 和 onOpen() 回调方法的子类。您可能还想实现 onDowngrade(),但这不是必需的。

例如,这是一个使用上面显示的一些命令的 SQLiteOpenHelper 实现:

  public class FeedReaderDbHelper extends SQLiteOpenHelper 

// 如果更改数据库架构,则必须增加数据库版本。 公共静态最终 int DATABASE_VERSION = 1; public static final String DATABASE_NAME = "FeedReader.db";

  public FeedReaderDbHelper(Context context) 
    super(context, DATABASE_NAME, null, DATABASE_VERSION);
 
 public void onCreate(SQLiteDatabase db) 
    db.execSQL(SQL_CREATE_ENTRIES);
 
 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) 
    // This database is only a cache for online data, so its upgrade policy is
    // to simply to discard the data and start over
    db.execSQL(SQL_DELETE_ENTRIES);
    onCreate(db);
 
 public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion)     
    onUpgrade(db, oldVersion, newVersion);

要访问您的数据库,请实例化您的 SQLiteOpenHelper 子类:

FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext()); 将信息放入数据库

通过将 ContentValues 对象传递给 insert() 方法将数据插入数据库:

// Gets the data repository in write mode
SQLiteDatabase db = mDbHelper.getWritableDatabase();

// Create a new map of values, where column names are the keys
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN_NAME_TITLE, title);
values.put(FeedEntry.COLUMN_NAME_SUBTITLE, subtitle);

// Insert the new row, returning the primary key value of the new row
long newRowId = db.insert(FeedEntry.TABLE_NAME, null, values);
The first argument for insert() is simply the table name.

第二个参数告诉框架在 ContentValues 为空的情况下要做什么(即,您没有输入任何值)。如果您指定列的名称,则框架会插入一行并将该列的值设置为 null。如果您指定 null,就像在此代码示例中一样,框架不会在没有值时插入一行。

要从数据库中读取数据,请使用 query() 方法,将您的选择条件和所需列传递给它。该方法结合了 insert() 和 update() 的元素,除了列列表定义了您要获取的数据,而不是要插入的数据。查询结果以 Cursor 对象的形式返回给您。

SQLiteDatabase db = mDbHelper.getReadableDatabase();

// Define a projection that specifies which columns from the database
// you will actually use after this query.
String[] projection = 
   FeedEntry._ID,
   FeedEntry.COLUMN_NAME_TITLE,
   FeedEntry.COLUMN_NAME_SUBTITLE
;

// Filter results WHERE "title" = 'My Title'
String selection = FeedEntry.COLUMN_NAME_TITLE + " = ?";
String[] selectionArgs =  "My Title" ;

// How you want the results sorted in the resulting Cursor
String sortOrder =
FeedEntry.COLUMN_NAME_SUBTITLE + " DESC";

Cursor c = db.query(
   FeedEntry.TABLE_NAME,                     // The table to query
   projection,                               // The columns to return
   selection,                                // The columns for the WHERE      clause
   selectionArgs,                            // The values for the WHERE clause
   null,                                     // don't group the rows
   null,                                     // don't filter by row groups
   sortOrder                                 // The sort order
 );

要查看光标中的一行,请使用光标移动方法之一,您必须始终在开始读取值之前调用该方法。通常,您应该首先调用 moveToFirst(),它将“读取位置”放置在结果中的第一个条目上。对于每一行,您可以通过调用 Cursor get 方法之一来读取列的值,例如 getString() 或 getLong()。对于每个 get 方法,您必须传递所需列的索引位置,您可以通过调用 getColumnIndex() 或 getColumnIndexOrThrow() 来获得。例如:

cursor.moveToFirst();
  long itemId = cursor.getLong(
  cursor.getColumnIndexOrThrow(FeedEntry._ID)
);

【讨论】:

感谢您的长回答,但我的问题是关于标准提供程序的表名,如 android.provider.CallLog。我怎样才能获得它的名称以在 SQL 中使用?我知道标准 android 的 API 中的每个提供程序都有 CONTENT_URI 静态字段,但没有像 TABLE_NAME 这样的字段。是否有可靠的算法从 CONTENT_URI 中提取表名?或者Android中没有什么可靠的,这是我唯一的选择——只是硬编码表名? Android 开发人员是否认为没有人应该将普通的旧 SQL 用于联系人和电话等标准表?

以上是关于在android中获取sqlite SQL join的表名的主要内容,如果未能解决你的问题,请参考以下文章

Android Sqlite 数据库 db.exec() 在 SQL 文本中返回语法错误

如何从mysql中获取数据并存储在android的Sqlite数据库中

Android Sqlite:如何从 Listview 中获取 SQLite rowId?

我们可以在 Android 中使用 SQLite 数据库上的所有 SQL 查询和函数吗? [关闭]

如何使用 SQLite 在 Android 中获取查询的行数?

Android-sqlite-SQL语句大全