用逻辑降维打击 SPARQL 查询:无痛写 NOT EXISTS (7)

内容预告

在编写查询时,NOT EXISTS往往是最反直觉且难以理解的部分。
本文将分享如何清晰且轻松地掌握SPARQL中的NOT EXISTS查询方法,这些技巧同样适用于其他查询语言。

查询语言为什么设计了个这么奇怪的 NOT EXISTS?

用于表达否定:用于表达不满足某些条件的情况。例如,查询非O型血的人:

SELECT ?person WHERE{
?person a ex:Person . 
FILTER NOT EXITSTS {?person ex:bloodType "O"^^xsd:string}
}

处理空值:例如,查询没有女朋友的人:

SELECT ?person WHERE{
?person a ex:Person . 
FILTER NOT EXISTS {?person ex:girlFriend ?gf}
}

表达全称量词:例如,查询所有孩子都是女儿的人:
过去老师教我们的方法,直接用嘴说:“不存在这样的人,他孩子不是女儿”。

SELECT ?parent WHERE{
?person a ex:Person . 
?parent ex:hasChild ?child . 
FILTER NOT EXISTS {?parent ex:hasChild ?child1. 
			    ?child1 ex:sex ?sex . 
			    FILTER(?sex != "W")}
}

查询是写出来了,但说的像人话吗?听不懂,很反直觉。

查询结果只有 ex:p1 满足。ex:p2 没有孩子,ex:p3有个儿子,ex:p4有一个儿子一个女儿: image.png

逻辑基础

使用自然语言表达直接写查询可能会显得笨拙而且不好理解。但如果我们转换成逻辑语言,表达就会更加流畅。以下是一些基本的逻辑公式:
命题逻辑

  • 蕴含 (Implication): ( p → q ) ≡ ( ¬ p ∨ q ) (p\rightarrow q) \equiv (\neg p\lor q) (pq)(¬pq) (只有 p p pTrue 并且 q q qFalse 的时候 p → q p\rightarrow q pq 为假).
  • 双重否定 ¬ ¬ p ≡ p \neg \neg p \equiv p ¬¬pp.
  • 摩根定律 (De Mogen’s Law): ¬ ( p ∧ q ) ≡ ( ¬ p ∨ ¬ q ) \neg(p\land q)\equiv (\neg p \lor \neg q) ¬(pq)(¬p¬q) ¬ ( p ∨ q ) ≡ ( ¬ p ∧ ¬ q ) \neg(p\lor q)\equiv (\neg p\land \neg q) ¬(pq)(¬p¬q). 举个例子,“鱼与熊掌不可兼得”: ¬ ( \neg( ¬(🐟 ∧ \land 🐻 ) ≡ ( ¬ )\equiv (\neg )(¬ 🐟 ∨ ¬ \lor \neg ¬ 🐻 ) ) ) 等价于 “没有🐟或者没有🐻”.
    谓词逻辑
  • 全称量词否定 ¬ ( ∀ x ) [ f ( x , y ) ] ≡ ( ∃ x ) [ ¬ f ( x , y ) ] \neg(\forall x)[f(x,y)]\equiv (\exists x)[\neg f(x,y)] ¬(x)[f(x,y)](x)[¬f(x,y)] (不是所有的 x x x 都有谓词 f ( x , y ) f(x,y) f(x,y),等同于存在某个 x x x 没有谓词 f ( x , y ) f(x,y) f(xy)).

无痛写查询 (^▽^) 步骤:

1.查询的自然语言表达。“查询孩子都是女儿的人”: 我们要查找这样的人:如果他有孩子,那么这个孩子一定是女儿.
2.自然语言转逻辑语言 { p ∣ P e r s o n ( p ) ∧ ( ∀ c ) [ ( h a s C h i l d ( p , c ) → h a s S e x ( c , \{p|Person(p)\land (\forall c)[(hasChild(p,c)\rightarrow hasSex(c, {pPerson(p)(c)[(hasChild(p,c)hasSex(c,“W” ) ) ] } ))]\} ))]}
3.逻辑语言变换。因为 SPARQL 没有全称量词表达,只能表达存在量词 (也就是 FILTER NOT EXISTS)。这里需要把 ∀ c \forall c c ∄ c \not\exists c c 表达:

  • 把条件改用双重否定 P e r s o n ( p ) ∧ ¬ ¬ ( ∀ c ) [ h a s C h i l d ( p , c ) → h a s S e x ( c , Person(p)\land \neg\neg(\forall c)[hasChild(p,c)\rightarrow hasSex(c, Person(p)¬¬(c)[hasChild(p,c)hasSex(c,“W” ) ] )] )]
  • 将第二个否定用全称量词否定得到 P e r s o n ( p ) ∧ ¬ ( ∃ c ) [ ¬ ( h a s C h i l d ( p , c ) → h a s S e x ( c , Person(p)\land \neg(\exists c)[\neg (hasChild(p,c)\rightarrow hasSex(c, Person(p)¬(c)[¬(hasChild(p,c)hasSex(c,“W” ) ) ] ))] ))] 这里只对
  • 蕴含改用逻辑 得到 P e r s o n ( p ) ∧ ¬ ( ∃ c ) [ ¬ ( ¬ h a s C h i l d ( p , c ) ∨ h a s S e x ( c , Person(p)\land \neg(\exists c)[\neg (\neg hasChild(p,c)\lor hasSex(c, Person(p)¬(c)[¬(¬hasChild(p,c)hasSex(c,“W” ) ) ] ))] ))]
  • 使用 摩根定律 化掉括号外的否定 ¬ \neg ¬ 得到 P e r s o n ( p ) ∧ ¬ ( ∃ c ) [ ( h a s C h i l d ( p , c ) ∧ ¬ h a s S e x ( c , Person(p)\land \neg(\exists c)[(hasChild(p,c)\land \neg hasSex(c, Person(p)¬(c)[(hasChild(p,c)¬hasSex(c,“W” ) ) ] ))] ))]
  • 使用 ∄ \not \exists 得到 P e r s o n ( p ) ∧ ( ∄ c ) [ ( h a s C h i l d ( p , c ) ∧ ¬ h a s S e x ( c , Person(p)\land (\not\exists c)[(hasChild(p,c)\land \neg hasSex(c, Person(p)(c)[(hasChild(p,c)¬hasSex(c,“W” ) ) ] ))] ))],此时连接词都要是 ∧ \land ,因为 SPARQL 所有的子句都是用逻辑 (.) 连接的。
    4.对照着写 SPARQL
