Làm cách nào để nối hai bảng SQLite trong ứng dụng Android của tôi?


95

Lý lịch

Tôi có một dự án Android có cơ sở dữ liệu với hai bảng: tbl_questiontbl_alternative.

Để điền các chế độ xem với các câu hỏi và lựa chọn thay thế, tôi đang sử dụng con trỏ. Không có vấn đề gì trong việc lấy dữ liệu tôi cần cho đến khi tôi cố gắng nối hai bảng.

    Tbl_question  
    -------------
    _Tôi  
    câu hỏi  
    Thể loại ID  
    Tbl_alternative
    ---------------
    _Tôi 
    câu hỏi 
    Thể loại ID 
    thay thế

Tôi muốn một cái gì đó như sau:

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

Đây là nỗ lực của tôi:

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;

Tôi thấy cách này để tạo truy vấn khó hơn SQL thông thường, nhưng tôi đã nhận được lời khuyên nên sử dụng cách này vì nó ít bị lỗi hơn.

Câu hỏi

Làm cách nào để nối hai bảng SQLite trong ứng dụng của tôi?


lỗi gì bạn nhận được? Bạn đã thử kiểm tra bằng cách thay thế chuỗi được nối bằng một chuỗi ký tự chưa? (FWIW, tôi đã không phải sử dụng con trỏ kể từ năm 1986 hoặc lâu hơn. Nhưng tôi không phát triển cho Android.)
Mike Sherrill 'Cat Recall'

Tôi không thấy vị trí / cách các cột nối bên trong đang được chỉ định trong khối mã con trỏ. SQL sẽ là "chọn danh sách cols mong muốn từ kết nối bên trong T1 T2 trên T1.questionid = T2.questionid và T1.categoryid = T2.categoryid trong đó T1.categoryid = {giá trị danh mục mong muốn}"
Tim

Đó là lỗi của tôi: tên cột không rõ ràng: _id :, trong khi biên dịch: CHỌN DISTINCT _id, hình ảnh, câu hỏi, thay thế, questiononid TỪ tbl_question INNER JOIN tbl_alternative WHERE categoryid = 2 AND _id = questiononid. Vì vậy, tôi giả sử rằng tôi cần chỉ định tbl_question._id = tbl_alternative.questionid, nhưng tôi không biết cách thực hiện điều này theo cách truy vấn mà tôi sử dụng ở trên. Và tôi không biết những gì để trở lại nếu tôi sử dụng một "thường xuyên" sql-cú pháp: "Chọn 'từ tbl_question INNER JOIN tbl_alternative ON tbl_question._id = tbl_alternative.questionid VÀ tbl_question.categoryid = tbl_alternative.categoryid = categoryId;
kakka47

@Tim: theo cách của bạn, làm cách nào để tạo phương thức trong lớp db-helper? Tôi có nên trả lại con trỏ không? Tôi đang học trong khi tiến hành, bất kỳ đề xuất nào giúp mã của tôi tốt hơn, tôi rất biết ơn!
kakka47

ở đây tôi đã đăng câu trả lời stackoverflow.com/questions/31567564/…
Sagar

Câu trả lời:


204

Bạn cần phương thức rawQuery .

Thí dụ:

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

Sử dụng ? các ràng buộc thay vì đặt các giá trị vào truy vấn sql thô.


1
@necromancer làm cách nào để bạn thấy việc tạo ra một VIEWlựa chọn tốt hơn rawQuery?
Muhammad Babar

Nên làm gì sau khi truy vấn? Làm thế nào để bạn biết id thuộc về bảng nào và điều gì sẽ xảy ra nếu cả hai bảng có cùng tên cột cho một số cột? hoặc nếu có nhiều mục cho bảng thứ hai?
nhà phát triển android

@ android-developer "Nên làm gì sau khi truy vấn?" - bạn nhận được Con trỏ, làm bất cứ điều gì bạn muốn với dữ liệu: P; 2. "Làm thế nào để bạn biết id nào ..." - đó là một JOIN, vì vậy tùy thuộc vào loại nào , bạn có thể nhận được các khóa được phổ biến LUÔN, KHÔNG BAO GIỜ, hoặc MAYBE; 3. "... cùng một tên cột cho một số cột" - có thể xảy ra, nhưng sự mơ hồ CÓ THỂ bị loại bỏ. Thành thật mà nói, tôi không biết liệu bạn có thể tìm nạp bằng "Table.column" bằng getColumnIndex hay không, Bạn luôn có thể tính toán chỉ mục theo cách thủ công, chỉ cần gỡ lỗi-kiểm tra nó; 4. "nhiều mục cho bảng thứ hai" cũng phụ thuộc vào loại tham gia.
leRobot

