替代联合和子查询来创建索引视图
Posted
技术标签:
【中文标题】替代联合和子查询来创建索引视图【英文标题】:Alternative to replace Union and subqueries to create Indexed view 【发布时间】:2014-09-05 18:45:23 【问题描述】:当我在 SQL Server 上运行此查询以创建索引视图时,删除子查询和联合出现错误
CREATE VIEW [dbo].[view_ToolGroup]
WITH SCHEMABINDING
AS
SELECT
toolGroup.ToolGroupId,toolGroupToTool.ToolId, toolGroupApp.AppId as TGAppId,
purposeToToolGroup.PurposeId as TGPurposeId, TGRole.RoleId as TGRoleId
FROM
[dbo].[toolGroup], [dbo].[purposeToTG], [dbo].[toolGroupToTool],
[dbo].[toolGroupToeApp] as toolGroupApp,
[dbo].[toolGroupToeAppToeRole] as toolGroupAppRole,
[dbo].[eRole] as TGRole
WHERE
toolGroup.ToolGroupId = purposeToToolGroup.ToolGroupId
and toolGroup.ToolGroupId = toolGroupToTool.ToolGroupId
and toolGroup.ToolGroupId = toolGroupApp.ToolGroupId
and toolGroupApp.toolGroupToeApplicationID=toolGroupAppRole.toolGroupToeApplicationID
and toolGroupAppRole.ToolgroupToeApplicationID in
(select ToolgroupToeApplicationID
from [dbo].[ToolgroupToeApplication])
and toolGroupAppRole.RoleId = TGRole.RoleId
UNION
SELECT
toolGroup.ToolGroupId, toolGroup.ToolGroupName,
null, null, null, null, null, null, null, null
FROM
[dbo].[toolGroup], [dbo].[toolGroupToeApplication]
WHERE
toolGroup.ToolGroupId = toolGroupToeApplication.ToolGroupId
and toolGroup.ToolGroupId not in
(select PurposeToToolGroup.ToolGroupId from [dbo].[PurposeToToolGroup])
and toolGroup.ToolGroupId in (select distinct ToolGroupId
from [dbo]. [toolGroupToeApplication] )'
GO
CREATE UNIQUE CLUSTERED INDEX IDX_view_ToolGroup
ON view_ToolGroup(ToolGroupId, ToolId, TGPurposeId, TGRoleId)
GO
有人可以提出替代解决方案来替换 UNION
和子查询吗?
【问题讨论】:
没有别无选择 - 如果您想创建索引视图,您必须删除UNION
和子查询。索引视图的要求非常苛刻——这是真的;要么你能做到——要么你不能创建索引视图。
Bad habits to kick : using old-style JOINs - 由于 ANSI-92 SQL 标准(多于 20 年前),不应使用
我使用了旧式逗号分隔的表列表....但它会导致 ToolGroupId、ToolId、TGPurposeId、TGRoleId 字段上的重复数据,并且它不允许在查询中使用 DISTINCT
如果您的查询是合理的(由于我不知道这些表代表什么,我现在不会尝试判断)并且它包含UNION
,那么很有可能您的查询的任何替代形式也将包括 UNION
或其他一些在索引视图中也不允许的构造。允许的规则不是任意的——它们是为了确保服务器可以根据对基表的更新生成健全的代码来维护视图,而无需重新扫描整个表。
正如@Damien_The_Unbeliever 所说,在索引视图中不允许使用这些构造是有原因的。如果您无法让此查询以任何其他方式执行,那么您最好的选择可能是将其物理化。
【参考方案1】:
根据上述所有建议,没有直接的方法可以做到这一点。但是我们可以作弊。您可以执行以下操作
-
将语句分成两个视图
为每个视图添加索引
将 IN 替换为 INNER JOIN
调用视图时用户不在
正如 Damien 所建议的,我现在尝试建议或尝试判断逻辑是愚蠢的,因为我们不知道表格代表什么。但是,我已经根据上面的代码重构了代码,您可以将其用作模板来构建您的实际查询。
希望对你有帮助
--Drop Index If Already Aresent
IF EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N'[dbo].[vw_ToolGroup_One]'))
DROP VIEW dbo.vw_ToolGroup_One
GO
IF EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N'[dbo].[vw_ToolGroup_Two]'))
DROP VIEW dbo.vw_ToolGroup_Two
GO
--Drop Dependant Tables
IF OBJECT_ID(N'toolGroup_tmp')>0
BEGIN
DROP TABLE toolGroup_tmp
END
CREATE TABLE toolGroup_tmp (ToolGroupId INT,ToolGroupName VARCHAR(100))
INSERT INTO toolGroup_tmp
SELECT 1,'ONE'
GO
IF OBJECT_ID(N'purposeToTG_tmp')>0
BEGIN
DROP TABLE purposeToTG_tmp
END
CREATE TABLE purposeToTG_tmp (ToolGroupId INT,PurposeId int)
INSERT INTO purposeToTG_tmp
SELECT 1,1
GO
IF OBJECT_ID(N'toolGroupToTool_tmp')>0
BEGIN
DROP TABLE toolGroupToTool_tmp
END
CREATE TABLE toolGroupToTool_tmp (ToolGroupId INT,ToolId INT)
INSERT INTO toolGroupToTool_tmp
SELECT 1,1
GO
IF OBJECT_ID(N'toolGroupToeApp_tmp')>0
BEGIN
DROP TABLE toolGroupToeApp_tmp
END
CREATE TABLE toolGroupToeApp_tmp (ToolGroupId INT,AppId INT,toolGroupToeApplicationID INT)
INSERT INTO toolGroupToeApp_tmp
SELECT 1,1,1
GO
IF OBJECT_ID(N'toolGroupToeAppToeRole_tmp')>0
BEGIN
DROP TABLE toolGroupToeAppToeRole_tmp
END
CREATE TABLE toolGroupToeAppToeRole_tmp (ToolGroupId INT,RoleId INT,toolGroupToeApplicationID INT)
INSERT INTO toolGroupToeAppToeRole_tmp
SELECT 1,1,1
GO
IF OBJECT_ID(N'ToolgroupToeApplication_tmp')>0
BEGIN
DROP TABLE ToolgroupToeApplication_tmp
END
CREATE TABLE ToolgroupToeApplication_tmp (ToolGroupId INT,ToolgroupToeApplicationID INT)
INSERT INTO ToolgroupToeApplication_tmp
SELECT 1,1
GO
IF OBJECT_ID(N'PurposeToToolGroup_tmp')>0
BEGIN
DROP TABLE PurposeToToolGroup_tmp
END
CREATE TABLE PurposeToToolGroup_tmp (ToolGroupId INT)
INSERT INTO PurposeToToolGroup_tmp
SELECT 1
GO
IF OBJECT_ID(N'eRole_tmp')>0
BEGIN
DROP TABLE eRole_tmp
END
CREATE TABLE eRole_tmp (RoleId INT)
INSERT INTO eRole_tmp
SELECT 1
GO
--Create Views
DECLARE @SQL NVARCHAR(MAX)
SET @SQL = '
CREATE VIEW dbo.vw_ToolGroup_One WITH SCHEMABINDING
AS
SELECT
tg.ToolGroupId,
tg.ToolGroupName,
tgtt.ToolId,
tga.AppId AS TGAppId,
pttg.PurposeId AS TGPurposeId,
tgr.RoleId AS TGRoleId
FROM dbo.toolGroup_tmp tg
INNER JOIN dbo.purposeToTG_tmp pttg
ON tg.ToolGroupId = pttg.ToolGroupId
INNER JOIN dbo.toolGroupToTool_tmp tgtt
ON tg.ToolGroupId = tgtt.ToolGroupId
INNER JOIN dbo.toolGroupToeApp_tmp tga
ON tg.ToolGroupId = tga.ToolGroupId
INNER JOIN dbo.toolGroupToeAppToeRole_tmp tgar
ON tga.toolGroupToeApplicationID = tgar.toolGroupToeApplicationID
INNER JOIN dbo.ToolgroupToeApplication_tmp tgta
ON tgta.ToolgroupToeApplicationID = tgar.ToolgroupToeApplicationID
INNER JOIN dbo.eRole_tmp tgr
ON tgar.RoleId = tgr.RoleId
'
EXEC SP_EXECUTESQL @SQL
SET @SQL = '
CREATE VIEW dbo.vw_ToolGroup_Two WITH SCHEMABINDING
AS
SELECT tg.ToolGroupId,
tg.ToolGroupName,
NULL AS ToolId,
NULL AS TGAppId,
NULL AS TGPurposeId,
NULL AS TGRoleId
FROM dbo.toolGroup_tmp tg
INNER JOIN dbo.ToolgroupToeApplication_tmp tgtea
ON tg.ToolGroupId = tgtea.ToolGroupId
'
EXEC SP_EXECUTESQL @SQL
-- Create Indexes
CREATE UNIQUE CLUSTERED INDEX IDX_view_ToolGroup_One
ON vw_ToolGroup_One(ToolGroupId, ToolGroupName, ToolId, TGPurposeId, TGRoleId);
CREATE UNIQUE CLUSTERED INDEX IDX_view_ToolGroup_Two
ON vw_ToolGroup_Two(ToolGroupId, ToolGroupName);
GO
-- Invoke Query
SELECT * FROM vw_ToolGroup_One
UNION ALL
SELECT * FROM vw_ToolGroup_Two tgt
WHERE NOT EXISTS ( SELECT 1
FROM dbo.PurposeToToolGroup_tmp pttg
WHERE pttg.ToolGroupId = tgt.ToolGroupId)
【讨论】:
这是正确的方法。转换和拆分查询,以便大部分工作都在索引视图中。您可以将 NOT EXISTS 转换为 LEFT JOIN。以上是关于替代联合和子查询来创建索引视图的主要内容,如果未能解决你的问题,请参考以下文章