Android 애플리케이션에서 두 개의 SQLite 테이블을 어떻게 조인합니까?


95

배경

나는 두 개의 테이블이있는 데이터베이스가 안드로이드 프로젝트를 가지고 tbl_questiontbl_alternative.

보기를 질문과 대안으로 채우기 위해 커서를 사용하고 있습니다. 두 테이블을 조인 할 때까지 필요한 데이터를 가져 오는 데 문제가 없습니다.

    Tbl_question  
    -------------
    _신분증  
    질문  
    categoryid  
    Tbl_alternative
    ---------------
    _신분증 
    질문 
    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 년 이후로 커서를 사용할 필요가
없었지만

내부 조인 열이 커서 코드 블록에서 지정되는 위치 / 방법을 알 수 없습니다. SQL은 "select desired-cols-list from T1 inner join T2 on T1.questionid = T2.questionid and T1.categoryid = T2.categoryid where T1.categoryid = {the desired category value}"
Tim

이것은 내 오류입니다 : 모호한 열 이름 : _id :, 컴파일하는 동안 : SELECT DISTINCT _id, 이미지, 질문, 대안, 질문 ID FROM tbl_question INNER JOIN tbl_alternative WHERE categoryid = 2 AND _id = questionid. 따라서 tbl_question._id = tbl_alternative.questionid를 지정해야한다고 가정하지만 위에서 사용하는 쿼리 방식에서는이를 수행하는 방법을 모르겠습니다. 그리고 "일반"sql-syntax를 사용하면 "Select 'from tbl_question INNER JOIN tbl_alternative ON tbl_question._id = tbl_alternative.questionid AND tbl_question.categoryid = tbl_alternative.categoryid = categoryid;
kakka47

@Tim : 당신이하는 방식, db-helper 클래스에서 메서드를 어떻게 구성합니까? 커서를 반환하지 않아야합니까? 나는 나 가면서 배우고 있으며, 내 코드를 더 좋게 만드는 제안에 감사드립니다!
kakka47

여기에 나는 답변을 게시했습니다. stackoverflow.com/questions/31567564/…
Sagar

답변:


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무엇입니까?
Muhammad Babar 2014

쿼리 후 무엇을해야합니까? 어떤 ID가 어떤 테이블에 속하는지 어떻게 알 수 있으며 두 테이블이 일부 열에 대해 동일한 열 이름을 가지고 있다면 어떻게됩니까? 또는 두 번째 테이블에 여러 항목이있는 경우?
안드로이드 개발자

@ android-developer "쿼리 후에 무엇을해야합니까?" -당신은 커서를 얻습니다, 당신이 원하는 데이터로 무엇이든하세요 : P; 2. "어떤 ID를 아는 방법 ..."-JOIN이므로 어떤 유형 에 따라 ALWAYS, NEVER 또는 MAYBE로 채워진 키를 얻을 수 있습니다. 3. "... 일부 열에 대해 동일한 열 이름"-발생할 수 있지만 모호함을 제거 할 수 있습니다. 솔직히 getColumnIndex를 사용하여 "Table.column"으로 가져올 수 있는지 모르겠습니다. 항상 수동으로 인덱스를 계산할 수 있습니다. 디버그 테스트 만하면됩니다. 4. "두 번째 테이블의 여러 항목"도 조인 유형에 따라 다릅니다.
leRobot

보기가 일대 다 관계 (예 : RawContactsEntity ) 의 FULL OUTER JOIN이라고 가정하면 "many"테이블에서 null KEY가있는 행을 가져올 수 있지만 "1"테이블에서는 절대로 가져 오지 않으므로 커서 패스를 분기해야합니다. 다음과 같이 : while (cursor.moveToNext () {if (cursor.getValue (getManyTableKeyIndex ()) == NULL) {/ * 이것은 관계가없는 행이며 "1"테이블 데이터 만 있습니다. /} else {/ 이것은 관계 HIT이므로 두 테이블의 데이터가 있습니다 * /}} cursor.close ();
leRobot

FULL OUTER JOIN으로 여전히 얻을 수있는 "부모가 많지 않음"행에 대해 다른 분기를 추가하는 것을 잊었지만 관계가 엄격한 경우에는 일반적으로 그렇지 않습니다 (Android가 관계를 적용하는지 확실하지 않음). 모호한 열을 얻는 방법을 모르지만 열 모호한 뷰를 선언하여 SQLite CLI에서 테스트하고 "SELECT * FROM view_x"쿼리에서 해당 열의 이름이 무엇인지 확인할 수 있습니다. 그럼 당신은 당신이 (.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
Oracle과 같은 RDBMS의 경우 뷰는 성능 향상을 제공 할 수 있지만 제 경험으로는 성능 또는보고 목적으로 필요할 때만 사용됩니다. 또는 달리 말하면 사용하는 모든 조인 쿼리에 대해보기를 만들지 않습니다. 특히 SQLite에서는 성능상의 이점이 없다고 생각합니다.이 질문에 대한 답변에 따르면 SQLite는 하위 쿼리를 사용하여 쿼리를 다시 작성합니다. stackoverflow.com/questions/25577407/performance-penalty-for-unused-view#25580319 Android의 API는 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
이것은 좋은 접근 방식이지만 한 테이블의 열 이름이 다른 테이블의 열 이름과 동일한 경우 작동하지 않습니다. 두 테이블이 이름 ID를 갖는 열이있는 경우처럼, 다음이 발생합니다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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.