Jak połączyć dwie tabele SQLite w mojej aplikacji na Androida?


95

tło

Mam projekt systemu Android, który ma bazę danych z dwiema tabelami: tbl_questioni tbl_alternative.

Aby wypełnić widoki pytaniami i alternatywami, używam kursorów. Nie ma problemów z uzyskaniem potrzebnych danych, dopóki nie spróbuję połączyć dwóch tabel.

    Tbl_question  
    -------------
    _ID  
    pytanie  
    categoryid  
    Tbl_alternative
    ---------------
    _ID 
    questionid 
    categoryid 
    alternatywny

Chcę coś takiego:

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

To jest moja próba:

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;

Uważam, że ten sposób tworzenia zapytań jest trudniejszy niż zwykły SQL, ale otrzymałem radę, aby używać tego sposobu, ponieważ jest mniej podatny na błędy.

Pytanie

Jak połączyć dwie tabele SQLite w mojej aplikacji?


Jaki błąd otrzymujesz? Czy próbowałeś przetestować, zastępując połączony ciąg literałem? (FWIW, nie musiałem używać kursora od 1986 roku mniej więcej. Ale nie tworzę dla Androida.)
Mike Sherrill „Cat Recall”

Nie widzę, gdzie / jak kolumny złączenia wewnętrznego są określane w bloku kodu kursora. Kod SQL brzmiałby: „wybierz żądaną-listę-kolumn ze złączenia wewnętrznego T1 T2 na T1.questionid = T2.questionid i T1.categoryid = T2.categoryid, gdzie T1.categoryid = {żądana wartość kategorii}”
Tim

To jest mój błąd: niejednoznaczna nazwa kolumny: _id:, podczas kompilacji: SELECT DISTINCT _id, image, question, Alternative, questionid FROM tbl_question INNER JOIN tbl_alternative WHERE categoryid = 2 AND _id = questionid. Zakładam więc, że muszę określić tbl_question._id = tbl_alternative.questionid, ale nie wiem, jak to zrobić w sposób, którego używam powyżej. A ja nie wiem, co do powrotu, jeśli mogę użyć "regular" SQL składni: „Wybierz" z tbl_question INNER JOIN tbl_alternative NA tbl_question._id = tbl_alternative.questionid I tbl_question.categoryid = tbl_alternative.categoryid = IDkategorii;
kakka47

@Tim: tak jak to robisz, jak utworzyć metodę w klasie db-helper? Czy nie powinienem zwracać kursora? W trakcie nauki uczę się wszelkich sugestii, dzięki którym mój kod będzie lepszy, za które jestem wdzięczny!
kakka47

tutaj zamieściłem odpowiedź stackoverflow.com/questions/31567564/ ...
Sagar

Odpowiedzi:


204

Potrzebujesz metody rawQuery .

Przykład:

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

Posługiwać się ? wiązania zamiast umieszczania wartości w surowym zapytaniu sql.


1
@necromancer, jak widzisz stworzenie VIEWlepszej opcji niż rawQuery?
Muhammad Babar

Co należy zrobić po zapytaniu? Skąd wiesz, który identyfikator należy do której tabeli i co by było, gdyby obie tabele miały taką samą nazwę kolumny dla jakiejś kolumny? lub jeśli w drugiej tabeli jest wiele elementów?
programista Androida

@ android-developer "Co należy zrobić po zapytaniu?" - dostajesz Cursor, rób co chcesz z danymi: P; 2. „Skąd wiesz, który identyfikator ...” - to JOIN, więc w zależności od typu , możesz otrzymywać klucze zapełnione ZAWSZE, NIGDY lub MOŻE BYĆ; 3. „... ta sama nazwa kolumny dla niektórych kolumn” - może się zdarzyć, ale niejednoznaczność MOŻE zostać usunięta. Szczerze mówiąc, nie wiem, czy możesz pobrać za pomocą "Table.column" używając getColumnIndex. Zawsze możesz obliczyć indeks ręcznie, po prostu zdebuguj go; 4. „Wiele pozycji dla drugiej tabeli” zależy również od typu łączenia.
leRobot

