如何在我的 Android 应用程序中加入两个 SQLite 表?
Posted
技术标签:
【中文标题】如何在我的 Android 应用程序中加入两个 SQLite 表?【英文标题】:How do I join two SQLite tables in my Android application? 【发布时间】:2011-06-24 19:52:34 【问题描述】:背景
我有一个 android 项目,它有一个包含两个表的数据库:tbl_question
和 tbl_alternative
。
为了在视图中填充问题和备选方案,我使用了光标。在我尝试加入这两个表之前,获取我需要的数据没有问题。
Tbl_question ------------- _ID 题 类别ID Tbl_alternative --------------- _ID 问题ID 类别ID 选择我想要以下内容:
SELECT tbl_question.question, tbl_alternative.alternative where
categoryid=tbl_alternative.categoryid AND tbl_question._id =
tbl_alternative.questionid.`
这是我的尝试:
public Cursor getAlternative(long categoryid)
String[] columns = new String[] KEY_Q_ID, KEY_IMAGE, KEY_QUESTION, KEY_ALT, KEY_QID;
String whereClause = KEY_CATEGORYID + "=" + categoryid +" AND "+ KEY_Q_ID +"="+ KEY_QID;
Cursor cursor = mDb.query(true, DBTABLE_QUESTION + " INNER JOIN "+ DBTABLE_ALTERNATIVE, columns, whereClause, null, null, null, null, null);
if (cursor != null)
cursor.moveToFirst();
return cursor;
我发现这种方式比常规 SQL 更难形成查询,但我得到了使用这种方式的建议,因为它不易出错。
问题
如何在我的应用程序中连接两个 SQLite 表?
【问题讨论】:
您遇到什么错误?您是否尝试通过用字符串文字替换连接的字符串来进行测试? (FWIW,自 1986 年左右以来我就不必使用光标了。但我不是为 android 开发的。) 我看不到在光标代码块中指定内部连接列的位置/方式。 SQL 将是“在 T1.questionid =T2.questionid 和 T1.categoryid = T2.categoryid where T1.categoryid = the desired category value 的 T1 内部连接 T2 中选择所需的列列表” 这是我的错误:不明确的列名:_id:,编译时:SELECT DISTINCT _id, image, question, alternative, questionid FROM tbl_question INNER JOIN tbl_alternative WHERE categoryid=2 AND _id=questionid。所以我假设我需要指定 tbl_question._id = tbl_alternative.questionid,但我不知道如何以我上面使用的查询方式执行此操作。而且我不知道如果我使用“常规” sql-syntax 会返回什么:“Select ' from tbl_question INNER JOIN tbl_alternative ON tbl_question._id = tbl_alternative.questionid AND tbl_question.categoryid = tbl_alternative.categoryid = categoryid; @Tim:你这样做的方式,我如何在db-helper类中形成方法?我不应该返回游标吗?我边走边学,任何能让我的代码变得更好的建议我都非常感谢! 这里我已经发布了答案***.com/questions/31567564/… 【参考方案1】:你需要rawQuery方法。
例子:
private final String MY_QUERY = "SELECT * FROM table_a a INNER JOIN table_b b ON a.id=b.other_id WHERE b.property_id=?";
db.rawQuery(MY_QUERY, new String[]String.valueOf(propertyId));
使用 ?绑定而不是将值放入原始 sql 查询中。
【讨论】:
@necromancer 您如何看待创建VIEW
比rawQuery
更好的选择?
查询后应该做什么?你怎么知道哪个 id 属于哪个表,如果两个表的某个列具有相同的列名怎么办?或者如果第二个表有多个项目?
@android-developer "查询后应该做什么?" - 你得到一个光标,对数据做任何你想做的事情:P; 2. “你怎么知道哪个 id...” - 这是一个 JOIN,所以取决于哪个 type,你可能会得到 ALWAYS、NEVER 或 MAYBE 填充键; 3.“...某些列的相同列名” - 可能会发生,但可以消除歧义。老实说,我不知道您是否可以使用 getColumnIndex 获取“Table.column”,您始终可以手动计算索引,只需调试测试即可; 4.“第二张表的多项”也取决于连接类型。
假设 View 是一对多关系的 FULL OUTER JOIN(例如RawContactsEntity),您可以在 "many" 表上获取带有 null KEY 的行,但永远不会在 " 1" 表,所以你应该像这样分支你的游标传递: while (cursor.moveToNext() if (cursor.getValue(getManyTableKeyIndex()) == NULL) /* 这是一个无关系行,只有 "1 " table data / else / 这是一个关系 HIT,所以两个表都有数据 */ cursor.close();
我忘记为“没有父级很多”行添加另一个分支,您仍然可以通过 FULL OUTER JOIN 获得,但如果关系严格,通常不会(我不确定 Android强制关系)。真的不知道如何获得不明确的列,但是您可以通过声明具有列不明确性的视图并在“SELECT * FROM view_x”查询中查看这些列的名称来在任何 SQLite CLI 上对其进行测试。然后,您只需将其应用于您想要的任何 Android 助手风格(.query() / .rawQuery()...)【参考方案2】:
另一种方法是构造一个视图,然后像表格一样查询该视图。 在许多数据库管理器中,使用视图可以带来更好的性能。
CREATE VIEW xyz SELECT q.question, a.alternative
FROM tbl_question AS q, tbl_alternative AS a
WHERE q.categoryid = a.categoryid
AND q._id = a.questionid;
这是凭记忆写的,所以可能存在一些语法问题。 http://www.sqlite.org/lang_createview.html
我提到这种方法是因为您可以将 SQLiteQueryBuilder 与视图一起使用,因为您暗示它是首选的。
【讨论】:
对于像 Oracle 这样的 RDBMS,视图可以为您带来一些性能提升,但根据我的经验,它们仅在必要时用于性能或报告目的。或者换一种说法,您不会为您使用的每个连接查询创建一个视图。特别是在 SQLite 中,我不相信有任何性能提升——根据对这个问题的回答,SQLite 使用子查询重写了查询。 ***.com/questions/25577407/performance-penalty-for-unused-view#25580319 Android 的 API 可能会限制 OP 正在做的事情,但rawQuery()
是正确的答案。【参考方案3】:
除了@pawelzieba 的回答,这绝对是正确的,加入两个表,而你可以像这样使用INNER JOIN
SELECT * FROM expense INNER JOIN refuel
ON exp_id = expense_id
WHERE refuel_id = 1
通过这样的原始查询 -
String rawQuery = "SELECT * FROM " + RefuelTable.TABLE_NAME + " INNER JOIN " + ExpenseTable.TABLE_NAME
+ " ON " + RefuelTable.EXP_ID + " = " + ExpenseTable.ID
+ " WHERE " + RefuelTable.ID + " = " + id;
Cursor c = db.rawQuery(
rawQuery,
null
);
由于 SQLite 对原始查询方式的向后兼容支持,我们将该命令变成了这个 -
SELECT *
FROM expense, refuel
WHERE exp_id = expense_id AND refuel_id = 1
因此能够利用 SQLiteDatabase.query() 辅助方法
Cursor c = db.query(
RefuelTable.TABLE_NAME + " , " + ExpenseTable.TABLE_NAME,
Utils.concat(RefuelTable.PROJECTION, ExpenseTable.PROJECTION),
RefuelTable.EXP_ID + " = " + ExpenseTable.ID + " AND " + RefuelTable.ID + " = " + id,
null,
null,
null,
null
);
有关详细的博客文章,请查看此 http://blog.championswimmer.in/2015/12/doing-a-table-join-in-android-without-using-rawquery
【讨论】:
我不明白 Utils.concat 来自哪里。 这是一种很好的方法,但如果一个表中的任何列名与另一个表中的列名相同,则该方法将不起作用。就像如果两个表都有一个名称为 ID 的列,那么这将抛出android.database.sqlite.SQLiteException: ambiguous column name
【参考方案4】:
“歧义列”通常表示相同的列名出现在至少两个表中;数据库引擎无法判断您想要哪一个。使用完整的表名或表别名来消除歧义。
这是我在编辑器中碰巧看到的一个例子。这是别人的问题,但无论如何应该是有道理的。
select P.*
from product_has_image P
inner join highest_priority_images H
on (H.id_product = P.id_product and H.priority = p.priority)
【讨论】:
是的,我怀疑过。但我不知道如何指定表,因为方式:DBTABLE_QUESTION.KEY_QUESTION,不起作用。但是当我使用 rawQuery 方式时,很容易定义哪一列属于哪个表。以上是关于如何在我的 Android 应用程序中加入两个 SQLite 表?的主要内容,如果未能解决你的问题,请参考以下文章
如何在我的组件VUE 2(webpack)中加载外部js文件?