【问题标题】:SQLite optimizing multi-select insertSQLite 优化多选插入
【发布时间】:2010-10-18 18:42:41
【问题描述】:

我已经使用 SQL 多年了,但很少使用简单的插入和选择等...所以我不是 SQL 专家。我想知道是否可以帮助我优化我在 SQLite 上执行的更复杂的 SQL 语句,从 PHP 到 PDO。

该语句似乎工作正常,只是似乎比我预期的要花更长的时间(或者也许我只是期待太多)。

这是 SQL:

INSERT OR IGNORE INTO MailQueue(SubscriberID, TemplateID)
    SELECT Subscribers.ID, '1' AS TemplateID
    FROM Subscribers 
    INNER JOIN SubscriberGroups ON Subscribers.ID=SubscriberGroups.SubscriberID
    WHERE SubscriberGroups.GroupID IN ('1', '2', '3')
    AND Subscribers.ID NOT IN 
        ( 
        SELECT Subscribers.ID FROM Subscribers 
        INNER JOIN SubscriberGroups ON Subscribers.ID=SubscriberGroups.SubscriberID
        WHERE SubscriberGroups.GroupID IN ('4', '5', '6')
        );

我得到的是一个或多个组中的订阅者列表。我想将订阅者添加到邮件队列,选择属于一个或多个组 (1,2,3) 的订阅者,但排除也属于另一组 (4,5,6) 的订阅者。

首先,上面的 SQL 是典型的如何做到这一点的吗?

其次,我应该具备哪些指标才能使这项工作尽可能高效?

目前,在平均规格 LAMP 上处理大约 5000 条订阅者记录(和少数组)大约需要 30 秒。

归根结底,性能并不是那么关键,但我想更好地理解这些东西,因此非常感谢任何见解。

布拉德

【问题讨论】:

    标签: sql sqlite


    【解决方案1】:

    额外的连接很可能会杀死你。如果你这样做了:

    SELECT Subscribers.ID, '1' AS TemplateID
    FROM Subscribers 
    WHERE EXISTS( SELECT *
                    FROM SubscriberGroups
                   WHERE Subscribers.ID=SubscriberGroups.SubscriberID
                                     AND SubscriberGroups.GroupID IN ('1', '2', '3') )
    
      AND NOT EXISTS( SELECT *
                        FROM SubscriberGroups
                       WHERE Subscribers.ID=SubscriberGroups.SubscriberID 
                         AND SubscriberGroups.GroupID IN ('4', '5', '6')
        );
    

    您还需要确保在 SubscriberGroups(SubscriberID, GroupID) 上有一个索引

    我的猜测是订阅者已经有了 ID 索引,对吧?

    编辑: 另一种选择,可能会也可能不会更快。看看每个的查询计划看看...

    这可能是一次索引扫描,可能比两次索引查找要快,但这取决于 SQLite 的优化器...

    SELECT Subscribers.ID, '1' AS TemplateID
    FROM Subscribers 
    INNER JOIN( SELECT SUM( CASE WHEN GroupID IN('1', '2', '3') THEN 1 ELSE 0 END ) AS inGroup,
                       SUM( CASE WHEN GroupID IN('4', '5', '6') THEN 1 ELSE 0 END ) AS outGroup,
                       SubscriberID
                                FROM SubscriberGroups
                             WHERE SubscriberGroups.GroupID IN ('1', '2', '3', '4', '5', '6' )
              ) SubscriberGroups
           ON Subscribers.ID=SubscriberGroups.SubscriberID
          AND inGroup  > 0
          AND outGroup = 0
    

    【讨论】:

    • 谢谢马特,太好了。您的第一个解决方案将其从 30 秒缩短到大约 5 或 6 秒,这已经足够了。我没有尝试第二种选择,因为我不太了解它,但如果它成为问题,我会记住它。再次感谢
    【解决方案2】:

    另一种可能更快的编写 SQL 的方法(我没有要测试的 SQLite):

    SELECT
         S.ID,
         '1' AS TemplateID     -- Is this really a string? Does it need to be?
    FROM
         Subscribers S
    LEFT OUTER JOIN SubscriberGroups SG ON
         SG.SubscriberID = S.ID
    WHERE
         SG.SubscriberID IS NULL AND
         EXISTS
         (
              SELECT
                   *
              FROM
                   SubscriberGroups SG2
              WHERE
                   SG2.SubscriberID = S.ID AND
                   SG2.GroupID IN ('1', '2', '3')  -- Again, really strings?
         )
    

    Matt 的方法也应该很好用。这完全取决于 SQLite 如何决定创建查询计划。

    另外,请注意我的 cmets。如果这些确实在您的数据库中定义为 INT 数据类型,则将有一些额外的处理来在两种不同的数据类型之间进行转换。如果它们是数据库中的字符串,那有什么原因吗?这些列中是否有非数字值?

    【讨论】:

    • 谢谢汤姆,你在引用的 ID 上是对的……不知道为什么我把它们放在里面。我没有尝试您的建议,因为马特的似乎运作良好,而您的似乎错过了排除组部分(4,5,6)。还是谢谢!
    • 实际上更仔细地查看了这一点,我意识到其中一个 ID 列未声明为 INTEGER,这就是我需要引号的原因。 SQLite 对类型并不挑剔,这就是我错过它的原因。更改为整数并删除引号现在大约需要 1/2 秒。谢谢!
    • 我的应该处理排除项。你测试了吗?这就是整个 LEFT JOIN 的意义所在。检查 WHERE 子句中连接表中的 NOT NULL 列,如果它为 NULL,则表明不存在匹配项。
    猜你喜欢
    • 2013-12-13
    • 2013-01-20
    • 2019-06-03
    • 1970-01-01
    • 2015-05-01
    • 1970-01-01
    • 2014-09-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多