Повышение эффективности SQL выбирает и объединяет в sqlite - PullRequest
1 голос
/ 13 сентября 2010

Я работаю над личным проектом, сосредоточенным на анализе текста в базе данных.Я собираюсь сделать что-нибудь интересное и узнать о SQL и sqlite.Поэтому, имея в виду мои начинающие способности, я хотел бы получить совет, как сделать это более эффективно.

Скажем, например, я хочу выбрать типы продуктов в статье A.Я разбираю свою статью, и если я нахожу еду F, то добавляю F к таблице предметов .Затем я добавляю A.id и F.id к результатам .Когда я анализирую свою статью и нахожу еду G, которая уже существует в элементах , все, что я делаю, это добавляю A.id и G.id к результатам .

Таким образом, мои схемы выглядят примерно так:

  • статьи: id, article
  • результаты: id, item_id, article_id
  • предметов: id, foodtype, food

Если я хочу найти все статьи, в которых говорится о oranges и grapes и любых vegetable, тогда я 'Начну с чего-то вроде этого:

SELECT * 
  FROM articles 
INNER JOIN results ON articles.id = results.article_id  
INNER JOIN items ON results.item_id = items.id

и добавлю:

WHERE foodtype='vegetable' OR food='orange' OR food='grape'

На самом деле моя база данных намного больше.Есть тысячи статей и более ста тысяч добытых «продуктов».Большинство этих запросов, к которым я присоединяюсь к 3 таблицам, не возвращаются, даже если я ограничу результаты до 100 результатов.Я попытался создать индекс для полей, которые обычно содержатся в моих предложениях WHERE, например food и foodtype, но улучшения не произошло.

Существуют ли улучшения, которые я могу внести в свою базу данныхили запрос?

Ответы [ 4 ]

6 голосов
/ 13 сентября 2010

Получить только нужные вам столбцы

Первая проблема с запросом состоит в том, что SELECT * возвращает все столбцы из всех таблиц , включенных в запрос.Это означает, что возвращаются значения в критериях JOIN по обе стороны от оценки.Лучше выписать фактические столбцы, которые вам нужны, потому что у всех трех перечисленных вами столбцов есть столбец id, что усложняет поиск правильного значения, если только не используется порядковый номер (не очень хорошая практика - измените положение, поиск данных - это не то, что нужнодолжно быть).

Использование псевдонимов таблицы минимизирует то, что вам нужно использовать для ссылки на конкретную таблицу:

SELECT a.article 
  FROM ARTICLES a
  JOIN RESULTS r ON r.article_id = a.id
  JOIN ITEMS i ON i.id = r.item_id

Индексирование

Индексирование внешних ключей - что выиспользование критерия для соединения JOIN должно быть вторым в списке после первичного ключа таблицы.

Затем необходимо периодически запускать команду ANALYZE , поскольку статистика ...

... не обновляется автоматически при изменении содержимого базы данных.Если содержимое базы данных существенно изменяется или если изменяется схема базы данных, следует рассмотреть возможность повторного запуска команды ANALYZE для обновления статистики.

Эти статистические данные используются оптимизатором для своего запроса.решение, наряду с наличием индексов.

ИЛИ печально сказываются на производительности

Вы можете попробовать переписать запрос, чтобы он не использовал ИЛИ с UNION:

SELECT a.article 
  FROM ARTICLES a
  JOIN RESULTS r ON r.article_id = a.id
  JOIN ITEMS i ON i.id = r.item_id
 WHERE i.foodtype = 'vegetable'
UNION 
SELECT a.article 
  FROM ARTICLES a
  JOIN RESULTS r ON r.article_id = a.id
  JOIN ITEMS i ON i.id = r.item_id
 WHERE i.food IN ('orange', 'grape')

Имейте в виду, что UNION медленнее, чем UNION ALL, поскольку UNION удаляет дубликаты.UNION ALL быстрее, потому что не удаляет дубликаты.

1 голос
/ 14 сентября 2010

Эти запросы могут быть удивительно быстрыми в SQLite. Я делаю что-то сопоставимое

FOODTYPE
foodtypeid integer primary key
foodtypedesc  text

FOOD
foodid integer primary key
foodtypeid integer (indexed)
fooddesc text (indexed)

ARTICLE
articleid integer primary key 
title


ARTICLEFOOD
id integer primary key autoincrement
articleid integer   (indexed)
foodid integer      (indexed)
foodtypeid integer  (indexed) [EDIT: forgot to add this column yesterday)

ПРИМЕЧАНИЕ: все первичные ключи индексируются, а столбцы, отмеченные для индексации, должны быть проиндексированы.

 select title, foodesc, foodtypedesc
 from articlefood AF
 join article A on AF.articleid=A.articleid
 join FOOD F on AF.foodid = F.foodid and fooddesc
 join FOODTYPE FT on FT.foodtypeid = F.foodtypeid 
 where .....

или вы можете использовать встроенные представления, которые также могут быть очень быстрыми в SQLite при наличии подходящих индексов. Следующий запрос вернет все идентификаторы товаров, которые соответствуют указанному продукту (ам) и указанному типу (ам). Поведение UNION по умолчанию заключается в удалении дублирующихся строк, и, поскольку мы запрашиваем только articleid (с целью объединения чего-либо в этот список идентификаторов), приведенный ниже запрос приведет к набору отдельных идентификаторов статей, соответствующих критериям:

 select articleid from  ARTICLEFOOD 
 JOIN
 (
    select foodid from FOOD where  .... 
 ) as MyFoods
 ON ARTICLEFOOD.foodid = MyFoods.foodid

 UNION

 select articleid from  ARTICLEFOOD 
 JOIN
 (
    select foodtypeid from FOODTYPE where  .... 
 ) as MyFoodTypes
 ON ARTICLEFOOD.foodtypeid = MyFoodTypes.foodtypeid

Тим

1 голос
/ 13 сентября 2010

Первый из всех SELECT * - это зло. Независимо от того, сколько индексов вы создадите, ваш запрос не будет покрыт (если вы не проиндексируете всю таблицу, что затем сделает сканирование индекса и сканирование таблицы одинаковыми). 1. Выберите столбцы, которые вы хотите отобразить. 2. Добавьте кастомный индекс в столбцы идентификаторов. 3. добавить некластеризованный столбец в предложении WHERE 4. Поместите индекс покрытия в столбцы в вашем запросе выбора.

Лучший способ настроить запрос - это посмотреть на план выполнения и посмотреть на узкое место, но, поскольку его нет в вашем вопросе, это лучшее предположение, которое я могу принять

0 голосов
/ 13 сентября 2010

Всегда внутри присоединяйтесь к наименьшему столу первым. Я подозреваю, что у вас не будет столько предметов, сколько статей (может быть?). Так что это должно быть "маленькое внутреннее соединение, большее внутреннее соединение, самое большое".

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...