MySQL: как определить, на какие строки в таблицах A и B ссылаются строки в таблице C за линейное время? - PullRequest
1 голос
/ 05 января 2011

Я работаю с плохо спроектированной базой данных, которую я не могу реструктурировать. В этой базе данных есть три таблицы (назовем их « companiesA », « companiesB » и « items »), которые участвуют в запросе, который Мне нужно оптимизировать. ' companiesA ' и ' companiesB ' описывают компании одинаково, так как значения столбцов одинаковы, но они представляют две разные группы компаний и имеют разные имена столбцов. По сути, столбцы идентификатора и названия компании: « aID » и « aName » в « companiesA » и « idB » и ' nameB ' в ' companiesB '. « items » содержит столбец « companyID », который содержит значение внешнего ключа из одной из двух таблиц компании.

Запрос, который мне нужно оптимизировать, получает количество идентификаторов и имен компаний на странице из объединения двух таблиц, отсортированных по столбцу имен, с добавленным столбцом, в котором указано, имеет ли компания строки какие-либо элементы, связанные с ним. Этот запрос также может фильтровать по названиям компаний, если пользователь запрашивает его во внешнем интерфейсе. В своем текущем состоянии, я думаю, что он выполняется за время 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 '). Я полагаю, что могу посчитать, что мне повезло, что с 1500 компаниями и 9000 наименований этот алгоритм занимает всего семь секунд.

Если бы я писал это на другом языке, на котором у меня был доступ к таблицам, я мог бы легко написать это за O (компании + товары), но мне трудно понять, как это сделать в MySQL. , Возможно ли это сделать, желательно без хранимых функций или процедур? Я могу добавить их, если это необходимо, но мне было трудно добавлять их через phpMyAdmin, поскольку хост сервера позволяет этому интерфейсу получать доступ к базе данных только через графический интерфейс.

1 Ответ

1 голос
/ 05 января 2011

В этом решении я принял смелое предположение, что названия компаний в каждой из таблиц уникальны, используя 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.Как и раньше, если имена и идентификаторы между двумя таблицами являются взаимоисключающими, вы можете использовать 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  
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...