由于LEFT JOIN或子查询,无法在视图上创建CLUSTERED INDEX

时间:2012-03-12 15:00:25

标签: sql sql-server tsql clustered-index

我为视图中使用的查询创建了两个选项,返回我需要的结果。我需要重写任一选项,以便可以在索引视图中使用它。两者都失败了 在视图上创建唯一聚簇索引时。第一个由于LEFT OUTER JOIN而失败,第二个由于子查询而失败。我相信两者也会失败 自我加入。

找到Creating Indexed Views后,有一大堆无法使用的TSQL语法元素。其中:派生表,UNION,EXCEPT,INTERSECT,子查询, 外部或自联接,TOP,ORDER BY,DISTINCT,MAX ......

查询应为每个唯一CompanyID获得最大Company。还需要显示StatusName表中的Statuses,我只是添加它以防它影响 解决方案。它目前是INNER JOIN,所以它不会导致创建索引的问题。

Companies表的示例,其中所有3列均为INT

CompanyID Company Revision
1         1       1
2         1       2
3         2       1
4         2       2

查询应返回:

CompanyID Company Revision
2         1       2
4         2       2

以下是我创建的两个选项:

SELECT t1.CompanyID, t1.Company, t1.Revision, Statuses.StatusName
FROM dbo.Companies AS t1

LEFT OUTER JOIN dbo.Companies AS t2
ON t1.Company = t2.Company AND t1.CompanyID < t2.CompanyID

INNER JOIN dbo.Statuses
ON dbo.Statuses.StatusID = t1.StatusID

WHERE t2.Company IS NULL

另一个:

SELECT t1.CompanyID, t1.Company, t1.Revision, Statuses.StatusName
FROM dbo.Companies AS t1

INNER JOIN dbo.Statuses
ON dbo.Statuses.StatusID = t1.StatusID

WHERE t1.Company NOT IN (SELECT t2.Company from dbo.Companies AS t2 WHERE t1.CompanyID < t2.CompanyID)

所以,我的问题是,是否可以重写查询以在索引视图中使用?

我正在使用MS SQL Server 2008 R2和2005。

2 个答案:

答案 0 :(得分:3)

为什么不尝试另一种方式,而不是创建排他性视图:

CREATE VIEW dbo.HighestCompany
AS
  SELECT t1.CompanyID, t1.Company, t1.Revision, s.StatusName
    FROM dbo.Companies AS t1
    INNER JOIN (
      SELECT Company, HighestCompany = MAX(CompanyID) 
      FROM dbo.Companies GROUP BY Company
    ) AS t2
    ON t1.Company = t2.Company
    AND t1.CompanyID = t2.HighestCompany -- not sure if CompanyID is unique
    INNER JOIN dbo.Statuses AS s
    ON s.StatusID = t1.StatusID;

您仍然无法在此处创建索引视图,但它可能比您当前拥有的版本稍微好一点(当然,取决于几个因素,包括公司和选择性的索引)。

除此之外,我认为要提高性能,您需要查看基表上的索引策略。为什么您的公司表允许多个公司使用相同的名称和不同的ID?也许这是问题的一部分,您应该将当前相关的公司存储在一个单独的表中。

你可以这样做(请记住我在这里猜测数据类型和最佳索引):

CREATE SCHEMA hold AUTHORIZATION dbo;
GO
CREATE SCHEMA cache AUTHORIZATION dbo;
GO
CREATE TABLE dbo.HighestCompany
(
  CompanyID INT, 
  Company NVARCHAR(255) PRIMARY KEY,
  Revision INT,
  StatusName NVARCHAR(64)
);
GO
CREATE TABLE cache.HighestCompany
(
  CompanyID INT, 
  Company NVARCHAR(255) PRIMARY KEY,
  Revision INT,
  StatusName NVARCHAR(64)
);
GO

现在,您经常认为需要刷新此数据,您可以运行执行以下操作的作业:

TRUNCATE TABLE cache.HighestCompany;

INSERT cache.HighestCompany(CompanyID, Company, Revision, StatusName)
SELECT t1.CompanyID, t1.Company, t1.Revision, s.StatusName
        FROM dbo.Companies AS t1
        INNER JOIN (
          SELECT Company, HighestCompany = MAX(CompanyID) 
          FROM dbo.Companies GROUP BY Company
        ) AS t2
        ON t1.Company = t2.Company
        AND t1.CompanyID = t2.HighestCompany
        INNER JOIN dbo.Statuses AS s
        ON s.StatusID = t1.StatusID;

-- this is a fast, metadata operation that should result
-- in minimal blocking and disruption to end users:
BEGIN TRANSACTION;
  ALTER SCHEMA hold TRANSFER dbo.HighestCompany;
  ALTER SCHEMA dbo TRANSFER cache.HighestCompany;
  ALTER SCHEME cache TRANSFER hold.HighestCompany;
COMMIT TRANSACTION;

如果你发现这些公司经常变化,或者数据真的需要高达一秒,这是不切实际的,你可以用@Dems建议的触发器做类似的事情。

答案 1 :(得分:1)

不幸的是,你不能。

您的查询不仅需要LEFT JOIN,而且要将同一个表格左键连接到自身。引用BooksOnline和你的问题...

The SELECT statement in the view cannot contain the following Transact-SQL syntax elements: 
 - Outer or self joins.

另一种选择可能是创建一个您通过触发器保持更新的真实映射表。正在Companies更改创建/删除的记录,并且更新的记录更改为Statuses


同样,视图在线扩展到使用它们的查询中(除非您特别说明NOEXPAND提示)。您是否检查过查询的执行计划,看看是否可以在基表上创建更合适的索引?

修改

另一种查询布局,就像一个选项......

;WITH
  sequenced_companies
AS
(
  SELECT
    ROW_NUMBER() OVER (PARTITION BY Company ORDER BY CompanyID DESC) AS sequence_id,
    *
  FROM
    dbo.companies
)
SELECT
  *
FROM
  sequenced_companies
INNER JOIN
  dbo.statuses
    ON statuses.StatusID = sequenced_companies.StatusID
WHERE
  sequenced_companies.sequence_id = 1

使用(Company, CompanyID DESC)上的索引,这应该非常快。 (虽然仍然不适合可索引的视图。)