极简逻辑表达式的设计和查询

在数据库开发中,对两个关系表进行连接查询,能够直接做“逻辑或”的查询,而对于逻辑与和逻辑非的查询,则稍复杂点,需要编写额外的代码来实现。在关系型数据库中,所谓的连接,实际上是集合的包含,只要包含一项,就满足连接条件,实现的逻辑或,这种设计,能够满足绝大多数的查询需求。有时,对于一条数据,可能需要通过多个逻辑表达式来定性,比如,判定一篇文章是否跟Microsoft Azure有关,通常简单的做法是从多个关键字的逻辑组合来定性:文章中同时含有关键字“Microsoft”和“Azure”,或者同时含有关键字“Windows”和“Azure”,把这种逻辑组合抽象成表达式,就是:( Microsoft & Azure ) | ( Windows & Azure )。

逻辑表达式的基本操作符是:与(&),或(|)和非(!),逻辑表达式的最小组合是:A&B,A|B 和 !A。关系型数据库的开发人员,在设计逻辑表达式时,必须保证满足业务需求,同时,尽可能支持多种逻辑组合,通常情况下,按照表达式的关系,我们把逻辑表达式拆分成四个元数据类型:Expression(表达式),SubExpression(子表达式),Operator(操作符)和Operand(操作数)。

在我目前接触的项目中,业务需求的逻辑表达式的组合相对简单,标准的表达式格式如下所示:

Expression = ((A & B) | (C & D)) & !(E | F)

该逻辑表达式表示:查询语句返回的结果集中,不能包含E和F,但是,必须包含A和B,或者包含C且不包含D。

对于该类表达式,我们可以抽象成更为通用的逻辑公式是:

Expression= (SubExpression1 | SubExpression2 | ...) & !ExcludeExpression
SubExpression=Operand1 & Operand2 & ...
ExcludeExpression=Operand1 | Operand1 | ...

这种高度概括的表达式蕴含的逻辑关系是:SubExpression中操作数之间的关系是逻辑与,ExcludeExpression中操作数之间的关系是逻辑或,该表达式蕴含的意思是:任意一条数据,不能够包括ExcludeExpression中的任何一个项目,但是,必须至少满足一个子表达式(SubExpression),子表达式中的项目都是逻辑与的关系。

一,用关系表存储逻辑表达式

举个例子,为了描述方便,我们用下面的逻辑表达式来说明:

Expression= ((A & B & C) | (C & D)) & !(E | F)

该表达式可以拆分成三个子表达式:A & B & C,C & D, !(E | F),对于子表达式 (A & B & C,C & D),其操作数之间的关系都是逻辑与;而对于子表达式!(E | F),其操作数之间的关系是逻辑或。

1,通过XML文档表示逻辑表达式

逻辑表达式的子表达式之间,不是无关系的,为了在不同的应用程序间传递逻辑表达式,数据结构必须包含该表达式的所有关系和操作数,XML文档特别适合表达有特定关系的数据结构,按照之前拆分的四种元数据类型,我们把XML文档定义为三种不同的标签,分别是<Expression>,<SubExpression>和<Operand>,并为标签设置不同的属性,通过格式化的XML实现逻辑表达式的转存,示例如下:

declare @xml xml
set @xml=‘
<Expression ID="1">
  <SubExpression ID="1" OperandType="Tag" Operator="and">
    <Operand ID="1" />
    <Operand ID="2" />
    <Operand ID="3" />
  </SubExpression>
  <SubExpression ID="2" OperandType="Tag" Operator="and">
    <Operand ID="4" />
    <Operand ID="5" />
  </SubExpression>
  <SubExpression ID="3" OperandType="Tag" Operator="not">
    <Operand ID="6"  />
    <Operand ID="7"  />
  </SubExpression>
</Expression>‘

2,把逻辑表达式存储到关系表

