Android Room @ Отношения и индексы - PullRequest
0 голосов
/ 04 октября 2019

У меня есть этот класс в моей настройке комнаты, который объединяет данные из моей таблицы states с соответствующими данными из моей таблицы countries:

public class StateWithCountryData {

    @Embedded
    public State state;

    @Relation(parentColumn = "country_id", entityColumn = "_id", entity = Country.class)
    public Country country;
}

Как вы можете видеть в моей @ Relation code, мой столбец states. country_id присоединен к моему столбцу countries. _id.

Я хотел бы знать, будет ли код @Relation создаватьавтоматический индекс для states. country_id или мне нужно создать индекс самостоятельно в моем State классе сущности?

Если последнее, как мне добавить требуемый индекс к моему состоянию класса сущности? Вот оно:

@Entity(tableName = "states")
public class State {

    @PrimaryKey
    @ColumnInfo(name = "_id")
    private long stateId;

    @NonNull
    @ColumnInfo(name = "state_name")
    private String stateName;

    @ColumnInfo(name = "country_id")
    private long countryId;

    @ColumnInfo(name = "last_modified")
    private Date lastModified;

    public State(long stateId, @NonNull String stateName, long countryId, Date lastModified) {
        this.stateId = stateId;
        this.stateName = stateName;
        this.countryId = countryId;
        this.lastModified = lastModified;
    }

    public long getStateId() {
        return stateId;
    }

    public void setStateId(long stateId) {
        this.stateId = stateId;
    }

    @NonNull
    public String getStateName() {
        return stateName;
    }

    public void setStateName(@NonNull String stateName) {
        this.stateName = stateName;
    }

    public long getCountryId() {
        return countryId;
    }

    public void setCountryId(long countryId) {
        this.countryId = countryId;
    }

    public Date getLastModified() {
        return lastModified;
    }

    public void setLastModified(Date lastModified) {
        this.lastModified = lastModified;
    }
}

1 Ответ

1 голос
/ 05 октября 2019

Что я хотел бы знать, так это то, что код @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: -

enter image description here

Запрос 2: -

enter image description here

После создания индекса

Запрос 1: -

enter image description here

Запрос 2: -

enter image description here

Используемый код (с использованием 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;
  • Обратите внимание, что это может быть немного несправедливым сравнением / тестом, поскольку создание индекса может повлиять на последующие условия выполнения. В идеале запросы должны выполняться отдельно друг от друга и отдельно от создания индекса.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...