MSSQL: повторно используемый способ запроса подмножества всех данных в таблице

#sql-server

#sql-server

Вопрос:

Предположим, что система, в которой People есть доступ к Buildings зданиям и в зданиях, имеет доступ к определенным Rooms . Доступ определяется в соответствии со связанными Permissions таблицами или, в случае пользователей с полным доступом, полным доступом.

Таблицы БД определяются следующим образом:

 buildings (id INT)
rooms (id INT, building_id INT)
people (id INT, has_full_access BIT)
building_permissions (building_id INT, person_id INT)
room_permissions (room_id INT, person_id INT)
  

В настоящее время у меня есть функции с табличным значением, которые возвращают таблицы с авторизованными идентификаторами зданий и комнат на основе идентификатора пользователя и наличия у них полного доступа.

 CREATE FUNCTION fn_get_authorised_buildings (@person_id INT, @has_full_access BIT)
RETURNS TABLE AS
RETURN
(
    SELECT b.id
    FROM buildings b
    WHERE @has_full_access = 1

    UNION

    SELECT b.id
    FROM buildings b
        INNER JOIN building_permissions bp ON bp.building_id = b.id
    WHERE bp.person_id = @person_id 
);

CREATE FUNCTION fn_get_authorised_rooms (@person_id INT, @has_full_access BIT)
RETURNS TABLE AS
RETURN
(
    SELECT r.id
    FROM rooms r
    WHERE @has_full_access = 1

    UNION

    SELECT r.id
    FROM rooms r
        INNER JOIN room_permissions rp ON rp.room_id = r.id
    WHERE rp.person_id = @person_id 
);
  

Во всей системе, когда задействованы здания и помещения, мне нужно фильтровать эти таблицы на основе разрешений пользователя. Если пользователь имеет полный доступ, то должны быть включены все строки, в противном случае только те, на которые у них есть разрешение.

Мои запросы (упрощенные) выглядят примерно так:

 DECLARE @person_id INT = 123
DECLARE @has_full_access BIT = 1

DECLARE @authorised_buildings TABLE (id INT) 
INSERT INTO @authorised_buildings SELECT id FROM fn_get_authorised_buildings(@person_id , @has_full_access)

DECLARE @authorised_rooms TABLE (id INT) 
INSERT INTO @authorised_rooms SELECT id FROM fn_get_authorised_rooms(@person_id, @has_full_access)

--Example A
SELECT * 
FROM buildings b
    INNER JOIN rooms r ON r.building_id = b.id
WHERE 1 = 1
    AND b.id IN (SELECT id FROM @authorised_buildings)
    AND r.id IN (SELECT id FROM @authorised_rooms)

--Example B
SELECT * 
FROM floors f -- or other tables that are related to rooms
    INNER JOIN rooms r ON r.floor_id = f.id
WHERE 1 = 1
    AND r.id IN (SELECT id FROM @authorised_rooms)
  

Есть ли лучший способ сделать это?

Здесь
РЕДАКТИРОВАТЬ: это скрипка с настройкой

Комментарии:

1. Если бы вы предоставили Sql-скрипку, было бы намного проще протестировать решение

2. @Pinx0, я добавил скрипку. Я впервые создаю скрипку, поэтому буду признателен за любую конструктивную критику.

3. Да, это приятно. Я протестировал его и соответствующим образом обновил свое решение.

Ответ №1:

Вы можете создать подобное представление, которое содержит всю логику:

 CREATE VIEW bulding_rooms_per_person AS
SELECT p.id AS person_id, b.id as building_id, r.id AS room_id
FROM people p 
LEFT JOIN building_permissions bp ON bp.person_id = p.id
LEFT JOIN room_permissions rp ON rp.person_id = p.id
INNER JOIN buildings b ON b.id = bp.building_id OR p.has_full_access = 1
INNER JOIN rooms r ON r.building_id = b.id AND (r.id = rp.room_id OR p.has_full_access = 1)
  

А затем просто выполните запрос по нему:

 SELECT * from bulding_rooms_per_person WHERE person_id = @person_id 
  

Комментарии:

1. Таким образом, представление будет использоваться вместо функции с табличным значением? Есть ли разница в производительности между ними?

2. Существует разница в производительности между представлением и многозначной табличной функцией, такой как ваша. Не так много между представлением и встроенной табличной функцией, просто запросы разные.