XML格式的文档适合数据的传递,不适合直接存储在关系型数据库中,并且XML格式的文档也不利于对数据执行查询操作,因此,必须把XML文档存储到数据表中,以利用关系数据库引擎执行集合操作的优势,实现数据的快速查询。当应用程序接收到这个XML文档,必须把XML蕴含的表达式拆分成:表达式(Expression),子表达式(SubExpression),操作数(Operand)和操作符(Operator),例如,我们可以利用以下脚本创建数据表:

create table dbo.Operands
(
    ExpressionID bigint not null,
    SubExpressionID smallint not null,
    OperandID bigint not null,          --EntityID, TagID
    Operator varchar(8) not null,       --&,!
    OperandType varchar(8) not null,    --Entity,Tag
    primary key clustered(ExpressionID,SubExpressionID,OperandID)
)
with(data_compression=page);
go

注:OperandType是操作数的类型,分为:Entity和Tag,相应的操作数ID(OperandID)就是EntityID和TagID,本例只使用TagID,EntityID没有使用。

SQL Server内置函数用于解析XML文档,通过XML解析函数,我们可以利用TSQL脚本把XML文档插入到表Operands中。这意味着,在表Operands中,当Operator列值为not时,表示逻辑非,表示OperandID是<ExcludeExpression>标签存储的操作数;当Operator列值为and时,表示逻辑与,表示OperandID是<SubExpression>标签存储的操作数。

;with cte_Expressions as
(
    select e.v.query(‘.‘) as Expression
        ,e.v.value(‘@ID‘,‘int‘) as ExpressionID
    from @xml.nodes(‘/Expression‘) as e(v)
)
,cte_SubExpression as
(
    select e.ExpressionID
        ,se.SubExpression
        ,se.SubExpressionID
        ,se.OperandType
        ,se.Operator
    from cte_Expressions e
    cross apply
    (
        select t.v.query(‘.‘) as SubExpression
            ,t.v.value(‘@ID‘,‘int‘) as SubExpressionID
            ,t.v.value(‘@OperandType‘,‘varchar(16)‘) as OperandType
            ,t.v.value(‘@Operator‘,‘varchar(16)‘) as Operator
        from e.Expression.nodes(‘/Expression/SubExpression‘) as t(v)
    ) as se
)
--insert into dbo.Operands
select p.ExpressionID
    ,p.SubExpressionID
    ,d.OperandID
    ,p.Operator
    ,p.OperandType
from cte_SubExpression p
cross apply
(
    select t.v.value(‘@ID‘,‘int‘) as OperandID
        ,t.v.value(‘@Exclude‘,‘int‘) as Exclude
    from p.SubExpression.nodes(‘/SubExpression/Operand‘) as t(v)
) as d

二,使用TSQL实现逻辑与和逻辑非

数据表包含的数据是Tag和Data之间的映射关系,如果Data包含的Tag满足逻辑表达式,那么就认为Data和逻辑表达式(Expression)之间存在映射关系,也就是说,Data满足Expression。

例如,有以下数据表DataTable,该表只有两列DataID和TagID,表示Data具有相应的Tag:

create table dbo.DataTable
(
    DataID bigint not null,
    TagID bigint not null,
)
go

insert into dbo.DataTable
(DataID,TagID)
values
(1,1)
,(1,2)
,(1,3)
,(1,7)
,(2,4)
,(2,5)
,(3,1)
,(3,2)
,(3,3)
,(4,1)
,(4,3)
,(6,6)
go

1,逻辑非的实现

逻辑非的含义是:在DataTable中,一个DataID不能包含任意一个ExcludeExpression中的TagID。

;with cte_exclude_data
as
(
    select distinct
        dt.DataID
    from dbo.DataTable dt
    inner join dbo.Operands o
        on dt.TagID=o.OperandID
    where Operator=‘not‘
)
select dt.DataID
    ,dt.TagID
from dbo.DataTable dt
left join cte_exclude_data ed
    on dt.DataID=ed.DataID
where ed.DataID is null

2,逻辑与的实现

逻辑与的含义是:在DataTable中,一个DataID必须包含SubExpression中的所有Tag。

