sql - 如何创建子项计数的索引 View

标签 sql sql-server tsql common-table-expression indexed-view

我正在尝试获取一个具有父子关系的表并获取子项的数量。我想利用 COUNT_BIG(*) 创建 child 数量的索引 View 。

问题是,在我的索引 View 中,我不想消除没有子项的实体,而是希望这些实体的 Count 为 0。

给定

> Id | Entity | Parent
> -: | :----- | :-----
>  1 | A      | null  
>  2 | AA     | A     
>  3 | AB     | A     
>  4 | ABA    | AB    
>  5 | ABB    | AB    
>  6 | AAA    | AA    
>  7 | AAB    | AA    
>  8 | AAC    | AA    

我想创建一个返回的索引 View

> Entity | Count
> :----- | ----:
> A      |     2
> AA     |     3
> AB     |     2
> ABA    |     0
> ABB    |     0
> AAA    |     0
> AAB    |     0
> AAC    |     0

这是我的 SQL,可以运行,但使用 LEFT JOIN 和 CTE(索引 View 中不允许两者)

    DROP TABLE IF EXISTS Example
    CREATE TABLE Example (
      Id INT primary key,
      Entity varchar(50),
      Parent varchar(50)
    )
    
    
    INSERT INTO Example
    VALUES 
       (1, 'A', NULL)
      ,(2, 'AA',  'A')
      ,(3, 'AB','A')
      ,(4, 'ABA', 'AB')
      ,(5, 'ABB', 'AB')
      ,(6, 'AAA', 'AA')
      ,(7, 'AAB', 'AA')
      ,(8, 'AAC', 'AA')
    
    
    
    SELECT *
    FROM Example
    
    ;WITH CTE AS (
     SELECT Parent, COUNT(*) as Count
      FROM dbo.Example
      GROUP BY Parent
    )
      
    SELECT e.Entity, COALESCE(Count,0) Count
    FROM dbo.Example e
    LEFT JOIN CTE g
    ON e.Entity = g.Parent


GO

最佳答案

我认为您无法使用 CTE 或 LEFT JOIN 来实现这一点,因为有很多 restriction using the indexed views

解决方法

我建议将查询分成两部分:

  1. 创建索引 View 而不是公共(public)表表达式 (CTE)
  2. 创建执行 LEFT JOIN 的非索引 View

除此之外,在表示例中的Entity列上创建一个非聚集索引。

那么当你查询非索引 View 时,它将使用索引

--CREATE TABLE
CREATE TABLE Example (
  Id INT primary key,
  Entity varchar(50),
  Parent varchar(50)
)

--INSERT VALUES
INSERT INTO Example
VALUES 
   (1, 'A', NULL)
  ,(2, 'AA',  'A')
  ,(3, 'AB','A')
  ,(4, 'ABA', 'AB')
  ,(5, 'ABB', 'AB')
  ,(6, 'AAA', 'AA')
  ,(7, 'AAB', 'AA')
  ,(8, 'AAC', 'AA')

--CREATE NON CLUSTERED INDEX
CREATE NONCLUSTERED INDEX idx1 ON dbo.Example(Entity);

--CREATE Indexed View

CREATE VIEW dbo.ExampleView_1
    WITH SCHEMABINDING
    AS 
 SELECT Parent, COUNT_BIG(*) as Count
  FROM dbo.Example
  GROUP BY Parent

CREATE UNIQUE CLUSTERED INDEX idx ON dbo.ExampleView_1(Parent);

--Create non-indexed view
CREATE VIEW dbo.ExampleView_2
    WITH SCHEMABINDING
    AS 
    SELECT e.Entity, COALESCE(Count,0) Count
    FROM dbo.Example e
    LEFT JOIN dbo.ExampleView_1 g
    ON e.Entity = g.Parent

因此,当您执行以下查询时:

SELECT * FROM dbo.ExampleView_2 WHERE Entity = 'A'

可以看到执行计划中使用了 View 聚簇索引和表非聚簇索引:

enter image description here

其他信息

我没有找到其他解决方法来替换索引 View 中 LEFT JOINUNIONCTE 的使用,您可以检查许多类似的 Stackoverflow 问题:

<小时/>

更新 1 - Split View与笛卡尔连接

为了确定更好的方法,我尝试比较两种建议的方法。

--The other approach (cartesian join)
CREATE TABLE TwoRows (
    N INT primary key
)

INSERT INTO TwoRows
VALUES (1),(2)

CREATE VIEW dbo.indexedView  WITH SCHEMABINDING AS
    SELECT 
        IIF(T.N = 2, Entity, Parent) as Entity
        , COUNT_BIG(*) as CountPlusOne
        , COUNT_BIG(ALL IIF(T.N = 2, NULL, 1)) as Count
    FROM dbo.Example E1
    INNER JOIN dbo.TwoRows T
        ON 1=1
    WHERE IIF(T.N = 2, Entity, Parent) IS NOT NULL
    GROUP BY IIF(T.N = 2, Entity, Parent)
GO

CREATE UNIQUE CLUSTERED INDEX testIndex ON indexedView(Entity)

我在单独的数据库上创建了每个索引 View 并执行了以下查询:

SELECT * FROM View WHERE Entity = 'AA'

分割 View

enter image description here

笛卡尔连接

enter image description here

时间统计

时间统计显示,笛卡尔连接方法的执行时间高于分割 View 方法,如下图所示(笛卡尔连接在右侧):

enter image description here

添加WITH(NOEXPAND)

我还尝试在笛卡尔连接方法中添加WITH(NOEXPAND)选项,以强制数据库引擎使用索引 View 聚集索引,结果如下:

enter image description here

我清除了所有缓存并进行了比较,时间统计比较显示 Split View方法仍然比笛卡尔连接方法更快(右侧的WITH(NOEXPAND)方法) :

enter image description here

关于sql - 如何创建子项计数的索引 View ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54684093/

相关文章:

sql - 无法绑定(bind)多部分标识符

c# - 确保所有更新都成功完成?

sql - Postgres 函数创建但不执行

python - 从 pyodbc 调用过程时出错

SQL Server BIGINT 或 DECIMAL(18,0) 主键

sql - MS SQL Server 获取逗号之间的值

sql - 在 SQL Server 上仅显示一个数据库

sql - 更新标识列中的值

sql - 如何从 SQL Server 中的表中获取 50% 的记录?

mysql - 在 MySQL 中使用 EXISTS