Объединить эти два запроса в один запрос - PullRequest
1 голос
/ 11 ноября 2009

У меня есть следующие запросы:

SELECT Sites.EDISID, Sites.[Name], (SUM(DLData.Quantity) / 8) AS TiedDispense
FROM Sites
  JOIN UserSites 
    ON UserSites.EDISID = Sites.EDISID
  JOIN Users
    ON Users.[ID] = UserSites.UserID
 JOIN MasterDates 
  ON MasterDates.EDISID = UserSites.EDISID
 JOIN DLData
  ON DLData.DownloadID = MasterDates.[ID]
 JOIN Products 
  ON Products.[ID] = DLData.Product
 LEFT JOIN SiteProductTies 
  ON SiteProductTies.EDISID = UserSites.EDISID
  AND SiteProductTies.ProductID = Products.[ID]
 LEFT JOIN SiteProductCategoryTies 
  ON SiteProductCategoryTies.EDISID = UserSites.EDISID
  AND SiteProductCategoryTies.ProductCategoryID = Products.CategoryID
 WHERE Users.[ID] = @UserID
  AND (COALESCE(SiteProductTies.Tied, SiteProductCategoryTies.Tied, Products.Tied) = @Tied OR @Tied IS NULL)
  AND MasterDates.[Date] BETWEEN @From AND @To
  AND MasterDates.[Date] >= Sites.SiteOnline
 GROUP BY Sites.EDISID, Sites.[Name]

SELECT Sites.EDISID, Sites.[Name], SUM(Delivery.Quantity) AS TiedDelivered
FROM Sites
  JOIN UserSites 
    ON UserSites.EDISID = Sites.EDISID
  JOIN Users
    ON Users.[ID] = UserSites.UserID
 JOIN MasterDates 
  ON MasterDates.EDISID = UserSites.EDISID
 JOIN Delivery
  ON Delivery.DeliveryID = MasterDates.[ID]
 JOIN Products 
  ON Products.[ID] = Delivery.Product
 LEFT JOIN SiteProductTies 
  ON SiteProductTies.EDISID = UserSites.EDISID
  AND SiteProductTies.ProductID = Products.[ID]
 LEFT JOIN SiteProductCategoryTies 
  ON SiteProductCategoryTies.EDISID = UserSites.EDISID
  AND SiteProductCategoryTies.ProductCategoryID = Products.CategoryID
 WHERE Users.[ID] = @UserID
  AND (COALESCE(SiteProductTies.Tied, SiteProductCategoryTies.Tied, Products.Tied) = @Tied OR @Tied IS NULL)
  AND MasterDates.[Date] BETWEEN @From AND @To
  AND MasterDates.[Date] >= Sites.SiteOnline
 GROUP BY Sites.EDISID, Sites.[Name]

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

В настоящее время я использую их как два отдельных подзапроса в третьем запросе. По одному они занимают примерно 1-2 секунды каждый. Как два подзапроса они занимают от 6 до 10 секунд (в зависимости от нагрузки), и оба возвращают только 47 строк (хотя они касаются тысяч строк).

Я думал, что их объединение даст мне приличную скорость - тем более, что этот запрос будет называться много.

Однако мои попытки потерпели неудачу из-за изменения количества строк, когда я пытаюсь объединить их. Я пробовал разные комбинации JOIN, но ничего не дает правильных результатов.

Есть ли у SO'ers какие-либо предложения?

Ответы [ 5 ]

1 голос
/ 12 ноября 2009

Вот мой переписать ваши запросы в один запрос:

   SELECT t.edisid,
          t.name, 
          SUM(dd.quantity) / 8 AS TiedDispense,
          SUM(d.quantity) AS TiedDelivered
     FROM SITES t
     JOIN USERSITES us ON us.edisid = t.esisid
     JOIN USERS u ON u.id = us.userid
     JOIN MASTERDATES md ON md.edisid = us.edisid
                        AND md.date >= t.siteonline
LEFT JOIN DLDATA dd ON dd.downloadid = md.id
LEFT JOIN DELIVERY d ON d.deliveryid = md.id
     JOIN PRODUCTS p ON p.id IN (dd.product, d.product)
LEFT JOIN SITEPRODUCTTIES spt ON spt.edisid = us.edisid
                             AND spt.productid = p.id
LEFT JOIN SITEPRODUCTCATEGORYTIES spct ON spct.edisid = us.edisid
                                      AND spct.productcategoryid = p.categoryid
    WHERE u.id = @UserID
      AND (@Tied IS NULL OR COALESCE(spt.tied, spct.tied, p.tied) = @Tied)
      AND md.date BETWEEN @From AND @To      
 GROUP BY t.edisid, t.name

В зависимости от ваших данных, СОЕДИНЕНИЯ к DLDATA и DELIVERY могут быть внутренними объединениями.

Было бы хорошо привыкнуть использовать псевдонимы таблиц.

1 голос
/ 11 ноября 2009

Я написал глупый ответ на этот вопрос, и он меня напугал, поэтому я стал больше разбираться в этом - в основном вы хотите поместить групповые байсы в объединения. У меня нет времени на редактирование вашего кода, но я думаю, что этот пример поможет вам:

create table #prod(
prodid int,
prodamount int)

create table #del(
delid int,
delamount int)

create table #main(
id int,
name varchar(50))

insert into #main(id,name)
select 1, 'test 1'
union select 2, 'test 2'
union select 3, 'test 3'
union select 4, 'test 4'

