微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

如何解决从SQL结果集中选择最具体的结果

| 下面是简化的DDL和DML,代表了我一直在消耗大量大脑物质的东西。长时间的读者,第一时间的发帖人,希望这篇文章不要打破任何SO礼节或使用太多墨水。 业务组织中某些部门的资源可以打开或关闭(允许)。我在下面使用了一个快捷方式,只是定义了Sections表,但是也将有一个Companies and Divisions表。 我可以使用Company,Division和Section的任意组合设置资源的权限。 仅为部门设置的权限将仅胜过为部门或公司设置的权限。 将Company,Division和Section的Y权限设置为NULL意味着,如果没有特定于该业务部门的权限,则他们将基于此\“ default \”值访问资源。 目前,我正在通过对多个WSELECT子句进行最先确定的WHERE子句(使用与所提供业务部门相同的Company,Division和Section来查找ResourcePermission),对最不明确的(所有三个均为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\',\'Blue Section\',\'Bluejays\')
INSERT INTO Sections VALUES (\'Company 1\',\'Division B\',\'Division C\',\'Bluejays\')
INSERT INTO Sections VALUES(\'Company 2\',\'Redskins\')
INSERT INTO Sections VALUES (\'Company 2\',\'Bluejays\')
INSERT INTO Sections VALUES (\'Company 2\',\'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,\'Y\')
INSERT INTO ResourcePermissions VALUES(\'Irish\',\'N\')
INSERT INTO ResourcePermissions VALUES(\'Irish\',\'Company 1\',\'N\')
INSERT INTO ResourcePermissions VALUES(\'french\',\'Y\')
INSERT INTO ResourcePermissions VALUES(\'french\',\'Company 2\',\'Y\')
INSERT INTO ResourcePermissions VALUES(\'English\',\'N\')
INSERT INTO ResourcePermissions VALUES(\'English\',\'Y\')
查询
  SELECT ResourceID,Company,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
    

解决方法

如果您使用解析查询,可以在一个查询中完成,但是我个人会使用临时表和几个查询。
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查询版本可能会运行得更快。     ,我认为这不是一个容易设置的问题,但是由于架构的非规范化结构使其变得更加困难。我假设实际上有一个层次结构设置,其中每个公司都有多个部门,每个部门都有多个部门。然后,您应该有3个表,分别为
Companies
Divisions
Sections
,而
Sections
表的划分只包含FK。 (可以从中确定其公司。) 但是,
Companies
Divisions
Sections
具有(至少)两个共同的属性。他们有父母(最高级别的父母除外),可以出现在
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,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的“ 17”功能。我将在这里留下不完整的答案(稍后将进行编辑),因为有几种解决方法(无论是在
RECURSIVE
部分之前还是之后加入),我都需要仔细考虑。 [编辑]好,这是一个示例查询,但是基于重构模式。我尚未对其进行测试,但是它应该列出所有具有权限的资源。次要mod可以添加任何级别的mod。并且在增加更多级别的公司实体方面,结构是灵活的。
WITH RECURSIVE permissions_search(resource_id,element_id,parent,permission) AS
(
SELECT resource_id,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,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;
    

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。