;with cte_operands as
(
    select o.ExpressionID
        ,o.SubExpressionID
        ,o.OperandID
        ,count(0) over(partition by o.ExpressionID,o.SubExpressionID) as Operands
    from dbo.Operands o
    where o.Operator=‘and‘
)
select o.ExpressionID
    ,o.SubExpressionID
    ,o.Operands
    ,dt.DataID
from cte_operands o
inner join dbo.DataTable dt
    on o.OperandID=dt.TagID
group by o.ExpressionID
    ,o.SubExpressionID
    ,o.Operands
    ,dt.DataID
having count(distinct dt.TagID)=o.Operands

3,查询脚本示例 

把逻辑与和逻辑非的代码合并到一起,就能实现表达式蕴含的逻辑,这里给出一个示例脚本:

;with cte_operands as
(
    select o.ExpressionID
        ,o.SubExpressionID
        ,o.OperandID
        ,count(o.OperandID) over(partition by o.ExpressionID,o.SubExpressionID) as Operands
    from dbo.Operands o
    where Operator=‘and‘
)
,cte_exclude_data as
(
    select distinct
        d.DataID
    from dbo.DataTable d
    inner join dbo.Operands o
        on d.TagID=o.OperandID
    where o.Operator=‘not‘
)
,cte_data as
(
    select d.DataID
        ,d.TagID
    from dbo.DataTable d
    left join cte_exclude_data ed
        on d.DataID=ed.DataID
    where ed.DataID is null
)
select o.ExpressionID
    ,o.SubExpressionID
    ,dt.DataID
from cte_operands o
inner join cte_data dt
    on o.OperandID=dt.TagID
group by o.ExpressionID
    ,o.SubExpressionID
    ,dt.DataID
    ,o.Operands
having count(distinct dt.TagID)=o.Operands

附上较复杂的逻辑表达式,该格式能够表达的逻辑组合更为丰富,后续我会分享深入探索的随笔:

Expression = ((A & B) | (C & ! D)) & !(E | F)

本文列举的逻辑表达式非常简单,鉴于本人的知识和经验有限,难免有纰漏,如果有更好的解决方案,还请不吝告知,十分感谢!

时间: 2024-08-18 14:23:14

极简逻辑表达式的设计和查询的相关文章

张艾迪(创始人):视觉计算极简主义的设计

AOOOiA.global创始人艾迪张:面临着新互联网的时代的到来.全球各行业对产品设计和色彩搭配上进行了新的色彩标准化.就想人们喜欢Apple的黑与白.喜欢AOOOiA.Global/224 的赤橙黄绿青蓝紫的鲜亮的色彩世界一样;不同忽视的是.更明亮.更清晰.更拟真化的视觉体验与视觉计算中.每一帧的色彩.每一帧的画质.都将更加标志性的展现与运用在各个领域与产品之中:全球市场喜欢不断的跟随市场色彩的变化.而忽略了真实化色彩世界的本真进化::让我们随着色彩进化论从回到19世纪:直到19世纪.我们所

精美的极简主义的网页设计

极简主义界面设计是需要UI设计师用有限的资源来传达诠释艺术,也正因为元素的简约,也造就了它一定的独特性.就网站设计而言,极简主义的趋势看起来似乎已经巩固住了它的地位.最后,你肯定也跃跃欲试了,但是在一展身手之前,不妨从蓝蓝设计下面网站设计例子中得到启发,这些网站是极简主义在现代网页设计的代表作品. 极简主义的网站设计欣赏:http://www.lanlanwork.com/ Anonymous DesignBlvd Impress a Penguin Beatbox Academy Lorenz

Resty 一款极简的restful轻量级的web框架

https://github.com/Dreampie/Resty Resty 一款极简的restful轻量级的web框架 开发文档 如果你还不是很了解restful,或者认为restful只是一种规范不具有实际意义,推荐一篇osc两年前的文章:RESTful API 设计最佳实践 和 Infoq的一篇极其理论的文章 理解本真的REST架构风格 虽然有点老,介绍的也很简单,大家权当了解,restful的更多好处,还请google 拥有jfinal/activejdbc一样的activerecor