insert into #prod(prodid,prodamount)
select 1, 10
union select 1, 20
union select 1, 30
union select 2, 5

insert into #del(delid,delamount)
select 1, 9
union select 1, 8
union select 3, 7

/** wrong **/

select m.id, m.name, isnull(sum(p.prodamount),0), isnull(sum(d.delamount),0)
from #main m
left join #prod p on p.prodid = m.id
left join #del d on d.delid = m.id
group by m.id, m.name

/** right! **/
select id, name, isnull(myprod.prodtot,0) as prodtot, isnull(mydel.deltot,0) as deltot
from #main
left join 
    (SELECT prodid, SUM(prodamount) AS prodtot
    FROM #prod
    GROUP BY prodid) myprod on #main.id = myprod.prodid
left join 
    (SELECT delid, SUM(delamount) AS deltot
    FROM #del
    GROUP BY delid) mydel on #main.id = mydel.delid



drop table #prod
drop table #del
drop table #main
1 голос
/ 11 ноября 2009

вы можете попробовать:

SELECT Sites.EDISID, 
    Sites.[Name], 
    (SUM(DLData.Quantity) / 8) AS TiedDispense, 
    SUM(Delivery.Quantity) AS TiedDelivered
FROM Sites  
JOIN UserSites     
    ON UserSites.EDISID = Sites.EDISID  
JOIN Users    
    ON Users.[ID] = UserSites.UserID 
JOIN MasterDates   
    ON MasterDates.EDISID = UserSites.EDISID 
JOIN DLData  
    ON DLData.DownloadID = MasterDates.[ID] 
JOIN Products   
    ON Products.[ID] = DLData.Product
LEFT JOIN Delivery  
    ON Delivery.DeliveryID = MasterDates.[ID] 
LEFT JOIN SiteProductTies   
    ON SiteProductTies.EDISID = UserSites.EDISID  AND SiteProductTies.ProductID = Products.[ID] 
LEFT JOIN SiteProductCategoryTies   
    ON SiteProductCategoryTies.EDISID = UserSites.EDISID  
    AND SiteProductCategoryTies.ProductCategoryID = Products.CategoryID 
WHERE Users.[ID] = @UserID  
    AND (COALESCE(SiteProductTies.Tied, SiteProductCategoryTies.Tied, Products.Tied) = @Tied 
        OR @Tied IS NULL)  
    AND MasterDates.[Date] BETWEEN @From AND @To  
    AND MasterDates.[Date] >= Sites.SiteOnline 
GROUP BY Sites.EDISID, Sites.[Name]

Я сделал левое соединение, но внутреннее соединение может работать в зависимости от ваших данных. Я также проверил бы, чтобы все эти поля внешнего ключа были проиндексированы.

1 голос
/ 11 ноября 2009

После быстрого просмотра вашего запроса, я не могу быть уверен, что это правильно, не понимая бизнес-правил, лежащих в основе данных. Тем не менее, вы можете сделать это, если хотите:

SELECT 

Sites.EDISID, Sites.[Name], 
CASE WHEN Delivery.DeliveryID IS NULL THEN 0 ELSE SUM(Delivery.Quantity) END TiedDelivered, 
CASE WHEN DLData.[ID] IS NULL THEN 0 ELSE (SUM(DLData.Quantity) / 8) END TiedDispense 

FROM Sites
  JOIN UserSites 
    ON UserSites.EDISID = Sites.EDISID
  JOIN Users
    ON Users.[ID] = UserSites.UserID
 JOIN MasterDates 
  ON MasterDates.EDISID = UserSites.EDISID
 LEFT JOIN Products 
  ON Products.[ID] = DLData.Product

 LEFT JOIN DLData
  ON DLData.DownloadID = MasterDates.[ID]
 LEFT JOIN Delivery
  ON Delivery.DeliveryID = MasterDates.[ID]

 LEFT JOIN SiteProductTies 
  ON SiteProductTies.EDISID = UserSites.EDISID
  AND SiteProductTies.ProductID = Products.[ID]
 LEFT JOIN SiteProductCategoryTies 
  ON SiteProductCategoryTies.EDISID = UserSites.EDISID
  AND SiteProductCategoryTies.ProductCategoryID = Products.CategoryID
 WHERE Users.[ID] = @UserID
  AND (COALESCE(SiteProductTies.Tied, SiteProductCategoryTies.Tied, Products.Tied) = @Tied OR @Tied IS NULL)
  AND MasterDates.[Date] BETWEEN @From AND @To
  AND MasterDates.[Date] >= Sites.SiteOnline
  AND (DLData.[DownloadID] IS NOT NULL OR DELIVERY.DeliveryID IS NOT NULL)
 GROUP BY Sites.EDISID, Sites.[Name]

один ключ к этому является этой частью:

  AND (DLData.[DownloadID] IS NOT NULL OR DELIVERY.DeliveryID IS NOT NULL)

Что в значительной степени основано на допущениях ваших бизнес-правил, но может компенсировать дополнительные строки, возвращаемые двумя левыми объединениями. Вы также можете играть с чем-то вроде этого, если хотите:

AND ( TiedDelivered != 0 AND TiedDispense != 0)

надеюсь, это поможет.

-Стив

0 голосов
/ 11 ноября 2009

Не желая казаться чрезмерно глупым, но я думаю, объединение не поможет (потребует небольшого изменения имени возвращаемого столбца ...)?

...