我正在使用一个设计糟糕的数据库,我不能自由重组。在这个数据库中,有三个表(我们称之为' companiesA ',' companiesB '和' items ')我需要优化的查询。 ' companiesA '和' companiesB '以相同的方式描述公司,因为列值相同,但它们代表两个不同的公司组,并且具有不同的列名。基本上,ID和公司名称列在' companiesA '中的' aID '和' aName ',以及' idB '> em> companiesB '中的em>'和' nameB '。 ' items '包含一个列' companyID ',其中包含来自两个公司表之一的外键值。
我需要优化的查询从两个表的并集中获取页面的公司ID和名称,按名称列排序,并添加一列,说明行的公司是否有与之关联的项目。如果用户在前端请求,则此查询还可以按公司名称进行筛选。在目前的状态下,我认为它运行在THETA(公司*项目)时间,这非常缓慢:
select
a.aID as companyID,
a.aName as companyName,
(select
count(companyID)
from
items
where
companyID = a.aID
) as items
from
companiesA as a
where
a.aName like '%<string>%'
union
select
b.idB as companyID,
b.nameB as companyName,
(select
count(companyID)
from
items
where
companyID = b.idB
) as items
from
companiesB as b
where
b.nameB like '%<string>%'
order by
companyName ASC
limit
[optional_starting_index, ] 50;
在此查询返回时,items列包含实际计数并不重要(这是我能够干净地返回关于整个' items '表的值的唯一方法)。我想幸运的是,我有幸拥有1500家公司和9000个项目,这个算法只需要7秒钟。
如果我用另一种我自己访问表格的语言写这个,我可以轻松地在O(公司+项目)时间写这个,但我发现很难弄清楚如何在MySQL中这样做。是否可以这样做,最好没有存储功能或程序?我可以在必要时添加它们,但是我现在很难通过phpMyAdmin添加它们,因为服务器的主机只允许该接口通过GUI访问数据库。
答案 0 :(得分:1)
在这个解决方案中,我采用了大胆的假设,即每个表中的公司名称都是唯一的Union All
。如果不是,那么您可以切换回Union
但是您将获得使列表唯一的性能影响。基本上,我正在消除您对相关子查询的需求,以通过使用派生表来返回计数。
Select Companies.CompanyID, Companies.CompanyName
, Coalesce(ItemTotals.ItemCount,0) As ItemCount
From (
Select a.aID As CompanyID, a.aName As CompanyName
From companiesA As a
Where a.aName Like '%<string>%'
Union All
Select b.IDB, b.nameB
From companiesB As b
Where b.bName Like '%<string>%'
) As Companies
Left Join (
Select companyID, Count(*) As ItemCount
From items
Group By companyID
) As ItemTotals
On ItemTotals.companyID = Companies.CompanyID
Order By Company.CompanyName
这是另一种变体。除了我用两个Group By查询替换相关子查询之外,这个与原始类似。和以前一样,如果两个表之间的名称和ID是互斥的,则可以使用Union All
,否则您将需要使用Union
。
Select Z.CompanyId, Z.CompanyName, Z.ItemCount
From (
Select A.companyID, A.aName As CompanyName
, Count(I.CompanyID) As ItemCount
From companiesA As A
Left Join items As I
On I.CompanyId = A.CompanyId
Where A.aName Like '%<string>%'
Group By A.companyID, A.aName
Union All
Select B.companyID, B.bName, Count(I.CompanyID)
From companiesB As B
Left Join items As I
On I.CompanyId = B.CompanyId
Where B.bName Like '%<string>%'
Group By B.companyID, B.bName
) As Z
Order By Z.CompanyName