SQLite: как объединить таблицы и представить курсор с помощью SimpleCursorAdapter

#android

#Android

Вопрос:

Я давно использую SQLite в Android, но впервые выполняю операцию объединения таблиц. Я полностью расстроен этим, потому что работал над этим весь день.

Теперь у меня есть 2 таблицы, FTSProfile и FTSCell, и я хочу объединить их, используя общий ключ A с ЛЕВЫМ СОЕДИНЕНИЕМ. Итак, я внедрил серию кода, ContentProvider и Database, чтобы выполнить то, что я хочу. В базе данных я использовал SQLiteQueryBuilder для построения запроса и, следовательно, получения нужного мне курсора. Я использовал:

 SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
    builder.setTables(FTS_VIRTUAL_TABLE_PROFILE " LEFT JOIN " FTS_VIRTUAL_TABLE_CELL " ON " FTS_VIRTUAL_TABLE_CELL "." Database.KEY_CID "=" FTS_VIRTUAL_TABLE_PROFILE "." Database.KEY_CELL_ID);
    builder.setProjectionMap(mColumnMap_CombinedTable);
  

и моя хэш-карта похожа:

 HashMap<String,String> map = new HashMap<String,String>();
      for (String key: KEYS_PROFILE) map.put(key, FTS_VIRTUAL_TABLE_PROFILE "." key);
      for (String key: KEYS_CELL) map.put(key, FTS_VIRTUAL_TABLE_CELL "." key);
      map.put(BaseColumns._ID, BaseColumns._ID);
      map.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID, "rowid AS "   SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID);
      map.put(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID, "rowid AS "   SearchManager.SUGGEST_COLUMN_SHORTCUT_ID);
  

И затем, когда я запускаю программу и пытаюсь открыть список для результата, я получаю SQLiteException: no such column: _id...

У меня нет идей, как это решить, но я действительно хочу знать, как я могу от этого избавиться. Кто-нибудь может помочь?

Ответ №1:

Забавный вопрос. Я просто смотрел на то же самое 30 минут назад ;). Вы могли бы попробовать последовать примеру, используемому в родном приложении contact для Android.

http://www.google.com/codesearch/p?hl=en#cbQwy62oRIQ/src/com/android/providers/contacts/ContactsProvider2.javaamp;q=ContactsProvider.javaamp;d=5

Они объявляют интерфейс

 private interface DataContactsQuery {
        public static final String TABLE = "data "
                  "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) "
                  "JOIN contacts ON (raw_contacts.contact_id = contacts._id)";

        public static final String[] PROJECTION = new String[] {
            RawContactsColumns.CONCRETE_ID,
            DataColumns.CONCRETE_ID,
            ContactsColumns.CONCRETE_ID
        };

        public static final int RAW_CONTACT_ID = 0;
        public static final int DATA_ID = 1;
        public static final int CONTACT_ID = 2;
    }
  

Затем при попытке запросить результаты они используют этот фрагмент кода.

 Cursor cursor = null;
        try {
            cursor = mDb.query(DataContactsQuery.TABLE, DataContactsQuery.PROJECTION,
                    mSb.toString(), mSelectionArgs.toArray(EMPTY_STRING_ARRAY), null, null,
                    Contacts.IN_VISIBLE_GROUP   " DESC, "   Data.RAW_CONTACT_ID);
            if (cursor.moveToFirst()) {
                dataId = cursor.getLong(DataContactsQuery.DATA_ID);
                rawContactId = cursor.getLong(DataContactsQuery.RAW_CONTACT_ID);
                contactId = cursor.getLong(DataContactsQuery.CONTACT_ID);
            } else {
                // No contact found, return a null URI
                return -1;
            }
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
  

Полезно ли это?

Комментарии:

1. но проблема в том, что SimpleCursorAdapter требует поля «_id» (базовые столбцы. _ID) для выделения данных. Итак, если бы я не объявил такое поле при создании базы данных, мне не разрешили бы присоединиться к таблице. Я думаю о том, как я могу создать столбец «_id» после того, как я присоединюсь к таблице, используя метод, который я реализовал.

2. Ссылка недоступна. Пара вопросов. Как вы получаете MDB? Кажется, что нетривиально подключиться к базе данных контактов, поскольку это не задокументировано. И что такое mSb?

3. MDB — это ваш экземпляр SQLiteDatabase. mSB — это StringBuilder. Пришлось просмотреть исходный код релиза.

4. Вы также можете присоединиться к другим идентификаторам. Вам не нужно назначать поле _id, вы также можете объединить по имени, например: raw_contacts.contact_name = contacts.name

Ответ №2:

Как насчет создания VIEW в вашей базе данных? Если это частый запрос, отобразите его в виде представления, и вы сможете избежать многих проблем с настройкой соединения и просто беспокоиться о критериях выбора. Если вы можете записать соединение как обычный SQL, то вы должны быть в состоянии создать представление для него, а затем использовать это.

Проблема, которую я вижу в вашем коде, заключается в том, что вы пытаетесь выбрать столбец с именем _id (который необходим для CursorAdapter ), но этот столбец не существует. Вы сопоставляете BaseColumns._ID с BaseColumns._ID , но это всего лишь константы, и если в вашей базе данных нет столбца _id , то вы получите ошибку, которую видите.

 CREATE VIEW FooBar AS 
    SELECT Foo.field1 AS FooField1, 
           Foo.field2 AS FooField2, 
           Bar.field1 AS BarField1, 
           Bar.field2 AS BarField2, 
           (Foo._id * 1000000000)   Bar._id AS _id 
    FROM Foo LEFT JOIN Bar ON Foo.Field1 = Bar.Field2;
  

Это (Foo._id * 1000000000) Bar._id AS _id быстрый, грязный и, вероятно, болезненно неправильный способ синтеза unique _id для строки, возвращаемой представлением, который вам понадобится, если вы используете это в качестве адаптера. Недостатком этого также является то, что он доступен только для чтения. Вы не можете обновить VIEW .

Как только у вас есть свой VIEW , вы можете SELECT использовать его так, как если бы это была сама таблица. т.е., SELECT * FROM FooBar WHERE BarField1 > 10;

Комментарии:

1. Сначала спасибо за ваше предложение =] Но я думаю, что сейчас моя проблема больше связана с тем, как добавить столбец с именем «_id» в мою таблицу объединения. Изначально в моих 2 таблицах нет столбцов с именем «_id», и каким-то образом я запрашиваю это в projectionmap, поэтому, даже если я создам базу данных без этого, я смогу ввести курсор в DataAdapter. Однако после того, как я выполняю объединение, поле _id исчезает, и я ищу способ добавить его обратно

2. ОК… Если ваша объединенная строка содержит целое поле, уникальное во всем наборе результатов, вы можете использовать свою карту проекций, чтобы вернуть его как _id . map.put("myUniqueNumericField", BaseColumns._ID); Это приведет к тому, что запрос будет помечать этот столбец как _id, позволяя CursorAdapter функционировать.

3. Упс! map.put(BaseColumns._ID, "myUniqueNumericField"); Опасности публикации из памяти…