sql - 如何创建儿童计数的索引视图
问题描述
我正在尝试使用具有父子关系的表并获取孩子的数量。我想通过利用创建一个孩子数量的索引视图COUNT_BIG(*)
。
问题是,在我的索引视图中,我不想消除没有孩子的实体,而是希望这些实体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
我想创建一个返回的索引视图
> Entity | Count
> :----- | ----:
> A | 2
> AA | 3
> AB | 2
> ABA | 0
> ABB | 0
> AAA | 0
> AAB | 0
> AAC | 0
这是我的 SQL,但使用了 LEFT JOIN 和 CTE(索引视图中都不允许使用)
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 来实现这一点,因为使用索引视图有很多限制。
解决方法
我建议将查询分为两部分:
- 创建索引视图而不是公用表表达式 (CTE)
- 创建一个执行 LEFT JOIN 的非索引视图
Entity
除此之外,在 Table 的列上创建一个非聚集索引Example
。
然后当您查询非索引视图时,它将使用索引
--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'
可以看到执行计划中使用了视图聚集索引和表非聚集索引:
附加信息
我没有找到其他解决方法来替换索引视图中的LEFT JOIN
orUNION
或 or ,您可以查看许多类似的 Stackoverflow 问题:CTE
更新 1 - 拆分视图与笛卡尔连接
为了确定更好的方法,我尝试比较两种建议的方法。
--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)
我在单独的数据库上创建了每个索引视图并执行了以下查询:
SELECT * FROM View WHERE Entity = 'AA'
拆分视图
笛卡尔连接
时间统计
时间统计显示,笛卡尔连接方式的执行时间高于拆分视图方式,如下图所示(笛卡尔连接在右边):
添加 WITH(NOEXPAND)
我还尝试添加WITH(NOEXPAND)
笛卡尔连接方法选项,以强制数据库引擎使用索引视图聚集索引,结果如下:
我清除了所有缓存并进行了比较,时间统计比较表明,拆分视图方法仍然比笛卡尔连接方法快(WITH(NOEXPAND)
右边的方法):
推荐阅读
- typescript - 如何在 TypeScript 中编写类型安全的断言函数?
- version-control - 忽略 repo 中的文件夹 gitignore
- autosys - 如何将 autosys 作业开箱即用?
- c - 结构中动态数组的内存损坏?
- node.js - Redis 的 EVAL atomic wrt 是连接还是整个数据存储?
- regex - 匹配不在括号内的浮点数
- ssis - SSIS 数据源 - 忽略随机格式错误的行
- javascript - 在 React 中 useEffect 在值从其他组件更改后不会更新
- r - R:根据两个条件创建一个变量
- c# - c#错误cs0117'System.Diagnostics.Debug'不包含'Log'的定义