【问题标题】:Speed up SQLite query, can I do it without a union?加速 SQLite 查询,我可以不用联合吗?
【发布时间】:2012-03-25 16:56:23
【问题描述】:

大家好,stackoverflow 社区的各位!我已经访问这个网站多年了,这是我的第一篇文章

假设我有一个包含三个表的数据库:

  • 组(GroupID、GroupType、max1、大小)
  • 糖果(candyID,name,selected)
  • 成员(组 ID、名称 ID)

示例:糖果工厂。
在糖果工厂里,80 种不同的糖果生产了 10 种糖果袋。

所以:有 10 种独特的组类型(包),具有 3 种不同的大小:(4、5、6);一组是 80 种独特糖果的组合。

据此,我创建了一个数据库,(有一些关于哪些糖果组合进入组的规则)。

此时我有一个包含 40791 个独特糖果袋的数据库。

现在我想将糖果集合与 DB 中的所有糖果袋进行比较,因此我希望 DB 中缺少 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 新手,非常渴望学习/了解新方法。

你好,罗格

【问题讨论】:

  • 不要更新您的数据库来进行选择。它根本不可扩展(两个用户如何同时进行选择?)并且会降低性能,因为写入比读取慢。另请注意,UNION 或尤其是 UNION ALL 通常非常快,并且通常胜过更复杂的单个查询。
  • 您的示例架构有问题,因为没有什么可以加入 groupsmemberscandies 所以它将进行交叉连接,除非 members.memberID 应该是 members.candyID

标签: sql sqlite


【解决方案1】:

我不能 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。它们是初始构建的捷径,也是后期开发的潜在灾难。但这是一个不同的问题,如果您愿意,可以在线阅读。

【讨论】:

    【解决方案2】:

    在进行选择时不要更新数据库,您的第一次更新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;
    

    这至少应该比在查询之前更新表中的所有记录更好。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-10-29
      • 2023-02-11
      • 2015-02-28
      • 2016-04-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多