Qui*_*ver 3 join sql-server subquery
我会说,我是比较新的数据库和SQL,我试图做的除了率先View在SQL Server这之前的同事创建的。
我正在尝试加入Customer_Snacks表格,以便我可以获得Snack Quantity每个客户的订单。我遇到的问题是,当我做一个LEFT OUTER JOIN,据我所知,应该工作时,其他一些列数据发生了巨大的变化。
我设法通过使用子查询使其按照我希望的方式工作,但速度非常慢。运行查询需要一分钟多的时间,作为回报,调用该视图的应用程序超时。
这是我目前有效的(我知道,它很乱):
SELECT
cl.CustomerID,
cl.FirstName + ' ' + c.LastName as CustomerDisplay,
cl.InvoiceID,
cl.MealPlanID,
mp.DeliveryDate1 as DeliveryDate,
SUM(CASE WHEN cll.Name = 'Meal' THEN cll.Quantity ELSE 0 END) as RegularQuantity,
SUM(CASE WHEN cll.Name = 'Meal' THEN cll.Quantity * Amount ELSE 0 END) as RegularTotal,
SUM(CASE WHEN cll.Name = 'EXTRAPROTEINMEAL' THEN cll.Quantity ELSE 0 END) as ProteinQuantity,
SUM(CASE WHEN cll.Name = 'EXTRAPROTEINMEAL' THEN cll.Quantity * Amount ELSE 0 END) as ProteinTotal,
SUM(CASE WHEN cll.Name = 'TAX' THEN Amount ELSE 0 END) as TotalTax,
SUM(CASE WHEN cll.Name = 'SNACKS' THEN Amount ELSE 0 END) AS SnackTotal,
-- Here is my subquery that I would like to integrate into this view with a join so that it's much faster
(SELECT SUM(cs.Quantity) FROM Fustomer_Snacks as cs WHERE cs.CustomerID = cl.CustomerID and cs.DeliveryDate = mp.DeliveryDate1) as SnackQuantity,
------------------------------------------------------------------
CAST(SUM((CASE WHEN cll.Name = 'home' or cll.Name = 'work' THEN 1 ELSE 0 END)) as bit) as IsDelivery,
SUM(CASE WHEN (cll.Name = 'home' or cll.Name = 'work' or cll.Name = 'pickup') and cll.Quantity > 0 THEN cll.Quantity * Amount else 0 END) as DeliveryCharge,
cl.Balance as PrePromoTotal,
ISNULL(cl.Balance * (pc.Discount * .01), 0) * -1 as PromoDiscount,
cl.FinalBalance - ISNULL((cl.FinalBalance * (pc.Discount * .01)),0) Total
FROM
Customer_Ledger as cl
INNER JOIN
Customer_Ledger_LineItems as cll
on cl.ID = cll.LedgerID
INNER JOIN
MealPlan as mp
on cl.MealPlanID = mp.ID
INNER JOIN
Customer as c
on cl.CustomerId = c.Id
LEFT OUTER JOIN
PromoCode as pc
on cl.BalancePromoId = pc.Id
GROUP BY
cl.CustomerID,
c.FirstName,
c.LastName,
MealPlanID,
InvoiceID,
mp.DeliveryDate1,
cl.Balance,
cl.FinalBalance,
pc.Discount
Run Code Online (Sandbox Code Playgroud)
就像我说的,我目前拥有它的方式,它有效,但它太慢了。一旦我尝试LEFT OUTER JOIN它,它就会快得多,但其他列就像RegularQuantity其他数字一样膨胀(我不确定这是怎么发生的)。
如果有人能指导我朝着正确的方向前进,那就太好了!
您的问题很可能是,当您添加零食时,您有多个表为您的GROUP BY语句中的列返回不止一行- 可能是Customer_Snacks和Customer_Ledger_LineItems。
当数据库引擎执行连接时,它会匹配第一个表中具有值ABC 的多行与第二个表中具有相同值ABC 的多行(外部连接的工作方式略有不同,但我们暂时忽略它) .
我猜对于给定的Customer_Ledger行,您绑定到Customer一行、MealPlan一行、一个(或零)个PromoCode row, and multipleCustomer_Ledger_LineItems` 行。
我也在猜测Customer_Ledger一行及其关联的MealPlan行,您可以带回多Customer_Snacks行。
因此,当我们将所有行放在一起时,假设我们有:
Customer排Customer_Ledger排MealPlan排PromoCode排Customer_Ledger_LineItems排将所有这些匹配在一起,您得到1 * 1 * 1 * 1 * 3= 3 行。
现在,添加 2Customer_Snacks行。这些与我们已有的所有行相匹配,因此我们得到1 * 1 * 1 * 1 * 3 * 2= 6 行。
当我们计算总和时,我们会从Customer_Ledger_LineItems行中得到两倍的总和,因为现在每行出现两次。并且,我们将从 中获得总和的三倍Customer_Snacks,因为这些行现在每行出现三倍。基本上,您在CROSS JOIN相关Customer_Ledger_LineItems行和Customer_Snacks行之间得到一个。
有两种方法可以避免这种情况。您已经采用了第一个选项 - 使用子查询从其中一个表中获取总和,将多行返回到我们的分组值。
另一个选项非常相似:使用您需要的所有相关值构建一个临时表(或 CTE),以便您在加入数据时只有一行包含您需要的总和。在您的情况下,这看起来像:
;WITH SnackSums AS
(SELECT cs.CustomerID
,cs.DeliveryDate
,SUM(cs.Quantity) as SnackQuantity
FROM Customer_Snacks cs
GROUP BY cs.CustomerID, cs.DeliveryDate
)
Run Code Online (Sandbox Code Playgroud)
并且会在SELECT您的查询之前进行。然后,您只需放置ss.SnackQuantity子查询所在的位置,然后添加:
LEFT OUTER JOIN
SnackSums ss
ON (cl.CustomerId = ss.CustomerId AND ss.DeliveryDate = mp.DeliveryDate1)
Run Code Online (Sandbox Code Playgroud)
就在您的GROUP BY. 最后,您还需要添加ss.SnackQuantity到GROUP BY,因为在主查询的上下文中,这是一个独立的、非聚合的值。
现在,我不保证这会比子查询更快。但是,它应该是准确的 - 如果它不准确,那么速度很快就没关系。