Что я хотел бы знать, так это то, что код @Relation создаст автоматический индекс для states.country_id или мне нужно создать индекс самостоятельно в моем классе сущностей State?
Чтобы проверить, посмотрите на сгенерированный код для @database, например, вы получите что-то вроде: -
final SupportSQLiteOpenHelper.Callback _openCallback = new RoomOpenHelper(configuration, new RoomOpenHelper.Delegate(1) {
@Override
public void createAllTables(SupportSQLiteDatabase _db) {
_db.execSQL("CREATE TABLE IF NOT EXISTS `Country` (`id` INTEGER NOT NULL, `name` TEXT, PRIMARY KEY(`id`))");
_db.execSQL("CREATE TABLE IF NOT EXISTS `states` (`_id` INTEGER NOT NULL, `state_name` TEXT NOT NULL, `country_id` INTEGER NOT NULL, `last_modified` TEXT, PRIMARY KEY(`_id`))");
_db.execSQL("CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)");
_db.execSQL("INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '008d8fa1f719c970d7a1182e9e43f80b')");
}
.....
Как видите, ответ: нет .
Если последнее, как мне добавить требуемый индекс в мой класс сущности State?
Вы можете использовать: -
@Entity(tableName = "states"
, indices = {@Index(name = "ixCountryId", value = "country_id")} //<<<<<<<<<< ADDED
)
А теперьсгенерированный код: -
@Override
protected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration configuration) {
final SupportSQLiteOpenHelper.Callback _openCallback = new RoomOpenHelper(configuration, new RoomOpenHelper.Delegate(1) {
@Override
public void createAllTables(SupportSQLiteDatabase _db) {
_db.execSQL("CREATE TABLE IF NOT EXISTS `Country` (`id` INTEGER NOT NULL, `name` TEXT, PRIMARY KEY(`id`))");
_db.execSQL("CREATE TABLE IF NOT EXISTS `states` (`_id` INTEGER NOT NULL, `state_name` TEXT NOT NULL, `country_id` INTEGER NOT NULL, `last_modified` TEXT, PRIMARY KEY(`_id`))");
_db.execSQL("CREATE INDEX IF NOT EXISTS `ixCountryId` ON `states` (`country_id`)");
_db.execSQL("CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)");
_db.execSQL("INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '74ee12325d1720c24abff0e5ce479d81')");
т.е. была добавлена строка
_db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `ixCountryId` ON `states` (`country_id`)");
.
Более простой вариант заключается в кодировании наУровень @ColumnInfo вместо кодирования указывает на уровне @Entity, например,
@ColumnInfo(name = "country_id", index = true)
private long countryId;
. Это автоматически сгенерирует имя индекса и приведет, в указанном выше случае, к следующему включению в сгенерированный код (вместоранее показанная строка): -
_db.execSQL("CREATE INDEX IF NOT EXISTS `index_states_country_id` ON `states` (`country_id`)");
Вы также можете использовать оба (не то, что выЯ сделаю это, поскольку это пустая трата / неэффективно), что приведет к: -
_db.execSQL("CREATE INDEX IF NOT EXISTS `ix01` ON `states` (`country_id`)");
_db.execSQL("CREATE INDEX IF NOT EXISTS `index_states_country_id` ON `states` (`country_id`)");
- кодированию на уровне @Entity предлагает более широкие возможности, такие как сложные (с несколькими столбцами) или уникальные индексы. *
или мне нужно самому создать индекс в классе моего объекта State?
Не обязательно, а, возможно, и нет, поскольку индекс оказывает отрицательное влияние,хотя в зависимости от количества стран и штатов, если оно достаточно мало, влияние может быть незначительным.
Рассмотрим следующий тестовый код, который создает 10000 стран и 500000 штатов, случайным образом распределенных по странам (около 50 штатов на страну).
Использование двух основных запросов: -
SELECT * FROM states WHERE country_id > 500;
SELECT * FROM states JOIN Country ON Country.id = states.country_id WHERE country_id < 500;
до и после создания индекса, время до индексации составляет
SELECT * FROM states WHERE country_id > 500
> OK
> Time: 0.563s
SELECT * FROM states JOIN Country ON Country.id = states.country_id WHERE country_id < 500
> OK
> Time: 0.074s
и после созданияИндекс: -
SELECT * FROM states WHERE country_id > 500
> OK
> Time: 2.764s
SELECT * FROM states JOIN Country ON Country.id = states.country_id WHERE country_id < 500
> OK
> Time: 0.158s
Так медленно с индексом (примерно в 5 раз медленнее для первого запроса и 9,5 медленнее для второго запроса).
Кодтакже выполняет EXPLAIN QUERY PLAN перед выполнением запросов.
Перед созданием индекса: -
Запрос 1: -
Запрос 2: -
После создания индекса
Запрос 1: -
Запрос 2: -
Используемый код (с использованием Navicat) : -
DROP TABLE IF EXISTS Country;
DROP TABLE IF EXISTS states;
CREATE TABLE IF NOT EXISTS `Country` (`id` INTEGER NOT NULL, `name` TEXT, PRIMARY KEY(`id`));
CREATE TABLE IF NOT EXISTS `states` (`_id` INTEGER NOT NULL, `state_name` TEXT NOT NULL, `country_id` INTEGER NOT NULL, `last_modified` TEXT, PRIMARY KEY(`_id`));
/* Load data into the Country table */
WITH RECURSIVE cte1(counter,base) AS (
SELECT 1, 'COUNTRY_A'
UNION ALL SELECT counter+1,'COUNTRY_A' FROM cte1 LIMIT 10000
)
INSERT INTO Country (name) SELECT base||counter FROM cte1;
/* Load fata into the states table */
WITH RECURSIVE cte1(counter,base,cid) AS (
SELECT 1,'STATE_S', (abs(random()) % (SELECT count() FROM Country)) + 1
UNION ALL SELECT
counter+1,
'STATE_S',
(abs(random()) % (SELECT count() FROM Country)) + 1
FROM cte1 LIMIT 500000
)
INSERT INTO states (state_name, country_id) SELECT base||counter, cid FROM cte1;
EXPLAIN QUERY PLAN
SELECT * FROM states JOIN Country ON Country.id = states.country_id WHERE country_id < 500;
EXPLAIN QUERY PLAN
SELECT * FROM states WHERE country_id > 500;
SELECT * FROM states WHERE country_id > 500;
SELECT * FROM states JOIN Country ON Country.id = states.country_id WHERE country_id < 500;
/* Create the Index */
CREATE INDEX IF NOT EXISTS `ix01` ON `states` (`country_id`);
EXPLAIN QUERY PLAN
SELECT * FROM states JOIN Country ON Country.id = states.country_id WHERE country_id < 500;
EXPLAIN QUERY PLAN
SELECT * FROM states WHERE country_id > 500;
SELECT * FROM states WHERE country_id > 500;
SELECT * FROM states JOIN Country ON Country.id = states.country_id WHERE country_id < 500;
/* Show states per country */
SELECT Country.name,count() AS states_inCountry FROM States JOIN Country ON country_id = Country.id GROUP BY country_id;
/* Clean up */
DROP INDEX IF EXISTS ix01;
DROP TABLE IF EXISTS states;
DROP TABLE If EXISTS Country;
- Обратите внимание, что это может быть немного несправедливым сравнением / тестом, поскольку создание индекса может повлиять на последующие условия выполнения. В идеале запросы должны выполняться отдельно друг от друга и отдельно от создания индекса.