我有一张员工表,其中包含一般员工信息。另一个包含用户信息的User表。用户可以创建员工。
当用户创建员工时,用户将部门,产品,子产品和区域分配给员工。
用户他们可以访问特定的部门,产品,子产品和区域。
e.g。用户A可以访问D1部门,产品P1(地区=亚洲,美国),P2(地区=亚洲),P3(地区=亚洲,美国)。
Division是Product的父级。每个部门都可以有很多产品。
当我说用户A可以访问产品P1(地区=亚洲,美国)时,这意味着用户A可以添加产品= P1和地区=亚洲或美国的员工。
他无法将员工添加到产品P1或任何其他产品本身的任何其他区域。
如果假设用户A在db中添加了500名员工,则另一位用户B已添加500名其他员工,依此类推。
如何撰写有效的查询以获取我有权访问的员工?
请注意,具有相同访问权限的其他用户可能会添加员工,我也应该能够看到这些员工。
下面是我拥有的数据库架构。
--------------------------------------------------------
-- DDL for Table BI_DIVISION
--------------------------------------------------------
CREATE TABLE "HEADCOUNT_BI"."BI_DIVISION"
( "DIVISION_ID" NUMBER(*,0) NOT NULL
"DIVISION_NAME" VARCHAR2(4000)
) ;
--------------------------------------------------------
-- DDL for Table BI_PRODUCT
--------------------------------------------------------
CREATE TABLE "HEADCOUNT_BI"."BI_PRODUCT"
( "PRODUCT_ID" NUMBER(*,0) NOT NULL ,
"PRODUCT_NAME" VARCHAR2(4000),
"DIVISION_ID" NUMBER(*,0)
) ;
--------------------------------------------------------
-- DDL for Table BI_SUB_PRODUCT
--------------------------------------------------------
CREATE TABLE "HEADCOUNT_BI"."BI_SUB_PRODUCT"
( "SUB_PRODUCT_ID" NUMBER(*,0) NOT NULL,
"SUB_PRODUCT_NAME" VARCHAR2(4000),
"PRODUCT_ID" NUMBER(*,0),
) ;
--------------------------------------------------------
-- DDL for Table BI_REGION
--------------------------------------------------------
CREATE TABLE "HEADCOUNT_BI"."BI_REGION"
( "REGION_ID" NUMBER(*,0) NOT NULL,
"REGION_NAME" VARCHAR2(4000) NOT NULL ENABLE
) ;
--------------------------------------------------------
-- DDL for Table BI_EMPLOYEE
--------------------------------------------------------
CREATE TABLE "HEADCOUNT_BI"."BI_EMPLOYEE"
( "EMP_ID" NUMBER(*,0) NOT NULL ,
"DIVISION_ID" NUMBER(*,0),
"PRODUCT_ID" NUMBER(*,0),
"SUB_PRODUCT_ID" NUMBER(*,0),
"REGION_ID" NUMBER(*,0) ,
"CONFIDENTIAL" VARCHAR2(1) DEFAULT 'Y'
);
--------------------------------------------------------
-- DDL for Table BI_USER
--------------------------------------------------------
CREATE TABLE "HEADCOUNT_BI"."BI_USER"
( "USER_ID" NUMBER(*,0) NOT NULL,
"FIRSTNAME" VARCHAR2(4000),
"LASTNAME" VARCHAR2(4000)
) ;
--------------------------------------------------------
-- DDL for Table BI_USER_ACCESS
--------------------------------------------------------
CREATE TABLE "HEADCOUNT_BI"."BI_USER_ACCESS"
( "USER_ACCESS_ID" NUMBER(*,0) NOT NULL,
"USER_ID" NUMBER(*,0),
"DIVISION_ID" NUMBER(*,0),
"PRODUCT_ID" NUMBER(*,0),
"SUB_PRODUCT_ID" NUMBER(*,0),
"REGION_ID" NUMBER(*,0),
"ACCESS_LEVEL" NUMBER(*,0),
"CONFIDENTIAL" VARCHAR2(1) DEFAULT 'Y'
) ;
Insert into BI_DIVISION (DIVISION_ID,DIVISION_NAME) values (1,'DIVISION 1');
Insert into BI_DIVISION (DIVISION_ID,DIVISION_NAME) values (2,'DIVISION 2');
Insert into BI_PRODUCT (PRODUCT_NAME,DIVISION_ID,PRODUCT_ID) values ('PRODUCT 1',1,1);
Insert into BI_PRODUCT (PRODUCT_NAME,DIVISION_ID,PRODUCT_ID) values ('PRODUCT 2',1,2);
Insert into BI_PRODUCT (PRODUCT_NAME,DIVISION_ID,PRODUCT_ID) values ('PRODUCT 3',2,3);
Insert into BI_PRODUCT (PRODUCT_NAME,DIVISION_ID,PRODUCT_ID) values ('PRODUCT 4',2,4);
Insert into BI_SUB_PRODUCT (SUB_PRODUCT_ID,SUB_PRODUCT_NAME,PRODUCT_ID) values (1,'SUB PRODUCT 1', 1);
Insert into BI_SUB_PRODUCT (SUB_PRODUCT_ID,SUB_PRODUCT_NAME,PRODUCT_ID) values (2,'SUB PRODUCT 2', 1);
Insert into BI_SUB_PRODUCT (SUB_PRODUCT_ID,SUB_PRODUCT_NAME,PRODUCT_ID) values (3,'SUB PRODUCT 3', 2);
Insert into BI_SUB_PRODUCT (SUB_PRODUCT_ID,SUB_PRODUCT_NAME,PRODUCT_ID) values (4,'SUB PRODUCT 4', 2);
Insert into BI_SUB_PRODUCT (SUB_PRODUCT_ID,SUB_PRODUCT_NAME,PRODUCT_ID) values (5,'SUB PRODUCT 5', 3);
Insert into BI_REGION (REGION_ID,REGION_NAME) values (1,'Americas');
Insert into BI_REGION (REGION_ID,REGION_NAME) values (2,'Asia');
Insert into BI_REGION (REGION_ID,REGION_NAME) values (3,'Germany');
Insert into BI_REGION (REGION_ID,REGION_NAME) values (4,'Japan');
Insert into BI_REGION (REGION_ID,REGION_NAME) values (5,'Pacific');
Insert into BI_REGION (REGION_ID,REGION_NAME) values (6,'ROE');
Insert into BI_REGION (REGION_ID,REGION_NAME) values (7,'United Kingdom');
Insert into BI_USER (USER_ID,FIRSTNAME,LASTNAME) values (1,'Adam,'Smith);
Insert into BI_USER (USER_ID,FIRSTNAME,LASTNAME) values (2,'Steve','Jones');
-- user with user id = 1 has access to division 1 , product 1 , sub product 1 in regons americas, asia, germany with ACCESS_LEVEL = write access (2) and also access to confidential data
Insert into BI_USER_ACCESS (USER_ACCESS_ID,USER_ID,DIVISION_ID,PRODUCT_ID,SUB_PRODUCT_ID,REGION_ID,ACCESS_LEVEL, CONFIDENTIAL) values (1,1,1,1,1,1,2,'Y');
Insert into BI_USER_ACCESS (USER_ACCESS_ID,USER_ID,DIVISION_ID,PRODUCT_ID,SUB_PRODUCT_ID,REGION_ID,ACCESS_LEVEL, CONFIDENTIAL) values (1,1,1,1,1,2,2,'Y');
Insert into BI_USER_ACCESS (USER_ACCESS_ID,USER_ID,DIVISION_ID,PRODUCT_ID,SUB_PRODUCT_ID,REGION_ID,ACCESS_LEVEL, CONFIDENTIAL) values (1,1,1,1,1,3,2,'Y');
-- user with user id = 1 has access to division 1 , product 2 , sub product 4 in regons americas, asia, germany with ACCESS_LEVEL = write access (2) and also NO access to confidential data
Insert into BI_USER_ACCESS (USER_ACCESS_ID,USER_ID,DIVISION_ID,PRODUCT_ID,SUB_PRODUCT_ID,REGION_ID,ACCESS_LEVEL, CONFIDENTIAL) values (1,1,1,2,4,1,2,'N');
Insert into BI_USER_ACCESS (USER_ACCESS_ID,USER_ID,DIVISION_ID,PRODUCT_ID,SUB_PRODUCT_ID,REGION_ID,ACCESS_LEVEL, CONFIDENTIAL) values (1,1,1,2,4,2,2,'N');
Insert into BI_USER_ACCESS (USER_ACCESS_ID,USER_ID,DIVISION_ID,PRODUCT_ID,SUB_PRODUCT_ID,REGION_ID,ACCESS_LEVEL, CONFIDENTIAL) values (1,1,1,2,4,3,2,'N');
-- employees in division 1 , product 1, sub product 1 and region americas and not confi.
Insert into BI_EMPLOYEE (EMP_ID,DIVISION_ID,PRODUCT_ID,SUB_PRODUCT_ID,REGION_ID,CONFIDENTIAL) values (1,'1','1','1',1,'N');
Insert into BI_EMPLOYEE (EMP_ID,DIVISION_ID,PRODUCT_ID,SUB_PRODUCT_ID,REGION_ID,CONFIDENTIAL) values (2,'1','1','1',1,'N');
Insert into BI_EMPLOYEE (EMP_ID,DIVISION_ID,PRODUCT_ID,SUB_PRODUCT_ID,REGION_ID,CONFIDENTIAL) values (3,'1','1','1',2,'N');
Insert into BI_EMPLOYEE (EMP_ID,DIVISION_ID,PRODUCT_ID,SUB_PRODUCT_ID,REGION_ID,CONFIDENTIAL) values (4,'1','1','1',2,'N');
Insert into BI_EMPLOYEE (EMP_ID,DIVISION_ID,PRODUCT_ID,SUB_PRODUCT_ID,REGION_ID,CONFIDENTIAL) values (5,'1','1','1',7,'N');
Insert into BI_EMPLOYEE (EMP_ID,DIVISION_ID,PRODUCT_ID,SUB_PRODUCT_ID,REGION_ID,CONFIDENTIAL) values (11,'1','1','2',1,'N');
Insert into BI_EMPLOYEE (EMP_ID,DIVISION_ID,PRODUCT_ID,SUB_PRODUCT_ID,REGION_ID,CONFIDENTIAL) values (12,'1','1','2',2,'N');
Insert into BI_EMPLOYEE (EMP_ID,DIVISION_ID,PRODUCT_ID,SUB_PRODUCT_ID,REGION_ID,CONFIDENTIAL) values (13,'1','1','2',3,'N');
Insert into BI_EMPLOYEE (EMP_ID,DIVISION_ID,PRODUCT_ID,SUB_PRODUCT_ID,REGION_ID,CONFIDENTIAL) values (14,'1','1','2',2,'N');
Insert into BI_EMPLOYEE (EMP_ID,DIVISION_ID,PRODUCT_ID,SUB_PRODUCT_ID,REGION_ID,CONFIDENTIAL) values (15,'1','1','2',3,'N');
Insert into BI_EMPLOYEE (EMP_ID,DIVISION_ID,PRODUCT_ID,SUB_PRODUCT_ID,REGION_ID,CONFIDENTIAL) values (111,'2','3','5',1,'N');
Insert into BI_EMPLOYEE (EMP_ID,DIVISION_ID,PRODUCT_ID,SUB_PRODUCT_ID,REGION_ID,CONFIDENTIAL) values (112,'2','3','5',2,'N');
Insert into BI_EMPLOYEE (EMP_ID,DIVISION_ID,PRODUCT_ID,SUB_PRODUCT_ID,REGION_ID,CONFIDENTIAL) values (113,'2','3','5',3,'N');
Insert into BI_EMPLOYEE (EMP_ID,DIVISION_ID,PRODUCT_ID,SUB_PRODUCT_ID,REGION_ID,CONFIDENTIAL) values (114,'2','3','5',4,'N');
Insert into BI_EMPLOYEE (EMP_ID,DIVISION_ID,PRODUCT_ID,SUB_PRODUCT_ID,REGION_ID,CONFIDENTIAL) values (115,'2','3','5',5,'N');
以下是我到目前为止所写的查询,但我不确定这是否是最佳方式。
SELECT
*
FROM
BI_EMPLOYEE e
JOIN BI_USER_ACCESS uad On uad.DIVISION_ID = e.DIVISION_ID and uad.USER_ID = 137
JOIN BI_USER_ACCESS uap On uap.PRODUCT_ID = e.PRODUCT_ID and uap.USER_ID = 137
JOIN BI_USER_ACCESS uasp On uasp.SUB_PRODUCT_ID = e.SUB_PRODUCT_ID and uasp.USER_ID = 137
JOIN BI_USER_ACCESS uar On uar.REGION_ID = e.REGION_ID and uar.SUB_PRODUCT_ID = e.SUB_PRODUCT_ID and uar.USER_ID = 137
编辑1:
我已经使用db脚本和一些示例数据更新了我的问题。
答案 0 :(得分:1)
用户可以访问的员工列表将通过以下查询提供:
SELECT *
FROM bi_employee e
WHERE EXISTS (SELECT NULL
FROM bi_user_access ua
WHERE ua.division_id = e.division_id
AND ua.product_id = e.product_id
AND ua.sub_product_id = e.sub_product_id
AND ua.region_id = e.region_id
AND (e.confidential = 'N' OR ua.confidential = 'Y')
AND ua.user_id = :user_id);
使用您的数据样本,用户1可以访问员工1到4。
答案 1 :(得分:1)
您的问题明确询问用什么最快方式获取用户可访问的员工列表。所以我会回答这个问题。
几年前我曾经在类似的系统上工作,我们可以非常快速地评估这些信息。原则是一样的,但我们有更多的标准(部门,产品,地区,国家,城市,单位,部门等)。
如果性能非常重要,则值得将查询结果实现到表中,例如ACL_CACHE(USER_ID, EMP_ID)
让用户可以访问员工的查询变得微不足道了:
SELECT EMP_ID
FROM ACL_CACHE
WHERE USER_ID = ####
如果您想限制用户可以根据访问级别查看的结果,您还可以将ACL_CACHE
表加入其他查询。
这非常有效,并且在与大量员工和/或用户合作时带来了实实在在的好处;我们通常使用约500,000条记录。
显然缺点是你需要让ACL_CACHE
表保持最新状态。这意味着其他一些事务可能会变得有点慢。例如,在添加新员工时,您还需要为可以查看新员工的所有用户添加记录到ACL_CACHE
表。
根据我们的经验,这类交易的额外延迟对于用户来说并不明显,并且非常值得牺牲所有只读交易的工作速度快一个数量级。
或者,您可以将ACL_CACHE
表更新为一次大量更新中的夜间作业,如果您能够将数据长达24小时“旧”。由于您的表名为“HEADCOUNT_BI”,我猜测如果您的员工人数报告始终准确到昨天晚上,那么这可能是可以接受的。
答案 2 :(得分:0)
此查询将USER_ACCESS
表连接到EMPLOYEE
表。它会过滤所提供的USER_ACCESS
上的USER_ID
表格(137),然后加入EMPLOYEE
只会返回具有相同DIVISION_ID
和PRODUCT_ID
的员工记录存在于USER_ACCESS
表中。
select e.*
from BI_USER_ACCESS a
join BI_EMPLOYEE e
on a.DIVISION_ID = e.DIVISION_ID
and a.PRODUCT_ID = e.PRODUCT_ID
where a.USER_ID = 137
它只选择EMPLOYEE
数据(例如*),但您可以(比方说)将USER_ACCESS
表加入USER
表并返回用户数据(如果需要)。但是,要根据EMPLOYEE
中的2个字段返回USER_ACCESS
数据,应该这样做。
那能得到你想要的吗?