IN()选择php中的慢速DB中的快速。重写到连接的速度更慢

IN()选择php中的慢速DB中的快速。重写到连接的速度更慢,php,mysql,sql,performance,Php,Mysql,Sql,Performance,我有一个带有2个子选择的查询,在PHP页面中处理它需要20秒以上(只是为了填充一个下拉框!),但是当我通过phpmyadmin运行查询时,它会在0.4秒内返回所有结果 SELECT ID, SupplierName FROM tblsuppliers WHERE ID IN ( SELECT DISTINCT Supplier FROM tblmovements WHERE SuppIsDisputed =1 ) OR ID IN ( SELECT DISTINCT UsedSupplier F

我有一个带有2个子选择的查询,在PHP页面中处理它需要20秒以上(只是为了填充一个下拉框!),但是当我通过phpmyadmin运行查询时,它会在0.4秒内返回所有结果

SELECT ID, SupplierName
FROM tblsuppliers
WHERE ID
IN (
SELECT DISTINCT Supplier
FROM tblmovements
WHERE SuppIsDisputed =1
)
OR ID
IN (
SELECT DISTINCT UsedSupplier
FROM tblsundries
WHERE SuppIsDisputed =1
)
ORDER BY SupplierName ASC
所以我认为sub-selects和IN()都很慢而且很昂贵,我将重写为join

SELECT 
DISTINCT 
tblsuppliers.ID,
tblsuppliers.SupplierName
 FROM 
tblsuppliers 
LEFT JOIN tblmovements ON tblsuppliers.ID=tblmovements.Supplier 
LEFT JOIN tblsundries ON tblsuppliers.ID=tblsundries.UsedSupplier 
WHERE 
tblmovements.SuppIsDisputed=1
OR
tblsundries.SuppIsDisputed=1
ORDER BY tblsuppliers.SupplierName ASC
使用
groupbytblsuppliers.ID
即使在phpmyadmin(页面超时)中运行,查询也需要60秒以上的时间,但使用distinct完成,但只需20秒,因此直接针对数据库执行查询时仍比子选择慢

所以我想我应该分析一下这些问题

子选择查询在分析时提供以下信息

Status          Time
starting            0.000042
checking permissions0.000004
checking permissions0.000001
checking permissions0.000002
Opening tables  0.000025
System lock     0.000006
init            0.000032
optimizing          0.000006
statistics          0.000007
preparing           0.000007
executing           0.000001
Sorting result  0.000052
optimizing          0.000006
statistics          0.000008
preparing           0.003540
optimizing          0.000012
statistics          0.000010
preparing           0.408007
Sending data    0.000031
end             0.000004
query end           0.000006
closing tables  0.000011
freeing items   0.000085
logging slow query  0.000002
cleaning up     0.000001
Status          Time
starting            0.000045
checking permissions0.000003
checking permissions0.000001
checking permissions0.000005
Opening tables  0.000027
System lock     0.000006
init            0.000027
optimizing          0.000009
statistics          0.000021
preparing           0.000011
Creating tmp table  0.000132
executing           0.000002
Copying to tmp table20.071386
Sorting result  0.000090
Sending data    0.000019
end             0.000002
removing tmp table  0.000007
end             0.000003
query end           0.000003
closing tables  0.000010
freeing items   0.000087
logging slow query  0.000002
logging slow query  0.000001
cleaning up     0.000001
解释了为什么在phpmyadmin中它很快,但没有解释为什么在PHP中运行相同的查询需要20秒

当分析连接查询时,它提供了以下信息

Status          Time
starting            0.000042
checking permissions0.000004
checking permissions0.000001
checking permissions0.000002
Opening tables  0.000025
System lock     0.000006
init            0.000032
optimizing          0.000006
statistics          0.000007
preparing           0.000007
executing           0.000001
Sorting result  0.000052
optimizing          0.000006
statistics          0.000008
preparing           0.003540
optimizing          0.000012
statistics          0.000010
preparing           0.408007
Sending data    0.000031
end             0.000004
query end           0.000006
closing tables  0.000011
freeing items   0.000085
logging slow query  0.000002
cleaning up     0.000001
Status          Time
starting            0.000045
checking permissions0.000003
checking permissions0.000001
checking permissions0.000005
Opening tables  0.000027
System lock     0.000006
init            0.000027
optimizing          0.000009
statistics          0.000021
preparing           0.000011
Creating tmp table  0.000132
executing           0.000002
Copying to tmp table20.071386
Sorting result  0.000090
Sending data    0.000019
end             0.000002
removing tmp table  0.000007
end             0.000003
query end           0.000003
closing tables  0.000010
freeing items   0.000087
logging slow query  0.000002
logging slow query  0.000001
cleaning up     0.000001
我发现有点奇怪,它解释了使用distinct时20秒的处理时间。不确定如何提高复制到tmp表的时间

基于以上,我有以下问题

  • 有没有更好的方法将subselect查询重写为join 基于还是一般更快
  • 为什么在phpmyadmin中运行子选择只需0.4秒 /在mysqlclient中,但在php中需要花费很长时间
  • 有没有办法停止(即使其服务器设置发生更改) 为基于联接的查询复制到tmp表需要20秒 +
  • 我只是个白痴吗
  • 下面是生成下拉列表的PHP块(使用子选择查询)供参考

    以下列没有索引,但为0或1的tinyint(1)值

    tblmovements.SuppIsDisputed=1
    tblsundries.SuppIsDisputed=1
    
    子选择解释:

    id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
    1   PRIMARY     tblsuppliers    ALL     NULL    NULL    NULL    NULL    1341    Using where; Using filesort
    3   DEPENDENT SUBQUERY  tblsundries     index_subquery  UsedSupplier    UsedSupplier    4   func    22  Using where
    2   DEPENDENT SUBQUERY  tblmovements    index_subquery  Supplier    Supplier    8   func    157     Using where
    
    加入解释:

    id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
    1   SIMPLE  tblsuppliers    ALL     NULL    NULL    NULL    NULL    1403    Using temporary; Using filesort
    1   SIMPLE  tblmovements    ref     Supplier    Supplier    8   ggdashboard.tblsuppliers.ID     157     Distinct
    1   SIMPLE  tblsundries     ref     UsedSupplier    UsedSupplier    4   ggdashboard.tblsuppliers.ID     22  Using where; Distinct
    

    从explain输出的行数来看,两个表中似乎只有很少的行具有
    suppliedsisputed=1
    ,因此这些列上的IDEX对于这种情况是非常有选择性的。我会尝试添加这些索引,并将您的查询重写为两个独立联接的并集,这将导致在应用并集后仅对准备好的行集进行文件排序,以便按供应商名称对行进行排序,并且由于行数较低,很容易将其放入内存中

    因此,请先运行以下命令:

    ALTER TABLE tblmovements ADD INDEX m_disputed( SuppIsDisputed );
    ALTER TABLE tblsundries ADD INDEX s_disputed( SuppIsDisputed );
    
    然后在代码中使用此查询:

    ( SELECT DISTINCT s.Id, s.SupplierName FROM tblsuppliers s
    JOIN tblmovements m ON s.ID = m.Supplier
    WHERE m.SuppIsDisputed = 1 )
    UNION
    ( SELECT DISTINCT s.ID, s.SupplierName FROM tblsuppliers s
    JOIN tblsundries sd ON s.ID = sd.UsedSupplier
    WHERE sd.SuppIsDisputed = 1 )
    ORDER BY SupplierName
    
    这是您的查询:

    SELECT ID, SupplierName
    FROM tblsuppliers
    WHERE ID IN (SELECT DISTINCT Supplier
                 FROM tblmovements
                 WHERE SuppIsDisputed = 1
                ) OR
          ID IN (SELECT DISTINCT UsedSupplier
                 FROM tblsundries
                 WHERE SuppIsDisputed = 1
                )
    ORDER BY SupplierName ASC;
    
    为了提高性能,我将提出一个由三部分组成的解决方案。首先,重写查询以使用
    exists
    而不是
    中的

    SELECT ID, SupplierName
    FROM tblsuppliers s
    WHERE EXISTS (SELECT 1
                  FROM tblmovements m
                  WHERE m.SuppIsDisputed = 1 and s.id = m.Supplier
                 ) OR
          EXISTS (SELECT 1
                  FROM tblsundries su
                  WHERE su.SuppIsDisputed = 1 and s.id = su.UsedSupplier
                 )
    ORDER BY SupplierName ASC;
    
    其次,在子查询中使用的表上添加索引以加快查找速度:
    tblmovements(Supplier,supplierdisputed)
    tblsundries(UsedSupplier,supplierdisputed)


    最后,在外部查询上添加索引以避免最终排序:
    tblsuppliers(SupplierName,id)

    查看执行计划更容易解决性能问题。在查询前添加
    EXPLAIN
    。对于不是很大的数据库,以秒为单位的时间通常意味着使用了错误的或没有索引。我应该提到的是,数据库很大:)TBL供应商只有5k个项目TBL移动是120万个项目TBL库存是500k个项目。解释将很快完成。所有索引都已按照您指定的位置就位,因此我将尝试更改查询,看看会发生什么。关于为什么使用直接选择子查询会是0.4,但在php中会慢一些,有什么建议吗?好的,它比连接快一点,但比当前存在的子查询慢一点。你的版本:查询花费了9.6913秒,在php中是phpmyadmin,仍然是20秒+@Dave。不,我想不出一个好的理由。可能您连接到了一个不同的(测试)数据库,该数据库有空表,或者您的数据库测量了对一种情况下返回的第一行的响应,而不是对第二种情况下返回的所有行的响应。或者,一个计时在冷数据库上完成,另一个计时在热数据库上完成。或者完全是别的。@Dave。你能用表格的大小和每个条件下你期望的匹配数来编辑你的问题吗?所有这些都直接在测试DB上运行(尽管是实时数据集的镜像),我确实先检查了一下,但是pio的联合似乎已经大大提高了速度,所以现在就继续吧。总体数据库大小在评论中排名靠前,我预计tblmovements会有1700个结果,tblsundries会有几百个结果,但如果供应商id上有一个不同的/分组的选择,则过滤后返回的结果总共应该是199个。tblmovements是340mb,tblsundries是85ish mb,tblsuppliers是2mb,如果您想知道确切的数据大小:)好的,这是一个真正的性能提升,在phpmyadmin中提高了0.1秒,在php脚本中提高了2秒,这要好多了。不知道为什么我在tbh之前没有想到使用工会。我将尝试在具有类似查询的其他4个页面上实现相同的结构(不同但相似的一个在7个表中,因此不确定一个联合将如何运作)