Учитывая следующие таблицы, мне нужно отфильтровать данные вещи на основе того, какой пользователь совершает вызов, а также дополнительные (необязательные) фильтры, которые состоят из комбинации user_id / role.
В любое время пользователь должен получать результаты только для тех вещей, с которыми он связан, если у него нет полного доступа. Дополнительные фильтры - это фильтры И , означающие, что результаты должны удовлетворять всем фильтрам.
Параметры @user_id, @has_full_access и user_id / role фильтруются через Dapper.
CREATE TABLE users
(
id int NOT NULL
CONSTRAINT PK_users PRIMARY KEY CLUSTERED (id)
)
CREATE TABLE user_roles
(
user_id int NOT NULL,
role varchar(10) NOT NULL
CONSTRAINT FK_user_roles_users FOREIGN KEY(user_id) REFERENCES users(id)
)
CREATE TABLE things
(
id int NOT NULL
CONSTRAINT PK_things PRIMARY KEY CLUSTERED (id)
)
CREATE TABLE thing_permissions
(
thing_id int NOT NULL,
user_id int NOT NULL,
role varchar(10) NOT NULL
CONSTRAINT FK_thing_permissions_things FOREIGN KEY(thing_id) REFERENCES things(id),
CONSTRAINT FK_thing_permissions_users FOREIGN KEY(user_id) REFERENCES users(id)
)
INSERT INTO users VALUES (1)
INSERT INTO users VALUES (2)
INSERT INTO users VALUES (3)
INSERT INTO users VALUES (4)
INSERT INTO users VALUES (5)
INSERT INTO user_roles VALUES (1, 'Admin')
INSERT INTO user_roles VALUES (2, 'Creator')
INSERT INTO user_roles VALUES (2, 'Owner')
INSERT INTO user_roles VALUES (3, 'Creator')
INSERT INTO user_roles VALUES (3, 'Owner')
INSERT INTO user_roles VALUES (4, 'Creator')
INSERT INTO user_roles VALUES (5, 'Owner')
INSERT INTO things VALUES (1)
INSERT INTO things VALUES (2)
INSERT INTO things VALUES (3)
INSERT INTO things VALUES (4)
INSERT INTO things VALUES (5)
INSERT INTO thing_permissions VALUES (1, 2, 'Creator')
INSERT INTO thing_permissions VALUES (1, 3, 'Creator')
INSERT INTO thing_permissions VALUES (1, 2, 'Owner')
INSERT INTO thing_permissions VALUES (2, 2, 'Creator')
INSERT INTO thing_permissions VALUES (2, 5, 'Owner')
INSERT INTO thing_permissions VALUES (3, 4, 'Creator')
INSERT INTO thing_permissions VALUES (3, 3, 'Owner')
INSERT INTO thing_permissions VALUES (3, 5, 'Owner')
INSERT INTO thing_permissions VALUES (4, 3, 'Creator')
INSERT INTO thing_permissions VALUES (4, 5, 'Owner')
INSERT INTO thing_permissions VALUES (5, 2, 'Creator')
Ниже приведены некоторые примеры различных входных комбинаций, а также ожидаемых результатов.
--Scenario 1:
--Expected Results: 1, 2, 3, 4, 5
DECLARE @user_id int = 1
DECLARE @has_full_access bit = 1
DECLARE @filters TABLE (user_id int, [role] varchar(10))
--Scenario 2:
--Expected Results: 1, 2, 5
DECLARE @user_id int = 2
DECLARE @has_full_access bit = 0
DECLARE @filters TABLE (user_id int, [role] varchar(10))
--Scenario 3:
--Expected Results: 1
DECLARE @user_id int = 1
DECLARE @has_full_access bit = 1
DECLARE @filters TABLE (user_id int, [role] varchar(10))
INSERT INTO @filters VALUES (2, 'Creator')
INSERT INTO @filters VALUES (2, 'Owner')
--Scenario 4:
--Expected Results: 3
DECLARE @user_id int = 1
DECLARE @has_full_access bit = 1
DECLARE @filters TABLE (user_id int, [role] varchar(10))
INSERT INTO @filters VALUES (3, 'Owner')
INSERT INTO @filters VALUES (5, 'Owner')
--Scenario 5:
--Expected Results: 1
DECLARE @user_id int = 2
DECLARE @has_full_access bit = 0
DECLARE @filters TABLE (user_id int, [role] varchar(10))
INSERT INTO @filters VALUES (3, 'Creator')
--Scenario 6:
--Expected Results: no results
DECLARE @user_id int = 1
DECLARE @has_full_access bit = 1
DECLARE @filters TABLE (user_id int, [role] varchar(10))
INSERT INTO @filters VALUES (2, 'Creator')
INSERT INTO @filters VALUES (4, 'Creator')
Здесь - это скрипта SQL с настройкой.
На данный момент у меня есть следующая функция, которая возвращает все вещи , а также роли, с которыми связан пользователь.
FUNCTION GetMyThings (@user_id INT, @has_full_access BIT)
RETURNS TABLE
AS
RETURN
(
SELECT t.id, 'Admin' AS role
FROM things t
WHERE @has_full_access = 1
UNION
SELECT t.id, tp.role
FROM things t
INNER JOIN thing_permissions tp ON tp.thing_id = t.id
WHERE tp.user_id = @user_id
)
Я использую эту функцию для получения списка вещей , к которым имеет доступ вызывающий пользователь, а также для каждого пользователя в фильтрах. Наконец я возвращаю результаты, которые есть в обоих этих наборах данных.
DECLARE @my_things TABLE (id INT)
INSERT INTO @my_things SELECT id FROM GetMyThings(@user_id, @has_full_access)
DECLARE @filtered_things TABLE (id INT)
INSERT INTO @filtered_things SELECT ft.id FROM @filters f CROSS APPLY (SELECT DISTINCT id, role FROM GetMyThings(f.user_id, 0)) ft WHERE ft.role = f.role GROUP BY ft.id HAVING COUNT(ft.id) >= (SELECT COUNT(user_id) FROM @filters)
DECLARE @has_filter BIT = (SELECT has_filter = CASE WHEN (COUNT(user_id) > 0) THEN 1 ELSE 0 END FROM @filters)
DECLARE @final_things TABLE (id INT)
INSERT INTO @final_things SELECT id FROM @my_things WHERE @has_filter = 0 OR id IN (SELECT id FROM @filtered_things)
SELECT * FROM @final_things
Есть ли лучший способ сделать это? Мое решение работает, но с большими наборами данных кажется, что функция замедляет запрос по сравнению с выбором из исходных данных.
Я также пытался использовать представления, но, поскольку мне нужен параметр @has_full_access и отдельные элементы SELECT, объединенные вместе, я не могу добавить WHERE в каждый элемент SELECT.