内容预告
在编写查询时,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
有一个儿子一个女儿:
逻辑基础
使用自然语言表达直接写查询可能会显得笨拙而且不好理解。但如果我们转换成逻辑语言,表达就会更加流畅。以下是一些基本的逻辑公式:
命题逻辑:
蕴含
(Implication): ( p → q ) ≡ ( ¬ p ∨ q ) (p\rightarrow q) \equiv (\neg p\lor q) (p→q)≡(¬p∨q) (只有 p p p 为True
并且 q q q 为False
的时候 p → q p\rightarrow q p→q 为假).双重否定
: ¬ ¬ p ≡ p \neg \neg p \equiv p ¬¬p≡p.摩根定律
(De Mogen’s Law): ¬ ( p ∧ q ) ≡ ( ¬ p ∨ ¬ q ) \neg(p\land q)\equiv (\neg p \lor \neg q) ¬(p∧q)≡(¬p∨¬q) 和 ¬ ( p ∨ q ) ≡ ( ¬ p ∧ ¬ q ) \neg(p\lor q)\equiv (\neg p\land \neg q) ¬(p∨q)≡(¬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(x,y)).
无痛写查询 (^▽^) 步骤:
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,
{p∣Person(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
不是没有孩子的吗?怎么也被检索出来了???
分析下为啥跟预期的结果不一样
可以确定的是,最后的逻辑表达式一定等价于原命题,那我们来看一眼原命题:
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,
{p∣Person(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
)。此外,任何一个有女儿的人都会被查出来,结果是这样的
之前的文章
知识图谱 (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 这个网页自行尝试
参考
- Scientific Data Management & Knowledge Graph, by Maria-Esther Vidal
- https://rdfplayground.dcc.uchile.cl