Bagaimana cara menggabungkan dua tabel SQLite di aplikasi Android saya?

95

Latar Belakang

Saya memiliki proyek Android yang memiliki database dengan dua tabel: tbl_questiondan tbl_alternative.

Untuk mengisi pandangan dengan pertanyaan dan alternatif, saya menggunakan kursor. Tidak ada masalah dalam mendapatkan data yang saya perlukan sampai saya mencoba menggabungkan kedua tabel tersebut.

    Tbl_question  
    -------------
    _Indo  
    pertanyaan  
    categoryid  
    Tbl_alternative
    ---------------
    _Indo 
    questionid 
    categoryid 
    alternatif

Saya ingin sesuatu seperti berikut ini:

SELECT tbl_question.question, tbl_alternative.alternative where 
categoryid=tbl_alternative.categoryid AND tbl_question._id = 
tbl_alternative.questionid.` 

Ini adalah usaha saya:

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;

Saya menemukan cara ini untuk membentuk kueri lebih sulit daripada SQL biasa, tetapi telah mendapat saran untuk menggunakan cara ini karena tidak terlalu rentan terhadap kesalahan.

Pertanyaan

Bagaimana cara menggabungkan dua tabel SQLite dalam aplikasi saya?

kakka47
sumber
Kesalahan apa yang Anda dapatkan? Apakah Anda mencoba menguji dengan mengganti string yang digabungkan dengan string literal? (FWIW, saya tidak perlu menggunakan kursor sejak 1986 atau lebih. Tapi saya tidak mengembangkan untuk android.)
Mike Sherrill 'Cat Recall'
Saya tidak melihat di mana / bagaimana kolom gabungan-dalam ditentukan di blok kode kursor. SQL akan menjadi "pilih-kolom-daftar yang diinginkan dari T1 bergabung dalam T2 di T1.questionid = T2.questionid dan T1.categoryid = T2.categoryid di mana T1.categoryid = {nilai kategori yang diinginkan}"
Tim
Ini adalah kesalahan saya: nama kolom ambigu: _id:, saat menyusun: PILIH DISTINCT _id, gambar, pertanyaan, alternatif, questionid DARI tbl_question INNER GABUNG tbl_alternative WHERE categoryid = 2 DAN _id = questionid. Jadi saya berasumsi bahwa saya perlu menentukan tbl_question._id = tbl_alternative.questionid, tetapi saya tidak tahu bagaimana melakukan ini dengan cara kueri yang saya gunakan di atas. Dan saya tidak tahu apa yang harus dikembalikan jika saya menggunakan sintaks sql "biasa": "Pilih 'dari tbl_question INNER JOIN tbl_alternative ON tbl_question._id = tbl_alternative.questionid AND tbl_question.categoryid = tbl_alternative.categoryid = categoryid;
kakka47
@ Tim: cara Anda melakukannya, bagaimana cara saya membentuk metode di kelas db-helper? Haruskah saya tidak mengembalikan kursor? Saya belajar sambil berjalan, saran apa pun yang membuat kode saya lebih baik, saya bersyukur!
kakka47
di sini saya telah memposting jawaban stackoverflow.com/questions/31567564/…
Sagar

Jawaban:

204

Anda membutuhkan metode rawQuery .

Contoh:

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)});

Gunakan? binding alih-alih memasukkan nilai ke dalam kueri sql mentah.

pawelzieba.dll
sumber
1
@necromancer bagaimana Anda melihat membuat VIEWopsi yang lebih baik daripada rawQuery?
Muhammad Babar
Apa yang harus dilakukan setelah kueri? Bagaimana Anda tahu id mana yang termasuk dalam tabel mana, dan bagaimana jika kedua tabel memiliki nama kolom yang sama untuk beberapa kolom? atau jika ada beberapa item untuk tabel kedua?
pengembang android
@ android-developer "Apa yang harus dilakukan setelah kueri?" - Anda mendapatkan Cursor, lakukan apa pun yang Anda inginkan dengan data: P; 2. "Bagaimana Anda tahu id mana ..." - ini adalah GABUNG, jadi tergantung pada jenisnya , Anda mungkin SELALU, TIDAK PERNAH, atau MUNGKIN diisi kunci; 3. "... nama kolom yang sama untuk beberapa kolom" - dapat terjadi, tetapi ambiguitas DAPAT dihilangkan. Sejujurnya saya tidak tahu apakah Anda dapat mengambil dengan "Table.column" menggunakan getColumnIndex, Anda selalu dapat menghitung indeks secara manual, cukup debug-test; 4. "beberapa item untuk tabel kedua" juga tergantung pada jenis gabungan.
leRobot
dengan asumsi View adalah FULL OUTER JOIN dari relasi 1-ke-banyak (misalnya RawContactsEntity ), Anda bisa mendapatkan baris dengan null KEY pada tabel "banyak", tetapi tidak pernah pada tabel "1" sehingga Anda harus mencabangkan cursor pass seperti ini: while (cursor.moveToNext () {if (cursor.getValue (getManyTableKeyIndex ()) == NULL) {/ * ini adalah baris tanpa hubungan, hanya memiliki "1" data tabel /} lain {/ ini relasi HIT, jadi ada data dari kedua tabel * /}} cursor.close ();
leRobot
Saya lupa menambahkan percabangan lain untuk baris "no parent many" yang masih bisa Anda dapatkan dengan FULL OUTER JOIN, tetapi biasanya tidak jika relasinya ketat (saya tidak yakin apakah Android memberlakukan hubungan). Tidak benar-benar tahu cara mendapatkan kolom yang ambigu, tetapi Anda dapat mengujinya pada CLI SQLite apa pun dengan mendeklarasikan tampilan dengan ambiguitas kolom dan melihat nama apa yang didapat kolom tersebut pada kueri "SELECT * FROM view_x". Kemudian Anda cukup menerapkannya ke jenis pembantu Android apa pun yang Anda inginkan (.query () / .rawQuery () ...)
leRobot
20

Cara alternatif adalah dengan membuat tampilan yang kemudian dikueri seperti tabel. Di banyak manajer database menggunakan tampilan dapat menghasilkan kinerja yang lebih baik.

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;

Ini dari memori jadi mungkin ada beberapa masalah sintaksis. http://www.sqlite.org/lang_createview.html

Saya menyebutkan pendekatan ini karena Anda dapat menggunakan SQLiteQueryBuilder dengan tampilan seperti yang Anda maksudkan bahwa itu lebih disukai.

phreed
sumber
4
Ketika datang ke RDBMS seperti Oracle, pandangan dapat memberi Anda beberapa keuntungan kinerja tetapi menurut pengalaman saya, mereka hanya digunakan bila diperlukan untuk kinerja atau tujuan pelaporan. Atau dengan kata lain, Anda tidak membuat tampilan untuk setiap kueri gabungan yang Anda gunakan. Dan di SQLite khususnya, saya tidak percaya ada peningkatan kinerja - menurut jawaban atas pertanyaan ini, SQLite menulis ulang kueri menggunakan subkueri. stackoverflow.com/questions/25577407/performance-penalty-for-unused-view#25580319 API Android mungkin membatasi untuk apa yang dilakukan OP tetapi rawQuery()merupakan jawaban yang tepat.
spaaarky21
13

Selain jawaban @ pawelzieba yang pasti benar untuk menggabungkan dua tabel, sementara Anda dapat menggunakan INNER JOINseperti ini

SELECT * FROM expense INNER JOIN refuel
ON exp_id = expense_id
WHERE refuel_id = 1

melalui kueri mentah seperti ini -

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
);

karena dukungan SQLite yang kompatibel dengan cara primitif dalam membuat kueri, kami mengubah perintah itu menjadi ini -

SELECT *
FROM expense, refuel
WHERE exp_id = expense_id AND refuel_id = 1

dan karenanya dapat mengambil advanatage dari metode helper 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
);

Untuk posting blog rinci, periksa http://blog.championswimmer.in/2015/12/doing-a-table-join-in-android-without-using-rawquery

Arnav Gupta
sumber
1
Saya tidak mengerti dari mana Utils.concat berasal.
nicoqueijo
1
Ini adalah pendekatan yang baik tetapi tidak akan berfungsi jika nama kolom dalam satu tabel sama dengan nama kolom di tabel lain. Seperti jika kedua tabel memiliki kolom dengan ID nama, maka ini akan melemparandroid.database.sqlite.SQLiteException: ambiguous column name
Anup Sharma
8

"Kolom ambigu" biasanya berarti nama kolom yang sama muncul di setidaknya dua tabel; mesin database tidak tahu mana yang Anda inginkan. Gunakan nama tabel lengkap atau alias tabel untuk menghilangkan ambiguitas.

Inilah contoh yang kebetulan saya miliki di editor saya. Itu dari masalah orang lain, tapi tetap masuk akal.

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)
Mike Sherrill 'Cat Recall'
sumber
Ya, saya curiga. Tetapi saya tidak tahu bagaimana menentukan tabel, karena cara: DBTABLE_QUESTION.KEY_QUESTION, tidak berfungsi. Tetapi ketika saya menggunakan cara rawQuery, mudah untuk menentukan tabel mana yang termasuk dalam kolom.
kakka47