2

尝试创建对此设置最有效的索引:

查看(RANKED_PHONE):

SELECT PHONE_ID,
       CONTACT_ID,
       TYPE_CD,
       ROW_NUMBER() OVER( PARTITION BY CONTACT_ID, TYPE_CD 
                              ORDER BY UPDATE_DT DESC ) AS PHONE_RANK
FROM   (SELECT PHONE_ID,
               CONTACT_ID,
               TYPE_CD,
               UPDATE_DT
        FROM   contact_phone
        WHERE  PHONE_ID IN (SELECT MAX(PHONE_ID)
                            FROM   contact_phone WITH(NOLOCK)
                            GROUP  BY CONTACT_ID,
                                      TYPE_CD,
                                      PHONE_NUMBER)) nodupes 

当前指数:

CREATE CLUSTERED INDEX [CIX_contactphone_PHONEID]
  ON [contact_phone] ([PHONE_ID])
GO

CREATE NONCLUSTERED INDEX [NIX_contactphone_UPDATEDT]
  ON [contact_phone] ([UPDATE_DT])
GO

CREATE NONCLUSTERED INDEX [NIX_contactphone_CONTACTID_TYPECD_PHONENUMBER]
  ON [contact_phone] ([CONTACT_ID], [TYPE_CD], [PHONE_NUMBER])
GO 

其他注意事项:

  • 有一个 SSIS 包在 PHONE_ID 上运行查找以检查是否插入或更新
  • 通过查找过滤掉已使用的 PHONE_ID 的特定 CONTACT_ID,将在选择中进一步过滤此视图。

编辑:我不妨包括我正在做的其余部分,这样你就可以看到最终图片:

SELECT @phone1 = COALESCE(CELL1, PERS1, BUS1, OTHER1, FAX1)
FROM(SELECT CELL1 = MAX(CASE WHEN PHONE_RANK = 1 THEN CELL END)
        , PERS1 = MAX(CASE WHEN PHONE_RANK = 1 THEN PERS END)
        , BUS1 = MAX(CASE WHEN PHONE_RANK = 1 THEN BUS END)
        , OTHER1 = MAX(CASE WHEN PHONE_RANK = 1 THEN OTHER END)
        , FAX1 = MAX(CASE WHEN PHONE_RANK = 1 THEN FAX END)
    FROM(
        SELECT * FROM [RANKED_PHONE]
        WHERE CONTACT_ID = @key
    ) phone
    PIVOT(MAX(PHONE_ID) 
        FOR TYPE_CD IN ([CELL],[PERS],[BUS],[OTHER],[FAX])
    ) as pvt) phones

选择@phone1 后,将“AND PHONE_ID NOT IN (@phone1)”添加到从 RANKED_PHONE 中选择的 where 子句中,然后添加 phone2.. 等等。此查询的目的是填充一组电话号码字段根据业务规则按一定的选择顺序。我想放入一个视图以尽可能多地缓存它。因为我需要过滤掉已经使用过的电话号码,所以我无法将透视放入视图中,因为它们需要在被透视之前进行过滤。

* 主要编辑 *

这里发生了一些变化:

  • 因为我在后续查询中过滤掉了使用过的电话号码,所以我想我可以完全去掉重复数据删除部分。
  • 我随机发现在某些情况下,UPDATE_DATE 完全相同,因此我不得不在 ORDER BY 中为分区添加另一个字段。

此外,这里有一些关于进一步选择语句的更多见解,这可以说明为什么这不是一个容易放入 TVF 的查询。合并列中的差异对应于 select 语句中的不同输出列(参见 select 语句的第一个编辑示例):

  1. SELECT @phone1 = COALESCE(CELL1, PERS1, BUS1, OTHER1, FAX1)
    ...
    WHERE CONTACT_ID = @key
    
  2. SELECT @phone2 = COALESCE(CELL2, PERS1, PERS2, BUS1, BUS2, OTHER1, OTHER2, FAX1, FAX2)
    ... 
    WHERE CONTACT_ID = @key
        AND PHONE_NUMBER NOT IN (SELECT PHONE_NUMBER FROM contact_phone WITH(NOLOCK)
            WHERE PHONE_ID IN (@phone1))
    
  3. SELECT @phone3 = COALESCE(CELL3, PERS1, PERS2, PERS3, BUS1, BUS2, BUS3, OTHER1, OTHER2, OTHER3, FAX1, FAX2, FAX3)
    ...
    WHERE CONTACT_ID = @key
        AND PHONE_NUMBER NOT IN (SELECT PHONE_NUMBER FROM contact_phone WITH(NOLOCK)
            WHERE PHONE_ID IN (@phone1,@phone2))
    

