从SQL结果集中选取最具体的结果

从SQL结果集中选取最具体的结果,sql,sql-server,tsql,Sql,Sql Server,Tsql,下面是一个简化的DDL和DML来代表我一直在燃烧大量大脑物质的东西。长期读者,第一次海报,希望不要打破任何这样的礼仪或在这篇文章中使用太多的墨水 对于业务组织内的某些单位,可以打开或关闭(允许)资源。我在下面选择了一个快捷方式,只定义了Sections表,但也会有一个Companys和Divisions表 我可以使用公司、部门和部门的任意组合设置资源的权限 仅针对某个部门的权限集将超过仅针对某个部门或公司的权限集 将公司、部门和部门的权限设置为Y且为NULL意味着,如果没有特定于该业务部门的权限

下面是一个简化的DDL和DML来代表我一直在燃烧大量大脑物质的东西。长期读者,第一次海报,希望不要打破任何这样的礼仪或在这篇文章中使用太多的墨水

对于业务组织内的某些单位,可以打开或关闭(允许)资源。我在下面选择了一个快捷方式,只定义了Sections表,但也会有一个Companys和Divisions表

我可以使用公司、部门和部门的任意组合设置资源的权限

仅针对某个部门的权限集将超过仅针对某个部门或公司的权限集

将公司、部门和部门的权限设置为Y且为NULL意味着,如果没有特定于该业务部门的权限,则他们将基于此“默认”值访问资源

目前,我正在通过使用最具体的WHERE子句(查找与所提供的业务单元相同的公司、部门和部门的ResourcePermission)进行多次选择,找到与业务单元最相关的权限,并将其设置为最不具体的权限(三者均为NULL)。一共八个

如果以后需要增加更多的业务级别(部门、用户组…),那么选择将具有兔子的繁殖习惯

有没有更好的方法在SQL中实现这一点,或者更适合在过程代码中实现这一点

运行下面的最终选择将为您提供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')
查询:
我认为这不是一个容易解决的问题,但模式的非规范化结构使问题变得更加困难。我假设实际上有一个层次结构,每个公司有多个部门,每个部门有多个部门。然后您应该有三个表,
公司
部门
部分
,并且
部分
表的部门只有一个FK。(其公司可由此确定。)

然而,
公司
部门
部门
共享(至少)两个共同属性。它们有父级(最高级别除外),可以出现在
ResourcePermissions
中。所以我们这里的概念上要求继承。由于大多数RDBMS(有些是Postgresql,我不知道SQL Server)中对继承的支持非常弱,所以您必须自己使用触发器进行大部分设置。在继承根目录下是一个表[pseudo 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)
您的
部分
等表从
元素id
继承它们的键,但它们的文本名称和其他数据是它们自己的表的本地数据。接下来,您需要具有SQL Server递归功能的
。我将在这里留下不完整的答案(稍后将进行编辑),因为有几种方法可以决定是在
递归部分之前还是之后加入,我需要仔细考虑

[编辑]好的,这里是一个示例查询,但基于重构模式。我还没有测试过它,但它应该给出一个拥有权限的所有资源的列表。次要MOD可以在任何级别添加没有MOD的MOD。在增加更多层次的公司实体方面,该结构是灵活的

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;

如果使用分析查询,这可以在一个查询中完成,但我个人会使用临时表和几个查询

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
,并在循环中过滤最大优先级。(甚至将
优先级的计算推出数据库。)

换一种方式,您可以了解分析查询,并可以使用第一个查询作为第二个查询的输入,该查询根据最高优先级第一个标记资源的权限,然后输入到仅选择最高优先级的第三个查询。这将把工作推到数据库上,但我倾向于发现这种方法可读性较差


顺便说一句,值得注意的是,如果
resourcepowpermissions
变大,那么现有的查询将不能很好地利用索引。因此,8查询版本可能会运行得更快。

您的最终目标是什么?查看一组公司、部门和部门的所有资源及其权限?或者只是对所有公司/部门/部门的特定资源的权限?我想查看所有资源及其对一组公司、部门和部门的权限。但是每个资源只有一个权限,即“最佳匹配”权限。您需要提供“最佳匹配”的量化、确定性定义。例如,一个公式或过程,返回一个表示拟合“优度”的数字,其中数值越高拟合越好。现在,您提供的内容仍然模棱两可,因此无法解决。@rbaryyoung:我很确定他所说的“最佳”是指在公司结构的最小层次上,许可不为空。好的,我使用了一个非常主观的术语。我试着量化一下。在最后一个查询中,我想要的英文权限是Y,因为Company1,Div A,Blue部分的ResourcePermission。法国的许可将是Y,因为公司1,空部门,蓝色部分。最后,爱尔兰的许可将是N,因为空公司,Div A,Blue Section许可。
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;