0

我有一个 TPC-H 数据库和工作负载,我正在处理以下查询(TPC-H 中的查询 21):

SELECT  TOP 100
    S_NAME, COUNT(*) AS NUMWAIT
FROM    SUPPLIER S
JOIN NATION N ON S.S_NATIONKEY = N.N_NATIONKEY  AND N.N_NAME        = 'JORDAN'
JOIN LINEITEM L1 ON S.S_SUPPKEY = L1.L_SUPPKEY AND L1.L_RECEIPTDATE > L1.L_COMMITDATE
JOIN ORDERS O ON O.O_ORDERKEY = L1.L_ORDERKEY AND O.O_ORDERSTATUS       = 'F'
WHERE EXISTS    (
            SELECT  *
            FROM    LINEITEM L2
            WHERE   L2.L_ORDERKEY   = L1.L_ORDERKEY AND
                L2.L_SUPPKEY    <> L1.L_SUPPKEY 
        )
GROUP   BY  S_NAME
ORDER   BY  NUMWAIT DESC, S_NAME

该查询对原始查询进行了轻微修改,只是为了使问题更清楚。查询的想法是找到让多供应商订单等待的供应商名称。查询标识给定国家的供应商。该查询的主要问题是识别多供应商订单,这基本上是存在条件。

如果我创建以下索引:

CREATE VIEW LINEITEM_VIOLATE1 WITH SCHEMABINDING AS
SELECT L_ORDERKEY, L_LINENUMBER, L_SUPPKEY
FROM dbo.LINEITEM
WHERE L_RECEIPTDATE > L_COMMITDATE
GO

CREATE UNIQUE CLUSTERED INDEX noncluster_idx_lineitem_orderkey1
ON LINEITEM_VIOLATE1(L_ORDERKEY, L_LINENUMBER, L_SUPPKEY)
GO

CREATE NONCLUSTERED INDEX nonclustered_idx_lineitem_suppkey1
ON [dbo].[LINEITEM_VIOLATE1] ([L_SUPPKEY])
INCLUDE ([L_ORDERKEY])
GO

CREATE NONCLUSTERED INDEX change2
ON [dbo].[ORDERS] ([O_ORDERSTATUS])
INCLUDE ([O_ORDERKEY])
GO

CREATE NONCLUSTERED INDEX change3
ON [dbo].[LINEITEM] ([L_ORDERKEY])
INCLUDE ([L_SUPPKEY])
GO

我得到了很好的查询计划,结果如下:

Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'ORDERS'. Scan count 1, logical reads 1719, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'LINEITEM'. Scan count 137252, logical reads 437982, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'LINEITEM_VIOLATE1'. Scan count 362, logical reads 1484, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'SUPPLIER'. Scan count 1, logical reads 205, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'NATION'. Scan count 1, logical reads 1, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 938 ms,  elapsed time = 1134 ms.
SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.

搜索具有多个唯一供应商的订单的任务需要一半以上的时间。它可以在 LINEITEM 的许多逻辑读取中观察到,在查询计划中(自联接估计有 79% 的成本),我还测试了删除导致一半 CPU 时间的存在子句。无法为此子查询创建索引视图,因为它包含自联接。我想在不进行非规范化的情况下实现最大性能。你知道如何进一步提升这个查询的性能吗?感谢您的任何见解。


