Androidアプリケーションで2つのSQLiteテーブルを結合するにはどうすればよいですか?


95

バックグラウンド

私は2つのテーブルを持つデータベースを持っているAndroidのプロジェクトを持っている:tbl_questiontbl_alternative

ビューに質問と代替案を取り込むために、カーソルを使用しています。2つのテーブルを結合するまで、必要なデータを取得するのに問題はありません。

    Tbl_question  
    -------------
    _id  
    質問  
    categoryid  
    Tbl_alternative
    ---------------
    _id 
    質問ID 
    categoryid 
    代替

次のようなものが欲しい:

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よりも難しいと思いますが、エラーが発生しにくいため、この方法を使用するようアドバイスを受けています。

質問

アプリケーションで2つのSQLiteテーブルを結合するにはどうすればよいですか?


どのようなエラーが発生していますか?連結された文字列を文字列リテラルに置き換えてテストしてみましたか?(FWIW、1986年以降カーソルを使用する必要がありませんでした。しかし、Android向けに開発していません。)
Mike Sherrill 'Cat Recall'

カーソルコードブ​​ロックでinner-join-columnsが指定されている場所/方法がわかりません。SQLは、「T1.questionid = T2.questionidおよびT1.categoryid = T2.categoryidのT1内部結合T2からdesired-cols-listを選択します。ここでT1.categoryid = {目的のカテゴリ値}」
Tim

これは私のエラーです:あいまいな列名:_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を指定する必要があると想定していますが、上記で使用したクエリ方法でこれを行う方法がわかりません。; tbl_question INNERから「」を選択しtbl_alternative ON tbl_question._id = tbl_alternative.questionid AND tbl_question.categoryid = tbl_alternative.categoryid =区分登録しよう:そして、私は「通常の」SQL構文を使用する場合に返すかわからない
kakka47

@ティム:あなたがそれを行う方法、どのように私はdb-ヘルパークラスでメソッドを形成するのですか?カーソルを返すべきではありませんか?私は自分が進むにつれて学んでいます、私のコードをより良くするどんな提案も感謝しています!
kakka47

ここで私が投稿した回答stackoverflow.com/questions/31567564/...
サーガル

回答:


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がどのテーブルに属しているのか、および両方のテーブルの一部の列に同じ列名がある場合はどうすればわかりますか?または、2番目のテーブルに複数のアイテムがある場合
Android開発者

@ android-developer「クエリの後に何をすべきですか?」-あなたはカーソルを取得し、データ:Pであなたがやりたいことを何でもします; 2.「どのIDを知っているのか...」-これはJOINであるため、タイプに応じて、ALWAYS、NEVER、またはMAYBEのキーが生成される可能性があります。3.「...一部の列で同じ列名」-発生する可能性がありますが、あいまいさは取り除くことができます。正直なところ、getColumnIndexを使用して「Table.column」でフェッチできるかどうかはわかりません。いつでも手動でインデックスを計算できます。デバッグテストを行うだけです。4.「2番目のテーブルの複数のアイテム」も、結合タイプによって異なります。
leRobot 2016年

ビューが1 対多のリレーション(例:RawContactsEntity)のFULL OUTER JOINであると想定すると、「多」のテーブルではヌルKEYを持つ行を取得できますが、「1」のテーブルでは決して行できないため、カーソルパスを分岐する必要があります。のように:while(cursor.moveToNext(){if(cursor.getValue(getManyTableKeyIndex())== NULL){/ *これは関係のない行ですが、「1」のテーブルデータしかありません/} else {/これはリレーションHITなので、両方のテーブルからのデータがあります* /}} 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の回答に加えて、2つのテーブルを結合することは間違いなく正しいですが、次の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 2018

1
これは良い方法ですが、あるテーブルの列名が別のテーブルの列名と同じである場合は機能しません。両方のテーブルに名前IDを持つ列がある場合と同様に、これはスローされますandroid.database.sqlite.SQLiteException: ambiguous column name
Anup Sharma '31

8

「あいまいな列」は、通常、同じ列名が少なくとも2つのテーブルに表示されることを意味します。データベースエンジンはどちらが必要かを判断できません。あいまいさをなくすには、完全なテーブル名またはテーブルエイリアスを使用してください。

これがたまたま私のエディタにある例です。他人の問題ですが、とにかく理にかなっているはずです。

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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.