使用联合/交叉连接优化视图
Optimization of view with union / cross join
我想问一下我的一个具体观点和潜在的优化。
看起来很随意 tables 设置 - 客户(~50.000 行)和用户(~250 行),而大多数用户可以访问所有客户(事实上,有不同的权限级别customers 地理位置,但它似乎与此问题无关,我将在下面 post 附加问题),某些用户(RoleId = 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] 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 table user:location 而客户有
总是一个位置)。
- 目前我正在几个父视图中额外检查它 - 会吗
无论如何显着提高父视图的性能?
提前致谢
编辑:
根据Gordons的回答,我尝试修改视图如下,很有帮助。
现在我正在考虑更多地使用此视图 - 我的意思是,在这种情况下(当大多数用户可以访问客户时)是一种更好的方法来仅显示受限客户并询问是否客户和用户不在所选视图中? (应用程序是用 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
影响此查询性能的不是 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)
上的索引。
尝试通过查询获得更好的性能:
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]
我想问一下我的一个具体观点和潜在的优化。
看起来很随意 tables 设置 - 客户(~50.000 行)和用户(~250 行),而大多数用户可以访问所有客户(事实上,有不同的权限级别customers 地理位置,但它似乎与此问题无关,我将在下面 post 附加问题),某些用户(RoleId = 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] 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 table user:location 而客户有 总是一个位置)。
- 目前我正在几个父视图中额外检查它 - 会吗 无论如何显着提高父视图的性能?
提前致谢
编辑: 根据Gordons的回答,我尝试修改视图如下,很有帮助。
现在我正在考虑更多地使用此视图 - 我的意思是,在这种情况下(当大多数用户可以访问客户时)是一种更好的方法来仅显示受限客户并询问是否客户和用户不在所选视图中? (应用程序是用 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
影响此查询性能的不是 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)
上的索引。
尝试通过查询获得更好的性能:
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]