超级简单查询需要39秒才能执行

时间:2013-04-04 11:02:32

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

我的数据库有大约150K DocumentNames条记录,而我所做的只是与NameTypes的简单联接。 NameTypeID是DocumentNames中的外键。

这是我的疑问:

With cte as 
(
    Select ROW_NUMBER() OVER 
    (Order By nm.Name asc ) 
    peta_rn,    
    dn.DocumentNameID,
    dn.DocumentID  
    From DocumentNames dn

    Left Join NameTypes nm On dn.NameTypeID = nm.NameTypeID
) 
Select * from cte Where peta_rn >= 10000 And peta_rn <= 10050

这是截图:

enter image description here

排序需要90%的成本。我完全感到困惑,此时我该怎么做。我想敲我的头,但我不能,因为周围还有其他人。请建议我该怎么做?

3 个答案:

答案 0 :(得分:3)

如果由于某些任意原因(如求职面试),您需要优化此类查询,请尝试UNION

[已更正以识别别名列]

With cte as 
(
  Select ROW_NUMBER() OVER 
  (Order By nm.Name asc ) AS
  peta_rn,    
  dn.DocumentNameID,
  dn.DocumentID  
  From DocumentNames dn

  INNER Join NameTypes nm On dn.NameTypeID = nm.NameTypeID /* note change */
),  
cte2 as 
(
  Select ROW_NUMBER() OVER 
  () AS /* yes, this is random */
  peta_rn,    
  dn.DocumentNameID,
  dn.DocumentID  
  From DocumentNames dn

  WHERE dn.NameTypeID  NOT IN SELECT (nm.NameTypeID FROM NameTypes nm)
) 

Select * from cte Where peta_rn >= 10000 And peta_rn <= 10050

UNION

Select * from cte2 Where peta_rn >= 10000 And peta_rn <= 10050

我从未对CTE进行UNION,因此您可能需要一些额外的括号才能使其合法化。整个结果也是ORDER BY。我会把它留作练习。

关键是INNER JOIN将能够使用nm.Name上的索引,而第二个子句将能够执行索引的反半连接。两个索引查询应该比一个未编制索引的查询快得多。

答案 1 :(得分:2)

对于它的价值,这里是some SQLFiddle来测试任何语法。

到目前为止,最简单的方法是确保以NameTypes顺序插入Name,因此NameTypeID按字母顺序分配。

在这种情况下,无需加入NameTypes表。你可以这样做,

WITH [CTE] AS
(
SELECT
            ROW_NUMBER() OVER (ORDER BY [NameTypeID] ASC) [PetaRN],    
            [DocumentNameID],
            [DocumentID]
    FROM
            [DocumentNames]
) 
SELECT
            [PetaRN],
            [DocumentNameID],
            [DocumentID]
    FROM
            [CTE]
    WHERE
            [PetaRN] BETWEEN 10000 AND 10050
    ORDER BY
            [PetaRN] ASC;

怎么样

WITH [CTE] AS
(
SELECT
            ROW_NUMBER() OVER (ORDER BY [NameTypeID] ASC) [PetaRN],    
            [DocumentNameID],
            [DocumentID]
    FROM
            [DocumentNames]
) 
SELECT TOP 50
            [PetaRN],
            [DocumentNameID],
            [DocumentID]
    FROM
            [CTE]
    WHERE
            [PetaRN] >= 10000
    ORDER BY
            [PetaRN] ASC;

在SQL 2005上测试大型数据集时,我注意到CTE对大型结果集的效果不佳,这可能与服务器上的资源可用性有关。反直觉使用临时表可能会更快。这也允许您索引行号,允许快速页面选择,但这必须抵消插入成本。试试吧,看看。

CREATE TABLE #Peta
(
    [PetaRN] BigInt NOT NULL CONSTRAINT [PK_Peta] PRIMARY KEY CLUSTERED,
    [DocumentNameID] Int NOT NULL,
    [DocumentID] Int NOT NULL
);

INSERT #Peta
SELECT
            ROW_NUMBER() OVER (ORDER BY [NameTypeID] ASC) [PetaRN],    
            [DocumentNameID],
            [DocumentID]
    FROM
            [DocumentNames];

SELECT TOP 50
            [PetaRN],
            [DocumentNameID],
            [DocumentID]
    FROM
            #Peta
    WHERE
            [PetaRN] >= 10000
    ORDER BY
            [PetaRN] ASC;


DROP TABLE #Peta;

答案 2 :(得分:1)

你可以尝试一些事情:

  1. 使聚集索引以Name开头...使用宽聚簇索引效率不高但是给它一个去看它是否会停止排序

  2. 永久创建一个包含订单号的列。这取决于您的表的更新频率。

  3. 请发布您的表格和索引定义。