下面是一个简化的DDL和DML来表示我一直在燃烧大量脑部物质的东西。长时间阅读器,第一次海报,希望不会破坏任何SO礼仪或在这篇文章中使用太多墨水。
可以为业务组织内的某些单元打开或关闭(允许)资源。我在下面选择了一个快捷方式,只是定义了Sections表,但也有一个Companies和Divisions表。
我可以使用Company,Division和Section的任意组合设置资源的权限。
仅为部门设置的权限集合将超出仅为部门或公司设置的权限。
为Company,Division和Section设置为NULL的权限为NULL意味着如果没有特定于该业务单位的权限,那么他们将根据此“默认”值访问资源。
目前,我通过首先使用最具体的WHERE子句(查找ResourcePermission,其中Company,Division和Section等于所提供的业务单位的SELECT)来为业务部门找到最相关的权限。最不具体(所有三个都为NULL)。共有八个选择。
如果以后有更多的业务水平需要添加(部门,用户组......),那么SELECT会对兔子的繁殖习惯有所了解。
有没有更好的方法在SQL中实现这一点,或者它更适合在程序代码中实现这一点。
运行下面的最终SELECT将为您提供9个资源权限。我只想要指定业务单位最具特色的三个。
CREATE TABLE Resources (
ResourceID varchar(20) NOT NULL PRIMARY KEY NONCLUSTERED,
ResourceName varchar(100) NOT NULL)
GO
CREATE TABLE ResourcePermissions (
PermissionID int identity(1,1) PRIMARY KEY NONCLUSTERED,
ResourceID varchar(20) CONSTRAINT [FK_Resources] FOREIGN KEY REFERENCES Resources(ResourceID),
Company varchar(10) NULL,
Division varchar(10) NULL,
Section varchar(20) NULL,
Permitted char(1) NOT NULL)
GO
CREATE TABLE Sections (
Company varchar(10) NOT NULL,
Division varchar(10) NOT NULL,
Section varchar(20) NOT NULL,
SectionName varchar(50) NOT NULL,
CONSTRAINT PK_Sections PRIMARY KEY (Company, Division, Section) )
GO
INSERT INTO Sections VALUES('Company 1','Division A','Red Section','Redskins')
INSERT INTO Sections VALUES ('Company 1','Division A','Blue Section','Bluejays')
INSERT INTO Sections VALUES ('Company 1','Division B','Red Section','Redskins')
INSERT INTO Sections VALUES ('Company 1','Division B','Blue Section','Bluejays')
INSERT INTO Sections VALUES ('Company 1','Division C','Red Section','Redskins')
INSERT INTO Sections VALUES ('Company 1','Division C','Blue Section','Bluejays')
INSERT INTO Sections VALUES('Company 2','Division A','Red Section','Redskins')
INSERT INTO Sections VALUES ('Company 2','Division A','Blue Section','Bluejays')
INSERT INTO Sections VALUES ('Company 2','Division B','Red Section','Redskins')
INSERT INTO Sections VALUES ('Company 2','Division B','Blue Section','Bluejays')
INSERT INTO Sections VALUES ('Company 2','Division C','Red Section','Redskins')
INSERT INTO Sections VALUES ('Company 2','Division C','Blue Section','Bluejays')
INSERT INTO Resources VALUES('Irish','Irish Resource')
INSERT INTO Resources VALUES('English','English Resource')
INSERT INTO Resources VALUES('French','French Resource')
INSERT INTO ResourcePermissions VALUES('Irish', NULL, NULL, NULL, 'Y')
INSERT INTO ResourcePermissions VALUES('Irish', NULL, NULL, 'Blue Section', 'N')
INSERT INTO ResourcePermissions VALUES('Irish', NULL, 'Division A', 'Blue Section', 'N')
INSERT INTO ResourcePermissions VALUES('Irish', 'Company 1', 'Division A', NULL, 'N')
INSERT INTO ResourcePermissions VALUES('French', NULL, 'Division B', 'Blue Section', 'Y')
INSERT INTO ResourcePermissions VALUES('French', 'Company 2', NULL, 'Blue Section', 'N')
INSERT INTO ResourcePermissions VALUES('French', 'Company 1', NULL, 'Blue Section', 'Y')
INSERT INTO ResourcePermissions VALUES('French', NULL, NULL, 'Blue Section', 'Y')
INSERT INTO ResourcePermissions VALUES('French', NULL, 'Division B', 'Red Section', 'N')
INSERT INTO ResourcePermissions VALUES('French', NULL, 'Division C', 'Red Section', 'Y')
INSERT INTO ResourcePermissions VALUES('English', NULL, 'Division B', 'Blue Section', 'Y')
INSERT INTO ResourcePermissions VALUES('English', 'Company 2', NULL, 'Blue Section', 'N')
INSERT INTO ResourcePermissions VALUES('English', NULL, 'Division A', 'Blue Section', 'N')
INSERT INTO ResourcePermissions VALUES('English', NULL, NULL, 'Blue Section', 'N')
INSERT INTO ResourcePermissions VALUES('English', 'Company 1', 'Division A', 'Blue Section', 'Y')
SELECT ResourceID, Company, Division, Section, Permitted
FROM ResourcePermissions
WHERE (Company = 'Company 1' OR Company IS NULL)
AND (Division = 'Division A' OR Division IS NULL)
AND (Section = 'Blue Section' OR Section IS NULL)
ORDER BY ResourceID
答案 0 :(得分:1)
如果您使用分析查询,可以在一个查询中完成此操作,但我个人会使用临时表和多个查询。
CREATE TEMPORARY TABLE _ResourceDetail AS
SELECT ResourceID
, Company
, Division
, Section
, Permitted
, CASE WHEN Company IS NULL THEN 0 ELSE 1 END
+ CASE WHEN Division IS NULL THEN 0 ELSE 2 END
+ CASE WHEN Section IS NULL THEN 0 ELSE 4 END
AS Priority
FROM ResourcePermissions
WHERE (Company = 'Company 1' OR Company IS NULL)
AND (Division = 'Division A' OR Division IS NULL)
AND (Section = 'Blue Section' OR Section IS NULL);
CREATE TEMPORARY TABLE _BestResource AS
SELECT ResourceID, max(Priority) as MaxPriority
FROM _ResourceDetail
GROUP BY ResourceID;
SELECT d.ResourceID
, d.Company
, d.Division
, d.Section
, d.Permitted
FROM _ResourceDetail d
JOIN _BestResource b
ON d.ResourceID = b.ResourceID
AND d.Priority = b.MaxPriority
ORDER BY d.ResourceID;
或者,您可以轻松地在第一个查询上放置ORDER BY
,并轻松过滤循环中的最大优先级。 (或者甚至将Priority
的计算推出数据库。)
走另一条路,你可以了解分析查询,并可以使用第一个查询作为第二个查询的输入,第二个查询首先根据最高优先级标记追索权限,然后输入第三个只选择最高优先级。这会将工作推向数据库,但我倾向于发现这种方法的可读性较差。
顺便提一下,值得注意的是,如果ResourcePermissions
变大,那么您的查询将不适合利用索引。因此,8查询版本的运行速度可能会明显加快。
答案 1 :(得分:0)
我不认为这是一个容易设置的问题,但是由于模式的非规范化结构使得它变得更难。我假设实际上有一个分层设置,每个公司有多个部门,每个部门有多个部分。然后你应该有三个表,Companies
,Divisions
,Sections
,而Sections
表只有一个FK用于它的除法。 (其公司可以从中确定。)
但是,Companies
,Divisions
和Sections
共享(至少)两个共同的属性。他们有父母(最高级别除外),他们可以出现在ResourcePermissions
。所以我们在这里概念性地要求继承。由于在大多数RDBMS中支持继承非常弱(有些在Postgresql中,我不了解SQL Server),因此您必须使用触发器自行完成大部分设置。在继承根是一个表[伪DDL]
CorporateElement
element_id SERIAL (PK, AutoIncrement, etc.)
parent int (FK references CorporateElement.id)
level int or enum (Division, Section, etc., but not as text,
as an enum or an FK into a list of these)
ResourcePermissions
resource_id int (FK references Resource.resource_id)
element_id int (FK references CorporateElement.element_id)
您的Section
等等表从element_id
继承了它们的密钥,但它们的文本名称和其他数据对于它们自己的表是本地的。接下来,您需要SQL Server的WITH RECURSIVE
功能。我将在这里留下不完整的答案(将在稍后编辑),因为有几种方法可以解决这个问题 - 无论是在RECURSIVE
部分之前还是之后加入 - 我需要仔细考虑。< / p>
[edit] 好的,这是一个示例查询,但基于重构架构。我没有测试它,但它应该提供具有权限的所有资源的列表。次要mods可以添加任何级别的无。并且结构在增加更多级别的公司实体方面是灵活的。
WITH RECURSIVE permissions_search(resource_id, element_id, parent, permission) AS
(
SELECT resource_id, element_id, parent, permission
FROM resources
JOIN resource_permissions
ON resources.resource_id= resource_permissions.resource_id
JOIN corporate_elements
ON corporate_elements.elements_id=resource_permissions.elements_id
WHERE corporate_element.level=section /* enum or magic int value */
UNION ALL
SELECT
resource_id, element_id, permission
FROM permissions_search ps
JOIN resource_permissions
ON resources.resource_id= resource_permissions.resource_id
JOIN corporate_elements
ON corporate_elements.elements_id=resource_permissions.elements_id
WHERE (corporate_elements.elements_id=ps.parent) AND (ps.permission IS NULL)
)
SELECT * FROM permissions_search WHERE permission IS NOT NULL;