我只是添加了查询计划的文本表示:

    100 1   SELECT TOP 100   S_NAME,   COUNT(*) AS NUMWAIT  FROM SUPPLIER S  JOIN NATION N ON S.S_NATIONKEY = N.N_NATIONKEY AND N.N_NAME  = 'JORDAN'  JOIN LINEITEM L1 ON S.S_SUPPKEY = L1.L_SUPPKEY AND L1.L_RECEIPTDATE > L1.L_COMMITDATE  JOIN ORDERS O ON O.O_ORDERKEY = L1.L_ORDERKEY AND O.O_ORDERSTATUS  = 'F'  WHERE EXISTS ( SELECT *     FROM LINEITEM L2     WHERE L2.L_ORDERKEY = L1.L_ORDERKEY AND      L2.L_SUPPKEY <> L1.L_SUPPKEY     )  GROUP BY S_NAME  ORDER BY NUMWAIT DESC,    S_NAME    ----- 1   1   0   NULL    NULL    NULL    NULL    100 NULL    NULL    NULL    49,24822    NULL    NULL    SELECT  0   NULL
    100 1     |--Sort(TOP 100, ORDER BY:([Expr1016] DESC, [S].[S_NAME] ASC))    1   2   1   Sort    TopN Sort   TOP 100, ORDER BY:([Expr1016] DESC, [S].[S_NAME] ASC)   NULL    100 0,01126126  0,5933996   36  49,24822    [S].[S_NAME], [Expr1016]    NULL    PLAN_ROW    0   1
    0   0          |--Compute Scalar(DEFINE:([Expr1016]=CONVERT_IMPLICIT(int,[Expr1024],0)))    1   3   2   Compute Scalar  Compute Scalar  DEFINE:([Expr1016]=CONVERT_IMPLICIT(int,[Expr1024],0))  [Expr1016]=CONVERT_IMPLICIT(int,[Expr1024],0)   9931,421    0   0,6267112   36  48,64356    [S].[S_NAME], [Expr1016]    NULL    PLAN_ROW    0   1
    362 1               |--Hash Match(Aggregate, HASH:([S].[S_NAME]), RESIDUAL:([SUPPLIER].[S_NAME] as [S].[S_NAME] = [SUPPLIER].[S_NAME] as [S].[S_NAME]) DEFINE:([Expr1024]=COUNT(*)))    1   4   3   Hash Match  Aggregate   HASH:([S].[S_NAME]), RESIDUAL:([SUPPLIER].[S_NAME] as [S].[S_NAME] = [SUPPLIER].[S_NAME] as [S].[S_NAME])   [Expr1024]=COUNT(*) 9931,421    0   0,6267112   36  48,64356    [S].[S_NAME], [Expr1024]    NULL    PLAN_ROW    0   1
    63631   1                    |--Hash Match(Inner Join, HASH:([L1].[L_ORDERKEY])=([O].[O_ORDERKEY])) 1   5   4   Hash Match  Inner Join  HASH:([L1].[L_ORDERKEY])=([O].[O_ORDERKEY]) NULL    48872,45    0   4,75613 32  48,01685    [S].[S_NAME]    NULL    PLAN_ROW    0   1
    132283  1                         |--Nested Loops(Left Semi Join, OUTER REFERENCES:([L1].[L_ORDERKEY], [L1].[L_SUPPKEY], [Expr1023]) OPTIMIZED WITH UNORDERED PREFETCH) 1   6   5   Nested Loops    Left Semi Join  OUTER REFERENCES:([L1].[L_ORDERKEY], [L1].[L_SUPPKEY], [Expr1023]) OPTIMIZED WITH UNORDERED PREFETCH    NULL    64854   0   0,3012108   36  41,18718    [S].[S_NAME], [L1].[L_ORDERKEY] NULL    PLAN_ROW    0   1
    137252  1                         |    |--Nested Loops(Inner Join, OUTER REFERENCES:([S].[S_SUPPKEY], [Expr1022]) OPTIMIZED WITH UNORDERED PREFETCH)    1   10  6   Nested Loops    Inner Join  OUTER REFERENCES:([S].[S_SUPPKEY], [Expr1022]) OPTIMIZED WITH UNORDERED PREFETCH    NULL    72060   0   0,3012108   40  1,893772    [S].[S_NAME], [LINEITEM_VIOLATE1].[L_ORDERKEY], [LINEITEM_VIOLATE1].[L_SUPPKEY] NULL    PLAN_ROW    0   1
    362 1                         |    |    |--Hash Match(Inner Join, HASH:([N].[N_NATIONKEY])=([S].[S_NATIONKEY])) 1   13  10  Hash Match  Inner Join  HASH:([N].[N_NATIONKEY])=([S].[S_NATIONKEY])    NULL    400 0   0,06381615  36  0,2325338   [S].[S_SUPPKEY], [S].[S_NAME]   NULL    PLAN_ROW    0   1
    1   1                         |    |    |    |--Table Scan(OBJECT:([NATION] AS [N]), WHERE:([NATION].[N_NAME] as [N].[N_NAME]='JORDAN'))    1   14  13  Table Scan  Table Scan  OBJECT:([NATION] AS [N]), WHERE:([NATION].[N_NAME] as [N].[N_NAME]='JORDAN')    [N].[N_NATIONKEY]   1   0,003125    0,0001845   36  0,0033095   [N].[N_NATIONKEY]   NULL    PLAN_ROW    0   1
    10000   1                         |    |    |    |--Table Scan(OBJECT:([SUPPLIER] AS [S]))  1   15  13  Table Scan  Table Scan  OBJECT:([SUPPLIER] AS [S])  [S].[S_SUPPKEY], [S].[S_NAME], [S].[S_NATIONKEY]    10000   0,1542361   0,011157    40  0,1653931   [S].[S_SUPPKEY], [S].[S_NAME], [S].[S_NATIONKEY]    NULL    PLAN_ROW    0   1
    137252  362                       |    |    |--Index Seek(OBJECT:([LINEITEM_VIOLATE1].[nonclustered_idx_lineitem_suppkey1]), SEEK:([LINEITEM_VIOLATE1].[L_SUPPKEY]=[SUPPLIER].[S_SUPPKEY] as [S].[S_SUPPKEY]) ORDERED FORWARD)  1   17  10  Index Seek  Index Seek  OBJECT:([LINEITEM_VIOLATE1].[nonclustered_idx_lineitem_suppkey1]), SEEK:([LINEITEM_VIOLATE1].[L_SUPPKEY]=[SUPPLIER].[S_SUPPKEY] as [S].[S_SUPPKEY]) ORDERED FORWARD [LINEITEM_VIOLATE1].[L_ORDERKEY], [LINEITEM_VIOLATE1].[L_SUPPKEY]   180,15  0,003125    0,000355165 15  1,360028    [LINEITEM_VIOLATE1].[L_ORDERKEY], [LINEITEM_VIOLATE1].[L_SUPPKEY]   NULL    PLAN_ROW    0   400
    132283  137252                        |    |--Index Seek(OBJECT:([LINEITEM].[change3] AS [L2]), SEEK:([L2].[L_ORDERKEY]=[LINEITEM].[L_ORDERKEY] as [L1].[L_ORDERKEY]),  WHERE:([LINEITEM].[L_SUPPKEY] as [L2].[L_SUPPKEY]<>[LINEITEM].[L_SUPPKEY] as [L1].[L_SUPPKEY]) ORDERED FORWARD) 1   23  6   Index Seek  Index Seek  OBJECT:([LINEITEM].[change3] AS [L2]), SEEK:([L2].[L_ORDERKEY]=[LINEITEM].[L_ORDERKEY] as [L1].[L_ORDERKEY]),  WHERE:([LINEITEM].[L_SUPPKEY] as [L2].[L_SUPPKEY]<>[LINEITEM].[L_SUPPKEY] as [L1].[L_SUPPKEY]) ORDERED FORWARD   NULL    1   0,003125    0,0001614009    11  38,95377    NULL    NULL    PLAN_ROW    0   72060
    729413  1                         |--Index Seek(OBJECT:([ORDERS].[change2] AS [O]), SEEK:([O].[O_ORDERSTATUS]='F') ORDERED FORWARD) 1   24  5   Index Seek  Index Seek  OBJECT:([ORDERS].[change2] AS [O]), SEEK:([O].[O_ORDERSTATUS]='F') ORDERED FORWARD  [O].[O_ORDERKEY]    729413  1,271023    0,8025113   11  2,073534    [O].[O_ORDERKEY]    NULL    PLAN_ROW    0   1
4

1 回答 1

0

您是否考虑过尝试以具有索引支持的具有子句的派生表为基础,而不是存在自引用?

FROM
(
SELECT
L_ORDERKEY
FROM
LINEITEM
GROUP BY
L_ORDERKEY
HAVING COUNT(DISTINCT L_SUPPKEY) > 1
)

CREATE NONCLUSTERED INDEX IX_LINEITEM_Mult_SUPPKEY
ON [dbo].[LINEITEM] ([L_ORDERKEY],[L_SUPPKEY])

假设一堆未说明的索引,您当前的查询性能将基于状态 F 中订单的选择性,或者基于匹配国家键中供应商的选择性。但是,如果您的多供应商订单很少,那么像这样扭转它可能会有所帮助。

我必须查看所有这些表的行数和选择性,然后确保所有需要的索引都到位。

于 2014-05-20T02:49:30.627 回答