如何在我的 Android 应用程序中加入两个 SQLite 表?

Posted

技术标签:

【中文标题】如何在我的 Android 应用程序中加入两个 SQLite 表?【英文标题】:How do I join two SQLite tables in my Android application? 【发布时间】:2011-06-24 19:52:34 【问题描述】:

背景

我有一个 android 项目,它有一个包含两个表的数据库:tbl_questiontbl_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 您如何看待创建VIEWrawQuery 更好的选择? 查询后应该做什么?你怎么知道哪个 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文件?

如何在 Spring Boot JPA 中加入两个表,我的代码出错了

如何在 symfony 5 中加入两个表?

如何在 JPA JPQL 查询中加入两个实体集合?

使用 Spark SQL 在 cassandra 中加入两个表 - 错误:缺少 EOF