我是一个组织用户管理结构,其中组织单位位置的Level 5
用户自动位于Level 4, 3, 2, 1
。
正如您在屏幕截图中看到的那样。
每个组织单位都有自己的ID
和每个用户。所以我有一个额外的表格,我存储每个级别的所有groupId<->userId
关系。在这种情况下,我可以获得所有用户,例如从级别3开始,因为所有用户都使用n:n表相关。
这样很容易查询用户是否在指定的组织单位中,但是......保持这种结构的一致性很复杂。
示例:如果我从Level 3/Employee
删除用户,则必须从Level 2
删除该用户,但如果此用户不在Level 1
,则必须从Level B/Employee
删除。< / p>
所以问题是。 由于我无法改变用户从下到上继承组织单位的方式,因此有一种方法或设计模式如何实现这种方法,让用户只引用一次到他的最终位置。
示例:我将用户引用到Level 5/employee
并且这是在数据库中保留的,但我不想为Level 4 - 1
添加关系,但我仍然想要快速查询用户是否例如在Level 2
。
我不想要任何细节代码,这更像是一个通用设计问题,因为我想重新设计我的用户管理,以减少错误。 如果我可以使用SQL快速查询数据(例如,使用MS SQL进行CTE查询),它也应该可以在MVC(使用RoR)环境中工作。
如果有人需要更多详细信息或信息,我会发布信息。
答案 0 :(得分:0)
我得到了很多答案:)我做了一些调查,现在有了最后的方法。
答案是嵌套树来执行快速查询。
我做了什么:
创建用户表。
CREATE TABLE [dbo].[actor_users](
[id] [int] NOT NULL,
[manager_id] [int] NULL,
[deputy_id] [int] NULL,
[username] [nvarchar](48) NOT NULL,
[pwd] [nvarchar](40) NULL,
[pwd_url] [char](38) NULL,
[guid] [char](38) NULL,
[deactivated] [smallint] NULL,
[lastname] [nvarchar](48) NULL,
[middlename] [nvarchar](48) NULL,
[firstname] [nvarchar](48) NULL,
[acronym] [nvarchar](16) NULL,
[employee_nr] [nvarchar](16) NULL,
[department] [nvarchar](250) NULL,
[cost_unit] [nvarchar](16) NULL,
[desc] [nvarchar](max) NULL,
[email] [nvarchar](192) NULL,
[sex] [int] NULL,
[group_ids] [nvarchar](4000) NULL,
[picture_id] [int] NULL,
[lcid] [int] NULL,
CONSTRAINT [ct_actor_users] PRIMARY KEY CLUSTERED
( [id] ASC ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY]
创建一个组表。
CREATE TABLE [dbo].[actor_groups](
[id] [int] NOT NULL,
[parent_id] [int] NULL,
[group_name] [nvarchar](max) NULL,
[group_type] [int] NOT NULL,
[group_reference] [int] NOT NULL,
[description] [nvarchar](max) NULL,
[depth] [int] NULL,
[left] [int] NULL,
[right] [int] NULL,
[id_path] [nvarchar](max) NULL,
[name_path] [nvarchar](max) NULL,
CONSTRAINT [ct_actor_groups] PRIMARY KEY CLUSTERED
( [id] ASC ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
我在用户表中填充了大约10k行的随机用户。
现在我创建了一些随机树
-- create some random tree for testing
DECLARE @id int;
DECLARE @name nvarchar(max);
WHILE (SELECT COUNT(*) FROM actor_groups)<1000
BEGIN
SET @id = (SELECT ISNull(MAX(id),0) + 1 FROM actors);
SET @name = 'random_ou' + CAST(NEWID() AS nvarchar(40));
INSERT INTO actor_groups (id, parent_id, group_name, group_type, group_reference, [description], depth, [left], [right], id_path, name_path)
SELECT @id
, (SELECT TOP 1 id FROM actor_groups ORDER BY NEWID()) AS parent_id
, @name group_name
, 3 group_type
, -1 group_reference
, '' [description]
, 0 depth
, 0 [left]
, 0 [right]
, '' id_path
, '' name_path
END
在此之后我必须更新嵌套的集合关系......
-- update tree
WHILE EXISTS (SELECT * FROM actor_groups WHERE depth IS NULL)
UPDATE tr SET
tr.depth = par.depth + 1 ,
tr.id_path = par.id_path + ',' + CAST(tr.id AS nvarchar(255)) ,
tr.name_path = (CASE par.id WHEN 40 THEN '' ELSE par.name_path + '/' END) + tr.group_name
FROM actor_groups AS tr
INNER JOIN actor_groups AS par ON (tr.parent_id = par.id)
WHERE par.depth >=0 AND tr.depth IS NULL
GO
-- left, right nested set
WITH treerows AS
( SELECT actor_groups.*, ROW_NUMBER() OVER (ORDER BY id_path) AS Row FROM actor_groups )
UPDATE actor_groups
SET [left] = tbl.Lft
, [right] = tbl.Rgt
FROM actor_groups
JOIN (SELECT
ER.id,
ER.id_path,
ER.depth,
ER.Row,
(ER.Row * 2) - ER.depth AS Lft,
((ER.Row * 2) - ER.depth) +
(
SELECT COUNT(*) * 2
FROM treerows ER2
WHERE ER2.id_path LIKE ER.id_path + ',%'
) + 1 AS Rgt
FROM treerows ER
) tbl ON tbl.id = actor_groups.id
现在我做了一些随机映射......
-- do some random mappings
DECLARE @map int;
DECLARE @mapuser int;
DECLARE @counter int;
SET @counter = 1;
WHILE @counter<1000
BEGIN
SET @map = (SELECT TOP 1 id FROM actor_groups ORDER BY NEWID())
SET @mapuser = (SELECT TOP 1 id FROM actor_users ORDER BY NEWID())
INSERT INTO actor_mappings ([group_id], [user_id], imported) VALUES (@map, @mapuser, 0)
SET @counter = @counter + 1;
END
所以现在我有了一个组和一个用户表。 用户充满了10.000个用户,我的树有大约1.000个节点。 我确实多次开始随机映射SQL,所以我有大约100.000个映射。
我的查询:
SELECT DISTINCT
m.[user_id] AS luserid
, org.[id] AS lgroupid
, m.imported AS bimported
FROM [test].[dbo].[actor_groups] org
JOIN [actor_groups] org2 ON org2.[left] BETWEEN org.[left] AND org.[right]
JOIN actor_mappings m ON org2.id = m.group_id
如果我不缩小查询,查询运行大约700毫秒。在我的测试中,寻求一个特殊的节点或用户大约需要150-300毫秒。
解决:强> 这可以使用嵌套集来完成。
使用我的1.000节点更新树的时间大约为1秒,查询数据时我的表上没有任何附加索引也总是低于1秒。
希望这可以帮助其他人面对同样的问题。