我正在使用一个设计不佳的数据库,我无权对其进行重组。在此数据库中,涉及三个表(我们称它们为“companiesA”、“companiesB”和“items”)我需要优化的查询。 'companiesA' 和 'companiesB' 描述公司的方式相同,因为列值相同,但它们代表两个不同的公司组并且具有不同的列名称。本质上,ID 和公司名称列是“companiesA”中的“aID”和“aName”,以及“idB” “companiesB”中的“em>”和“nameB”。 “items”包含一列“companyID”,其中包含来自两个公司表之一的外键值。
我需要优化的查询从两个表的联合中获取一个页面的公司 ID 和名称,按名称列排序,并添加一个列,说明该行的公司是否有任何关联的项目。如果用户在前端请求,此查询还可以按公司名称进行过滤。在目前的状态下,我认为它在 THETA(companies * items) 时间内运行,这太慢了:
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(companies + items) 时间内写这篇文章,但我发现很难弄清楚如何在 MySQL 中这样做.是否可以这样做,最好没有存储函数或过程?如果需要,我可以添加它们,但我很难通过 phpMyAdmin 添加它们,因为服务器的主机只允许该接口(interface)通过 GUI 访问数据库。
最佳答案
在这个解决方案中,我大胆假设每个表中的公司名称都是唯一的,方法是使用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
关于MySQL:如何在线性时间内确定表A和B中的哪些行被表C中的行引用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4599973/