SELECT ?p WHERE{
?p a ex:Person . 
FILTER NOT EXISTS {?p ex:hasChild ?c . ?c ex:sex ?sex . FILTER (?sex != "W")}
}

跑出来的结果竟然还多了个 ex:p2,什么鬼?ex:p2不是没有孩子的吗?怎么也被检索出来了???
image.png

分析下为啥跟预期的结果不一样

可以确定的是,最后的逻辑表达式一定等价于原命题,那我们来看一眼原命题: P e r s o n ( p ) ∧ ( ∀ c ) [ ( h a s C h i l d ( p , c ) → h a s S e x ( c , Person(p)\land (\forall c)[(hasChild(p,c)\rightarrow hasSex(c, Person(p)(c)[(hasChild(p,c)hasSex(c,“W” ) ) ] } ))]\} ))]}ex:p2 肯定满足 P e r s o n ( p ) Person(p) Person(p),那满足 ( ∀ c ) [ ( h a s C h i l d ( p , c ) → h a s S e x ( c , (\forall c)[(hasChild(p,c)\rightarrow hasSex(c, (c)[(hasChild(p,c)hasSex(c,“W” ) ) ] } ))]\} ))]} 吗?满足!因为ex:p2没有孩子,此时前件 h a s C h i l d ( p , c ) hasChild(p,c) hasChild(p,c) 为假,蕴含 中假推出任何布尔值都为真!

又来举例子:
(1) “如果我有一个亿,那么我和马斯克就是兄弟”,这句话是真的,假推出假为真。
(2) “如果我有一个亿,那么我就有一张嘴”,这句话也是真的,假推出真也为真。
逻辑就是这么神奇,逻辑就是这么反直觉!!!但是它就是真的。

经常不小心就范的错误 ❌ 案例

这里有同学可能会表达成 “我们要查找这样的父母,他有孩子并且是女孩儿”,
然后逻辑语言表达成了 { p ∣ P e r s o n ( p ) ∧ ( ∀ c ) [ h a s C h i l d ( p , c ) ∧ h a s S e x ( c , \{p|Person(p)\land (\forall c)[hasChild(p,c)\land hasSex(c, {pPerson(p)(c)[hasChild(p,c)hasSex(c,“W” ) ] } )]\} )]}
写成了这样的查询:

SELECT ?p WHERE{
?p a ex:Person .
?p ex:hasChild ?c .
?c ex:sex "W"
}

这里的查询不对:☝🏻因为查询没有表达全称量词 ∀ c \forall c c (也就是使用 FILTER NOT EXISTS)。此外,任何一个有女儿的人都会被查出来,结果是这样的
image.png

之前的文章

知识图谱 (1)
本体论介绍 (2)
动手构建你的第一个知识图谱 by RDF (3)
图数据模型介绍 (4) 数学符号警告
SPARQL查询:如何高效检索Web数据 (5)
深入探索 SPARQL,复杂查询和聚合函数 (6)

不定期更新专业知识和有趣的东西,欢迎反馈、点赞、加星

您的鼓励和支持是我坚持创作的最大动力!ღ( ´・ᴗ・` )

RDF toy 数据

@prefix ex: <http://example.org/> .

ex:p1 a ex:Person;
      ex:hasChild ex:c1 . 
ex:c1 ex:sex "W".

ex:p2 a ex:Person. 

ex:p3 a ex:Person;
      ex:hasChild ex:c2.
ex:c2 ex:sex "M".

ex:p4 a ex:Person; 
      ex:hasChild ex:c3, ex:c4.
ex:c3 ex:sex "W".
ex:c4 ex:sex "M".

可以到 https://rdfplayground.dcc.uchile.cl 这个网页自行尝试

参考

  1. Scientific Data Management & Knowledge Graph, by Maria-Esther Vidal
  2. https://rdfplayground.dcc.uchile.cl
  • 21
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值