【问题标题】:SQL Server : How to make this query more efficient/faster?SQL Server:如何使这个查询更高效/更快?
【发布时间】:2018-05-01 22:48:55
【问题描述】:

SQL 向导和大师的问题。我必须缩短这头野兽的执行时间。执行需要 5 分钟以上,有时会超时。我需要帮助了解如何提高效率。我要返回大约 100,000 行。

场景是:我正在尝试识别应该标记为“约会季节”的“有效订单”。这些订单将包含与有效“类别代码”相关联的商品 (dbo.DTITEMS ON od.CATEGORY = dbo.DTITEMS.CATEGORY)。

然后我使用这个查询的结果基本上用当前“约会季节”的值更新了一个订单字段“DTGSEASON”。

我引用了一个名为 DATING 的表(独立于 CRM.Dbo.Dating),其中存储了促销开始和结束日期、当前约会季节等参数。

这是我正在运行以查找“有效订单”的查询,称为 vDatingValidOrdersReg:

SELECT     
      h.CUSTOMER
    , h.ORDNUMBER
    , h.INVNETWTX
    , dtgseason.VALUE AS dtgseason
    , c.comp_dqdatingmin
    , dbo.DTITEMS.ALTMINIMUM
    , d.dat_datingapprovedon
    , d.dat_ordsincepromostart
    , h.EXPDATE
    , dbo.DATING.PROMOSTART
    , d.dat_season
    , d.dat_year
    , od.ITEM
    , od.CATEGORY
    , d.dat_DatingID
    , c.Comp_Name
    , d.dat_state
    , h.ORDUNIQ
    , c.comp_dqdatingmax
FROM CRM.dbo.Dating AS d 
INNER JOIN CRM.dbo.Company AS c 
    ON d.dat_CompanyId = c.Comp_CompanyId 
LEFT OUTER JOIN dbo.OEORDH AS h 
    ON c.Comp_IdCust = h.CUSTOMER 
LEFT OUTER JOIN dbo.OEORDHO AS dtgseason 
    ON h.ORDUNIQ = dtgseason.ORDUNIQ AND dtgseason.OPTFIELD = 'dtgseason'
INNER JOIN dbo.OEORDD AS od 
    ON h.ORDUNIQ = od.ORDUNIQ 
INNER JOIN dbo.DTITEMS 
    ON od.CATEGORY = dbo.DTITEMS.CATEGORY 
INNER JOIN dbo.DATING 
    ON d.dat_season = dbo.DATING.SEASON 
    AND d.dat_year = dbo.DATING.YEAR 
    AND dbo.DTITEMS.SEASON = dbo.DATING.SEASON 
    AND dbo.DTITEMS.YEAR = dbo.DATING.YEAR
WHERE (h.ORDDATE BETWEEN dbo.DATING.PROMOSTART AND dbo.DATING.PROMOEND) 
    AND (h.EXPDATE BETWEEN dbo.DATING.EXPSHIPST AND dbo.DATING.EXPSHIPEND) 
    AND (d.dat_state = 'Approve') 
    AND (d.dat_Deleted IS NULL) 
    AND (dbo.DATING.SEASCLOSED = 0) 
    AND (dbo.DTITEMS.ALTMINIMUM = 0) 
    AND (h.ORDNUMBER NOT IN (
                             SELECT     ORDNUMBER
                             FROM       dbo.vDatingValidOrdersAlt)) 
    AND (dbo.DATING.ORDERON = 1)

这是它引用的查询,它基本上是相同的,但查找具有不同项目类别的订单:vDatingValidOrdersAlt。这是我的问题吗?

SELECT     
    h.CUSTOMER
    , h.ORDNUMBER
    , h.INVNETWTX
    , dtgseason.VALUE AS dtgseason
    , c.comp_dqdatingmin
    , dbo.DTITEMS.ALTMINIMUM
    , d.dat_datingapprovedon
    , d.dat_ordsincepromostart
    , h.EXPDATE
    , dbo.DATING.PROMOSTART
    , d.dat_season
    , d.dat_year
    , od.ITEM
    , od.CATEGORY
    , d.dat_DatingID
    , c.Comp_Name
    , d.dat_state
    , h.ORDUNIQ, c.comp_dqdatingmax
