帮助改进SQL连接

帮助改进SQL连接,sql,sql-server,join,indexing,Sql,Sql Server,Join,Indexing,我有一个存储过程,可以运行该过程来更新用户余额的游戏点数。这是一个包含5个子查询的插入。我已将其中一个子查询隔离为减慢整个批处理速度的查询。如果没有它,存储过程将在2秒内运行。有了它,它将需要多达8秒的时间。8秒并不是世界末日,但为了可扩展性,我需要更快地完成它。以下是独立的子查询: (SELECT IsNull(Sum(A.TransAmount) + Sum(Case When A.BetResult = 1 Then (A.BetWinAmount + (A.TransAmount * -

我有一个存储过程,可以运行该过程来更新用户余额的游戏点数。这是一个包含5个子查询的插入。我已将其中一个子查询隔离为减慢整个批处理速度的查询。如果没有它,存储过程将在2秒内运行。有了它,它将需要多达8秒的时间。8秒并不是世界末日,但为了可扩展性,我需要更快地完成它。以下是独立的子查询:

(SELECT IsNull(Sum(A.TransAmount) + Sum(Case When A.BetResult = 1 Then (A.BetWinAmount + (A.TransAmount * -1)) End), 0)
            FROM User_T A
            LEFT OUTER JOIN User_TD B on A.TID = B.TID
            LEFT OUTER JOIN Lines_BL C ON B.LID = C.LID
            LEFT OUTER JOIN Lines_BM D ON C.BMID = D.BMID
            LEFT OUTER JOIN Event_M E ON D.EID = E.EID
            LEFT OUTER JOIN Event_KB F ON A.TransReason = F.BID
            LEFT OUTER JOIN Event_M G ON F.BID = G.EID
        where A.UserID = U.UserID AND (A.IsSettled = 1)
        AND 
        (
        (A.TransReason = 1 AND (datediff(dd, Convert(datetime, E.EDate, 101), Convert(datetime, @EndDate, 101)) = @DaysAgo)) OR 
        (A.TransReason >= 3000 AND (datediff(dd, Convert(datetime, G.EDate, 101), Convert(datetime, @EndDate, 101)) = @DaysAgo)
                AND  [dbo].[Event_CEAFKBID](A.TransReason) = 1) OR
        (A.TransReason BETWEEN 3 and 150 AND (datediff(dd, Convert(datetime, A.TransDT, 101), Convert(datetime, @EndDate, 101)) = @DaysAgo))
        )
我为进一步隔离所做的工作:当我只在连接上运行Select*时(没有where子句),在不到一秒钟的时间内,性能非常好->100000行。正如我在where子句中所添加的,我相信最大的减速来自'or'子句和/或需要评估的函数

据我所知,where子句中的函数对每一行进行求值,而不是以某种方式缓存函数的定义并进行求值。我确实在表上有索引,但我想知道其中是否有一些是不正确的


在您不了解完整的数据库结构的情况下,我确信很难确定问题出在哪里,但我想指出一个开始进一步隔离的方向

您可以将案例放在where cause中,而不是直接放在select第一行。 如果在此状态下您只使用表A、E和G,为什么需要放置多个联接


为了更好地执行查询,您可以在management Studio上使用执行计划。

您可以将案例放在where cause中,而不是直接放在select第一行。 如果在此状态下您只使用表A、E和G,为什么需要放置多个联接


为了更好地执行查询,您可以在management Studio上使用执行计划。

相关子查询是一种非常糟糕的编程技术,相当于在查询中使用游标。将其改为派生表


是的,这些功能会让你慢下来。如果必须转换为datetime,则需要固定数据库结构,并将数据正确存储为datetime

相关子查询是一种非常糟糕的编程技术,相当于在查询中使用游标。将其改为派生表


是的,这些功能会让你慢下来。如果必须转换为datetime,则需要固定数据库结构,并将数据正确存储为datetime

是否需要对
DATEDIFF
函数的datetime进行转换?您是将日期存储为测试,还是重新转换以消除时间?如果是,则不需要进行转换,因为包括时间在内的天数不同是正确的。

是否需要对
DATEDIFF
函数的日期时间进行转换?您是将日期存储为测试,还是重新转换以消除时间?如果是,则不需要这样做,因为包括时间在内的天数不同是正确的。

您应该检查外部联接是否必要-它们比内部联接更昂贵。您有一些来自主表的值,标记为A。您还有一个OR条件引用E,一个OR条件引用G。我希望按照以下行重新构造查询:

SELECT SUM(x.result)
  FROM (SELECT A.TransAmount + CASE WHEN A.BetResult = 1
                               THEN (A.BetWinAmount + (A.TransAmount * -1))
                               ELSE 0 END AS result
          FROM A
         WHERE A.TransReason BETWEEN 3 AND 150
           AND datediff(dd, Convert(datetime, A.TransDT, 101),
                            Convert(datetime, @EndDate,  101)) = @DaysAgo
           AND A.UserID = U.UserID    -- Where does alias U come from?
           AND A.IsSettled = 1
        UNION
        SELECT A.TransAmount + CASE WHEN A.BetResult = 1
                               THEN (A.BetWinAmount + (A.TransAmount * -1))
                               ELSE 0 END AS result
          FROM User_T   A
          JOIN User_TD  B ON A.TID  = B.TID
          JOIN Lines_BL C ON B.LID  = C.LID
          JOIN Lines_BM D ON C.BMID = D.BMID
          JOIN Event_M  E ON D.EID  = E.EID
         WHERE A.TransReason = 1
           AND datediff(dd, Convert(datetime, E.EDate,  101),
                            Convert(datetime, @EndDate, 101)) = @DaysAgo
           AND A.UserID = U.UserID    -- Where does alias U come from?
           AND A.IsSettled = 1
        UNION
        SELECT A.TransAmount + CASE WHEN A.BetResult = 1
                               THEN (A.BetWinAmount + (A.TransAmount * -1))
                               ELSE 0 END S result
          FROM User_T   A
          JOIN User_TD  B ON A.TID  = B.TID
          JOIN Lines_BL C ON B.LID  = C.LID
          JOIN Lines_BM D ON C.BMID = D.BMID
          JOIN Event_M  E ON D.EID  = E.EID
          JOIN Event_KB F ON A.TransReason = F.BID
          JOIN Event_M  G ON F.BID  = G.EID
         WHERE A.TransReason >= 3000
           AND datediff(dd, Convert(datetime, G.EDate, 101),
                            Convert(datetime, @EndDate, 101)) = @DaysAgo
           AND [dbo].[Event_CEAFKBID](A.TransReason) = 1
           AND A.UserID = U.UserID    -- Where does alias U come from?
           AND A.IsSettled = 1
       ) AS x
这里的想法是,内部连接查询将比外部连接查询更快,对中间结果求和对DBMS来说并不困难(无论如何,它都是这样做的)。它可能还避免了使用
IFNULL


别名U可能是对外部查询的引用,它是外部查询的一部分。

您应该检查外部联接是否必要-它们比内部联接更昂贵。您有一些来自主表的值,标记为A。您还有一个OR条件引用E,一个OR条件引用G。我希望按照以下行重新构造查询:

SELECT SUM(x.result)
  FROM (SELECT A.TransAmount + CASE WHEN A.BetResult = 1
                               THEN (A.BetWinAmount + (A.TransAmount * -1))
                               ELSE 0 END AS result
          FROM A
         WHERE A.TransReason BETWEEN 3 AND 150
           AND datediff(dd, Convert(datetime, A.TransDT, 101),
                            Convert(datetime, @EndDate,  101)) = @DaysAgo
           AND A.UserID = U.UserID    -- Where does alias U come from?
           AND A.IsSettled = 1
        UNION
        SELECT A.TransAmount + CASE WHEN A.BetResult = 1
                               THEN (A.BetWinAmount + (A.TransAmount * -1))
                               ELSE 0 END AS result
          FROM User_T   A
          JOIN User_TD  B ON A.TID  = B.TID
          JOIN Lines_BL C ON B.LID  = C.LID
          JOIN Lines_BM D ON C.BMID = D.BMID
          JOIN Event_M  E ON D.EID  = E.EID
         WHERE A.TransReason = 1
           AND datediff(dd, Convert(datetime, E.EDate,  101),
                            Convert(datetime, @EndDate, 101)) = @DaysAgo
           AND A.UserID = U.UserID    -- Where does alias U come from?
           AND A.IsSettled = 1
        UNION
        SELECT A.TransAmount + CASE WHEN A.BetResult = 1
                               THEN (A.BetWinAmount + (A.TransAmount * -1))
                               ELSE 0 END S result
          FROM User_T   A
          JOIN User_TD  B ON A.TID  = B.TID
          JOIN Lines_BL C ON B.LID  = C.LID
          JOIN Lines_BM D ON C.BMID = D.BMID
          JOIN Event_M  E ON D.EID  = E.EID
          JOIN Event_KB F ON A.TransReason = F.BID
          JOIN Event_M  G ON F.BID  = G.EID
         WHERE A.TransReason >= 3000
           AND datediff(dd, Convert(datetime, G.EDate, 101),
                            Convert(datetime, @EndDate, 101)) = @DaysAgo
           AND [dbo].[Event_CEAFKBID](A.TransReason) = 1
           AND A.UserID = U.UserID    -- Where does alias U come from?
           AND A.IsSettled = 1
       ) AS x
这里的想法是,内部连接查询将比外部连接查询更快,对中间结果求和对DBMS来说并不困难(无论如何,它都是这样做的)。它可能还避免了使用
IFNULL


别名U可能是对外部查询的引用,它是外部查询的一部分。

我怀疑您最大的性能影响来自相关子查询(无论U.UserId后面是什么表)和嵌入函数调用dbo.Event\U CEAFKBID。当然,很大程度上取决于表的大小(读取的行数)。所有这些datetime转换都不会有帮助,并且会产生一种非常强烈的“糟糕的设计”味道,但我认为它们不会对性能造成太大影响

那些左侧的外部联接很难看,因为优化器必须检查它们所有的行—因此,如果“A”很大,则必须执行所有行上的所有联接,即使那里没有数据。如果可以用内部联接替换它们,那么就这样做,但我猜不是因为“表E或表G”逻辑。Lesses,看起来你得到的肯定是三个独立的查询合并成一个;如果你把它分成三个,联合在一起,它看起来就像下面的弗兰肯斯坦查询。我不知道这是否会运行得更快(见鬼,我甚至无法调试查询并确保panetheses平衡),但如果您拥有相对于逻辑的稀疏数据,那么这应该运行得相当快。(为了使代码更清晰,我取出了日期转换,您必须将它们重新插入。)


我怀疑您最大的性能影响来自相关子查询(无论U.UserId后面是什么表)和嵌入函数调用dbo.Event_CEAFKBID。当然,很大程度上取决于表的大小(读取的行数)。所有这些datetime转换都不会有帮助,并且会产生一种非常强烈的“糟糕的设计”味道,但我认为它们不会对性能造成太大影响

那些左侧的外部联接很难看,因为