Come posso unire due tabelle SQLite nella mia applicazione Android?


95

sfondo

Ho un progetto Android che ha un database con due tabelle: tbl_questione tbl_alternative.

Per popolare le viste con domande e alternative sto usando i cursori. Non ci sono problemi nell'ottenere i dati di cui ho bisogno finché non provo a unire le due tabelle.

    Tbl_question  
    -------------
    _id  
    domanda  
    categoryid  
    Tbl_alternative
    ---------------
    _id 
    questionid 
    categoryid 
    alternativa

Voglio qualcosa di simile al seguente:

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

Questo è il mio tentativo:

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;

Trovo che questo modo per formare query sia più difficile del normale SQL, ma ho ricevuto il consiglio di usarlo in questo modo poiché è meno soggetto a errori.

Domanda

Come posso unire due tabelle SQLite nella mia applicazione?


Che errore stai ricevendo? Hai provato a testare sostituendo la stringa concatenata con una stringa letterale? (FWIW, non ho dovuto usare un cursore dal 1986 o giù di lì. Ma non sviluppo per Android.)
Mike Sherrill 'Cat Recall'

Non vedo dove / come vengono specificate le colonne di join interno nel blocco di codice del cursore. L'SQL sarebbe "selezionare la lista-colonne desiderate da T1 inner join T2 su T1.questionid = T2.questionid e T1.categoryid = T2.categoryid dove T1.categoryid = {il valore della categoria desiderata}"
Tim

Questo è il mio errore: nome colonna ambiguo: _id:, durante la compilazione: SELECT DISTINCT _id, image, question, alternative, questionid FROM tbl_question INNER JOIN tbl_alternative WHERE categoryid = 2 AND _id = questionid. Quindi presumo di dover specificare tbl_question._id = tbl_alternative.questionid, ma non so come farlo nel modo di query che uso sopra. E non so cosa restituire se utilizzo una sintassi sql "normale": "Seleziona 'da tbl_question INNER JOIN tbl_alternative ON tbl_question._id = tbl_alternative.questionid AND tbl_question.categoryid = tbl_alternative.categoryid = categoryid;
kakka47

@ Tim: il modo in cui lo fai, come faccio a formare il metodo nella classe db-helper? Non dovrei restituire un cursore? Sto imparando man mano che procedo, sono grato di qualsiasi suggerimento che migliori il mio codice!
kakka47

qui ho pubblicato la risposta stackoverflow.com/questions/31567564/…
Sagar

Risposte:


204

Hai bisogno del metodo rawQuery .

Esempio:

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

Uso ? associazioni invece di inserire valori in query sql non elaborate.


1
@necromancer come vedi la creazione di VIEWun'opzione migliore di rawQuery?
Muhammad Babar

Cosa fare dopo la query? Come fai a sapere quale ID appartiene a quale tabella e cosa succede se entrambe le tabelle hanno lo stesso nome di colonna per una colonna? o se ci sono più elementi per la seconda tabella?
sviluppatore Android

@ android-developer "Cosa si dovrebbe fare dopo la query?" - ottieni un Cursore, fai quello che vuoi con i dati: P; 2. "Come fai a sapere quale id ..." - è un JOIN, quindi a seconda del tipo , potresti ottenere chiavi popolate SEMPRE, MAI o FORSE; 3. "... stesso nome di colonna per alcune colonne" - può accadere, ma l'ambiguità PU essere rimossa. Onestamente non so se puoi recuperare con "Table.column" usando getColumnIndex, puoi sempre calcolare l'indice manualmente, basta eseguirne il debug-test; 4. "più elementi per la seconda tabella" dipende anche dal tipo di join.
leRobot

supponendo che la vista sia una FULL OUTER JOIN di una relazione 1-a-molti (ad esempio RawContactsEntity ), puoi ottenere righe con KEY nullo sulla tabella "molti", ma mai sulla tabella "1" quindi dovresti diramare il tuo cursore passare in questo modo: while (cursor.moveToNext () {if (cursor.getValue (getManyTableKeyIndex ()) == NULL) {/ * questa è una riga senza relazione, ha solo "1" dati di tabella /} else {/ questa è una relazione HIT, quindi ci sono dati da entrambe le tabelle * /}} cursor.close ();
leRobot

Ho dimenticato di aggiungere un altro branching per le righe "no parent many" che puoi ancora ottenere con FULL OUTER JOIN, ma di solito non lo farà se la relazione è rigorosa (non sono sicuro che Android imponga le relazioni). Non so davvero come ottenere colonne ambigue, ma puoi semplicemente testarlo su qualsiasi CLI di SQLite dichiarando una vista con ambiguità di colonna e vedere quale nome ricevono quelle colonne su una query "SELECT * FROM view_x". Quindi lo applichi a qualsiasi tipo di supporto Android desideri (.query () / .rawQuery () ...)
leRobot

20

Un modo alternativo è costruire una vista che viene quindi interrogata proprio come una tabella. In molti gestori di database l'utilizzo di una vista può portare a prestazioni migliori.

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;

Questo viene dalla memoria, quindi potrebbero esserci alcuni problemi sintattici. http://www.sqlite.org/lang_createview.html

Ho menzionato questo approccio perché poi puoi usare SQLiteQueryBuilder con la vista come hai implicato che fosse preferito.


4
Quando si tratta di RDBMS come Oracle, le visualizzazioni possono darti alcuni miglioramenti in termini di prestazioni ma, nella mia esperienza, vengono utilizzate solo quando necessario per scopi di prestazioni o report. O, in altre parole, non crei una vista per ogni query di join che usi. E in SQLite in particolare, non credo che ci sia alcun miglioramento delle prestazioni: in base alla risposta a questa domanda, SQLite riscrive la query utilizzando una sottoquery. stackoverflow.com/questions/25577407/performance-penalty-for-unused-view#25580319 L'API di Android potrebbe limitare ciò che sta facendo l'OP ma rawQuery()è la risposta giusta.
spaaarky21

13

Oltre alla risposta di @ pawelzieba, che è decisamente corretta, per unire due tavoli, mentre puoi usare un INNER JOINcome questo

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

tramite query grezza come questa -

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

a causa del supporto retrocompatibile di SQLite del modo primitivo di interrogare, trasformiamo quel comando in questo:

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

e quindi essere in grado di sfruttare il metodo di supporto 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
);

Per un post dettagliato sul blog, controlla questo http://blog.championswimmer.in/2015/12/doing-a-table-join-in-android-without-using-rawquery


1
Non capisco da dove provenga Utils.concat.
nicoqueijo

1
Questo è un buon approccio ma non funzionerà se il nome di una colonna in una tabella è uguale al nome di una colonna in un'altra tabella. Come se entrambe le tabelle avessero una colonna con ID nome, allora questo lanceràandroid.database.sqlite.SQLiteException: ambiguous column name
Anup Sharma

8

"Colonna ambigua" di solito significa che lo stesso nome di colonna appare in almeno due tabelle; il motore di database non è in grado di dire quale si desidera. Utilizzare nomi di tabella completi o alias di tabella per rimuovere l'ambiguità.

Ecco un esempio che mi è capitato di avere nel mio editor. Viene da un problema di qualcun altro, ma dovrebbe comunque avere senso.

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)

Sì, lo sospettavo. Ma non sapevo come specificare le tabelle, poiché il modo: DBTABLE_QUESTION.KEY_QUESTION, non funzionava. Ma quando utilizzo il metodo rawQuery è stato facile definire a quale tabella apparteneva la colonna.
kakka47
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.