FROM CRM.dbo.Dating AS d 
INNER JOIN CRM.dbo.Company AS c 
    ON d.dat_CompanyId = c.Comp_CompanyId 
LEFT OUTER JOIN dbo.OEORDH AS h 
    ON c.Comp_IdCust = h.CUSTOMER 
LEFT OUTER JOIN dbo.OEORDHO AS dtgseason 
    ON h.ORDUNIQ = dtgseason.ORDUNIQ AND dtgseason.OPTFIELD = 'dtgseason' 
INNER JOIN dbo.OEORDD AS od 
    ON h.ORDUNIQ = od.ORDUNIQ 
INNER JOIN dbo.DTITEMS 
    ON od.CATEGORY = dbo.DTITEMS.CATEGORY 
INNER JOIN dbo.DATING 
    ON d.dat_season = dbo.DATING.SEASON 
    AND d.dat_year = dbo.DATING.YEAR 
    AND dbo.DTITEMS.SEASON = dbo.DATING.SEASON 
    AND dbo.DTITEMS.YEAR = dbo.DATING.YEAR
WHERE (h.ORDDATE BETWEEN dbo.DATING.PROMOSTART AND dbo.DATING.PROMOEND) 
    AND (h.EXPDATE BETWEEN dbo.DATING.EXPSHIPST AND dbo.DATING.EXPSHIPEND)
    AND (d.dat_state = 'Approve') 
    AND (d.dat_Deleted IS NULL) 
    AND (dbo.DATING.SEASCLOSED = 0) 
    AND (dbo.DTITEMS.ALTMINIMUM > 0) 
    AND (dbo.DATING.ORDERON = 1)

必须有一种方法可以减少此查询的资源密集型,但我不知道如何去做。有什么想法和建议?

【问题讨论】:

  • 请考虑在格式化查询时更好地使用空格。这些查询很难阅读,因为所有内容都浓缩为几行。作为查询的表现。您需要为表、索引以及查询计划提供 DDL。如果没有所有这些,我们将无法告诉您如何有效地改进查询。
  • 此外,您的查询中有一些LEFT OUTER JOINs,但是,请在您的WHERE 中引用该对象(例如h.EXPDATE)。这隐含地将LEFT OUTER JOIN 转换为INNER JOIN。这是故意的吗?如果是这样,您应该使用INNER JOIN,如果不是,您需要解决您的WHEREON 子句..
  • @Larnu 我试过了...编辑:看起来 Sean Lange 也尝试了格式化哈哈。
  • @JacobH 比以前好多了。现在我们只需要等待 DDL、查询计划、索引详细信息,以及为什么 OP 在 WHERE 子句中使用 OR 子句和 IS NULL 表达式引用具有 LEFT JOIN 的对象;这意味着它不再是LEFT JOIN。有几个问题要他们回答。 :)
  • 如果第二个查询是您在第一个查询中选择的视图,则您不需要返回除ORDNUMBER 之外的任何列。这是一个简单的性能提升。我会创建一个辅助视图,它只为您的 NOT IN 子句返回一列。

标签: sql sql-server database


【解决方案1】:

您在这两个查询中都有逻辑问题。您对 dbo.OEORDH 有一个左连接,但在该表上有一个 where 子句。这在逻辑上将您的左连接更改为内连接。至于性能,我看不到您可以在 sql 中更改的任何明显内容。但是,如果您有涵盖这些查询的索引,我会感到惊讶。我们需要查看表定义和索引。此外,发布执行计划对于弄清楚这一点至关重要。

这是这两个查询在某些格式下的样子。

SELECT h.CUSTOMER
    , h.ORDNUMBER
    , h.INVNETWTX
    , dtgseason.VALUE AS dtgseason
    , c.comp_dqdatingmin
    , di.ALTMINIMUM
    , d.dat_datingapprovedon
    , d.dat_ordsincepromostart
    , h.EXPDATE
    , dd.PROMOSTART
    , d.dat_season
    , d.dat_year
    , od.ITEM
    , od.CATEGORY
    , d.dat_DatingID
    , c.Comp_Name
    , d.dat_state
    , h.ORDUNIQ
    , c.comp_dqdatingmax
