【问题标题】:Alternatives To Increase Performance? Using Outer Apply w/ Top1 and Order提高性能的替代方案?使用带 Top1 和顺序的外部应用
【发布时间】:2014-02-18 15:06:11
【问题描述】:

感谢上一个问题中的所有帮助,我的项目取得了很大进展,甚至学到了很多东西。我希望能在我目前面临的性能下降问题上获得一些帮助。基本上,我们正在使用一个数据库,该数据库有一个名为“AlertHistory”的表,并且该表没有被索引(我知道,...)。我的目标是获取 AlertHistory 表中与当前 alertid 相关的最新条目。我的想法是使用外连接,但这对我们的生产服务器造成了巨大的性能损失(执行时间从大约 20 秒到大约 3 分钟)。

SELECT
    AllAlerts.AlertID as AlertID,
    Queues.QueueID as QueueID,
    Queues.QueueName as QueueName,
    AllAlerts.ConnectorID as ConnectorID,
    AllAlerts.DeviceID as DeviceID,
    AllAlerts.SituationMessage as Summary,
    AllAlerts.AlertDetail as Detail,
    AllAlerts.Acknowledged as Acknowledged,
    "AcknowledgedBy" =
        CASE
            WHEN AllAlerts.Acknowledged = '1' THEN History.CreatedBy
            WHEN AllAlerts.Acknowledged = '0' THEN ' '
        END,
    AllAlerts.AssignedTo as AssignedTo,
    AllAlerts.ReportedTime as CreatedTime,
    AllAlerts.ClearedTime as ClearedTime

from 
    [ISG_SOI ].[dbo].[Alerts] as AllAlerts

    inner join [ISG_SOI ].[dbo].[AlertQueueAssignments] as QA 
        on QA.[AlertID] = AllAlerts.[AlertID]
    inner join [ISG_SOI ].[dbo].[AlertQueues] AS Queues 
        on Queues.[QueueID] = QA.[QueueID]

    outer apply (
        select top 1 History.CreatedBy 
        from [ISG_SOI ].[dbo].[AlertHistory] as History
        where History.ColumnName = '73549' and History.AlertID = AllAlerts.AlertID
        order by History.CreatedTime DESC
    ) as History

where Queues.QueueName = 'OCC'

只是为了解释应用,ColumnName 是我需要获取的静态列。此数据库使用整数标识符 73549。当然我可以使用“声明”并声明一个变量,但我们的实现软件不支持 (BOXI)。

【问题讨论】:

  • 为什么会同时标记 [mysql] 和 [tsql]?看来这是SQL-Server?你能索引这个表吗?如果不是这样,您实际上可以通过将其整体复制到临时表中,对其进行索引然后重新运行您的查询来获得更好的结果。
  • 对不起,调整了标签,你是对的!不幸的是,我无法索引此表,因为它是由 BOXI 应用程序创建的表。我将不得不做一些关于复制到临时表的阅读。不确定这在性能方面如何发挥作用,因为该表可能有成千上万行。
  • 您遇到的问题是,您当前的查询将一遍又一遍地扫描所有这些行,很多次。 需要使数据结构化和可搜索。通过复制到临时表,您可以重新组织一次(对表进行一次完整扫描),然后快速搜索多次(在表上进行许多小搜索)。您越关心行数,您对数据的结构/排序/索引就越重要。 请注意,您只需复制那里的数据where History.ColumnName = '73549',这也将节省一些时间和服务器负载。
  • 您真的需要在选择中发布所有列(带有 case 和 isnull)来描述问题吗?贴出最简单的例子来重现问题。
  • @Blam 指出,谢谢,清理了原帖以便于阅读

标签: tsql sql-server-2012 business-objects


【解决方案1】:

值得一试的选择...

(尝试让 SQL Server 对数据进行一次排序,然后查询它,而不使用临时表或索引真实表。)

inner join [ISG_SOI ].[dbo].[AlertQueueAssignments] as QA 
    on QA.[AlertID] = AllAlerts.[AlertID]
inner join [ISG_SOI ].[dbo].[AlertQueues] AS Queues 
    on Queues.[QueueID] = QA.[QueueID]
inner join
(
    SELECT
        ROW_NUMBER() OVER (PARTITION BY AlertID ORDER BY CreatedTime DESC) AS CreatedOrdinal,
        *
    FROM
        [ISG_SOI ].[dbo].[AlertHistory]
    WHERE
        ColumnName = '73549'
)
    AS History
        ON  History.AlertID        = AllAlerts.AlertID
        AND History.CreatedOrdinal = 1

