Android uygulamamda iki SQLite tablosunu nasıl birleştiririm?


97

Arka fon

İki tablo içeren bir veritabanına sahip bir Android projem var: tbl_questionve tbl_alternative.

Görüşleri sorular ve alternatiflerle doldurmak için imleçler kullanıyorum. İki tabloyu birleştirmeye çalışana kadar ihtiyacım olan verileri elde etmede sorun yok.

    Tbl_question  
    -------------
    _İD  
    soru  
    kimlik kategorisi  
    Tbl_alternative
    ---------------
    _İD 
    soru kimliği 
    kimlik kategorisi 
    alternatif

Aşağıdaki gibi bir şey istiyorum:

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

Bu benim girişimim:

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;

Normal SQL'den daha zor sorgu oluşturmanın bu yolunu buluyorum, ancak daha az hataya meyilli olduğundan bu şekilde kullanma tavsiyesi aldım.

Soru

Uygulamamda iki SQLite tablosunu nasıl birleştiririm?


Hangi hatayı alıyorsun? Birleştirilmiş dizeyi değişmez bir dize ile değiştirerek testi denediniz mi? (FWIW, 1986'dan beri imleç kullanmak zorunda kalmadım. Ancak android için geliştirmiyorum.)
Mike Sherrill 'Cat Recall'

İmleç kod bloğunda iç birleşim sütunlarının nerede / nasıl belirtildiğini görmüyorum. SQL "T1.questionid = T2.questionid ve T1.categoryid = T2.categoryid üzerinde T1 iç birleşiminden istenen cols-listesinden seçin, burada T1.categoryid = {istenen kategori değeri}"
Tim

Bu benim hatam: belirsiz sütun adı: _id:, derleme sırasında: SELECT DISTINCT _id, image, question, alternative, questionid from tbl_question INNER JOIN tbl_alternative WHERE categoryid = 2 AND _id = questionid. Bu yüzden tbl_question._id = tbl_alternative.questionid belirtmem gerektiğini varsayıyorum, ancak bunu yukarıda kullandığım sorgu biçiminde nasıl yapacağımı bilmiyorum. Ve "normal" bir sql-sözdizimi kullanırsam ne döndüreceğimi bilmiyorum: tbl_question INNER JOIN tbl_alternative ON tbl_question._id = tbl_alternative.questionid AND tbl_question.categoryid = tbl_alternative.categoryid = categoryid;
kakka47

@Tim: Bunu yapma şeklinize göre, yöntemi db-helper sınıfında nasıl oluşturabilirim? Bir imleci geri döndürmemeli miyim? İlerledikçe öğreniyorum, kodumu daha iyi hale getiren öneriler için minnettarım!
kakka47

Yanıtlar:


206

RawQuery yöntemine ihtiyacınız var .

Misal:

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

Kullanılsın mı? değerleri ham sql sorgusuna koymak yerine bağlamalar.


1
@necromancer bundan VIEWdaha iyi bir seçenek oluşturmayı nasıl görüyorsunuz rawQuery?
Muhammad Babar

Sorgudan sonra ne yapılmalı? Hangi kimliğin hangi tabloya ait olduğunu nasıl anlarsınız ve her iki tabloda da bir sütun için aynı sütun adı varsa? veya ikinci tablo için birden fazla öğe varsa?
android geliştiricisi

@ android-developer "Sorgudan sonra ne yapılmalı?" - bir İmleç alırsanız, verilerle istediğinizi yapın: P; 2. "Hangi kimliği nasıl anlarsınız ..." - bu bir JOIN'dir, bu nedenle hangi türe bağlı olarak HER ZAMAN, ASLA veya MAYBE doldurulmuş anahtarlar alabilirsiniz; 3. "... bazı sütun için aynı sütun adı" - olabilir, ancak belirsizlik KALDIRILABİLİR. Dürüst olmak gerekirse getColumnIndex kullanarak "Table.column" ile getirip getiremeyeceğinizi bilmiyorum. İndeksi her zaman manuel olarak hesaplayabilirsiniz, sadece hata ayıklama testi yapın; 4. "İkinci tablo için birden çok öğe" ayrıca birleştirme türüne bağlıdır.
leRobot

Görünümün 1-çok ilişkisinin TAM DIŞ BİRLEŞTİRİLMESİ olduğunu varsayarsak (örn. RawContactsEntity ), "çok" tablosunda null KEY içeren satırlar alabilirsiniz, ancak hiçbir zaman "1" tablosunda değil, bu nedenle imleç geçişinizi dallara ayırmalısınız şöyle: while (cursor.moveToNext () {if (cursor.getValue (getManyTableKeyIndex ()) == NULL) {/ * bu bir ilişkisiz satırdır, yalnızca "1" tablo verisi içerir /} else {/ this is bir HIT ilişkisi, yani her iki tablodan da veri var * /}} cursor.close ();
leRobot

FULL OUTER JOIN ile hala alabileceğiniz "çok sayıda ebeveyn yok" satırı için başka bir dallanma eklemeyi unuttum, ancak genellikle ilişki katı ise (Android'in ilişkileri zorladığından emin değilim). Belirsiz sütunların nasıl elde edileceğini gerçekten bilmiyorum, ancak herhangi bir SQLite CLI üzerinde sütun belirsizliği olan bir görünüm bildirerek test edebilir ve bu sütunların "SELECT * FROM view_x" sorgusunda hangi adı aldığını görebilirsiniz. Sonra bunu istediğiniz Android yardımcı çeşidine uygulayın (.query () / .rawQuery () ...)
leRobot

20

Alternatif bir yol, daha sonra tıpkı bir tablo gibi sorgulanan bir görünüm oluşturmaktır. Çoğu veritabanı yöneticisinde bir görünüm kullanmak daha iyi performans sağlayabilir.

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;

Bu hafızadan geldiğinden bazı sözdizimsel sorunlar olabilir. http://www.sqlite.org/lang_createview.html

Bu yaklaşımdan bahsediyorum çünkü o zaman tercih edildiğini ima ettiğiniz gibi SQLiteQueryBuilder'ı görünümle kullanabilirsiniz.


4
Oracle gibi RDBMS'ler söz konusu olduğunda, görünümler size bazı performans kazanımları sağlayabilir ancak deneyimlerime göre, bunlar yalnızca performans veya raporlama amaçları için gerekli olduğunda kullanılır. Ya da başka bir deyişle, kullandığınız her birleştirme sorgusu için bir görünüm oluşturmazsınız. Ve özellikle SQLite'de herhangi bir performans kazancı olduğuna inanmıyorum - bu sorunun cevabına göre, SQLite sorguyu bir alt sorgu kullanarak yeniden yazar. stackoverflow.com/questions/25577407/performance-penalty-for-unused-view#25580319 Android'in API'sı, OP'nin yaptığı şeyi sınırlıyor olabilir, ancak rawQuery()doğru cevap budur.
spaaarky

13

Bir kullanabilirsiniz süre, iki tablo katılmak için kesinlikle doğrudur @ pawelzieba cevabı, ek olarak INNER JOINböyle

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

bunun gibi ham sorgu aracılığıyla -

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'ın ilkel sorgulama yöntemine geriye dönük uyumlu desteği nedeniyle, bu komutu şuna dönüştürüyoruz -

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

ve dolayısıyla SQLiteDatabase.query () yardımcı yönteminin avantajlarından yararlanabilirsiniz

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

Ayrıntılı bir blog yayını için bu http://blog.championswimmer.in/2015/12/doing-a-table-join-in-android-without-using-rawquery'ye bakın


1
Utils.concat'in nereden geldiğini anlamıyorum.
nicoqueijo

1
Bu iyi bir yaklaşımdır, ancak bir tablodaki herhangi bir sütun adı başka bir tablodaki bir sütun adıyla aynıysa işe yaramayacaktır. Her iki tablonun da adı kimliğine sahip bir sütun varsa, o zaman bu atacaktırandroid.database.sqlite.SQLiteException: ambiguous column name
Anup Sharma

8

"Belirsiz sütun" genellikle aynı sütun adının en az iki tabloda göründüğü anlamına gelir; veritabanı motoru hangisini istediğinizi söyleyemez. Belirsizliği gidermek için tam tablo adlarını veya tablo takma adlarını kullanın.

İşte editörümde olan bir örnek. Başkasının probleminden, ama yine de mantıklı olmalı.

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)

Evet, bundan şüphelendim. Ancak, DBTABLE_QUESTION.KEY_QUESTION yolu çalışmadığı için tabloları nasıl belirteceğimi bilmiyordum. Ancak rawQuery yöntemini kullandığımda hangi sütunun hangi tabloya ait olduğunu tanımlamak kolay oldu.
kakka47
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.