【问题标题】:JOIN or 2 queries - 1 large table, 1 small, hardware limitedJOIN 或 2 个查询 - 1 个大表,1 个小表,硬件受限
【发布时间】:2016-01-27 12:48:41
【问题描述】:

我有一个页面,其中有一个 <select> 菜单,其中包含一个小表(229 行)中的所有值,例如 <option value='KEY'>VALUE</option>

此选择菜单是针对在大型表(350 万行)上运行的查询的过滤器。 大表中有一个外键引用小表中的KEY。

但是,在大表查询的结果中,我还需要显示来自小表的相对VALUE

我可以很容易地执行INNER JOIN 来检索结果,或者我可以对较小的表执行单独的“预”查询,将其值提取到数组中,然后让应用程序获取VALUE来自小表结果。

应用程序是用 PHP 编写的。

硬件资源是个问题(现在无法升级到更高的实例,老板受限)-我在 Amazon Web Services 实例上的 t2.micro RDS 上运行它。 我在 WHERE 和 HAVING 子句中的列上添加了单个索引和覆盖索引,并且我的服务器报告我有 46mb RAM 可用。

鉴于上述情况,我知道JOIN 可能很昂贵,尤其是在大桌子上。在我可以协商更好的资源之前,执行 2 次查询并让应用程序处理一些工作是否有意义?

编辑:

无加入:6.9 秒

SELECT nationality_id, COUNT(DISTINCT(txn_id)) as numtrans,
        SUM(sales) as sales, SUM(units) as units, YrQtr
FROM 1_txns
 GROUP BY nationality_id;

EXPLAIN
'1', 'SIMPLE', '1_txns', 'index', 'covering,nat', 'nat', '5', NULL, '3141206', NULL

加入:59.03 秒

SELECT 4_nationality.nationality, COUNT(DISTINCT(txn_id)) as numtrans,
        SUM(sales) as sales, SUM(units) as units, YrQtr
FROM 1_txns INNER JOIN 4_nationality USING (nationality_id)
 GROUP BY nationality_id
 HAVING YrQtr LIKE :period;
EXPLAIN
'1', 'SIMPLE', '4_nationality', 'ALL', 'PRIMARY', NULL, NULL, NULL, '229', 'Using temporary; Using filesort'
'1', 'SIMPLE', '1_txns', 'ref', 'covering,nat', 'nat', '5', 'reports.4_nationality.nationality_id', '7932', NULL

架构是

Table 1_txns (txn_id, nationality_id, yrqtr, sales, units)
Table 4_nationality (nationality_id, nationality)

我对每个 nationality_id、txn_id、yrqtr 都有单独的索引。在我的大交易表中。并且只是我的小表上的一个主键索引。

还有一点奇怪的是,没有连接的查询在结果中缺少一行!

【问题讨论】:

  • 在这种情况下我可以理解,当然。但我首先会尝试进行一些测量,看看 JOIN 一开始的表现会差多少。如果设置了适当的索引,那也不应该那么糟糕。 (而且它可以让你不必在以后重新编写代码。)
  • 向我们展示一些示例数据和期望结果。但是 db 是为了执行 JOIN 而构建的,只需在连接字段上创建正确的索引
  • 已更新,非常感谢您迄今为止的回复。
  • 这两个查询是不等价的。您正在对第二个使用限制。在 YrQtr 列上创建索引并在 where 子句中使用该列
  • 对不起,性能结果是等效查询,只是复制粘贴错误。我会试试的。

标签: php mysql join


【解决方案1】:

如果您的查找“菜单”列表只有所述的 229 行,并且它具有唯一键,并且您的菜单表在 (key, value) 上有索引,则连接将可以忽略不计...尤其是如果您的无论如何只查询基于单个键的结果。

对我来说更大的问题是关于你的 350 万条记录的表格。在 229 个“菜单”项中,它每次将返回平均超过 15k 条记录。而且我敢肯定,并非每个类别都是均衡的……有些可能有几百或几千个条目,有些可能有 30k+ 条目。是否有其他一些标准可以允许返回较小的子集?显然没有足够的信息来量化。

现在,在输入此内容时看到您修改过的帖子后,我看到您正在尝试进行聚合。否则,该表将针对历史数据进行修复。我建议在每个国籍/YrQtr 的基础上完成一个汇总表。这样,您可以直接查询该时间段是否早于当前讨论的时间段。如果是当前期间,则汇总生产中的总和。同样,由于事务在历史上不会发生变化,因此它们的计数也不会发生变化,并且您会立即从预汇总表中获得响应。

反馈

至于如何/何时实施汇总表。我将创建包含您需要的相应列的表...国籍、期间(年/月)以及不同交易的相应计数等。

然后,我将为您所有现有数据预先汇总一次,但不包括当前期间(年/月)。现在你已经建立了总结性的基线。

然后,在插入时向您的事务表添加一个触发器。然后,处理类似...(注意,这不是实际触发,而是做什么的上下文)

update summaryTable
   set numTrans = numTrans + 1,
       TotSales = TotSales + NEWENTRY.Sales,
       TotUnits = TotUnits + NEWENTRY.Units
   where
           Nationality = NEWENTRY.Nationality
       AND YrQtr = NEWENTRY.YrQtr

if # records affected by the update = 0
   Insert into SummaryTable 
      ( Nationality, 
        YrQtr, 
        NumTrans, 
        TotSales, 
        TotUnits )
     values
     (  NEWENTRY.Nationality,
        NEWENTRY.YrQtr,
        1,
        NEWENTRY.Sales,
        NEWENTRY.Units )

现在,在将每条记录插入事务表后,您的聚合将始终在汇总表中保持同步。您始终可以查询此汇总表而不是完整的事务表。如果您从来没有针对给定国籍/YrQtr 的活动,则不存在任何记录。

【讨论】:

  • 就该项目的长期计划而言,这听起来像是要走的路。如何让汇总表自动更新?
  • @AdamCopley,请参阅触发器概念的修订答案
【解决方案2】:

首先,将HAVING 移动到WHERE,这样查询的其余部分就可以少做。二、将nationality的查找延迟到GROUP BY之后:

SELECT  
        ( SELECT  nationality
            FROM  4_nationality
            WHERE  nationality_id = t.nationality_id 
        ) AS nationality,
        COUNT(DISTINCT(txn_id)) as numtrans,
        SUM(sales) as sales,
        SUM(units) as units,
        YrQtr
    FROM  1_txns AS t
    WHERE  YrQtr LIKE :period
    GROUP BY  nationality_id;

如果可能,请避免使用通配符,只需使用YrQtr = :period。这将使INDEX(YrQtr, nationality_id) 获得更高的性能。

【讨论】:

    猜你喜欢
    • 2014-03-12
    • 1970-01-01
    • 2012-12-16
    • 2015-05-13
    • 2011-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-15
    相关资源
    最近更新 更多