Search code examples
sqlsql-server-2008viewunioncross-join

Optimization of view with union / cross join


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:

  • To extend this view with another permission on customer's geographical location (m:n table user:location while customer has always one location).
  • Currently I am checking it extra in few parent views - would it increase performance of the parent views anyhow significantly?

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

Solution

  • 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]