ฉันจะเข้าร่วมตาราง SQLite สองตารางในแอปพลิเคชัน Android ของฉันได้อย่างไร


97

พื้นหลัง

ฉันมีโครงการ Android ที่มีฐานข้อมูลสองตาราง: tbl_questionและtbl_alternative.

เพื่อเติมเต็มมุมมองด้วยคำถามและทางเลือกฉันใช้เคอร์เซอร์ ไม่มีปัญหาในการรับข้อมูลที่ฉันต้องการจนกว่าฉันจะพยายามรวมสองตาราง

    Tbl_question  
    -------------
    _id  
    คำถาม  
    categoryid  
    Tbl_alternative
    ---------------
    _id 
    คำถาม 
    categoryid 
    ทางเลือก

ฉันต้องการสิ่งต่อไปนี้:

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 เป็นต้นมา แต่ฉันไม่ได้พัฒนาสำหรับ Android)
Mike Sherrill 'Cat Recall'

ฉันไม่เห็นว่าคอลัมน์การรวมภายในถูกระบุไว้ที่ใด / อย่างไรในบล็อกโค้ดเคอร์เซอร์ SQL จะเป็น "เลือกรายการที่ต้องการจาก T1 ภายในเข้าร่วม T2 บน T1.questionid = T2.questionid และ T1.categoryid = T2.categoryid โดยที่ T1.categoryid = {ค่าหมวดหมู่ที่ต้องการ}"
Tim

นี่คือข้อผิดพลาดของฉัน: ชื่อคอลัมน์ที่คลุมเครือ: _id: ขณะรวบรวม: SELECT DISTINCT _id, รูปภาพ, คำถาม, ทางเลือก, คำถามจาก tbl_question INNER JOIN tbl_alternative WHERE categoryid = 2 AND _id = questionid ดังนั้นฉันคิดว่าฉันต้องระบุ tbl_question._id = tbl_alternative.questionid แต่ฉันไม่รู้ว่าจะทำอย่างไรในแบบสอบถามที่ฉันใช้ข้างต้น และผมก็ไม่ทราบว่าจะกลับมาหากฉันใช้ "ปกติ" SQL ไวยากรณ์: "เลือกจาก tbl_question INNER JOIN tbl_alternative ON tbl_question._id = tbl_alternative.questionid และ tbl_question.categoryid = = tbl_alternative.categoryid CategoryID;
kakka47

@ ทิม: วิธีที่คุณทำฉันจะสร้างเมธอดในคลาส db-helper ได้อย่างไร ฉันไม่ควรคืนเคอร์เซอร์? ฉันกำลังเรียนรู้ในขณะที่ฉันไปคำแนะนำใด ๆ ที่ทำให้รหัสของฉันดีขึ้นฉันขอบคุณ
kakka47

ที่นี่ฉันได้โพสต์คำตอบstackoverflow.com/questions/31567564/…
Sagar

คำตอบ:


206

คุณต้องการเมธอด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อย่างไร?
Muhammad Babar

หลังแบบสอบถามควรทำอย่างไร คุณจะรู้ได้อย่างไรว่า id ใดเป็นของตารางใดและจะเกิดอะไรขึ้นถ้าทั้งสองตารางมีชื่อคอลัมน์เหมือนกันสำหรับบางคอลัมน์ หรือถ้ามีหลายรายการสำหรับตารางที่สอง?
นักพัฒนา Android

@ android-developer "หลังแบบสอบถามควรทำอย่างไร" - คุณได้รับเคอร์เซอร์ทำอะไรก็ได้ที่คุณต้องการกับข้อมูล: P; 2. "คุณรู้ได้อย่างไรว่า id ใด ... " - เป็น JOIN ดังนั้นขึ้นอยู่กับประเภทที่คุณอาจได้รับเสมอไม่เคยหรืออาจมีการเติมคีย์; 3. "... ชื่อคอลัมน์เดียวกันสำหรับบางคอลัมน์" - สามารถเกิดขึ้นได้ แต่สามารถลบความคลุมเครือได้ ฉันไม่รู้ว่าคุณสามารถดึงข้อมูลด้วย "Table.column" โดยใช้ getColumnIndex ได้หรือไม่คุณสามารถคำนวณดัชนีด้วยตนเองได้ตลอดเวลาเพียงแค่ debug-test 4. "หลายรายการสำหรับตารางที่สอง" ขึ้นอยู่กับประเภทการเข้าร่วมด้วย
leRobot

สมมติว่า View คือ FULL OUTER JOIN ของความสัมพันธ์แบบ 1 ต่อกลุ่ม (เช่นRawContactsEntity ) คุณจะได้แถวที่มี Null KEY ในตาราง "many" แต่จะไม่อยู่ในตาราง "1" ดังนั้นคุณควรแยกเคอร์เซอร์ของคุณ ดังนี้: ในขณะที่ (cursor.moveToNext () {if (cursor.getValue (getManyTableKeyIndex ()) == NULL) {/ * นี่คือแถวที่ไม่มีความสัมพันธ์มีเพียงข้อมูลตาราง "1" /} else {/นี่คือ ความสัมพันธ์ HIT จึงมีข้อมูลจากทั้งสองตาราง * /}} cursor.close ();
leRobot

ฉันลืมเพิ่มการแตกแขนงอื่นสำหรับแถว "ไม่มีผู้ปกครองหลายคน" ซึ่งคุณยังสามารถรับได้ด้วย FULL OUTER JOIN แต่โดยปกติจะไม่ได้หากความสัมพันธ์นั้นเข้มงวด (ฉันไม่แน่ใจว่า 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

และด้วยเหตุนี้จึงสามารถใช้ advanatage ของวิธีการตัวช่วย 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.cha 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
Anup Sharma

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.