FROM CRM.dbo.Dating AS d 
INNER JOIN CRM.dbo.Company AS c ON d.dat_CompanyId = c.Comp_CompanyId 
LEFT OUTER JOIN dbo.OEORDH AS h ON c.Comp_IdCust = h.CUSTOMER 
LEFT OUTER JOIN dbo.OEORDHO AS dtgseason ON h.ORDUNIQ = dtgseason.ORDUNIQ 
                                        AND dtgseason.OPTFIELD = 'dtgseason' 
INNER JOIN dbo.OEORDD AS od ON h.ORDUNIQ = od.ORDUNIQ 
INNER JOIN dbo.DTITEMS di ON od.CATEGORY = di.CATEGORY 
INNER JOIN dbo.DATING dd ON d.dat_season = dd.SEASON 
                        AND d.dat_year = dd.YEAR 
                        AND di.SEASON = dd.SEASON 
                        AND di.YEAR = dd.YEAR
WHERE h.ORDDATE BETWEEN dd.PROMOSTART AND dd.PROMOEND
    AND h.EXPDATE BETWEEN dd.EXPSHIPST AND dd.EXPSHIPEND
    AND d.dat_state = 'Approve'
    AND d.dat_Deleted IS NULL
    AND dd.SEASCLOSED = 0
    AND di.ALTMINIMUM = 0
    AND h.ORDNUMBER NOT IN (SELECT ORDNUMBER FROM dbo.vDatingValidOrdersAlt)
    AND dd.ORDERON = 1

还有第二个。

SELECT h.CUSTOMER
    , h.ORDNUMBER
    , h.INVNETWTX
    , dtgseason.VALUE AS dtgseason
    , c.comp_dqdatingmin
    , di.ALTMINIMUM
    , d.dat_datingapprovedon
    , d.dat_ordsincepromostart
    , h.EXPDATE
    , dd.PROMOSTART
    , d.dat_season
    , d.dat_year
    , od.ITEM
    , od.CATEGORY
    , d.dat_DatingID
    , c.Comp_Name
    , d.dat_state
    , h.ORDUNIQ
    , c.comp_dqdatingmax
FROM CRM.dbo.Dating AS d 
INNER JOIN CRM.dbo.Company AS c ON d.dat_CompanyId = c.Comp_CompanyId 
LEFT OUTER JOIN dbo.OEORDH AS h ON c.Comp_IdCust = h.CUSTOMER 
LEFT OUTER JOIN dbo.OEORDHO AS dtgseason ON h.ORDUNIQ = dtgseason.ORDUNIQ 
                                        AND dtgseason.OPTFIELD = 'dtgseason' 
INNER JOIN dbo.OEORDD AS od ON h.ORDUNIQ = od.ORDUNIQ 
INNER JOIN dbo.DTITEMS as di ON od.CATEGORY = di.CATEGORY 
INNER JOIN dbo.DATING as dd ON d.dat_season = dd.SEASON 
                            AND d.dat_year = dd.YEAR 
                            AND di.SEASON = dd.SEASON 
                            AND di.YEAR = dd.YEAR
WHERE h.ORDDATE BETWEEN dd.PROMOSTART AND dd.PROMOEND
    AND h.EXPDATE BETWEEN dd.EXPSHIPST AND dd.EXPSHIPEND
    AND d.dat_state = 'Approve'
    AND d.dat_Deleted IS NULL
    AND dd.SEASCLOSED = 0
    AND di.ALTMINIMUM > 0
    AND dd.ORDERON = 1

【讨论】:

  • 感谢到目前为止所做的一切。听起来我需要将 LEFT OUTER 连接更改为 OEORDH 到 INNER 连接,并更新 vDatingValidOrdersAlt 视图以仅返回 ORDNUMBER 值。我有估计的执行计划,但是在这里粘贴为 XML 太长了 - 有没有更好的方法来显示它?
  • @SQLLearner 有一个“粘贴计划”的谷歌
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-02
  • 2011-05-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多