JOIN或2个查询 - 1个大表,1个小,硬件有限

时间:2016-01-27 12:48:42

标签: php mysql join

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

此选择菜单是在大型表(3.5M行)上运行的查询的过滤器。 在大表中是一个从小表引用KEY的外键。

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

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

该应用程序是用PHP编写的。

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

鉴于上述情况,我知道VALUE可能很昂贵,特别是在大桌子上。在这里做2个查询是否有意义,让应用程序处理一些工作,直到我可以协商更好的资源?

编辑:

没有加入:6.9秒

JOIN

加入:59.03秒

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

架构是

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

我在每个nationality_id,txn_id,yrqtr上都有单独的索引。在我的大交易表中。而且只是我小桌子上的主键索引。

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

2 个答案:

答案 0 :(得分:1)

如果您的查找“菜单”列表只是所述的229行,并且它有一个唯一的键,并且您的菜单表上有索引(键,值),则连接可以忽略不计......特别是如果您的不管怎样,只能根据单个键查询结果。

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

现在,在看到您修改后的帖子时,我发现您正在尝试获取聚合。否则,该表将针对历史数据进行修复。我建议根据每个国籍/年度基础进行汇总表。这样,如果期间在当前期间之前,您可以直接查询。如果是当前期间,则从生产中汇总总和。同样,由于交易不会在历史上发生变化,因此它们的计数也不会发生,您将从预先汇总表中立即得到响应。

<强>反馈

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

然后,我会针对所有现有数据预先汇总一次,但不包括当前期间(年/月)。现在,您已在摘要中确定了基线。

然后,在插入时为您的事务表添加一个触发器。然后,处理类似......(和注意,这不是实际的触发,但是要做什么的背景)

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的活动,则不会有任何记录。

答案 1 :(得分:0)

首先,将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)获得更高的性能。