在SPARQL 101中,我们介绍了SPARQL是什么,它如何与其他查询语言相关,并且通过了基本的SPARQL语法。
这个课程建立在这个基础上,主要是以示例为基础。我们使用许多现实世界的SPARQL查询来说明查询语言的功能,作为提高效率的最快方法。
这是一个技术性的课程。
在本教程中,我们将使用DBpedia.org的DBpedia SPARQL终结点,DBpedia.org是一个免费提供的社区支持的数据库,填充有从维基百科提取的RDF数据。
来自此较少的所有样本查询可以直接粘贴到DBpedia的SPARQL用户界面进行测试。您将非常鼓励您随时随地查询语言。它有助于在另一个浏览器选项卡中保持打开,以便您可以在课程和查询之间来回切换。
我们先来查询一个三重模式(一个带有变量的RDF三元组)。变量组合在一起的几个三元组称为基本图形模式。这是最基本的查询类型。
回想一下,变量前面加上(?)一个问号。
还记得,“URI”和“资源”这个词通常是可交错使用的。
查询1:此查询返回所有标识城市类型为“德克萨斯州城市”的URI。回想一下,RDF资源由URI标识; 在讨论查询时,通常可以互换使用URI和资源。
PREFIX rdf:< http://www.w3.org/1999/02/22-rdf-syntax-ns#>
SELECT * WHERE {
?city rdf:type < http://dbpedia.org/class/yago/WikicatCitiesInTexas >
}
将其复制并粘贴到DBpedia的SPARQL UI中。如果您运行查询,您将获得一个包含德克萨斯州相当长的城市名单的表格。
现在让我们在查询中添加另一个三元组。
查询2:此查询返回“德克萨斯州城市”以及总人口数的城市。
PREFIX rdf:< http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs:< http://www.w3.org/2000/01/rdf-schema#>
PREFIX dbp:< http://dbpedia.org/ontology/>
SELECT * WHERE {
?city rdf:type < http://dbpedia.org/class/yago/WikicatCitiesInTexas>。
?city dbp:populationTotal?popTotal。
}}
请注意,变量?city用作查询中的三元组的主题,在单个资源上匹配两个语句。
如果将其输入到DBpedia SPARQL UI中,您将看到结果页与第一个查询大致相同,但现在有一个新列需要额外的信息。
SPARQL使用Turtle语法(最初在RDF Nuts&Bolts课程中描述),因此以下查询是相同的:
PREFIX rdf:< http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs:< http://www.w3.org/2000/01/rdf-schema#>
PREFIX dbp:< http://dbpedia.org/ontology/>
SELECT * WHERE {
?city rdf:type < http://dbpedia.org/class/yago/WikicatCitiesInTexas> ;
dbp:populationTotal?popTotal。
}}
你能说出什么不同,为什么这两个查询实际上是一样的?
我们再添加一个三重模式。这是一个具有三个三重图案的基本图形图案。
查询3:此查询返回“总部在德克萨斯城市”的城市总人口和城市人口。
PREFIX rdf:< http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs:< http://www.w3.org/2000/01/rdf-schema#>
PREFIX dbp:< http://dbpedia.org/ontology/>
SELECT * WHERE {
?city rdf:type < http://dbpedia.org/class/yago/WikicatCitiesInTexas> ;
dbp:populationTotal?popTotal;
dbp:populationMetro?popMetro。
}}
在DBpedia SPARQL UI中尝试这样做。现在,结果的大小从以前的查询中大大减少了!为什么会这样,鉴于查询本身是要求更多的信息,而不是更少?
实际上,通过请求附加信息,我们对查询进行了隐含的限制。具体来说,查询将只返回具有值为popTotal和PopMetro的城市的结果。只有popTotal的城市不是 popMetro不再出现了。
也就是说,我们要求城市人口和地铁总人口都有价值。如果城市资源不足或查询中的其他图形模式与数据不匹配。
但是,如果我们想要所有的城市出现,不管他们有地铁人口怎么办?这是OPTIONAL条款的地方。
OPTIONAL子句的想法是使您能够将数据存在,但如果不存在则忽略它。这是SPARQL优雅处理稀疏数据和缺失值的关键方式。
如果您来自SQL世界,则SPARQL OPTIONAL操作符相当于左侧外部连接。换句话说,即使没有任何内容与查询的“正确部分”匹配,结果将始终包含查询“左侧”部分的值。
查询4:此查询返回类型为“德克萨斯城市”的城市及其总人口,以及可选的地铁人口(如果存在)。
PREFIX rdf:< http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs:< http://www.w3.org/2000/01/rdf-schema#>
PREFIX dbp:< http://dbpedia.org/ontology/>
SELECT * WHERE {
?city rdf:type < http://dbpedia.org/class/yago/WikicatCitiesInTexas> ;
dbp:populationTotal?popTotal。
OPTIONAL {?city dbp:populationMetro?popMetro。}
}
再次,我们需要OPTIONAL运算符的原因是因为RDF中可能缺少信息。如果您来自SQL世界,则使用NULL表示缺少的信息。但是,RDF中没有空值。
再说一遍:RDF中没有NULL值。
三重存在或不存在。事实上,由于RDF数据与其物理数据表示无关,所以NULL的整体理念是完全不必要的。
关系数据库中的NULL值只是表格逻辑数据模型的一种体现; 需要一些方法来表示一个空单元格。在SQL中,如果在一个记录中一个值为NULL并且您执行SELECT * FROM表,则仍然会在结果中获得NULL。
在SPARQL中不是这样。相反,您根本无法获取记录(如我们在查询3中所示)。如果您仍然想要获得记录,则需要使用OPTIONAL。
一般来说,您应该对所有希望恢复的数据使用OPTIONAL,但这并不能帮助您根据需要过滤相应的结果集。例如,您通常会使用OPTIONAL作为您不确定将包含大量数据的谓词,或者如果存在则可以使用OPTIONAL,如果不在应用程序上下文中则忽略它。
以上显示的基本图形模式匹配可让您选择数据。但是,您通常不希望为每个查询返回与该模式匹配的所有数据。您希望以特定顺序返回数据,通常只想一次查看几个结果。
这是ORDER BY,LIMIT和OFFSET在手。它们一起使您能够一次下拉一个页面的查询结果。如果你来自SQL世界,这些运算符等同于SQL中的相同子句。
我们从ORDER BY开始吧。ORDER BY子句确定结果的顺序,并可以按升序或降序排列。
查询5:此查询返回“德克萨斯城市”类型的城市,其总人口以及可选的城市人口。结果按照总人口的顺序返回(所以像休斯敦这样的大城市将是第一个结果)。
PREFIX rdf:< http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs:< http://www.w3.org/2000/01/rdf-schema#>
PREFIX dbp:< http://dbpedia.org/ontology/>
SELECT * WHERE {
?city rdf:type < http://dbpedia.org/class/yago/WikicatCitiesInTexas> ;
dbp:populationTotal?popTotal。
OPTIONAL {?city dbp:populationMetro?popMetro。}
}
ORDER BY desc(?popTotal)
您也可以使用asc()选项以升序返回结果。
LIMIT子句对结果数量设置了上限。OFFSET子句使结果在指定的数字后开始。这些子句经常与ORDER BY结合使用来实现结果分页。
查询6:此查询返回“德克萨斯城市”类型的城市,其总人口以及可选的城市人口。结果按照总人口的顺序返回(所以大城市将是最好的结果)。最多返回10个结果,从第5 个结果开始。
PREFIX rdf:< http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs:< http://www.w3.org/2000/01/rdf-schema#>
PREFIX dbp:< http://dbpedia.org/ontology/>
SELECT * WHERE {
?city rdf:type < http://dbpedia.org/class/yago/WikicatCitiesInTexas> ;
dbp:populationTotal?popTotal。
OPTIONAL {?city dbp:populationMetro?popMetro。}
}
ORDER BY desc(?popTotal)
LIMIT 10
OFFSET 5
在这一点上,我们已经展示了如何匹配数据以及如何排序结果。然而,基本的匹配模式是不够的,因为您还需要能够过滤掉与模式匹配但不需要的结果。
FILTER子句限制返回哪些结果。您可以使用过滤器来执行以下操作:
使用图形模式和过滤器,SPARQL成为一种非常强大的语言,仅用于选择与特定条件匹配的数据。
现在我们给我们的查询添加一些约束。我们可以使用FILTER操作符来执行此操作,它使用布尔条件来过滤不需要的结果。允许以下过滤器:
查询7:与查询6相同,但仅返回总人数超过50,000的城市。
PREFIX rdf:< http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs:< http://www.w3.org/2000/01/rdf-schema#>
PREFIX dbp:< http://dbpedia.org/ontology/>
SELECT * WHERE {
?city rdf:type < http://dbpedia.org/class/yago/WikicatCitiesInTexas> ;
dbp:populationTotal?popTotal。
OPTIONAL {?city dbp:populationMetro?popMetro。}
FILTER(?popTotal> 50000)
}
ORDER BY desc(?popTotal)
查询8:此查询与查询7相同,但返回具有结果的每个城市的可读名称。
rdfs:label是通常用于表示资源的人类可读名称的RDFS谓词。您可能会看到在大多数本体中使用的都柏林核心元数据计划中的这个或者直接:标题。
PREFIX rdf:< http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs:< http://www.w3.org/2000/01/rdf-schema#>
PREFIX dbp:< http://dbpedia.org/ontology/>
SELECT * WHERE {
?city rdf:type < http://dbpedia.org/class/yago/WikicatCitiesInTexas> ;
dbp:populationTotal?popTotal;
rdfs:label?name
OPTIONAL {?city dbp:populationMetro?popMetro。}
FILTER(?popTotal> 50000)
}
ORDER BY desc(?popTotal)
发生了什么?!现在,每个城市资源都有几个结果行。这是为什么?
这是RDF中的常见事件。与SQL不同,很容易为特定属性的资源分配多个值。在这种情况下,数据集中有多种语言的rdfs:标签。
因此,在简单的表格结果格式中,我们将为每个重复的值获得多个结果。
由于我们不需要所有语言的所有结果,我们可以通过仅请求返回的英文值来简化查询。这样,RDF和SPARQL自然地支持国际化。
查询9:查询8,但请求仅匹配模式的英文标签。
PREFIX rdf:< http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs:< http://www.w3.org/2000/01/rdf-schema#>
PREFIX dbp:< http://dbpedia.org/ontology/>
SELECT * WHERE {
?city rdf:type < http://dbpedia.org/class/yago/WikicatCitiesInTexas> ;
dbp:populationTotal?popTotal;
rdfs:label?name
OPTIONAL {?city dbp:populationMetro?popMetro。}
FILTER(?popTotal> 50000 && langmatches(lang(?name),“EN”))
}
ORDER BY desc(?popTotal)
lang操作符提取绑定到?名称的值的语言标签。langmatches操作符与第一语言标签与第二语言范围匹配。
前面的查询可以等效重写,而不用langmatches操作符,并使用“=”和“en”(小写)而不是“EN”(大写):
PREFIX rdf:< http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs:< http://www.w3.org/2000/01/rdf-schema#>
PREFIX dbp:< http://dbpedia.org/ontology/>
SELECT * WHERE {
?city rdf:type < http://dbpedia.org/class/yago/WikicatCitiesInTexas> ;
dbp:populationTotal?popTotal;
rdfs:label?name
OPTIONAL {?city dbp:populationMetro?popMetro。}
FILTER(?popTotal> 50000 && lang(?name)=“en”)
}
ORDER BY desc(?popTotal)
查询10:此查询显示如何使用正则表达式过滤器。与查询9相同,但只匹配名称中具有“El”的城市。
PREFIX rdf:< http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs:< http://www.w3.org/2000/01/rdf-schema#>
PREFIX dbp:< http://dbpedia.org/ontology/>
SELECT * WHERE {
?city rdf:type < http://dbpedia.org/class/yago/WikicatCitiesInTexas> ;
dbp:populationTotal?popTotal;
rdfs:label?name
OPTIONAL {?city dbp:populationMetro?popMetro。}
FILTER(?popTotal> 50000 &&
langmatches(lang(?name),“EN”)&&
regex(str(?name),“El”))
}
ORDER BY desc(?popTotal)
str操作符提取绑定到?name的值的字符串。正则表达式运算符允许正则表达式,特别是XQuery接受的任何正则表达式。任何使用标准正则表达式的人都应该熟悉这些语法。
一个常见的错误是初学者用正则表达式来忘记它们是区分大小写的。使用“el”而不是“El”运行上一个查询,看看会发生什么。
任何查询语言的一个重要特征是否定。但是,在SPARQL 1.0中,没有明确的否定运算符。
然而,否定可以通过否定为失败,并使用OPTIONAL子句,BOUND运算符和逻辑not(!)运算符写入。OPTIONAL运算符将变量绑定到要排除的三元组,并且过滤器将删除这些情况。
这听起来很复杂,但一旦你看到它,它真的很简单。
查询11:本查询和以前一样,只不过它返回的仅是城市不具备地铁的人口。
PREFIX rdf:< http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs:< http://www.w3.org/2000/01/rdf-schema#>
PREFIX dbp:< http://dbpedia.org/ontology/>
SELECT * WHERE {
?city rdf:type < http://dbpedia.org/class/yago/WikicatCitiesInTexas> ;
dbp:populationTotal?popTotal;
rdfs:label?name
OPTIONAL {?city dbp:populationMetro?popMetro。}
FILTER(?popTotal> 50000 && langmatches(lang(?name),“EN”))
FILTER(!bound(?popMetro))
}
ORDER BY desc(?popTotal)
要了解它是如何工作的,首先意识到您需要OPTIONAL子句来阻止您将dbp:populationMetro的值错误地要求绑定到一个查询结果。
接下来,我们来看一下新的filter子句。BOUND运算符只是一个布尔测试,返回在返回的结果中是否绑定特定属性。你可以将“约束”视为“匹配”或“不为空”。所以当与逻辑非运算符(!)组合时,过滤器是“匹配总人口不限于任何值的城市”。
成功!
最后还要注意,有可能有不同的FILTER子句。您不需要将它们全部放入同一个FILTER子句中。例如,该查询在其自己的FILTER子句中指定了每个过滤器条件,这完全相同:
PREFIX rdf:< http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs:< http://www.w3.org/2000/01/rdf-schema#>
PREFIX dbp:< http://dbpedia.org/ontology/>
SELECT * WHERE {
?city rdf:type < http://dbpedia.org/class/yago/WikicatCitiesInTexas> ;
dbp:populationTotal?popTotal;
rdfs:label?name
OPTIONAL {?city dbp:populationMetro?popMetro。}
FILTER(?popTotal> 50000)
FILTER(langmatches(郎(?名称),“EN”)),
过滤器(!约束(?popMetro))
}
ORDER BY DESC(?popTotal)
UNION子句是两个基本图形模式之间的分离。换句话说,它是一个OR。
查询12:这与我们看到的查询大致相同,只返回类型为“德克萨斯城市” 或 “加州城市”类型的城市。
PREFIX rdf:< http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs:< http://www.w3.org/2000/01/rdf-schema#>
PREFIX dbp:< http://dbpedia.org/ontology/>
SELECT * WHERE {
{
?city rdf:type < http://dbpedia.org/class/yago/WikicatCitiesInTexas> ;
dbp:populationTotal?popTotal;
rdfs:label?name
OPTIONAL {?city dbp:populationMetro?popMetro。}
FILTER(?popTotal> 50000 && langmatches(lang(?name),“EN”))
}
UNION
{
?city rdf:type < http://dbpedia.org/class/yago/CitiesInCalifornia>;
dbp:populationTotal?popTotal;
rdfs:label?name
OPTIONAL {?city dbp:populationMetro?popMetro。}
FILTER(?popTotal> 50000 && langmatches(lang(?name),“EN”))
}
}
ORDER BY desc(?popTotal)
这是一个非常简单,天真的第一次尝试写这个表达。您可以看到,以前的查询有几个冗余的三重模式,可以简化。以下查询等同于前一个查询,但更简单:
PREFIX rdf:< http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs:< http://www.w3.org/2000/01/rdf-schema#>
PREFIX dbp:< http://dbpedia.org/ontology/>
SELECT * WHERE {
?city dbp:populationTotal?popTotal;
rdfs:label?name
OPTIONAL {?city dbp:populationMetro?popMetro。}
FILTER(?popTotal> 50000 && langmatches(lang(?name),“EN”))
{?city rdf:type < http://dbpedia.org/class/yago/WikicatCitiesInTexas>。}
UNION
{?city rdf:type < http://dbpedia.org/class/yago/CitiesInCalifornia>。}
}
ORDER BY desc(?popTotal)
请注意,重复的三重模式(例如两组结果共同的)不在UNION子句之外。
到目前为止,我们一直在查询单个RDF数据集。然而,如RDF 101 RDF数据中所解释的,即使在单个RDF数据库中,也被分解成称为命名图的子集。
到目前为止,在本教程中,我们所有的查询都假设我们关心的所有数据都在同一RDF图中,这被称为默认图。根据RDF数据库的实现,默认图表根本不包含任何内容,或整个数据库的元数据,也可能作为数据库中所有数据的代理服务器。
使用DBpedia,默认图形用作所有数据的代理。但是,它确实有许多命名图,我们可以使用它来定制查询,如果需要的话。
每个命名图由URI标识。
查询13:此查询返回“德克萨斯州城市”类型的城市以及包含每个城市资源的图表。
PREFIX rdf:< http://www.w3.org/1999/02/22-rdf-syntax-ns#>
SELECT * WHERE {
GRAPH?g {
?city rdf:type < http://dbpedia.org/class / yago / WikicatCitiesInTexas>。
}
}
结果显示,所有的数据都在同一个图形,默认图表。
一般来说,如果要查询特定的命名图形,请使用命名图形的特定URI替换变量?g。
到目前为止,我们刚刚执行SELECT查询。然而,正如我们在SPARQL 101中提到的,还有三种类型的查询:ASK,DESCRIBE,CONSTRUCT。
ASK查询检查给定查询模式是否至少有一个结果。结果是真的还是假的。
查询14:此查询询问奥斯汀是德克萨斯州的城市。
PREFIX rdf:< http://www.w3.org/1999/02/22-rdf-syntax-ns#>
ASK WHERE {
< http://dbpedia.org/resource/Austin,_Texas> rdf:type
< http ://dbpedia.org/class/yago/WikicatCitiesInTexas>。
}}
现在让我们用一个更复杂的问题使用ASK。
查询15:此查询询问德克萨斯州是否存在总人口超过60万的城市,而城市人口少于1,800,000
PREFIX rdf:< http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX dbp:< http://dbpedia.org/ontology/>
ASK WHERE {
?city rdf:type ; http://dbpedia.org/class/yago/WikicatCitiesInTexas> ;
dbp:populationTotal?popTotal;
dbp:populationMetro?popMetro。
FILTER(?popTotal> 600000 &&?popMetro <1800000)
}
DESCRIBE查询返回描述资源的RDF图。该返回表单的实现取决于每个查询引擎。
查询16:此查询返回描述奥斯汀的RDF图。
DESCRIBE < http://dbpedia.org/resource/Austin,_Texas>
请注意,如果您将此查询输入到DBpedia的SPARQL用户界面中,您将被允许下载文件。这样做的原因是DESCRIBE的结果是一个图形,在这种具体情况下,以RDF螺母和螺栓描述的N3序列化格式表示的图形。
如前所述,DESCRIBE的行为取决于实现。Virtuoso是对DBpedia提供支持的三重存储,其DESRIBE的实现是返回资源在主题或对象位置的RDF图。其他三重商店不需要具有相同的行为。
也可以在WHERE子句中具有三重模式的DESCRIBE查询。
查询17:此查询返回一个RDF图,描述德克萨斯州总体人口超过60万,城市人口少于180000的所有城市。
PREFIX rdf:< http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX dbp:< http://dbpedia.org/ontology/>
DESCRIBE?city WHERE {
?city rdf :type < http://dbpedia.org/class/yago/WikicatCitiesInTexas> ;
dbp:populationTotal?popTotal;
dbp:populationMetro?popMetro。
FILTER(?popTotal> 600000 &&?popMetro <1800000)
}
CONSTRUCT查询返回从CONSTRUCT查询中指定的图形模板创建的RDF图。更具体地,通过获取查询模式的结果并填充在构造模板中出现的变量的值来创建结果RDF图。
使用CONSTRUCT,您可以将RDF数据转换为具有不同词汇表的不同图形结构。如果您有自动生成的RDF数据,并希望使用众所周知的词汇表进行转换,或者合并来自多个词汇表的RDF数据,这可能很有用。因此,CONSTRUCT是从各种来源消费RDF的强大工具。
查询18:该查询为德克萨斯州的城市人口大于50万的城市构建了新的RDF图。
PREFIX rdf:< http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs:< http://www.w3.org/2000/01/rdf-schema#>
PREFIX dbp:< http://dbpedia.org/ontology/>
CONSTRUCT {
?city rdf:type < http://myvocabulary.com/LargeMetroCitiesInTexas> ;
< http://myvocabulary.com/cityName>?name;
< http://myvocabulary.com/totalPopulation>?popTotal;
< http://myvocabulary.com/metroPopulation>?popMetro。
} WHERE {
?city rdf:type < http://dbpedia.org/class/yago/WikicatCitiesInTexas> ;
dbp:populationTotal?popTotal;
rdfs:label?name;
dbp:populationMetro?popMetro。
FILTER(?popTotal> 500000 && langmatches(lang(?name),“EN”))
}
请注意,在上面的CONSTRUCT子句中,我们使用了我们自己的,组成词汇!
对于SPARQL端点,SELECT和ASK查询返回XML(application / sparql-results + xml)作为SPARQL查询的标准查询结果格式。还有一个非标准的JSON语法。要查看原始SPARQL结果的外观,请在DBpedia SPARQL UI中切换“结果格式”字段。
也就是说,您很少会自己解析SPARQL结果,因为您通常会通过RDF客户端库发出SPARQL查询,该库负责处理该问题。
DESCRIBE和CONSTRUCT都直接返回RDF图形,而不是以标准的SPARQL查询结果格式返回。例如,DBpedia将返回N3中的结果。如RDF Nuts&Bolts所述,有几个可用于表示RDF数据的RDF序列化,结果序列化格式将取决于实现