I would like to ask about one specific view I have and potential optimization.
Seems like quite a casual tables setup - Customers (~50.000 rows) and Users (~250 rows), while most users can access all the customers (infact, there is a different permission level on customers geographical location, but it does not seem to be relevant for this issue, I will post additional question below), there are certain users (with RoleId = 1) which can access only their very own customers. A former coleague of mine made a database view to evaluate if a certain user has access to certain customer.
Below is a view definition.
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]
Now I am looking for a better way to define this view and possibly remove the union join or cross join
or does it even hurt performance?
I was wondering if there is any best practice or completely different approach than the one used here. At least I am going to add UNION ALL
instead of UNION
here.
And the additional question - an idea came to my mind:
Thanks in advance
EDIT: According to Gordons answer, I tried to modify the view as follows, and it helped a lot.
Now I am thinking a bit more into the usage of this view - I mean, would in this case (when most of the users can access the customer) be a better approach to show only restricted customers and ask if the customer and user is not in the selected view? (app is written in C# .NET MVC above SP 2010).
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
Trying to get better performance with the query:
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
New idea (removing uniun):
It seams you are trying to have a white-list of customers that can be accessed with a user.
If so, I will suggest having a black-list of customers that can not be accessed with a user.
Something like this may help:
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]