查看 [RANKED_PHONE]:

SELECT PHONE_ID
    , CONTACT_KEY
    , TYPE_CD
    , PHONE_NUMBER
    , ROW_NUMBER() OVER(
        PARTITION BY CONTACT_KEY,TYPE_CD
        ORDER BY UPDATE_DATE DESC, [PRIMARY] DESC
    ) PHONE_RANK
FROM contact_phone WITH(NOLOCK)

指数:

CONSTRAINT [PK_contactphone_PHONEID] PRIMARY KEY ([PHONE_ID])

CREATE NONCLUSTERED INDEX [NIX_contactphone_UPDATEDATE_PRIMARY] ON [contact_phone] ([UPDATE_DATE],[PRIMARY])
GO

CREATE NONCLUSTERED INDEX [NIX_contactphone_CONTACTKEY_TYPECD_PHONENUMBER] ON [contact_phone] ([CONTACT_KEY],[TYPE_CD],[PHONE_NUMBER])
GO
4

2 回答 2

2

如果你改变定义

CREATE NONCLUSTERED INDEX [NIX_contactphone_CONTACTID_TYPECD_PHONENUMBER]
  ON [contact_phone] ([CONTACT_ID], [TYPE_CD], [PHONE_NUMBER])

CREATE NONCLUSTERED INDEX [NIX_contactphone_CONTACTID_TYPECD_PHONENUMBER]
  ON [contact_phone] ([CONTACT_ID], [TYPE_CD], [PHONE_NUMBER], PHONE_ID DESC) 
  INCLUDE (UPDATE_DT)

(注意:虽然这似乎包含一个额外的键列,但实际上并非如此。聚集索引键无论如何都会添加到所有非唯一非聚集索引的键中。但是,显式添加它允许定义ASC/ DESC。)

该索引确实有一个额外的INCLUDE-d 列来覆盖查询。

并将查询更改为

SELECT PHONE_ID,
       CONTACT_ID,
       TYPE_CD,
       ROW_NUMBER() OVER( PARTITION BY CONTACT_ID, TYPE_CD 
                              ORDER BY UPDATE_DT DESC ) AS PHONE_RANK
FROM   (SELECT PHONE_ID,
               CONTACT_ID,
               TYPE_CD,
               UPDATE_DT,
               RANK() OVER (PARTITION BY CONTACT_ID, TYPE_CD, PHONE_NUMBER 
                                        ORDER BY PHONE_ID DESC) AS PHONE_ID_RANK
        FROM   contact_phone
        ) nodupes 
        WHERE PHONE_ID_RANK = 1

这稍微改进了计划。

Plan

The ordering used by the first partition of CONTACT_ID, TYPE_CD, PHONE_NUMBER, PHONE_ID DESC does not match that desired by the second part of the query CONTACT_ID, TYPE_CD,UPDATE_DT though so you will always get a sort.

于 2013-06-22T11:51:46.627 回答
0

@Phrozt - your original indexes look fine as is except I would go ahead and make your clustered index unique.

There's a problem with your view definition though that leads to poor performance. The innermost subquery is only connected to the outer query by PHONE_ID ("WHERE PHONE_ID IN..."). Because of that the query optimizer does not know that you are actually deduping the numbers for that one CONTACT_ID, and ends up deduping the entire table.

I suggest changing the view to something like this:

SELECT PHONE_ID,
       CONTACT_ID,
       TYPE_CD,
       ROW_NUMBER() OVER( PARTITION BY CONTACT_ID, TYPE_CD 
                              ORDER BY UPDATE_DT DESC ) AS PHONE_RANK
FROM   (SELECT p.PHONE_ID,
               p.CONTACT_ID,
               p.TYPE_CD,
               p.UPDATE_DT
        FROM   contact_phone p
        INNER JOIN (SELECT CONTACT_ID, TYPE_CD, MAX(PHONE_ID) AS PHONE_ID
                    FROM   contact_phone WITH(NOLOCK)
                    GROUP  BY CONTACT_ID,
                              TYPE_CD,
                              PHONE_NUMBER) t
            ON p.CONTACT_ID = t.CONTACT_ID AND p.TYPE_CD = t.TYPE_CD AND p.PHONE_ID = t.PHONE_ID
) nodupes 

Also, as a side note, I doubt that using the view vs. just including the entire thing in the final query will make any noticeable difference performance-wise.

于 2013-06-23T03:18:23.563 回答