我正在尝试获取一个具有父子关系的表并获取子项的数量。我想利用 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 。
解决方法
我建议将查询分成两部分:
- 创建索引 View 而不是公共(public)表表达式 (CTE)
- 创建执行 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 聚簇索引和表非聚簇索引:
其他信息
我没有找到其他解决方法来替换索引 View 中 LEFT JOIN
或 UNION
或 CTE
的使用,您可以检查许多类似的 Stackoverflow 问题:
- Indexing views with a CTE
- What to replace left join in a view so i can have an indexed view?
- Create an index on SQL view with UNION operators? Will it really improve performance?
更新 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
笛卡尔连接
时间统计
时间统计显示,笛卡尔连接方法的执行时间高于分割 View 方法,如下图所示(笛卡尔连接在右侧):
添加WITH(NOEXPAND)
我还尝试在笛卡尔连接方法中添加WITH(NOEXPAND)
选项,以强制数据库引擎使用索引 View 聚集索引,结果如下:
我清除了所有缓存并进行了比较,时间统计比较显示 Split View方法仍然比笛卡尔连接方法更快(右侧的WITH(NOEXPAND)
方法) :
关于sql - 如何创建子项计数的索引 View ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54684093/