如何提高连接多个表的SELECT性能

时间:2016-10-07 05:20:00

标签: mysql mamp

我有以下mySQL SELECT语句在小数据集上运行正常,但在增加音量时就死了:

SELECT DISTINCT Bookings.BookingId, Bookings.ResortId, Bookings.WeekBeginning, Bookings.DepartDate, Bookings.CancelledDate,Clients.FirstName, Clients.LastName, Clients.Email, Clients.Address1, Clients.City, Clients.State, Clients.CountryId, Clients.ClientType, Countries.Country, BookingAccommodation.AccomId, BookingAccommodation.ShareType, BookingProgram.ProgramId, Programs.ProgramDesc
    FROM Bookings, Clients, BookingProgram, BookingAccommodation, Countries, ClientType, Programs
    WHERE Bookings.BookingId = BookingProgram.BookingId
       AND Bookings.BookingId = BookingAccommodation.BookingId
       AND Bookings.WeekBeginning >= '2016-10-01' 
       AND BookingAccommodation.Nights > 0
       AND Clients.ClientId = Bookings.ClientId
       AND Clients.Email <> ''
       AND Clients.CountryId = Countries.CountryId
       AND Programs.ProgramId = BookingProgram.ProgramId

预订中的约10K记录和BookingAccommodation和BookingPrograms中的每个25K记录的卷量并不大,但查询在950秒内运行。我正在本地MAMP服务器上的phpAdmin的SQL窗口中运行查询。

将其拆分为3个查询,结果会在几分之一秒内回复:

SELECT DISTINCT Bookings.BookingId, Bookings.ResortId, Bookings.WeekBeginning, Bookings.DepartDate, Bookings.CancelledDate, Clients.FirstName, Clients.LastName, Clients.Email, Clients.Address1, Clients.City, Clients.State, Clients.CountryId, Clients.ClientType, Countries.Country
     FROM Bookings, Clients, Countries, ClientType
    WHERE Bookings.WeekBeginning >= '2016-10-01' 
       AND Clients.ClientId = Bookings.ClientId
       AND Clients.Email <> ''
       AND Clients.CountryId = Countries.CountryId

SELECT DISTINCT Bookings.BookingId, BookingAccommodation.AccomId, BookingAccommodation.ShareType
    FROM Bookings, BookingAccommodation
    WHERE Bookings.BookingId = BookingAccommodation.BookingId
       AND Bookings.WeekBeginning >= '2016-10-01' 
       AND BookingAccommodation.Nights > 0

SELECT DISTINCT Bookings.BookingId, BookingProgram.ProgramId, Programs.ProgramDesc
    FROM Bookings, BookingProgram, Programs
    WHERE Bookings.BookingId = BookingProgram.BookingId
       AND Bookings.WeekBeginning >= '2016-10-01' 
       AND Programs.ProgramId = BookingProgram.ProgramId

预订中的每条记录在BookingAccommodation和BookingProgram中有多条记录,但我只需要一条记录,因此SELECT DISTINCT。

  • 预订的主键是BookingId。
  • BookingAccommodation的主键是BookingId,AccomDate,AccomId
  • BookingProgram的主键是BookingId,ProgramId,AccomType

我试图用连接和子查询重写查询,但我显然没有做得对。如何将这3个查询加入到一个表现良好的查询中?

3 个答案:

答案 0 :(得分:1)

这些是使用子查询代替联接的基础(MySQL假定为FWIW)。对伪代码表示歉意,我认为尽快回答非常重要,因为这是我刚才遇到的这个问题上的热门文章之一。

客户预订了游轮。服务对象还应指定饮食(例如素食,纯素食,无大豆等)。因此,我们有三个表:

预订
Booking_Id,Booking_Date,Booking_Time,Client_Id

客户
Client_Id,Client_Name,Client_Phone,Client_DietId

饮食
Diet_Id,Diet_Name

我们现在要向礼宾提供完整的预订视图。

使用“ JOINS”:

SELECT Bookings.Booking_Id, Bookings.Booking_Date, Bookings.Booking_Time, Clients.Client_Name, Diets.Diet_Name FROM Bookings INNER JOIN Clients ON Bookings.Client_Id = Clients.Client_Id INNER JOIN Diets ON Clients.Client_DietId = Diets.Diet_Id

使用“ SUBQUERIES”:

我怎么想的是在那些单独的JOIN中创建“临时表”-当然,“临时表”可能是也可能不是准确的低级实现,等等。但是,有趣的是,子查询可能比大型联接要快(其他线程)。

我想从上述示例中进行单独的联接:

首先,我需要将客户的饮食与他们一起加入,然后将我的“餐桌”与预订一起加入。

因此,我最终得到了这一点(请注意在引用子查询时对表进行(重新)命名):

SELECT [RELEVANT FIELDS HERE ETC] FROM (SELECT Clients.Client_Id, Clients.Client_Name, Diets.Diet_Name FROM Clients INNER JOIN Diets ON Clients.Client_DietId = Diets.Diet_Id) AS ClientDetailsWithDiets INNER JOIN Bookings ON Bookings.Booking_Id = ClientDetailsWithDiets.Client_Id

现在,如果要联接另一个表,例如分配给特定预订的Staff,则上面的整个内容将被嵌套,依此类推,例如:

SELECT [RELEVANT FIELDS HERE ETC] FROM (SELECT [RELEVANT FIELDS HERE ETC] FROM (SELECT Clients.Client_Id, Clients.Client_Name, Diets.Diet_Name FROM Clients INNER JOIN Diets ON Clients.Client_DietId = Diets.Diet_Id) AS ClientDetailsWithDiets INNER JOIN Bookings ON Bookings.Booking_Id = ClientDetailsWithDiets.Client_Id) AS BookingDetailsFull INNER JOIN Staff ON BookingDetailsFull.Booking_Id = Staff.Booking_Id_Assigned

答案 1 :(得分:0)

尝试将其更改为

SELECT DISTINCT Bookings.BookingId, Bookings.ResortId, 
Bookings.WeekBeginning, Bookings.DepartDate, Bookings.CancelledDate,
Clients.FirstName, Clients.LastName, Clients.Email, Clients.Address1, 
Clients.City, Clients.State, Clients.CountryId, Clients.ClientType, Countries.Country,
BookingAccommodation.AccomId, BookingAccommodation.ShareType, BookingProgram.ProgramId,
Programs.ProgramDesc
    FROM Bookings
    JOIN Clients ON Clients.ClientId = Bookings.ClientId AND Bookings.WeekBeginning >= '2016-10-01' AND Clients.Email <> ''
    JOIN BookingProgram ON Bookings.BookingId = BookingProgram.BookingId
    JOIN BookingAccommodation ON Bookings.BookingId = BookingAccommodation.BookingId AND BookingAccommodation.Nights > 0
    JOIN Countries ON Clients.CountryId = Countries.CountryId
    JOIN Programs ON Programs.ProgramId = BookingProgram.ProgramId
    WHERE Bookings.WeekBeginning >= '2016-10-01';

如果这不能获得您想要的结果,请尝试EXPLAIN并查看查询计划。

请注意:我没有看到表ClientType在任何地方被使用,所以我没有将它包含在JOIN中

答案 2 :(得分:-1)

而不是花费更多时间来尝试改进select语句,因为它碰到了这么多表我选择将其拆分为原始问题中概述的单独查询。

最后,这是最快的实际解决方案。