【问题标题】:SubQuery vs TempTable before Merge合并前的子查询与临时表
【发布时间】:2013-04-11 04:39:38
【问题描述】:

我有一个复杂的查询,我想将其用作合并到表中的源。这将在数​​百万行中执行。目前我正在尝试通过在合并之前将其插入临时表来对数据应用约束。

操作是:

  • 过滤掉重复数据。
  • 加入一些表以提取更多数据
  • 插入临时表。

这里是查询。

-- Get all Orders that aren't in the system
WITH Orders AS
(
    SELECT *
    FROM [Staging].Orders o
    WHERE NOT EXISTS
    (
        SELECT 1
        FROM Maps.VendorBOrders vbo
        JOIN OrderFact of
        ON of.Id = vbo.OrderFactId
        AND InternalOrderId = o.InternalOrderId
        AND of.DataSetId = o.DataSetId
        AND of.IsDelete = 0
    )
)
INSERT INTO #VendorBOrders
    (
      CustomerId
     ,OrderId
     ,OrderTypeId
     ,TypeCode
     ,LineNumber
     ,FromDate
     ,ThruDate
     ,LineFromDate
     ,LineThruDate
     ,PlaceOfService
     ,RevenueCode
     ,BillingProviderId
     ,Cost
     ,AdjustmentTypeCode
     ,PaymentDenialCode
     ,EffectiveDate
     ,IDRLoadDate
     ,RelatedOrderId
     ,DataSetId
    )
SELECT
     vc.CustomerId
    ,OrderId
    ,OrderTypeId
    ,TypeCode
    ,LineNumber
    ,FromDate
    ,ThruDate
    ,LineFromDate
    ,LineThruDate
    ,PlaceOfService
    ,RevenueCode
    ,bp.Id
    ,Cost
    ,AdjustmentTypeCode
    ,PaymentDenialCode
    ,EffectiveDate
    ,IDRLoadDate
    ,ro.Id
    ,o.DataSetId
FROM
Orders o
-- Join related orders to match orders sharing same instance
JOIN Maps.VendorBRelatedOrder ro
ON ro.OrderControlNumber = o.OrderControlNumber
AND ro.EquitableCustomerId = o.EquitableCustomerId
AND ro.DataSetId = o.DataSetId
JOIN BillingProvider bp
ON bp.ProviderNPI = o.ProviderNPI
-- Join on customers and fail if the customer doesn't exist
LEFT OUTER JOIN [Maps].VendorBCustomer vc
ON vc.ExtenalCustomerId = o.ExtenalCustomerId
AND vc.VendorId = o.VendorId;

我想知道是否有什么我可以做的来优化它的时间。我曾尝试使用 DB Engine Tuner,但此查询比我正在运行的其他查询多占用 100 倍的 CPU 时间。还有什么我可以研究的,或者查询不能进一步改进吗?

【问题讨论】:

  • 尝试使用嵌套的CTE计算最终需要插入的数据,最后保留一个简单的INSERT

标签: sql sql-server tsql optimization


【解决方案1】:

CTE 只是语法

在该联接上评估(运行)该 CTE

首先将其作为选择语句运行(不插入)

如果选择很慢,那么:
将该 CTE 移动到 #TEMP,以便对其进行一次评估并具体化
在三个连接列上放置一个索引(如果适用,则为 PK)

如果选择不慢,那么它是 #VendorBOrders 上的插入时间
拳头只创建 PK 并对 PK 上的插入进行排序,以免使聚集索引碎片化
然后在插入完成后构建任何其他必要的索引

【讨论】:

    【解决方案2】:

    通常,当我进行速度测试时,我会检查 SQL 的各个部分以查看问题所在。打开“执行计划”,看看大部分时间都花在了哪里。此外,如果您只想快速而肮脏地突出显示您的 CTE 并运行它。这么快吗,是的,继续。

    我有时发现一个索引被关闭会抛出整个复杂的连接逻辑,只需让数据库执行大型任务的一部分然后找到那个部分。

    另一个想法是,如果您在生产环境或类似环境中有一个快速的 tempdb,那么也将您的 CTE 转储到一个临时表中。对此进行索引,看看是否可以加快速度。有时 CTE、表变量和临时表在连接时会失去一些性能。我发现在部分对象上创建索引有时会提高性能,但您也会在 tempdb 上增加更多负载来执行此操作,因此请记住这一点。

    【讨论】:

    • 有没有关于创建最优索引的文章?我一直在使用优化引擎来建议索引,但我开始认为这可能弊大于利。
    • 老实说,我对索引的 TSQL 知识最薄弱。我相信索引会降低插入的性能,因此您希望谨慎使用它们。但是有时使用临时表我已经成功地做到了这一点:插入#Object Select * from thing 或 Select * into #Object from thing 然后创建索引。因此,您无需添加索引即可获得插入速度,然后您可以在单个 int 字段上的所有内容上创建完整索引。我知道很多时候,当您有多个部门加入时,通常可能会有一个断点,即阻塞点。
    • 我不确定这是否在野外使用,但我可以在插入之前删除索引并在之后添加它们吗?再说一次,如果这个东西增长到可能无法工作的 TB 数据。
    • 取决于所花费的时间,在 QA/UAT/BETA 中测试事物然后运行生产是不同的。取决于你的大小和你想要的时间表。你可以,但它经常变成“我应该吗?”的问题。好处是如果它更快。您也可以尝试禁用然后重新启用它们。然后可能会在之后对索引进行增量调整。在生产环境中从头开始重新创建索引有时可能需要一段时间。对于临时表,我建议它通常高达几百千或几百万,如果它只是整数,如果你在 tempdb 上有良好的硬件,也不算太糟糕。
    猜你喜欢
    • 2013-02-27
    • 2018-06-20
    • 2012-12-15
    • 2018-08-03
    • 2013-02-07
    • 1970-01-01
    • 1970-01-01
    • 2020-05-30
    • 2014-04-30
    相关资源
    最近更新 更多