使用union / cross join优化视图

时间:2015-05-26 10:32:51

标签: sql sql-server-2008 view union cross-join

我想问一下我的一个具体观点和潜在的优化。

看起来像一个非常随意的表设置 - 客户(约50.000行)和用户(约250行),而大多数用户可以访问所有客户(事实上,客户的地理位置有不同的权限级别,但它确实似乎与此问题无关,我将在下面发布其他问题),有一些用户(使用RoleId = 1)只能访问他们自己的客户。 我以前的一个coleague做了一个数据库视图来评估某个用户是否可以访问某个客户。

以下是视图定义。

CREATE VIEW [dbo].[ViewUserAllowedCustomer]
AS 
SELECT
      u.[Id] UserId
    , c.[Id] CustomerId
FROM [dbo].[User] u
    CROSS JOIN [dbo].[Customer] c
WHERE
    u.[RoleId] NOT IN (1) --Specific role for which the below part is required

UNION

SELECT
      u.[Id] UserId
    , c.[Id] CustomerId
FROM [dbo].[User] u
    JOIN [dbo].[Customer] c ON u.[EmployeeId] = c.[EmployeeId]

现在我正在寻找一种更好的方法来定义这个视图并可能删除union join or cross join或甚至会损害性能?
我想知道是否有任何最佳实践或完全不同于一个在这里使用。至少我要在这里添加UNION ALL而不是UNION

另外一个问题 - 我想到了一个想法:

  • 使用客户的另一个权限扩展此视图 地理位置(m:n表用户:客户拥有的位置) 总是一个地方)。
  • 目前我在少数父视图中检查它 - 是吗? 无论如何,显着提高父视图的性能?

提前致谢

修改 根据Gordons的回答,我尝试按如下方式修改视图,并且它有很多帮助。

现在我正在考虑使用这个视图 - 我的意思是,在这种情况下(当大多数用户可以访问客户时)是一种更好的方法,只显示受限制的客户并询问客户和用户不在选定的视图中? (app是用SP 2010上面的C#.NET MVC编写的。)

SELECT u.[Id] as UserId, c.[Id] as CustomerId
FROM [dbo].[User] u JOIN
     [dbo].[Customer] c
     ON u.[EmployeeId] = c.[EmployeeId]
UNION ALL
SELECT u.[Id] as UserId, c.[Id] as CustomerId
FROM [dbo].[User] u CROSS JOIN
     [dbo].[Customer] c
WHERE NOT EXISTS (SELECT 1 FROM dbo.[User] u2 WHERE u2.Id = u.Id AND u.RoleId = 1) --this here might be changed for a casual != rule on RoleId, but this describes the original idea, which I think is pretty good

2 个答案:

答案 0 :(得分:1)

此查询的性能损害不是cross join,而是union。它有额外的逻辑来删除重复。

尝试稍微改写一下:

SELECT u.[Id] as UserId, c.[Id] as CustomerId
FROM [dbo].[User] u JOIN
     [dbo].[Customer] c
     ON u.[EmployeeId] = c.[EmployeeId]
UNION ALL
SELECT u.ID as UserId, c.ID as CustomerId
FROM [dbo].[User] u JOIN
     [dbo].[Customer] c
     ON u.[EmployeeId] = c.[EmployeeId]
WHERE NOT EXISTS (SELECT 1 FROM dbo.[User] u2 WHERE u2.EmployeeId = u.EmployeeId AND u.RoleId = 1);

您需要User(EmployeeId, RoleId)上的索引。

答案 1 :(得分:1)

尝试使用查询获得更好的性能:

CREATE VIEW [dbo].[ViewUserAllowedCustomer]
AS 
SELECT
      u.[Id] UserId
    , c.[Id] CustomerId
FROM [dbo].[User] u
    CROSS JOIN [dbo].[Customer] c
WHERE
    u.[RoleId] != 1 --Specific role for which the below part is required

UNION ALL

SELECT
      u.[Id] UserId
    , c.[Id] CustomerId
FROM [dbo].[User] u
    JOIN [dbo].[Customer] c ON u.[EmployeeId] = c.[EmployeeId]
WHERE u.[RoleId] = 1

新想法(删除uniun)
它接缝你试图有一个可以与用户访问的客户白名单 如果是这样,我会建议有一个用户无法访问的客户黑名单 这样的事情可能有所帮助:

CREATE VIEW [dbo].[ViewUserNotAllowedCustomer]
AS 
SELECT
      u.[Id] UserId
    , c.[Id] CustomerId
FROM [dbo].[User] u
    CROSS JOIN [dbo].[Customer] c
WHERE
    u.[RoleId] = 1 AND u.[EmployeeId] != c.[EmployeeId]