鉴于下表,我需要根据进行呼叫的用户以及由user_id / role组合组成的其他(可选)过滤器过滤事物数据。
除非有完全访问权限,否则用户在任何时候都应该只收到与其链接的内容有关的结果。其他过滤器是 AND 过滤器,这意味着结果应满足所有过滤器的要求。
使用Dapper传递参数@ user_id,@ has_full_access和user_id /角色过滤器。
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')
Here是带有设置的SQL Fiddle。
目前,我具有以下函数,该函数返回所有事物以及与用户链接的角色。
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 UNION组合在一起,所以无法为每个SELECT添加WHERE。