SQL-запрос очень дорогой - PullRequest
2 голосов
/ 02 июня 2019

У меня есть запрос в SQL Server, который ищет доступного поставщика услуг для записи встречи следующим образом:

  1. У меня есть поставщик услуг, связанный со многими с одним с таблицей услуг
  2. Такжепоставщик услуг связал многие с одним с таблицей сотрудников
  3. Таблица услуг со многими связал многих со многими с staffMember в таблице StaffMemberService
  4. У меня есть таблица встречиDetails, связанная со многими с одним с таблицей staffMember

Вы можете видеть детали на этой диаграмме:

enter image description here

Запрос очень дорогой, чтобы узнать, какие сервисы имеют открытые временные интервалы

--select * from ServiceProvider where Id > = 636 and CityId = 4 
DECLARE @longx      NVARCHAR(20);
DECLARE @laty       NVARCHAR(20);
DECLARE @userCityId INT;
DECLARE @userLocation GEOMETRY;
Declare @serviceName  NVARCHAR(50);
declare @startDate AS DATE;
declare @endDate   AS DATE;

SET @longx = '24.72977896594770';
SET @laty = '46.82470379239910';
SET @userCityId = 4;
SET @serviceName = '%hair%'
SET @userLocation = geometry::STPointFromText('POINT(' + @laty + ' ' + @longx + ')',0);
SET @startDate = '2019-06-01';
SET @endDate = '2019-06-05';

DECLARE @compareParts AS INT;
SET @compareParts = DATEDIFF(DAY, @startDate, @endDate) * 40 ;

-- get all services like serviceName
-- find out which SP offer them
-- find out if sp has availble time in the specific dates 
WITH Serv AS
(
    SELECT s.* 
    FROM Services s 
    INNER JOIN ServiceProvider sp ON sp.Id = s.ServiceProviderId
    WHERE sp.CityId = @userCityId 
      AND s.Name LIKE @serviceName 
),
StaffServices AS
(
     SELECT s.* 
     FROM StaffMembers s 
     INNER JOIN StaffMemberService sms ON sms.StaffMemberId = s.Id
     INNER JOIN Serv ON sms.ServiceId = Serv.Id
),
StaffMemberAppointment AS
(
    SELECT ad.StaffMemberId, ad.Id, ad.Duration,a.ServiceProviderId 
    FROM AppointmentDetails ad 
    INNER JOIN Appointments a ON ad.AppointmentId = a.Id
    INNER JOIN ServiceProvider sp ON sp.Id = a.ServiceProviderId
    INNER JOIN StaffMemberService sms ON sms.StaffMemberId = ad.StaffMemberId
    INNER JOIN Serv ON Serv.Id = sms.ServiceId
    WHERE a.StartDt BETWEEN @startDate AND @endDate
),
appointmentParts AS
(
    SELECT 
        sa.StaffMemberId, sa.ServiceProviderId,  
        SUM(Datepart(minute, sa.duration) + DATEPART(hour, sa.duration) * 60 ) /15 AS appointmentsparts
    FROM
        StaffMemberAppointment sa
    GROUP BY
        sa.StaffMemberId, sa.ServiceProviderId
)
SELECT 
    sm.*,
    ap.appointmentsparts 
FROM
    StaffMembers sm
INNER JOIN
    StaffServices ss ON ss.Id = sm.Id
LEFT JOIN 
    appointmentParts ap ON sm.Id = ap.StaffMemberId

Основная идея: я пытаюсь найти название службы как предоставленное имя, затем я пытаюсь выяснить, кто из сотрудников может оказать услуги, и проверить, сколько уже забронированных лотов времени на приемев конкретный диапазон дат, затем найдите поставщика услуг с услугами.

Статистика ввода-вывода

(5 row(s) affected)
Table 'Appointments'. Scan count 0, logical reads 12, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'AppointmentDetails'. Scan count 5, logical reads 16, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'ServiceProvider'. Scan count 0, logical reads 20, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'StaffMemberService'. Scan count 3, logical reads 28, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Services'. Scan count 6, logical reads 18, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'StaffMembers'. Scan count 0, logical reads 10, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

временной интервал составляет 1 15 мinutes.поэтому встреча с 30 минутами - это 2 временных интервала.Я не мог найти способ упростить этот запрос, может кто-нибудь помочь.

1 Ответ

1 голос
/ 02 июня 2019

Какой план выполнения вы получите, если отбросите CTE и используете один уровень объединений с OUTER APPLY для расчета количества слотов для встреч?

SELECT  sm.Id, sm.Name, sm.ServiceProviderId, sp.Name, appt.AppointmentSlots
FROM    Services svc 
        INNER JOIN ServiceProvider sp ON sp.Id = svc.ServiceProviderId
        INNER JOIN StaffMemberService sms ON sms.ServiceId = svc.Id
        INNER JOIN StaffMembers sm ON sm.Id = sms.StaffMemberId
        OUTER APPLY 
        (
            SELECT  SUM( Datepart(minute, ad.duration) 
                        + Datepart(hour, ad.duration)*60 
                    ) / 15 AS AppointmentSlots
            FROM    Appointments a INNER JOIN AppointmentDetails ad 
                        ON  ad.AppointmentId = a.Id
            WHERE   a.ServiceProviderId = sp.ID 
            AND     ad.StaffMemberId = sms.StaffMemberId
            AND     a.StartDt between @startDate and @endDate
        ) appt
WHERE   sp.CityId = @userCityId 
AND     svc.Name like @serviceName 
AND (   AppointmentSlots < @maxSlots 
OR      AppointmentSlots IS NULL
    )
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...