sql - 由于 LEFT JOIN 或子查询,无法在 View 上创建 CLUSTERED INDEX

标签 sql sql-server tsql clustered-index

我为 View 中使用的查询创建了两个选项,它们返回我需要的结果。我需要重写任一选项,以便它可以在 索引 View 中使用。都失败了 在 View 上创建唯一聚集索引 时。第一个因 LEFT OUTER JOIN 而失败,第二个因子查询而失败。我相信两者都会失败,因为 自连接。

找到Creating Indexed Views后, 有大量不能使用的 TSQL 语法元素。其中:派生表、UNION、EXCEPT、INTERSECT、子查询、 外连接或自连接、TOP、ORDER BY、DISTINCT、MAX……

查询应该为每个唯一的 Company 获取最大的 CompanyIDStatuses 表中的 StatusName 也需要显示,我只是添加它以防它影响 解决方案。它目前是一个 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)

所以,我的问题是,是否可以重写任何一个查询以在索引 View 中使用?

我使用的是 MS SQL Server 2008 R2 和 2005。

最佳答案

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

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;

您仍然无法为此创建索引 View ,但它可能比您当前拥有的版本要好一些(当然,取决于几个因素,包括公司索引和选择性)。

除此之外,我认为要提高性能,您需要查看基表的索引策略。为什么您的 Companies 表允许多个公司具有相同的名称和不同的 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 建议的那样使用触发器做类似的事情。

关于sql - 由于 LEFT JOIN 或子查询,无法在 View 上创建 CLUSTERED INDEX,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9669481/

相关文章:

tsql - SQL DateDiff Weeks - 需要和替代

mysql - 遍历具有变量名的不同表

mysql计算一个月中不同的天数

sql - 如何在 SQL Server 中禁用表或触发器级别的触发器嵌套?

mysql - 从 MySQL 几何列中检索坐标

sql-server - UPDATE 锁如何防止常见形式的死锁?

sql - 在不使用 ORDER BY 子句的情况下以相同的顺序获取记录

sql-server - 如何获取正在运行的sql的机器名和进程ID?

sql - 优化 SQL 连接单列与另一个表中的多列

c# - Entity Framework 死锁