zakładając, że widok jest PEŁNYM JOINEM ZEWNĘTRZNYM relacji 1-do-wielu (np. RawContactsEntity ), możesz uzyskać wiersze z pustym KLUCZEM w tabeli „wiele”, ale nigdy w tabeli „1”, więc powinieneś rozgałęzić przebieg kursora na przykład: while (kursor.moveToNext () {if (kursor.getValue (getManyTableKeyIndex ()) == NULL) {/ * to jest wiersz bez relacji, zawiera tylko dane tabeli „1” /} else {/ to jest relacja HIT, więc są dane z obu tabel * /}} cursor.close ();
leRobot

Zapomniałem dodać kolejną gałąź dla wierszy „no parent many”, które nadal można uzyskać za pomocą FULL OUTER JOIN, ale zwykle nie jest to możliwe, jeśli relacja jest ścisła (nie jestem pewien, czy Android wymusza relacje). Naprawdę nie wiem, jak uzyskać niejednoznaczne kolumny, ale możesz po prostu przetestować to na dowolnym interfejsie wiersza polecenia SQLite, deklarując widok z niejednoznacznością kolumn i zobaczyć, jaką nazwę te kolumny otrzymają w zapytaniu „SELECT * FROM view_x”. Następnie wystarczy zastosować że aby cokolwiek Android pomocnik smak chcesz (.query () / .rawQuery () ...)
leRobot

20

Alternatywnym sposobem jest utworzenie widoku, który jest następnie odpytywany tak jak tabela. W wielu menedżerach baz danych użycie widoku może skutkować lepszą wydajnością.

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;

To pochodzi z pamięci, więc mogą wystąpić pewne problemy składniowe. http://www.sqlite.org/lang_createview.html

Wspominam o tym podejściu, ponieważ wtedy możesz użyć SQLiteQueryBuilder z widokiem, jak sugerowałeś, że jest preferowany.


4
Jeśli chodzi o systemy RDBMS, takie jak Oracle, widoki mogą przynieść pewien wzrost wydajności, ale z mojego doświadczenia wynika, że ​​są one używane tylko wtedy, gdy jest to konieczne do celów wydajności lub raportowania. Inaczej mówiąc, nie tworzysz widoku dla każdego używanego zapytania sprzężenia. Zwłaszcza w przypadku SQLite nie sądzę, aby był jakikolwiek wzrost wydajności - zgodnie z odpowiedzią na to pytanie SQLite przepisuje zapytanie przy użyciu podzapytania. stackoverflow.com/questions/25577407/performance-penalty-for-unused-view#25580319 API Androida może ograniczać to, co robi OP, ale rawQuery()jest właściwą odpowiedzią.
spaaarky21

13

Oprócz odpowiedzi @ pawelzieba, która na pewno jest poprawna, należy dołączyć do dwóch stołów, podczas gdy można użyć INNER JOINtakiego

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

za pomocą surowego zapytania, takiego jak to -

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

ze względu na kompatybilność wsteczną SQLite z prymitywnym sposobem zapytań, zamieniamy to polecenie w to -

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

dzięki czemu można skorzystać z metody pomocniczej 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
);

Aby uzyskać szczegółowy post na blogu, sprawdź ten http://blog.championswimmer.in/2015/12/doing-a-table-join-in-android-without-using-rawquery


1
Nie rozumiem, skąd pochodzi Utils.concat.
nicoqueijo

1
Jest to dobre podejście, ale nie zadziała, jeśli nazwa kolumny w jednej tabeli jest taka sama, jak nazwa kolumny w innej tabeli. Na przykład, jeśli oba stoły mają kolumnę z identyfikatorem nazwy, to wyrzuciandroid.database.sqlite.SQLiteException: ambiguous column name
Anup Sharma

8

„Niejednoznaczna kolumna” zwykle oznacza, że ​​ta sama nazwa kolumny występuje w co najmniej dwóch tabelach; silnik bazy danych nie może powiedzieć, który z nich chcesz. Użyj pełnych nazw tabel lub aliasów tabel, aby usunąć niejednoznaczność.

Oto przykład, który miałem w moim edytorze. To problem kogoś innego, ale i tak powinno mieć sens.

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)

Tak, podejrzewałem to. Ale nie wiedziałem, jak określić tabele, ponieważ sposób: DBTABLE_QUESTION.KEY_QUESTION, nie działał. Ale kiedy używam metody rawQuery, łatwo było zdefiniować, do której tabeli należała kolumna.
kakka47
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.