#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.
Они объявляют интерфейс
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");
Опасности публикации из памяти…