Як об’єднати дві таблиці SQLite у моїй програмі Android?


95

Фон

У мене є проект Android, який має базу даних з двома таблицями: tbl_questionі tbl_alternative.

Для заповнення поглядів питаннями та альтернативами я використовую курсори. Не виникає проблем з отриманням потрібних даних, поки я не спробую приєднати дві таблиці.

    Tbl_question  
    -------------
    _id  
    питання  
    категоричний  
    Tbl_alternative
    ---------------
    _id 
    Questionid 
    категоричний 
    альтернатива

Я хочу щось на зразок наступного:

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, але я отримав пораду використовувати цей спосіб, оскільки він менш схильний до помилок.

Питання

Як мені приєднати дві таблиці SQLite у моїй програмі?


Яку помилку ви отримуєте? Ви спробували перевірити, замінивши зв’язаний рядок на рядок літералом? (FWIW, мені не доводилося користуватися курсором приблизно з 1986 року. Але я не розробляю програми для андроїда.)
Майк Шеррілл "Відкликання кота"

Я не бачу, де / як в блоці коду курсора вказуються стовпці внутрішнього об'єднання. SQL буде "вибрати бажаний список cols-списків з T1 внутрішнього об'єднання T2 на T1.questionid = T2.questionid і T1.categoryid = T2.categoryid де T1.categoryid = {бажане значення категорії}"
Тім,

Це моя помилка: неоднозначне ім’я стовпця: _id:, під час компіляції: SELECT DISTINCT _id, image, question, alternative, questionstio FROM tbl_question INNER JOIN tbl_alternative WHERE categoryid = 2 AND _id = questionid. Отже, я припускаю, що мені потрібно вказати tbl_question._id = tbl_alternative.questionid, але я не знаю, як це зробити в способі запиту, який я використовую вище. І я не знаю, що повернути, якщо я використовую "звичайний" sql-синтаксис: "Виберіть" з tbl_question ВНУТРІШНЄ ПРИЄДНАННЯ tbl_alternative ON tbl_question._id = tbl_alternative.questionid І tbl_question.categoryid = tbl_alternative.categoryid = categoryid;
kakka47

@Tim: як ти це робиш, як мені сформувати метод у класі db-helper? Чи не слід повертати курсор? Я вчуся по ходу, за будь-які пропозиції, які покращують мій код, я їм вдячний!
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?
Мухаммед Бабар,

Що слід робити після запиту? Звідки ви знаєте, який ідентифікатор належить до якої таблиці, і що, якби обидві таблиці мали одне і те ж ім'я стовпця для якогось стовпця? або якщо для другої таблиці є кілька елементів?
розробник android

@ android-developer "Що робити після запиту?" - ви отримуєте курсор, робіть із даними все, що завгодно: P; 2. "Звідки ви знаєте, який ідентифікатор ..." - це ПРИЄДНАННЯ, тож залежно від типу ви можете отримати ЗАВЖДИ, НІКОЛИ або МОЖЛИВО заповнені ключі; 3. "... однакова назва стовпця для деякого стовпця" - може трапитися, але неоднозначність МОЖНА видалити. Чесно кажучи, я не знаю, чи можете ви отримати за допомогою "Table.column" за допомогою getColumnIndex, Ви завжди можете обчислити індекс вручну, просто налагодіть його тестування; 4. "кілька елементів для другої таблиці" також залежить від типу об'єднання.
leRobot

припускаючи, що View - це ПОВНЕ ЗОВНІШНЄ ЗЄДНАННЯ відношення 1-до-багатьох (наприклад, RawContactsEntity ), ви можете отримати рядки з нульовим таблиці "багато", але ніколи в таблиці "1", тому вам слід розгалужувати прохід курсора приблизно так: while (cursor.moveToNext () {if (cursor.getValue (getManyTableKeyIndex ()) == NULL) {/ * це рядок, який не має відношення, містить лише дані таблиці "1" /} ще {/ це відношення HIT, тому є дані з обох таблиць * /}} cursor.close ();
leRobot

Я забув додати ще одне розгалуження для рядків "без батьків багато", які ви все ще можете отримати за допомогою ПОВНОГО ЗОВНІШНЬОГО ПРИЄДНАННЯ, але зазвичай цього не робитиме, якщо відносини суворі (я не впевнений, що 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
Що стосується RDBMS, таких як Oracle, подання можуть принести вам певний приріст продуктивності, але, на мій досвід, вони використовуються лише тоді, коли це необхідно для цілей продуктивності або звітування. Або, інакше кажучи, ви не створюєте подання для кожного використовуваного запиту на приєднання. І зокрема, в SQLite, я не вірю, що якийсь приріст продуктивності - відповідно до відповіді на це питання, SQLite переписує запит, використовуючи підзапит. stackoverflow.com/questions/25577407/performance-penalty-for-unused-view#25580319 API Android може обмежувати те, що робить OP, але rawQuery()це правильна відповідь.
spaaarky21

13

На додаток до відповіді @ pawelzieba, яка, безумовно, є правильною, об'єднати дві таблиці, тоді як ви можете використовувати 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

1
Це хороший підхід, але він не буде працювати, якщо будь-яке ім'я стовпця в одній таблиці збігається з ім'ям стовпця в іншій таблиці. Як якщо в обох таблицях є стовпець з ідентифікатором імені, то це викинеandroid.database.sqlite.SQLiteException: ambiguous column name
Ануп Шарма

8

«Неоднозначний стовпець» зазвичай означає, що одна і та ж назва стовпця відображається принаймні в двох таблицях; механізм баз даних не може визначити, який саме ви хочете. Використовуйте повні назви таблиць або псевдоніми таблиць, щоб усунути двозначність.

Ось приклад, який я випадково мав у своєму редакторі. Це з чужої проблеми, але все одно має сенс.

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.