如何在Android应用程序中联接两个SQLite表?


95

背景

我有一个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开发的。)
Mike Sherrill'Cat Recall'

我看不到在游标代码块中在哪里/如何指定内部联接列。SQL将是“从T1.questionid = T2.questionid和T1.categoryid = T2.categoryid的T1内部联接T2中选择所需的cols-list,其中T1.categoryid = {所需的类别值}”
Tim,

这是我的错误:不明确的列名:_id:,而正在编译:SELECT DISTINCT _id,图像,问题,替代项,questionid从tbl_question内部联接tbl_alternative WHERE categoryid = 2 AND _id = questionid。因此,我假设我需要指定tbl_question._id = tbl_alternative.questionid,但是我不知道如何在上面使用的查询方式中执行此操作。而且我不知道如果我使用“常规” sql语法会返回什么:“从tbl_question内部联接tbl_alternative ON选择tbl_question._id = tbl_alternative.questionid和tbl_question.categoryid = tbl_alternative.categoryid = categoryid;
kakka47

@Tim:您的方式,如何在db-helper类中形成方法?我不应该返回游标吗?我正在学习中,任何使我的代码更好的建议,我都非常感谢!
kakka47'2

在这里我已经发布了答案stackoverflow.com/questions/31567564/…–
萨加尔

Answers:


204

您需要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查询中。


1
@necromancer您如何看待VIEW比这更好的选择rawQuery
Muhammad Babar 2014年

查询后应该怎么做?您如何知道哪个ID属于哪个表,以及如果两个表的某个列具有相同的列名该怎么办?还是第二个表有多个项目?
Android开发人员

@ android-developer“查询后应该做什么?” -您得到一个游标,对数据:P;做任何您想做的事情。2.“您怎么知道哪个ID ...”-这是一个JOIN,因此,根据哪种类型,您可能会得到ALWAYS,NEVER或MAYBE填充键;3.“ ...某些列的相同列名”-可能发生,但可以消除歧义。老实说,我不知道是否可以使用getColumnIndex使用“ Table.column”进行获取。您始终可以手动计算索引,只需对其进行调试测试即可;4.“第二个表有多个项目”还取决于联接类型。
leRobot

假设View是一对多关系的FULL OUTER JOIN(例如RawContactsEntity),则可以在“ many”表上获得键值为null的行,而在“ 1”表上则无法获得,因此您应该分支指针传递像这样:while(cursor.moveToNext(){if(cursor.getValue(getManyTableKeyIndex())== NULL){/ *这是一个无关系的行,只有“ 1”个表数据/} else {/这是关系命中率,因此两个表中都有数据* /}} cursor.close();
leRobot

我忘记为“没有很多父级”行添加其他分支,您仍然可以通过FULL OUTER JOIN获得该分支,但是如果关系很严格,通常不会这样做(我不确定Android是否强制执行关系)。真的不知道如何获取不明确的列,但是您可以在任何SQLite CLI上通过声明具有列歧义性的视图并在“ SELECT * FROM view_x”查询中查看这些列的名称来对其进行测试。然后,将其应用于所需的任何Android帮助程序类(.query()/ .rawQuery()...)
leRobot

20

另一种方法是构造一个视图,然后像表一样对其进行查询。在许多数据库管理器中,使用视图可以提高性能。

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与视图一起使用,因为它暗示它是首选的。


4
当涉及到像Oracle这样的RDBMS时,视图可以为您带来一些性能提升,但是以我的经验,它们仅在出于性能或报告目的而必要时才使用。换句话说,您不会为您使用的每个联接查询创建一个视图。特别是在SQLite中,我认为不会有任何性能提升-根据此问题的答案,SQLite使用子查询重写查询。stackoverflow.com/questions/25577407/performance-penalty-for-unused-view#25580319 Android的API可能会限制OP的操作,但这rawQuery()是正确的答案。
spaaarky21 2016年

13

除了@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


1
我不明白Utils.concat的来源。
nicoqueijo

1
这是一种好方法,但是如果一个表中的任何列名与另一表中的列名相同,则将不起作用。就像两个表都具有名称ID的列一样,则将引发该问题android.database.sqlite.SQLiteException: ambiguous column name
Anup Sharma,

8

“歧义列”通常表示同一列名至少出现在两个表中;数据库引擎无法确定您要哪个。使用完整的表名或表别名来消除歧义。

这是我碰巧出现在编辑器中的一个示例。这是由于别人的问题造成的,但无论如何应该说得通。

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方法时,很容易定义哪个列属于哪个表。
kakka47 2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.