我想拿一张与父母子女关系的桌子来获取孩子的数量。我想利用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
答案 0 :(得分:3)
我认为您既不能使用CTE也不能使用LEFT JOIN来实现这一点,因为有很多restriction using the indexed views。
我建议将查询分为两部分:
除此之外,在表Entity
的{{1}}列上创建一个非聚集索引。
然后,当您查询非索引视图时,它将使用索引
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
或UNION
,您可以检查许多类似的Stackoverflow问题:
为了确定更好的方法,我试图比较两种建议的方法。
CTE
我在单独的数据库上创建了每个索引视图,并执行以下查询:
--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)
拆分视图
笛卡尔加入
时间统计
时间统计数据显示笛卡尔联接方法的执行时间比拆分视图方法高,如下图所示(笛卡尔联接在右侧):
添加WITH(NOEXPAND)
我还尝试在笛卡尔联接方法中添加SELECT * FROM View WHERE Entity = 'AA'
选项,以强制数据库引擎使用索引视图聚集索引,结果如下:
我清除了所有缓存并进行了比较,时间统计数据比较显示,拆分视图方法仍然比笛卡尔联接方法((右边的WITH(NOEXPAND)
方法)快:< / p>
答案 1 :(得分:2)
通过对行进行笛卡尔联接(计数为0(N = 2)),我可以完成我想做的事情。
创建称为两行的表,该表将复制孙子孙
DROP TABLE IF EXISTS TwoRows
CREATE TABLE TwoRows (
N INT primary key
)
INSERT INTO TwoRows
VALUES (1),(2)
获取原始表格
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')
创建索引视图
DROP VIEW IF EXISTS dbo.indexedView
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 indexedView
我无法避免使用COUNT_BIG(*)
答案 2 :(得分:0)
您可以在AFTER INSERT,UPDATE, DELETE
表上创建example
trigger,并创建一个新表来实现结果。
在触发器中,您可以使用任何语句。您可以通过两种方式执行此操作,具体取决于查询初始查询的速度。
例如,您可以截断每个INSERT/UPDATE/DELETE
上的表,然后计算计数并再次插入(如果查询很快)。
或者您可以依赖inserted
和deleted
表,它们是在触发器上下文中可见的特殊表,并显示行值如何变化。
例如,如果一条记录存在于inserted
表中而不存在于deleted
中-这是一个新行。您只能为它们计算COUNT
。
如果记录仅存在于deleted
表中-这是一个删除操作(我们需要删除预先计算的表中的行)。
两个表中都存在一行-这是一个更新-我们需要对该记录执行新的计数。
一件事在这里非常重要-不要一一操作行。对于以上三种情况,请始终按批处理行,否则最终将导致触发器性能不佳,这将延迟原始表的CRUD操作。