到底什么是极简主义?

“现在特别流行极简主义设计”,那么,到底什么是极简主义?极简主义不光存在于网页设计中,像Logo设计.印刷设计.包装设计也都有相应的极简主义风格.目前流行的极简主义网页设计视觉风格是:使用纯粹的色彩以及简练唯美的照片. 极简主义网页设计流行使用创新式导航,不但易用,而且有种简约美,符合极简主义“回归功能”与“视觉简约”的双重理念.简约的外观.易用的功能,并不意味着极简主义网页设计很简单.事实上,优秀的极简风格作品需要复杂的设计流程. 有一种不错的极简主义设计方法是:先设计一个风格复杂的网页,然后

Ui培训之如何设计极简三色图标

小编之前在ui培训课程里面了解到,在ui设计当中,时下图标设计流行的是一种不一样的极简风,这样会更加细腻有趣.配色是设计中永恒的话题,在我看来简易更是设计的精髓. Banana Bird 在刚刚接受ui培训的时候,一般会从单色,或者双色结构来授课,就如上图的banana bird图标,如此简易,但也明确体现图标所表达的内容. KZ 然而,许多设计师都认为三种色彩构成的配色是最平衡也是最漂亮的,多一色显得太花,少一色又过于朴素,这也是为什么许多配色方案的构成多为3种色彩.就如小米的包装设计,都是以

移动开发中的极简设计

原文链接: The Art of Minimalism in Mobile App UI Design 原文作者 : Nick Babich 译文出自 : 掘金翻译计划 译者 : edvardhua 校对者 : owenlyn, jiaowoyongqi, Graning 文章结尾有英文原文 设计是一件用户驱动很强的工作.随着用户越来越偏好更简洁的交互界面,如何剔除多余的元素,保留最基础最重要的元素是极简设计的关键.极简设计形式和功能完美结合.它最大的优点是极简的表现形式,简洁的线条,大方的留白

518抽奖软件:极简主义设计的践行者

软件用途 用于企业年会抽奖,通过电脑连接LED或投影仪,实现大屏幕随机滚动抽取照片.号码.姓名的电脑软件.软件自带音效.精美的背景图,可以提升大中型年会的气氛.也可用于婚庆等其它场所. 开发原因 市面上的同类软件太复杂.缺乏设计,主要表现为: 1. 含没必要的功能,如锁定屏幕.从某奖项开始抽.临时修改奖品: 2. 菜单混乱不直白,分类没用好,没表明软件主体结构: 3. 将复杂的高级功能显摆出来,只会让普通用户迷惑: 4. 界面细节上,文字按钮摆放显乱.留白不合理等. 图文教程 一.抽奖前设置 包

.NET Core实战项目之CMS 第七章 设计篇-用户权限极简设计全过程

写在前面 这篇我们对用户权限进行极简设计并保留其扩展性.首先很感谢大家的阅读,前面六章我带着大家快速入门了ASP.NET Core.ASP.NET Core的启动过程源码解析及配置文件的加载过程源码解析并引入依赖注入的概念.Git的快速入门.Dapper的快速入门.Vue的快速入门.不知道大伙掌握的怎么样了!如果你有兴趣的话可以加入我们的.NET Core实战项目群637326624跟更多的小伙伴共同进行交流下. 接下来我们就正式进入.NET Core实战项目之CMS的设计篇了.在设计篇呢,我们

通过极简模拟框架让你了解ASP.NET Core MVC框架的设计与实现[上篇]

<200行代码,7个对象--让你了解ASP.NET Core框架的本质>让很多读者对ASP.NET Core管道有了真实的了解.在过去很长一段时间中,有很多人私信给我:能否按照相同的方式分析一下MVC框架的设计与实现原理,希望这篇文章能够满足你们的需求.在对本章内容展开介绍之前,顺便作一下广告:<ASP.NET Core 3框架揭秘>已经开始销售,现时5折优惠还有最后4天,有兴趣的从这里入群购买. 目录一.Action元数据的解析     ActionDescriptor