2012-12-04 51 views
0

我喜歡組織用戶管理結構,其中組織用戶管理結構中組織用戶例如自動位於組織單位Level 4, 3, 2, 1中。層次結構組織單位自下而上方法

正如你可以在屏幕截圖中看到的。

enter image description here

每個組織單位都有它自己的ID和每個用戶也是如此。所以我有一個額外的表格,用於存儲每個級別的所有groupId<->userId關係。在這種情況下,我可以讓所有用戶例如從第3級開始,因爲所有用戶都使用n:n表進行關聯。

這樣就很容易查詢用戶是否在指定的組織單位中,但是......保持此結構一致性非常複雜。

示例:如果我從Level 3/Employee刪除用戶也必須從Level 2去除,但只有從Level 1如果此用戶未處於Level B/Employee

所以問題是。 由於我無法改變用戶從下往上繼承組織單位的方式,因此有一種方法或設計模式,即如何實現這種方法,讓用戶只參考一次他的最終位置。

示例:我將用戶引用到Level 5/employee,並且這種情況持久存在於數據庫中,但我不想爲Level 4 - 1添加關係,但如果用戶是例如用戶,我仍然希望快速查詢。在Level 2

我不想要任何細節代碼,這是一個更通用的設計問題,因爲我想重新設計我的用戶管理,使它更少出錯。 如果我可以用SQL快速查詢數據(例如使用MS SQL進行CTE查詢),但它通常應該在MVC(使用RoR)環境中工作,那也是非常棒的。

如果有人需要更多的細節或信息,我會張貼ofc。

回答

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秒。

希望這可以幫助其他人面臨同樣的問題。