替代联合和子查询来创建索引视图

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。

以上是关于替代联合和子查询来创建索引视图的主要内容,如果未能解决你的问题,请参考以下文章

mysql联合索引字段顺序

Mysql 索引 事物

联合分组子查询视图事务python操作mysql索引

如何给mysql表建立联合索引

替代索引视图

mysql 什么时候用单列索引?什么使用用联合索引?