Ускорить запрос SQLite, могу ли я сделать это без объединения? - PullRequest
1 голос
/ 25 марта 2012

Привет всем в сообществе stackoverflow!Я посещаю этот сайт в течение многих лет, и вот мой первый пост

Допустим, у меня есть база данных с тремя таблицами:

  • groups (GroupID, GroupType, max1, size)
  • candy (candyID, name, selected)
  • members (groupID, nameID)

Пример : кондитерская фабрика.
На кондитерской фабрике производится 10 видов конфетных пакетов из 80 различных конфет.

Итак: существует 10 уникальных групповых типов (пакетов) с 3 различными размерами: (4,5,6);группа - это комбинация из 80 уникальных конфет.

Из этого я делаю базу данных (с некоторыми правилами о том, какие комбинации конфет попадают в группу).

На данный момент у меня естьбаза данных с 40791 уникальными конфетными пакетами.

Теперь я хочу сравнить коллекцию конфет со всеми конфетными пакетами в БД, в результате я хочу, чтобы пакеты из БД, в которых отсутствуют 3 или менее конфет сколлекция сравнения.

-- restore candy status
update candies set selected = 0, blacklisted = 0;

-- set status for candies to be selected
update candies set selected = 1 where name in ('candy01','candy02','candy03','candy04');

select  groupId, GroupType, max, count(*) as remainingNum, group_concat(name,', ') as remaining
from groups natural join members natural join candies
where not selected 
group by groupid having  count(*) <= 3

UNION -- Union with groups which dont have any remaining candies and have a 100% match

select groupid, GroupType, max,  0 as remainingNum, "" as remaining
from groups natural join members natural join candies
where selected 
group by groupid having count(*) =groups.size;

Приведенный выше запрос делает это.Но я пытаюсь сделать это без объединения, потому что скорость - это главное.А также я новичок в SQL и очень хочу узнать / увидеть новые методы.

Привет, Рутгер

Ответы [ 2 ]

3 голосов
/ 25 марта 2012

Я не на 100% уверен в том, что вы выполняете с помощью этих запросов, поэтому я не рассматривал принципиально другой подход.Если вы можете включить пример данных, чтобы продемонстрировать свою логику, я могу взглянуть на это.Но, с точки зрения простого объединения двух ваших запросов, я могу это сделать.Прежде всего, следует соблюдать осторожность ...

SQL компилируется в планы запросов.Если план запроса для каждого запроса значительно отличается от другого, объединение их в один запрос может быть плохой идеей.В итоге вы можете получить единый план, который работает в обоих случаях, но не очень эффективен в обоих случаях.Один плохой план может быть намного хуже двух хороших планов => Более короткий, более компактный код не всегда дает более быстрый код.

Вы можете указать selected в своем GROUP BY вместо предложения WHERE;тот факт, что у вас есть два UNIONed запроса, показывает, что вы уже рассматриваете их как две отдельные группы.

Тогда единственное различие между вашими запросами - это фильтр на count(*), который можно согласовать сCASE WHEN оператор ...

SELECT
  groups.groupID,
  groups.GroupType,
  groups.max,
  CASE WHEN Candies.Selected = 0 THEN count(*)  ELSE 0 END as remainingNum,
  CASE WHEN Candies.Selected = 0 THEN group_concat(candies.name,', ') ELSE '' END as remaining
FROM
  groups
INNER JOIN
  members
    ON members.GroupID = groups.GroupID
INNER JOIN
  candies
    ON Candies.CandyID = members.CandyID
GROUP BY
  Groups.GroupID,
  Groups.GroupType,
  Groups.max,
  Candies.Selected
HAVING
  CASE
    WHEN Candies.Selected = 0 AND COUNT(*) <= 3           THEN 1
    WHEN Candies.Selected = 1 AND COUNT(*)  = Groups.Size THEN 1
                                                          ELSE 0
  END
  =
  1

Изменения макета происходят просто потому, что я не согласен с использованием NATURAL JOIN по причинам обслуживания.Они представляют собой кратчайший путь в начальной сборке и потенциальную катастрофу в дальнейшей разработке.Но это другая проблема, вы можете прочитать об этом онлайн, если захотите.

0 голосов
/ 25 марта 2012

Не обновляйте базу данных, когда вы делаете выбор, ваше первое обновление update candies set selected = 0, blacklisted = 0; будет применено ко всей таблице и переписывает каждую запись. Вы должны попробовать без использования selected, а также изменить свой союз на UNION ALL. В дополнение к этому вы пытаетесь использовать внутреннее соединение вместо естественного (но я не знаю вашу схему для конфет членам)

select  groupId, GroupType, max, count(*) as remainingNum, group_concat(name,', ') as remaining
from groups 
    inner join members on members.groupid = groups.groupid
    inner join candies on candies.candyid = member.candyid
where name NOT in ('candy01','candy02','candy03','candy04')
group by groups.groupid
having  count(*) <= 3

UNION ALL -- Union with groups which dont have any remaining candies and have a 100% match

select groupid, GroupType, max,  0 as remainingNum, "" as remaining
from groups 
    inner join members on members.groupid = groups.groupid
    inner join candies on candies.candyid = member.candyid
where name in ('candy01','candy02','candy03','candy04')
group by groupid
having count(*) = groups.size;

Это должно как минимум работать лучше, чем обновлять все записи в таблице, прежде чем запрашивать ее.

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