在此SQL查询中使用子查询的替代方法是什么,可以提供更好的性能?

时间:2015-03-17 15:44:07

标签: sql sql-server

我们有一个Transact-SQL查询,其中包含where部分中的子查询。内部连接会多次添加tOrderDetails字段的不必要结果,因此我们采用(性能较差)子查询。它经常超时。我们需要相同的结果,但性能更好。有什么建议吗?

SELECT sum (cast(Replace(tOrders.TotalCharges,'$','') as money)) as TotalCharges
FROM [ArtistShare].[dbo].tOrders
WHERE tOrders.isGiftCardRedemption = 0
  and tOrders.isTestOrder=0
  and tOrders.LastDateUpdate between @startDate and @endDate
  and (SELECT count(tORderDetails.ID)
       from tORderDetails
       where tORderDetails.ORderID = tORders.ORderID
         and tOrderDetails.isPromo=1) = 0
  and (SELECT top 1 tProjects.ProjectReleaseDt
       from tProjects JOIN tOrderDetails
         on tOrderDetails.ProjectID = tProjects.ID
       where tOrderDetails.OrderID = tOrders.OrderID) >= @startDate

3 个答案:

答案 0 :(得分:4)

如果您只想知道至少有一个存在不存在,为什么要统计记录?

SELECT     Sum (Cast(Replace(tOrders.TotalCharges, '$', '') AS MONEY)) AS TotalCharges 
FROM       [ArtistShare].[dbo].tOrders 
WHERE      tOrders.isGiftCardRedemption = 0 
AND        tOrders.isTestOrder = 0 
AND        tOrders.LastDateUpdate BETWEEN @startDate AND  @endDate 
AND        NOT EXISTS 
           ( 
                  SELECT 1 FROM   tORderDetails 
                  WHERE  tORderDetails.ORderID = tORders.ORderID 
                  AND    tOrderDetails.isPromo = 1 ) 
AND        EXISTS 
           ( 
                  SELECT 1 FROM   tProjects INNER JOIN   tOrderDetails 
                  ON     tOrderDetails.ProjectID = tProjects.ID 
                  WHERE  tOrderDetails.OrderID = tOrders.OrderID 
                  AND    tProjects.ProjectReleaseDt >= @startDate )

答案 1 :(得分:2)

虽然我认为EXISTS可能表现最佳,但您也可以考虑采用以下方法。当您在问题中提到您的查询“多次添加tOrderDetails字段的不必要结果”时,可能是因为您有多个tOrderDetail记录,因此您需要使用GROUP BY折叠它们。不是使用效率非常低的相关子查询,而是使用像INNER JOIN这样的单个子查询。

   SELECT
    sum (cast(Replace(tOrders.TotalCharges, '$', '') as money)) as TotalCharges
    FROM [ArtistShare].[dbo].tOrders
        INNER JOIN (
            SELECT OrderID
            FROM tOrderDetails d INNER JOIN tProjects p on d.ProjectID = p.ID
            WHERE d.isPromo = 0 AND p.ProjectReleaseDt > @startDate
            GROUP BY OrderID
        ) qualifyingOrders ON qualifyingOrders.OrderID = tOrders.OrderID
    WHERE  tOrders.isGiftCardRedemption = 0
        and tOrders.isTestOrder=0
        and tOrders.LastDateUpdate between @startDate and @endDate

同样,你应该将它与 EXISTS 方法进行比较,看看哪一个表现更好,并且对你想要达到的目标最有意义。

答案 2 :(得分:0)

您可以尝试的另一种方法是先将您的子查询数据放入临时表中,然后查询这些数据。

显然,我没有完全掌握您的数据或理解数据关系,但希望以下示例可以帮助您找到满足您需求的正确查询。

值得至少比较一下表现。

DECLARE @ORDERDETAILS TABLE
(
    INT idCount,
    INT orderId
)
INSERT INTO
    @ORDERDETAILS
SELECT
    COUNT(tORderDetails.ID),
    tORderDetails.ORderID
WHERE
    tOrderDetails.isPromo = 1
GROUP BY
    tORderDetails.ORderID

DECLARE @PROJECTS TABLE
(
    DATETIME releaseDte,
    INT orderId
)
INSERT INTO
    @PROJECTS
SELECT
    tProjects.ProjectReleaseDt,
    tOrderDetails.OrderID
FROM 
    tProjects JOIN tOrderDetails ON tOrderDetails.ProjectID = tProjects.ID

SELECT 
    sum (cast(Replace(tOrders.TotalCharges,'$','') as money)) as TotalCharges
FROM 
    [ArtistShare].[dbo].tOrders JOIN @ORDERDETAILS ON @ORDERDETAILS.orderId = tORders.ORderID
        JOIN @PROJECTS ON @PROJECTS.orderId = tOrders.OrderID)
WHERE tOrders.isGiftCardRedemption = 0
  and tOrders.isTestOrder=0
  and tOrders.LastDateUpdate between @startDate and @endDate
  AND @ORDERDETAILS.idCount = 0
  --and (SELECT count(tORderDetails.ID)
  --     from tORderDetails
  --     where tORderDetails.ORderID = tORders.ORderID
  --       and tOrderDetails.isPromo=1) = 0
  AND @PROJECTS.releaseDte >= @startDate --NOT QUITE SURE WHAT THIS EXACTLY MIMICS WHAT YOU ARE TRYING TO DO BUT IF NOT YOU COULD REMOVE THE JOIN AND SUB-QUERY AS FOLLOWS
  --AND (SELECT TOP 1 @PROJECTS.releaseDte WHERE PROJECTS.orderId = tOrders.OrderID) >= @startDate
  --and (SELECT top 1 tProjects.ProjectReleaseDt
  --     from tProjects JOIN tOrderDetails
  --       on tOrderDetails.ProjectID = tProjects.ID
  --     where tOrderDetails.OrderID = tOrders.OrderID) >= @startDate