【讨论】:

  • 我很想知道这个“技巧”是否有效,请告诉我们:)
  • 马上我可以告诉你,执行路径看起来好多了!!去看看它在 BOXI 中是如何工作的。会回来报告的。
  • 看来等待时间仍然是大约 3 分钟。我非常感谢 MatBailie 的帮助,我从您的回答中学到了一些东西。假设数据库由于 AlertHistory 表上缺少索引而无法容忍是否安全?
  • 我接受了这个答案,因为它确实对执行路径产生了积极影响,并且确实在一定程度上减少了查询时间。我将需要向数据库开发人员提交增强请求以改进 db 结构,以便我们能够有效地获取相应的数据。再次感谢你的帮助。确保学习体验
  • @paimon.soror - 祝你好运,很抱歉我们无法挽救局面。如果您能够创建临时表/表变量,那仍然可能是有益的。我不知道你的 BOXI 约束是什么。
【解决方案2】:
inner join 
(
    select History.AlertID, max(History.CreatedBy) as CreatedBy
    from [ISG_SOI ].[dbo].[AlertHistory] as History
    where History.ColumnName = '73549'
    group by History.AlertID
) as History
  on History.AlertID  = AllAlerts.AlertID  

您甚至可能想尝试加入提示
哈希或合并应该强制它只被评估一次
[高级查询调优概念][1]

下一步是使用该查询创建#temp。
在 AlertID 上声明 PK,因为查询优化器可以使用该信息。

【讨论】:

  • 缺少GROUP BY。此外,需要的是MAX(CreatedTime),然后是与该记录关联的CreatedBy。仅仅因为Zaza 有一个条目,并不意味着她总是最新的。
  • Msg 8155, Level 16, State 2, Line 65 No column name was specified for column 2 of 'History'.
  • 添加为。您已经在查询中使用 as all over。见更新。单独测试该查询。首先努力优化作品。
  • 我明白你现在做了什么。试试这个。谢谢
  • 它应该比 row_number 更快,因为它只返回所需的行。
【解决方案3】:

感谢@MatBailie 和@Blam 提供的帮助,这确实有助于改进查询:

    SELECT
    AllAlerts.AlertID as AlertID,
    Queues.QueueID as QueueID,
    Queues.QueueName as QueueName,
    AllAlerts.ConnectorID as ConnectorID,
    AllAlerts.DeviceID as DeviceID,
    AllAlerts.SituationMessage as Summary,
    AllAlerts.AlertDetail as Detail,
    AllAlerts.Acknowledged as Acknowledged,
    "AcknowledgedBy" = 
        CASE
            WHEN AllAlerts.Acknowledged = '1' THEN ISNULL(History.CreatedBy, '')
            WHEN AllAlerts.Acknowledged = '0' THEN ' '
        END,
    AllAlerts.AssignedTo as AssignedTo,
    DATEADD(HOUR, -5, AllAlerts.ReportedTime) as CreatedTime,
    DATEADD(HOUR, -5, AllAlerts.ClearedTime) as ClearedTime,
    AllAlerts.SvcDeskTicket as TicketID

from 
    [ISG_SOI ].[dbo].[Alerts] as AllAlerts

    inner join [ISG_SOI ].[dbo].[AlertQueueAssignments] as QA 
        on QA.[AlertID] = AllAlerts.[AlertID]
    inner join [ISG_SOI ].[dbo].[AlertQueues] AS Queues 
        on Queues.[QueueID] = QA.[QueueID]

    left join 
    (
        select History.AlertID, max(History.CreatedBy) as CreatedBy
          from [ISG_SOI ].[dbo].[AlertHistory] as History
         where History.ColumnName = '73549'
           and History.Currentvalue = 'true'
         group by History.AlertID
    ) as History
      on History.AlertID  = AllAlerts.AlertID 

where Queues.QueueName = 'OCC'

【讨论】:

  • 这很危险。尽管两条记录可能不是likely 具有相同的createdTime,但不能保证所有记录都在不同的时间。随着数据量的增加,两个警报具有相同createdTime 的记录的可能性增加,此时您将失败。此外,使用in 的扩展性非常差。为什么不简单地加入历史记录,然后再次加入子查询(SELECT AlertID, MAX(CreatedTime) AS CreatedTime FROM History WHERE <blah> GROUP BY AlertID)
  • @MatBailie 感谢您的回复。尽管您所说的有道理,但根据写入数据库的应用程序的工作方式,不会有 AlertIDacknowledgement 记录在同时。在那张纸条上,我将不得不试一试你评论的后半部分!感谢您花时间指导我。
  • @MatBailie 用我的新查询更新了我的答案。感谢您的帮助!
猜你喜欢
  • 2023-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-15
  • 1970-01-01
相关资源
最近更新 更多