个人指数与覆盖指数

时间:2018-07-17 03:11:45

标签: sql sql-server

我的任务是改善完全非规范化的表的性能。

它有30列,但是为了简单起见,我的示例有4列。

CREATE TABLE (
   Id UNIQUEIDENTIFIER NOT NULL,
   Location1 NVARCHAR(MAX) NULL,
   Location2 NVARCHAR(MAX) NULL,
   Location3 NVARCHAR(MAX) NULL,
   ...
   PersonId UNIQUEIDENTIFIER NULL
)

这包含位置的层次结构,并且在层次结构的末尾是分配给该位置的人员。

示例数据为:

  
      
  1. 建筑物A,NULL,NULL,NULL,NULL
  2.   
  3. A楼底层NULL,NULL,NULL
  4.   
  5. 第1楼底层A楼,NULL,NULL
  6.   
  7. 第1部分第1室第一层A楼,空
  8.   
  9. 'Craig'1号厅1楼A楼
  10.   
  11. “约翰”第1部分1楼A楼
  12.   
  13. 第2楼底层A楼,NULL,NULL
  14.   
  15. 第1部分第2楼第一层A楼,空
  16.   
  17. “彼得”一室2楼A楼
  18.   

因此,在这种情况下,我们有2个房间及其等级。第1部分的1号房间有2个人,第2部分的1号房间只有一个人。

令人印象深刻的桌子设计,我知道。

我要求我们放弃NVARCHAR(MAX)。这已更改为VARCHAR(80)。现在,这使我可以使用索引。我的问题是关于要使用的索引类型。

要找到所选行的父级,我需要这样做:

SELECT *
FROM MyTable
WHERE ISNULL(Location1,'') = ISNULL(MyLocation1,'') AND
      ISNULL(Location2,'') = ISNULL(MyLocation2,'') AND    
      ISNULL(Location3,'') = ISNULL(MyLocation3,'')

绝大多数查询都遵循这种模式。在“位置X”列中。

我不确定是否应该为每列创建一个索引。...,还是应该创建一个覆盖所有列的索引...,或者应为每列一个索引,包括其余列。

所以

  • Location1上的索引,Location2上的索引..等

OR

  • 位置1,位置2 ...上的索引...

OR

  • 位置1上的索引包括(位置2,位置3 ...)上的索引 位置2包含(位置1,位置3 ...)

我不确定要获得最佳效果的方向。

2 个答案:

答案 0 :(得分:0)

您应该在(location1, location2, location3)上创建索引-覆盖索引。

但是,您对NULL值有疑问。这些将阻碍索引的使用。为防止这种情况,请将默认值从NULL更改为''

CREATE TABLE t (
   Id UNIQUEIDENTIFIER NOT NULL,
   Location1 NVARCHAR(80) NOT NULL DEFAULT '',
   Location2 NVARCHAR(80) NOT NULL DEFAULT '',
   Location3 NVARCHAR(80) NOT NULL DEFAULT '',
   . . . 
)

答案 1 :(得分:0)

在所有相关列(location1, location2, location3)上应该有一个索引,但是此处的NULL值存在问题。

SELECT *
FROM MyTable
WHERE 
    ISNULL(Location1,'') = ISNULL(MyLocation1,'') AND
    ISNULL(Location2,'') = ISNULL(MyLocation2,'') AND    
    ISNULL(Location3,'') = ISNULL(MyLocation3,'')

在列值上使用ISNULL函数(或几乎所有函数)时,通常无法使用索引。您可以通过查看执行计划来确认。

如果您编写这样的查询:

SELECT *
FROM MyTable
WHERE 
    Location1 = ISNULL(MyLocation1,'') AND
    Location2 = ISNULL(MyLocation2,'') AND    
    Location3 = ISNULL(MyLocation3,'')

将使用索引,但查询不会产生正确的结果。


看看如何编写查询以及如何使用ISNULL,您似乎可以确定可以安全地使用''值代替NULL。理想情况下,应将所有这些列设为NON-NULL,并将所有NULL的值替换为''

由于您无法执行此操作,因此处理该问题的一种方法是为每个LocationN列创建计算的持久列,然后在这些计算的列上创建索引并在查询中使用这些计算的列:

CREATE TABLE dbo.MyTable
(
    ID int NOT NULL IDENTITY (1, 1),
    Location1 varchar(80) NULL,
    Location2 varchar(80) NULL,
    Location3 varchar(80) NULL,
    Location1_  AS ISNULL(Location1, '') PERSISTED,
    Location2_  AS ISNULL(Location2, '') PERSISTED,
    Location3_  AS ISNULL(Location3, '') PERSISTED 
)

创建索引

CREATE NONCLUSTERED INDEX [IX] ON [dbo].[MyTable]
(
    [Location1_] ASC,
    [Location2_] ASC,
    [Location3_] ASC
)

查询

SELECT *
FROM MyTable
WHERE 
    Location1_ = ISNULL(MyLocation1,'') AND
    Location2_ = ISNULL(MyLocation2,'') AND    
    Location3_ = ISNULL(MyLocation3,'')

此主题的另一个变体是创建单个计算列,并将所有字符串与某些定界符合并在一起,并在该单个列上创建索引。 可能会更有效。

ALTER TABLE dbo.MyTable ADD
LocationAll  AS 
    isnull([Location1],'') + '|' + 
    isnull([Location2],'') + '|' +
    isnull([Location3],'') + '|' PERSISTED 


CREATE NONCLUSTERED INDEX [IX_all] ON [dbo].[MyTable]
(
    [LocationAll] ASC
)

查询如下:

SELECT *
FROM MyTable
WHERE 
    LocationAll = 
        ISNULL(MyLocation1,'') + '|'
        ISNULL(MyLocation2,'') + '|'
        ISNULL(MyLocation3,'') + '|'

OR

SELECT *
FROM MyTable
WHERE 
    LocationAll LIKE
        ISNULL(MyLocation1,'') + '|'
        ISNULL(MyLocation2,'') + '|'
        ISNULL(MyLocation3,'') + '|' + '%'