giả sử Chế độ xem là THAM GIA NGOÀI TRỜI ĐẦY ĐỦ của quan hệ 1-nhiều (ví dụ: RawContactsEntity ), bạn có thể nhận được các hàng có KEY rỗng trên bảng "nhiều", nhưng không bao giờ có trên bảng "1", vì vậy bạn nên phân nhánh con trỏ của mình như vậy: while (cursor.moveToNext () {if (cursor.getValue (getManyTableKeyIndex ()) == NULL) {/ * đây là hàng không có quan hệ, chỉ có dữ liệu bảng "1" /} else {/ đây là một mối quan hệ HIT, vì vậy có dữ liệu từ cả hai bảng * /}} cursor.close ();
leRobot

Tôi đã quên thêm một phân nhánh khác cho các hàng "không có nhiều gốc" mà bạn vẫn có thể nhận được với FULL OUTER JOIN, nhưng thường thì không nếu mối quan hệ nghiêm ngặt (tôi không chắc liệu Android có thực thi mối quan hệ hay không). Không thực sự biết cách lấy các cột không rõ ràng, nhưng bạn có thể kiểm tra nó trên bất kỳ SQLite CLI nào bằng cách khai báo một chế độ xem có cột không rõ ràng và xem tên các cột đó nhận được trên truy vấn "SELECT * FROM view_x". Sau đó, bạn chỉ áp dụng được cho bất cứ điều gì hương vị helper Android bạn muốn (.query () / .rawQuery () ...)
leRobot

20

Một cách thay thế là tạo một khung nhìn mà sau đó được truy vấn giống như một bảng. Trong nhiều trình quản lý cơ sở dữ liệu, sử dụng dạng xem có thể mang lại hiệu suất tốt hơn.

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;

Đây là từ bộ nhớ nên có thể có một số vấn đề về cú pháp. http://www.sqlite.org/lang_createview.html

Tôi đề cập đến cách tiếp cận này vì sau đó bạn có thể sử dụng SQLiteQueryBuilder với chế độ xem như bạn ngụ ý rằng nó được ưu tiên hơn.


4
Khi nói đến RDBMS như Oracle, các khung nhìn có thể mang lại cho bạn một số lợi ích về hiệu suất nhưng theo kinh nghiệm của tôi, chúng chỉ được sử dụng khi cần thiết cho mục đích hiệu suất hoặc báo cáo. Hay nói một cách khác, bạn không tạo dạng xem cho mọi truy vấn nối mà bạn sử dụng. Và trong SQLite nói riêng, tôi không tin rằng có bất kỳ sự tăng hiệu suất nào - theo câu trả lời cho câu hỏi này, SQLite viết lại truy vấn bằng cách sử dụng một truy vấn con. stackoverflow.com/questions/25577407/performance-penalty-for-unused-view#25580319 API của Android có thể hạn chế những gì OP đang làm nhưng rawQuery()là câu trả lời đúng.
spaaarky 21

13

Ngoài câu trả lời @ pawelzieba, mà chắc chắn là chính xác, tham gia hai bảng, trong khi bạn có thể sử dụng một INNER JOINnhư thế này

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

thông qua truy vấn thô như thế này -

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

vì sự hỗ trợ tương thích ngược của SQLite đối với cách truy vấn nguyên thủy, chúng tôi chuyển lệnh đó thành:

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

và do đó có thể lấy advanatage của phương thức trợ giúp 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
);

Để có bài đăng trên blog chi tiết, hãy xem http://blog.championswimmer.in/2015/12/doing-a-table-join-in-android-without-using-rawquery


1
Tôi không hiểu Utils.concat đến từ đâu.
nicoqueijo

1
Đây là một cách tiếp cận tốt nhưng sẽ không hiệu quả nếu bất kỳ tên cột nào trong một bảng giống với tên cột trong bảng khác. Giống như nếu cả hai bảng có một cột có tên ID, thì đây sẽ némandroid.database.sqlite.SQLiteException: ambiguous column name
Anup Sharma

8

"Cột không rõ ràng" thường có nghĩa là tên cột giống nhau xuất hiện trong ít nhất hai bảng; cơ sở dữ liệu không thể cho biết bạn muốn cái nào. Sử dụng tên bảng đầy đủ hoặc bí danh bảng để loại bỏ sự không rõ ràng.

Đây là một ví dụ mà tôi tình cờ có trong trình soạn thảo của mình. Đó là vấn đề của người khác, nhưng dù sao cũng nên có ý nghĩa.

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)

Vâng, tôi đã nghi ngờ điều đó. Nhưng tôi không biết cách chỉ định các bảng, vì cách: DBTABLE_QUESTION.KEY_QUESTION, không hoạt động. Nhưng khi tôi sử dụng cách rawQuery, rất dễ dàng xác định cột nào thuộc về bảng nào.
kakka47
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.