權限,可分為“功能(操作)權限”和數據權限兩種,在系統中,兩種權限應當同時有效。例如,在windows系統中,某用戶具有新建一個文件的功能權限,該用戶在C盤沒有寫權限,但在D盤有寫權限;則該用戶不能把他創建的文件保存在C盤而只能保存在D盤。
在上述例子中,能否創建文件是由功能權限來控制的,能否保存文件是由數據權限進行控制的。只有兩者同時有效,用戶的業務才能順利進行。
簡單地說,權限管理就是對資源的管理。權限管理的目的就是建立分配資源的規則,以便用戶能夠通過這套規則,獲取他們應該獲得的資源。
2 功能權限:
也叫操作權限,指的是允許或拒絕用戶使用系統提供的某個功能。
2 數據權限:
指的是允許或拒絕用戶進行某個數據的增刪改查操作。
2 授權:
指的是分配具體的權限給具體的人。
2 鑒權:
指的是對具體人的行為,根據權限規則進行合法性鑒別。
對于授權來說,需要定義的有且只有權限和授權對象兩個要素。簡而述之,對于功能操作就是“什么功能授權給哪個用戶來操作”。同樣,對于數據,就是“什么數據授權給哪個用戶來操作”。
一般情況下,我們并不會對單一的功能/數據進行單用戶的授權管理,因為這樣用戶操作起來顯然非常麻煩。為了方便和簡化操作,一般的授權規則是:
哪些功能/數據授權給哪些用戶
在實際的授權管理中,我們總是根據業務的需求,將一些在業務上不可分割的、需要允許用戶一起使用的功能,組合成一個權限集合進行統一授權。對于這樣的權限集合,我們一般稱之為“角色”。也就是說,我們通過角色來定義用戶被允許使用哪些功能和訪問哪些數據。當然,我們一般把功能和數據分開來進行授權,以便獲得更加靈活的權限規則配置方法,以適應更廣泛的授權需求。
由于某些不同用戶在該業務上需要具有相同的權限,那么這些不同的用戶在特定的業務上就具有了共性,可以作為一個抽象的用戶來進行權限的授予。授權管理使用的抽象的用戶,也就是用戶集合,除了普遍使用的“用戶組”外,還可以引用別的業務中所使用的對象。例如組織機構管理中的“機構/部門”、“職位”和工作流中使用的“崗位”等,在授權管理中都是作為用戶集合使用,本質毫無二致。
通過讓抽象的用戶扮演角色,即可使這個抽象的用戶所代表的真實用戶獲得完成業務所需的權限。通過這樣的方式,可以簡化授權管理,方便用戶操作。
將特定的權限授予特定的人群的過程,我們稱之為“授權”。為了能夠方便地進行授權操作,我們必須要有一個能夠提供合理授權方法的用戶界面。
對于一個可擴展的系統來說,意味著功能是不斷變化的。為了適應這種不能事先確定的變化,必須將功能權限進行分散管理。分散管理的好處如下:
2 天然地支持業務的動態變化,系統實現簡單。
2 權限的調整范圍可控制在局部范圍,方便權限的管理和操作。
功能權限是單維度的,可以通過簡單的在功能列表或功能樹上進行勾選來確定一個“角色”所允許的功能操作。然后,讓相應用戶成為該“角色”的“扮演者”。這樣就可以把該角色所允許的功能操作授權給指定的用戶了。
如果需要有更多的不同角色,那么新建角色,勾選不同的被允許的功能操作,并分別讓相應的用戶成為新角色的成員即可。
如果某個特定用戶張三需要額外的一個權限,那么新建一個允許該功能操作的角色,并讓張三成為該角色成員即可使張三擁有額外的權限。
如果某個特定用戶李四需要比同一項目組的其他人少一個權限,那么新建一個拒絕該功能操作的角色,并讓李四成為該角色成員即可使李四不能進行該項操作。因為在鑒權過程中,拒絕的優先級要高于允許。
數據在系統中共同的特性有如下維度:
2 業務維度:不同的業務產生不同的數據
2 生產者維度:相同的業務會有多個數據生產者和生產部門
有些業務需要用戶訪問其他業務的數據,或者是其他數據生產者中特定生產者的生產的數據。簡單的說,就是數據權限的授予必須支持跨業務和跨部門。
由于數據的特殊性質,實際上在有限范圍內的授權比功能權限的授予更加方便。因為生產部門和生產者具有天然的分類屬性,所以象“本機構”、“本部門”、“本人”這些對生產者維度的進一步抽象就有了用武之地。
在不對業務維度做限定的情況下,就可以配置例如“允許本部門的成員管理(增刪改查)本部門的數據”這樣的權限規則。那么對于不同部門的用戶,這條共同的規則所產生的效果并不相同,具體的效果是與用戶所在的部門的業務和產生的數據相對應的。
根據以上分析,我們可以內置一些抽象規則,例如:
2 允許管理本機構(含下級機構/部門)的數據
2 允許管理本部門(含下級部門)的數據
2 僅允許管理本部門的數據
2 僅允許管理本人的數據
2 允許查看本機構(含下級機構/部門)的數據
2 允許查看本部門(含下級部門)的數據
2 僅允許查看本部門的數據
2 僅允許查看本人的數據
一般數據權限的授予只能局限于符合高度抽象規則所限定的范圍。如果要在這個范圍之外的數據進行授權,例如想讓財務部的人訪問采購部的數據,顯然是一般數據授權所不能支持的。
這個時候,我們就必須要提供用戶在角色中自由定義允許或禁止用戶訪問的數據集的方法。上面說過,一個數據需要在業務和生產者兩個維度上進行描述,才能確定數據。那么在定義角色所允許訪問的數據集時,因為授權分散在不同的業務中,所以業務是確定的,剩下的就是需要指定一個或多個生產者。在這里,生產者可以使用抽象方法進行歸納分類。
最后,類同于功能權限的授權方式,我們把定義好的角色分配給一個抽象的用戶即可將一個自定義的數據權限授予抽象用戶代表的真實用戶。
權限管理的具體實現方法,離不開數據結構的支持。相對來說,有具體的數據結構,我們也更容易理解權限的管理機制。在討論權限之前,我們還需要先了解權限管理的對象:功能資源和數據資源。這些資源同樣需要一個數據結構去進行定義。
組織機構表:
- CREATE TABLE Sys_Organization(
- [ID] VARCHAR(36) PRIMARY KEY NONCLUSTERED DEFAULT NEWID(),
- [SN] BIGINT CONSTRAINT IX_Sys_Organization UNIQUE CLUSTERED IDENTITY(1,1), --自增序列
- [ParentId] VARCHAR(36), --父節點ID
- [NodeType] INT NOT NULL, --節點類型:1、機構;2、部門;3、職位
- [Index] INT, --序號
- [Code] VARCHAR(32), --編碼
- [Name] NVARCHAR(32) NOT NULL, --名稱
- [Alias] NVARCHAR(16), --別名/簡稱
- [FullName] NVARCHAR(32), --全稱
- [PostId] VARCHAR(36), --崗位ID,字典
- [Validity] BIT DEFAULT 0 NOT NULL, --是否有效:0、無效;1、有效
- [CreatorUserId] VARCHAR(36), --創建人ID
- [CreateTime] DATETIME DEFAULT GETDATE() NOT NULL --創建時間
- )
- GO
用戶表:
- CREATE TABLE Sys_User(
- [ID] VARCHAR(36) PRIMARY KEY NONCLUSTERED, --此ID與主數據ID相同
- [SN] BIGINT CONSTRAINT IX_Sys_User UNIQUE CLUSTERED IDENTITY(1,1), --自增序列
- [Name] NVARCHAR(16) NOT NULL, --用戶名
- [LoginName] VARCHAR(36) NOT NULL, --登錄名
- [Password] VARCHAR(32) DEFAULT 'e10adc3949ba59abbe56e057f20f883e' NOT NULL, --登錄密碼,保存密碼的MD5值,初始密碼123456
- [Description] NVARCHAR(MAX), --描述
- [BuiltIn] BIT DEFAULT 0 NOT NULL, --是否預置:0、自定;1、預置
- [Validity] BIT DEFAULT 0 NOT NULL, --是否有效:0、無效;1、有效
- [CreatorUserId] VARCHAR(36), --創建人ID
- [CreateTime] DATETIME DEFAULT GETDATE() NOT NULL --創建時間
- )
- GO
3.2 資源
模塊表:
- CREATE TABLE Sys_Module(
- [ID] VARCHAR(36) PRIMARY KEY NONCLUSTERED DEFAULT NEWID(),
- [SN] BIGINT CONSTRAINT IX_Sys_Module UNIQUE CLUSTERED IDENTITY(1,1), --自增序列
- [ParentId] VARCHAR(36), --父模塊ID
- [Level] INT NOT NULL, --模塊級別:0、主窗體模塊;1、普通業務模塊;2、業務子模塊
- [Name] NVARCHAR(64) NOT NULL, --名稱
- [Location] VARCHAR(MAX) NOT NULL, --文件安裝路徑
- [DataTable] NVARCHAR(64), --模塊主數據表名稱
- [Description] NVARCHAR(MAX), --描述
- [RegisterTime] DATETIME DEFAULT GETDATE() NOT NULL --模塊注冊時間
- )
- GO
模塊功能表:
- CREATE TABLE Sys_ModuleAction(
- [ID] VARCHAR(36) PRIMARY KEY NONCLUSTERED DEFAULT NEWID(),
- [SN] BIGINT CONSTRAINT IX_Sys_ModuleAction UNIQUE CLUSTERED IDENTITY(1,1), --自增序列
- [ModuleId] VARCHAR(36) FOREIGN KEY REFERENCES Sys_Module(ID) ON DELETE CASCADE NOT NULL, --模塊注冊ID
- [Name] NVARCHAR(64) NOT NULL, --名稱
- [SubModuleId] VARCHAR(36) FOREIGN KEY REFERENCES Sys_Module(ID), --子模塊ID(功能作為子模塊入口時)
- [Description] NVARCHAR(MAX) --描述
- )
- GO
3.2.2 業務數據
物資數據表:
- CREATE TABLE MDG_Material(
- [MID] VARCHAR(36) PRIMARY KEY NONCLUSTERED FOREIGN KEY REFERENCES MasterData(ID) ON DELETE CASCADE, --主數據索引ID
- [SN] BIGINT CONSTRAINT IX_MDG_Material UNIQUE CLUSTERED IDENTITY(1,1), --自增序列
- [Index] INT, --序號
- [BarCode] VARCHAR(16), --條形碼
- [Brand] NVARCHAR(16), --品牌
- [Model] NVARCHAR(32), --型號
- [Size] DECIMAL(20,6), --規格
- [SizeType] VARCHAR(36) FOREIGN KEY REFERENCES MasterData(ID), --規格單位ID,字典
- [Color] NVARCHAR(8), --顏色
- [Material] NVARCHAR(8), --材質
- [StorageType] VARCHAR(36) FOREIGN KEY REFERENCES MasterData(ID), --存儲方式ID,字典
- [Description] NVARCHAR(MAX), --描述
- [Enable] BIT DEFAULT 1 NOT NULL, --是否可用:0、不可用;1、可用
- [CreatorDeptId] VARCHAR(36) FOREIGN KEY REFERENCES Sys_Organization(ID), --創建部門ID
- [CreatorUserId] VARCHAR(36) FOREIGN KEY REFERENCES Sys_User(ID) NOT NULL, --創建人ID
- [CreateTime] DATETIME DEFAULT GETDATE() NOT NULL --創建時間
- )
- GO
3.3 RBAC模型
角色表:
- CREATE TABLE Sys_Role(
- [ID] VARCHAR(36) PRIMARY KEY NONCLUSTERED DEFAULT NEWID(),
- [SN] BIGINT CONSTRAINT IX_Sys_Role UNIQUE CLUSTERED IDENTITY(1,1), --自增序列
- [Name] NVARCHAR(64) NOT NULL, --名稱
- [Description] NVARCHAR(MAX), --描述
- [BuiltIn] BIT DEFAULT 0 NOT NULL, --是否預置:0、自定;1、預置
- [CreatorUserId] VARCHAR(36) FOREIGN KEY REFERENCES Sys_User(ID) NOT NULL, --創建人ID
- [CreateTime] DATETIME DEFAULT GETDATE() NOT NULL --創建時間
- )
- GO
3.3.2 角色成員
角色成員(用戶)表:
- CREATE TABLE Sys_Role_User(
- [ID] VARCHAR(36) PRIMARY KEY NONCLUSTERED DEFAULT NEWID(),
- [SN] BIGINT CONSTRAINT IX_Sys_Role_User UNIQUE CLUSTERED IDENTITY(1,1), --自增序列
- [RoleId] VARCHAR(36) FOREIGN KEY REFERENCES Sys_Role(ID) ON DELETE CASCADE NOT NULL, --角色ID
- [UserId] VARCHAR(36) FOREIGN KEY REFERENCES Sys_User(ID) NOT NULL, --用戶ID
- [CreatorUserId] VARCHAR(36) FOREIGN KEY REFERENCES Sys_User(ID) NOT NULL, --創建人ID
- [CreateTime] DATETIME DEFAULT GETDATE() NOT NULL --創建時間
- )
- GO
3.3.3 角色權限
角色權限表:
- CREATE TABLE Sys_RolePerm_Module(
- [ID] VARCHAR(36) PRIMARY KEY NONCLUSTERED DEFAULT NEWID(),
- [SN] BIGINT CONSTRAINT IX_Sys_RolePerm_Module UNIQUE CLUSTERED IDENTITY(1,1), --自增序列
- [RoleId] VARCHAR(36) FOREIGN KEY REFERENCES Sys_Role(ID) ON DELETE CASCADE NOT NULL, --角色ID
- [ModuleId] VARCHAR(36) FOREIGN KEY REFERENCES Sys_Module(ID) ON DELETE CASCADE NOT NULL, --模塊注冊ID
- [Action] INT DEFAULT 0 NOT NULL, --操作行為:0、拒絕訪問;1、允許訪問
- [Mode] INT DEFAULT 0 NOT NULL, --數據授權范圍:-1、僅本人;0、僅本部門;1、本部門所有;2、本機構所有;3、全部;4、自定義
- [Permission] INT DEFAULT 0 NOT NULL, --數據權限:0、只讀;1、讀寫
- [CreatorUserId] VARCHAR(36) FOREIGN KEY REFERENCES Sys_User(ID) NOT NULL, --創建人ID
- [CreateTime] DATETIME DEFAULT GETDATE() NOT NULL --創建時間
- )
- GO
3.4 如何獲取用戶的權限
獲取指定用戶的功能權限需要先獲取該用戶可以訪問的模塊,此功能我們可以使用一個數據庫表值函數來返回指定用戶被授權訪問的模塊ID列表。在用戶啟動某一模塊時,使用一個數據庫表值函數來返回被授權功能的ID列表。
在用戶訪問數據時,我們需要對用戶訪問的數據根據授權情況進行過濾,并為這些數據加上權限標記,以便告知系統用戶被許可的操作方式(只讀/讀寫)。
我們可以在數據訪問層前端進行數據的過濾和標記,這種方式的優點是:
2 安全,外部沒有注入、篡改的機會。
2 高效,數據訪問層獲取的數據已經經過篩選,不會返回無效的數據。
2 兼容性好,和應用系統完全無關,即使應用系統通過存儲過程處理數據,也能完全兼容。
簡單地來說,這種機制只需要你在獲取數據的時候使用inner join一個表值函數,輸入函數的參數(模塊ID,登錄部門ID,用戶ID)即可。
- /*****表值函數:獲取當前登錄用戶允許訪問模塊*****/
-
- CREATE FUNCTION Get_PermModule(
- @UserId VARCHAR(36), --當前登錄用戶ID
- @OrgId VARCHAR(36) --當前登錄部門ID
- )
-
- RETURNS TABLE AS
-
- RETURN
- with Roles as(
- select R.RoleId --獲取當前用戶作為成員的角色ID
- from Sys_Role_User R
- where R.UserId = @UserId
- union
- select R.RoleId --獲取當前用戶所在用戶組作為成員的角色ID
- from Sys_Role_UserGroupR
- join Sys_UserGroupMemberG on G.GroupId = R.GroupId
- and G.UserId = @UserId
- union
- select R.RoleId --獲取當前用戶的職位作為成員的角色ID
- from Sys_Role_PositionR
- join Sys_User_Org P on P.OrgId = R.OrgId
- and P.UserId = @UserId
- join Sys_OrganizationO on O.ID = R.OrgId
- and O.ParentId = @OrgId
- union
- select R.RoleId --獲取當前用戶的職位作為成員的角色ID(職位對應部門被合并)
- from Sys_Role_PositionR
- join Sys_User_Org P on P.OrgId = R.OrgId
- and P.UserId = @UserId
- join Sys_OrganizationO on O.ID = R.OrgId
- join Sys_OrgMerger OM on OM.MergerOrgId = O.ParentId
- and OM.OrgId = @OrgId
- )
-
- select M.ModuleId from Roles R
- join Sys_RolePerm_ModuleM on M.RoleId = R.RoleId
- group by M.ModuleId
- having min(M.Action) > 0
-
- GO
4.1.2 獲取授權功能
- CREATE FUNCTION Get_PermAction(
- @ModuleId VARCHAR(36), --模塊ID
- @UserId VARCHAR(36), --當前登錄用戶ID
- @OrgId VARCHAR(36) --當前登錄部門ID
- )
-
- RETURNS TABLE AS
-
- RETURN
- with Roles as(
- select R.RoleId --獲取當前用戶作為成員的角色ID
- from Sys_Role_User R
- where R.UserId = @UserId
- union
- select R.RoleId --獲取當前用戶所在用戶組作為成員的角色ID
- from Sys_Role_UserGroupR
- join Sys_UserGroupMemberG on G.GroupId = R.GroupId
- and G.UserId = @UserId
- union
- select R.RoleId --獲取當前用戶的職位作為成員的角色ID
- from Sys_Role_PositionR
- join Sys_User_Org P on P.OrgId = R.OrgId
- and P.UserId = @UserId
- join Sys_OrganizationO on O.ID = R.OrgId
- and O.ParentId = @OrgId
- union
- select R.RoleId --獲取當前用戶的職位作為成員的角色ID(職位對應部門被合并)
- from Sys_Role_PositionR
- join Sys_User_Org P on P.OrgId = R.OrgId
- and P.UserId = @UserId
- join Sys_OrganizationO on O.ID = R.OrgId
- join Sys_OrgMerger OM on OM.MergerOrgId = O.ParentId
- and OM.OrgId = @OrgId
- )
-
- select A.ActionId from Roles R
- join Sys_RolePerm_ActionA on A.RoleId = R.RoleId
- join Sys_ModuleActionM on M.ID = A.ActionId
- and M.ModuleId = @ModuleId
- group by A.ActionId
- having min(A.Action) > 0
-
- GO
- CREATE FUNCTION DataPerm_Org(
- @ModuleId VARCHAR(36), --模塊ID
- @UserId VARCHAR(36), --當前登錄用戶ID
- @OrgId VARCHAR(36) --當前登錄部門ID
- )
-
- RETURNS @PermScope TABLE(
- OrgId VARCHAR(36),
- Permission INT
- ) AS
-
- BEGIN
- DECLARE @Mode INT
- DECLARE @Permission INT
-
- select @Mode = Mode, @Permission = Permission --獲取指定模塊對于當前登錄用戶和登錄部門的數據授權訪問范圍代碼和權限代碼
- from Get_PermData(@ModuleId, @UserId, @OrgId)
-
- if @Mode = 0
- insert into @PermScope --授權訪問范圍為僅本部門時,返回本部門(當前登錄部門)ID、合并到該部門的部門ID和權限代碼
- select @OrgId, @Permission
- union
- select MergerOrgId, @Permission
- from Sys_OrgMerger
- where OrgId = @OrgId
-
- else if @Mode between 1 and 3
- begin
- if @Mode = 2
- select @OrgId = dbo.Get_SupOrg(@OrgId, 1) --獲取上級機構ID
- if @Mode = 3
- select @OrgId = dbo.Get_SupOrg(@OrgId, 0) --獲取根機構ID
- insert into @PermScope --授權訪問范圍為本部門及下屬、本機構及下屬、全部時,返回本部門(當前登錄部門)和相應機構/部門ID、合并到上述部門的部門ID和權限代碼
- select ID, @Permission from Get_SubOrg(@OrgId)
- end
-
- else if @Mode = 4
- insert into @PermScope --自定義授權范圍和權限代碼
- select DC.OrgId, max(DC.Permission)
- from Sys_RolePerm_DataCustomDC
- join Sys_RolePerm_ModuleM on M.ID = DC.Perm_ModuleId
- and M.ModuleId = @ModuleId
- join Sys_OrganizationO on O.ID = DC.OrgId
- and O.Validity = 1
- group by DC.OrgId
- union
- select OM.MergerOrgId, max(DC.Permission)
- from Sys_RolePerm_DataCustomDC
- join Sys_RolePerm_ModuleM on M.ID = DC.Perm_ModuleId
- and M.ModuleId = @ModuleId
- join Sys_OrgMerger OM on OM.OrgId = DC.OrgId
- group by OM.MergerOrgId
-
- insert into @PermScope --無歸屬部門的數據可被所有人訪問
- select 'All', @Permission
-
- RETURN
- END
- GO
- CREATE FUNCTION DataPerm_User(
- @ModuleId VARCHAR(36), --模塊ID
- @UserId VARCHAR(36), --當前登錄用戶ID
- @OrgId VARCHAR(36) --當前登錄部門ID
- )
-
- RETURNS @PermScope TABLE(
- UserId VARCHAR(36),
- Permission INT
- ) AS
-
- BEGIN
- DECLARE @Mode INT
- DECLARE @Permission INT
-
- select @Mode = Mode, @Permission = Permission --獲取指定模塊對于當前登錄用戶和登錄部門的數據授權訪問范圍代碼和權限代碼
- from Get_PermData(@ModuleId, @UserId, @OrgId)
-
- if @Mode <0
- insert into @PermScope
- select @UserId, @Permission --授權訪問范圍為僅本人時,返回本人ID和權限代碼
-
- RETURN
- END
- GO
- CREATE FUNCTION Get_PermData(
- @ModuleId VARCHAR(36), --模塊ID
- @UserId VARCHAR(36), --當前登錄用戶ID
- @OrgId VARCHAR(36) --當前登錄部門ID
- )
-
- RETURNS TABLE AS
-
- RETURN
- with Roles as(
- select R.RoleId --獲取當前用戶作為成員的角色ID
- from Sys_Role_User R
- where R.UserId = @UserId
- union
- select R.RoleId --獲取當前用戶所在用戶組作為成員的角色ID
- from Sys_Role_UserGroupR
- join Sys_UserGroupMemberG on G.GroupId = R.GroupId
- and G.UserId = @UserId
- union
- select R.RoleId --獲取當前用戶的職位作為成員的角色ID
- from Sys_Role_PositionR
- join Sys_User_Org P on P.OrgId = R.OrgId
- and P.UserId = @UserId
- join Sys_OrganizationO on O.ID = R.OrgId
- and O.ParentId = @OrgId
- union
- select R.RoleId --獲取當前用戶的職位作為成員的角色ID(職位對應部門被合并)
- from Sys_Role_PositionR
- join Sys_User_Org P on P.OrgId = R.OrgId
- and P.UserId = @UserId
- join Sys_OrganizationO on O.ID = R.OrgId
- join Sys_OrgMerger OM on OM.MergerOrgId = O.ParentId
- and OM.OrgId = @OrgId
- )
-
- select max(M.Permission) as Permission, max(M.Mode) as Mode from Roles R
- join Sys_RolePerm_ModuleM on M.RoleId = R.RoleId
- and M.ModuleId = @ModuleId
- group by M.ModuleId
-
- GO
- CREATE FUNCTION Get_SupOrg(
- @DeptId VARCHAR(36), --部門ID
- @Type INT --機構類型:0、根機構;1、上級機構
- )
-
- RETURNS NVARCHAR(36) AS
- BEGIN
-
- DECLARE @NodeType INT = 0
- DECLARE @ParentId VARCHAR(36)
-
- while @NodeType !=1
- begin
- select @NodeType = NodeType * @Type, @ParentId = ParentId from Sys_Organizationwhere ID = @DeptId
- if @ParentId is null
- set @NodeType = 1
- if @NodeType != 1
- set @DeptId = @ParentId
- end
-
- RETURN @DeptId
- END
- GO
- CREATE FUNCTION Get_SubOrg(
- @OrgId VARCHAR(36) --組織機構ID
- )
-
- RETURNS TABLE AS
-
- RETURN
- with
- OrgList as (
- select @OrgId as ID
- union all
- select O.ID from Sys_OrganizationO
- join OrgList L on L.ID = O.ParentId
- where Validity = 1
- and NodeType < 3),
- MergerOrg as(
- select OM.MergerOrgId as ID from OrgList OL
- join Sys_OrgMerger OM on OM.OrgId = OL.ID
- union all
- select O.ID from Sys_OrganizationO
- join MergerOrg M on M.ID = O.ParentId
- where Validity = 1
- and NodeType < 3)
-
- select ID from OrgList
- union
- select ID from MergerOrg
-
- GO
|