SPARQL
RDF query language
- 14 min read
- 5 chapters
Related on Lably
Through Query language, Database and Logical conjunction
SPARQL allows for a query to consist of triple patterns, conjunctions, disjunctions, and optional patterns.
Implementations for multiple programming languages exist. There exist tools that allow one to connect and semi-automatically construct a SPARQL query for a SPARQL endpoint, for example ViziQuer. In addition, tools exist to translate SPARQL queries to other query languages, for example to SQL and to XQuery.
Features
SPARQL allows users to write queries that follow the RDF specification of the W3C. Thus, the entire dataset is "subject-predicate-object" triples. Subjects and predicates are always URI identifiers, but objects can be URIs or literal values. This single physical schema of three "columns" is hypernormalized in that what would be one relational record with (for example) four columns is now four triples with the subject being repeated over and over, the predicate essentially being the column name, and the object being the column value. The SPARQL syntax offers these features:
1. Subjects and Objects can be used to find the other including transitively.
Below is a set of triples. It should be clear that ex:sw001 and ex:sw002 link to ex:sw003, which itself has links:
<span id="mwbw"></span><span class="nn" id="mwcA">ex</span><span class="p" id="mwcQ">:</span><span class="nt" id="mwcg">sw001</span> <span class="nn" id="mwcw">ex</span><span class="p" id="mwdA">:</span><span class="nt" id="mwdQ">linksWith</span> <span class="nn" id="mwdg">ex</span><span class="p" id="mwdw">:</span><span class="nt" id="mweA">sw003</span> <span class="p" id="mweQ">.</span> <span class="nn" id="mweg">ex</span><span class="p" id="mwew">:</span><span class="nt" id="mwfA">sw002</span> <span class="nn" id="mwfQ">ex</span><span class="p" id="mwfg">:</span><span class="nt" id="mwfw">linksWith</span> <span class="nn" id="mwgA">ex</span><span class="p" id="mwgQ">:</span><span class="nt" id="mwgg">sw003</span> <span class="p" id="mwgw">.</span> <span class="nn" id="mwhA">ex</span><span class="p" id="mwhQ">:</span><span class="nt" id="mwhg">sw003</span> <span class="nn" id="mwhw">ex</span><span class="p" id="mwiA">:</span><span class="nt" id="mwiQ">linksWith</span> <span class="nn" id="mwig">ex</span><span class="p" id="mwiw">:</span><span class="nt" id="mwjA">sw004</span> <span class="p" id="mwjQ">,</span> <span class="nn" id="mwjg">ex</span><span class="p" id="mwjw">:</span><span class="nt" id="mwkA">sw006</span> <span class="p" id="mwkQ">.</span> <span class="nn" id="mwkg">ex</span><span class="p" id="mwkw">:</span><span class="nt" id="mwlA">sw004</span> <span class="nn" id="mwlQ">ex</span><span class="p" id="mwlg">:</span><span class="nt" id="mwlw">linksWith</span> <span class="nn" id="mwmA">ex</span><span class="p" id="mwmQ">:</span><span class="nt" id="mwmg">sw005</span> <span class="p" id="mwmw">.</span>
In SPARQL, the first time a variable is encountered in the expression pipeline, it is populated with result. The second and subsequent times it is seen, it is used as an input. If we assign ("bind") the URI ex:sw003 to the ?targets variable, then it drives a result into ?src; this tells us all the things that link to ex:sw003 (upstream dependency):
<span id="mwpA"></span><span class="k" id="mwpQ">SELECT</span> <span class="o" id="mwpg">*</span> <span class="k" id="mwpw">WHERE</span> <span class="p" id="mwqA">{</span> <span class="k" id="mwqQ">BIND</span><span class="p" id="mwqg">(</span><span class="nn" id="mwqw">ex</span><span class="p" id="mwrA">:</span><span class="nt" id="mwrQ">sw003</span> <span class="k" id="mwrg">AS</span> <span class="nv" id="mwrw">?targets</span><span class="p" id="mwsA">)</span> <span class="nv" id="mwsQ">?src</span> <span class="nn" id="mwsg">ex</span><span class="p" id="mwsw">:</span><span class="nt" id="mwtA">linksWith</span> <span class="nv" id="mwtQ">?targets</span> <span class="p" id="mwtg">.</span> <span class="c" id="mwtw"># ?src populated with ex:sw001, ex:sw002</span> <span class="p" id="mwuA">}</span>But with a simple switch of the binding variable, the behavior is reversed. This will produce all the things upon which ex:sw003 depends (downstream dependency):
<span id="mwvQ"></span><span class="k" id="mwvg">SELECT</span> <span class="o" id="mwvw">*</span> <span class="k" id="mwwA">WHERE</span> <span class="p" id="mwwQ">{</span> <span class="k" id="mwwg">BIND</span><span class="p" id="mwww">(</span><span class="nn" id="mwxA">ex</span><span class="p" id="mwxQ">:</span><span class="nt" id="mwxg">sw003</span> <span class="k" id="mwxw">AS</span> <span class="nv" id="mwyA">?src</span><span class="p" id="mwyQ">)</span> <span class="nv" id="mwyg">?src</span> <span class="nn" id="mwyw">ex</span><span class="p" id="mwzA">:</span><span class="nt" id="mwzQ">linksWith</span> <span class="nv" id="mwzg">?targets</span> <span class="p" id="mwzw">.</span> <span class="c" id="mw0A"># NOTICE! No syntax change! ?targets populated with ex:sw004, ex:sw006</span> <span class="p" id="mw0Q">}</span>Even more attractive is that we can easily instruct SPARQL to transitively follow the path:
<span id="mw1Q"></span><span class="k" id="mw1g">SELECT</span> <span class="o" id="mw1w">*</span> <span class="k" id="mw2A">WHERE</span> <span class="p" id="mw2Q">{</span> <span class="k" id="mw2g">BIND</span><span class="p" id="mw2w">(</span><span class="nn" id="mw3A">ex</span><span class="p" id="mw3Q">:</span><span class="nt" id="mw3g">sw003</span> <span class="k" id="mw3w">AS</span> <span class="nv" id="mw4A">?src</span><span class="p" id="mw4Q">)</span> <span class="c" id="mw4g"># Note the +; now SPARQL will also find ex:sw005 transitively via ex:sw004; ?targets is ex:sw004, ex:sw005, ex:sw006</span> <span class="nv" id="mw4w">?src</span> <span class="nn" id="mw5A">ex</span><span class="p" id="mw5Q">:</span><span class="nt" id="mw5g">linksWith</span><span class="o" id="mw5w">+</span> <span class="nv" id="mw6A">?targets</span> <span class="p" id="mw6Q">.</span> <span class="p" id="mw6g">}</span>Bound variables can therefore also be lists and will be operated upon without complicated syntax. The effect of this is similar to the following pseudocode:
<span id="mw7w"></span><span class="nf" id="mw8A">If</span> <span class="nv" id="mw8Q">?S</span> <span class="err" id="mw8g">is</span> <span class="nf" id="mw8w">bound</span> <span class="k" id="mw9A">to</span> <span class="p" id="mw9Q">(</span><span class="nn" id="mw9g">ex</span><span class="p" id="mw9w">:</span><span class="nt" id="mw-A">A</span><span class="p" id="mw-Q">,</span> <span class="nn" id="mw-g">ex</span><span class="p" id="mw-w">:</span><span class="nt" id="mw_A">B</span><span class="p" id="mw_Q">)</span> <span class="err" id="mw_g">and</span> <span class="nv" id="mw_w">?O</span> <span class="err" id="mwAQA">is</span> <span class="err" id="mwAQE">UN</span><span class="nf" id="mwAQI">bound</span> <span class="err" id="mwAQM">then</span> <span class="nv" id="mwAQQ">?S</span> <span class="nn" id="mwAQU">ex</span><span class="p" id="mwAQY">:</span><span class="nt" id="mwAQc">linksWith</span> <span class="nv" id="mwAQg">?O</span> <span class="err" id="mwAQk">behaves</span> <span class="err" id="mwAQo">like</span> <span class="k" id="mwAQs">a</span> <span class="err" id="mwAQw">forward</span> <span class="nn" id="mwAQ0">chain</span><span class="p" id="mwAQ4">:</span> <span class="err" id="mwAQ8">for</span> <span class="err" id="mwARA">each</span> <span class="err" id="mwARE">s</span> <span class="k" id="mwARI">in</span> <span class="nv" id="mwARM">?S</span><span class="p" id="mwARQ">:</span> <span class="err" id="mwARU">for</span> <span class="err" id="mwARY">each</span> <span class="err" id="mwARc">fetch</span> <span class="p" id="mwARg">(</span><span class="err" id="mwARk">s</span><span class="p" id="mwARo">,</span> <span class="nn" id="mwARs">ex</span><span class="p" id="mwARw">:</span><span class="nt" id="mwAR0">linksWith</span><span class="p" id="mwAR4">):</span> <span class="err" id="mwAR8">capture</span> <span class="err" id="mwASA">o</span> <span class="err" id="mwASE">append</span> <span class="err" id="mwASI">o</span> <span class="k" id="mwASM">to</span> <span class="nv" id="mwASQ">?O</span> <span class="nf" id="mwASU">If</span> <span class="nv" id="mwASY">?O</span> <span class="err" id="mwASc">is</span> <span class="nf" id="mwASg">bound</span> <span class="k" id="mwASk">to</span> <span class="p" id="mwASo">(</span><span class="nn" id="mwASs">ex</span><span class="p" id="mwASw">:</span><span class="nt" id="mwAS0">A</span><span class="p" id="mwAS4">,</span> <span class="nn" id="mwAS8">ex</span><span class="p" id="mwATA">:</span><span class="nt" id="mwATE">B</span><span class="p" id="mwATI">)</span> <span class="err" id="mwATM">and</span> <span class="nv" id="mwATQ">?S</span> <span class="err" id="mwATU">is</span> <span class="err" id="mwATY">UN</span><span class="nf" id="mwATc">bound</span> <span class="err" id="mwATg">then</span> <span class="nv" id="mwATk">?S</span> <span class="nn" id="mwATo">ex</span><span class="p" id="mwATs">:</span><span class="nt" id="mwATw">linksWith</span> <span class="nv" id="mwAT0">?O</span> <span class="err" id="mwAT4">behaves</span> <span class="err" id="mwAT8">like</span> <span class="k" id="mwAUA">a</span> <span class="err" id="mwAUE">backward</span> <span class="nn" id="mwAUI">chain</span><span class="p" id="mwAUM">:</span> <span class="err" id="mwAUQ">for</span> <span class="err" id="mwAUU">each</span> <span class="err" id="mwAUY">o</span> <span class="k" id="mwAUc">in</span> <span class="nv" id="mwAUg">?O</span><span class="p" id="mwAUk">:</span> <span class="err" id="mwAUo">for</span> <span class="err" id="mwAUs">each</span> <span class="err" id="mwAUw">fetch</span> <span class="p" id="mwAU0">(</span><span class="nn" id="mwAU4">ex</span><span class="p" id="mwAU8">:</span><span class="nt" id="mwAVA">linksWith</span><span class="p" id="mwAVE">,</span> <span class="err" id="mwAVI">o</span><span class="p" id="mwAVM">):</span> <span class="err" id="mwAVQ">capture</span> <span class="err" id="mwAVU">s</span> <span class="err" id="mwAVY">append</span> <span class="err" id="mwAVc">s</span> <span class="k" id="mwAVg">to</span> <span class="nv" id="mwAVk">?S</span>
2. SPARQL expressions are a pipeline
Unlike SQL which has subqueries and CTEs, SPARQL is much more like MongoDB or SPARK. Expressions are evaluated exactly in the order they are declared including filtering and joining of data. The programming model becomes what a SQL statement would be like with multiple WHERE clauses. The combination of list-aware subjects and objects plus a pipeline approach can yield extremely expressive queries spanning many different domains of data. JOIN as used in RDBMS and understanding the dynamics of the JOIN (e.g. what column in what table is suitable to join to another, inner vs. outer, etc.) is not relevant in SPARQL (and in some ways simpler) because objects, if a URI and not a literal, implicitly can be used only to find a subject. Here is a more comprehensive example that illustrates the pipeline using some syntax shortcuts.
<span id="mwAWA"></span><span class="c" id="mwAWE"># SELECT only the terminal values we need. If we did SELECT * (which</span> <span class="c" id="mwAWI"># is not nessarily bad), then "intermediate" variables ?vendor and ?owner</span> <span class="c" id="mwAWM"># would be part of the output.</span> <span class="k" id="mwAWQ">SELECT</span> <span class="nv" id="mwAWU">?slbl</span> <span class="nv" id="mwAWY">?vlbl</span> <span class="nv" id="mwAWc">?lei</span> <span class="nv" id="mwAWg">?lname</span> <span class="k" id="mwAWk">WHERE</span> <span class="p" id="mwAWo">{</span> <span class="c" id="mwAWs"># ?sw is unbound. Set predicate to rdf:type and object to ex:Software</span> <span class="c" id="mwAWw"># and collect all software instances. At same, pull the software</span> <span class="c" id="mwAW0"># label (a terse description) and populate ?slbl and also capture the</span> <span class="c" id="mwAW4"># vendor object into ?vendor.</span> <span class="nv" id="mwAW8">?sw</span> <span class="nn" id="mwAXA">rdf</span><span class="p" id="mwAXE">:</span><span class="nt" id="mwAXI">type</span> <span class="nn" id="mwAXM">ex</span><span class="p" id="mwAXQ">:</span><span class="nt" id="mwAXU">Software</span> <span class="p" id="mwAXY">;</span> <span class="nn" id="mwAXc">rdfs</span><span class="p" id="mwAXg">:</span><span class="nt" id="mwAXk">label</span> <span class="nv" id="mwAXo">?slbl</span> <span class="p" id="mwAXs">;</span> <span class="nn" id="mwAXw">ex</span><span class="p" id="mwAX0">:</span><span class="nt" id="mwAX4">vendor</span> <span class="nv" id="mwAX8">?vendor</span> <span class="p" id="mwAYA">.</span> <span class="c" id="mwAYE"># The above in "longhand" reveals the binding process:</span> <span class="c" id="mwAYI"># ?sw rdf:type ex:Software . # ?sw UNBOUND; is set here</span> <span class="c" id="mwAYM"># ?sw rdfs:label ?slbl . # ?sw bound; set unbound ?slbl</span> <span class="c" id="mwAYQ"># ?sw ex:vendor ?vendor . # ?sw still bound; set ?vendor</span> <span class="c" id="mwAYU"># Exclude open source software. Note ex:oss is a URI because it is</span> <span class="c" id="mwAYY"># an UNquoted string:</span> <span class="k" id="mwAYc">FILTER</span><span class="p" id="mwAYg">(</span><span class="nv" id="mwAYk">?vendor</span> <span class="k" id="mwAYo">NOT IN</span> <span class="p" id="mwAYs">(</span><span class="nn" id="mwAYw">ex</span><span class="p" id="mwAY0">:</span><span class="nt" id="mwAY4">oss</span><span class="p" id="mwAY8">))</span> <span class="c" id="mwAZA"># Next, dive into ?vendor object and extract legal entity identifier</span> <span class="c" id="mwAZE"># and owner of the data -- where owner is also an object. ?vendor is</span> <span class="c" id="mwAZI"># bound; ?vlbl, ?lei, and ?owner are unbound and will be populated:</span> <span class="nv" id="mwAZM">?vendor</span> <span class="nn" id="mwAZQ">rdfs</span><span class="p" id="mwAZU">:</span><span class="nt" id="mwAZY">label</span> <span class="nv" id="mwAZc">?vlbl</span> <span class="p" id="mwAZg">;</span> <span class="nn" id="mwAZk">ex</span><span class="p" id="mwAZo">:</span><span class="nt" id="mwAZs">LEI</span> <span class="nv" id="mwAZw">?lei</span> <span class="p" id="mwAZ0">;</span> <span class="nn" id="mwAZ4">ex</span><span class="p" id="mwAZ8">:</span><span class="nt" id="mwAaA">owner</span> <span class="nv" id="mwAaE">?owner</span> <span class="p" id="mwAaI">.</span> <span class="c" id="mwAaM"># Lastly, from owner object, capture last name:</span> <span class="nv" id="mwAaQ">?owner</span> <span class="nn" id="mwAaU">ex</span><span class="p" id="mwAaY">:</span><span class="nt" id="mwAac">lastname</span> <span class="nv" id="mwAag">?lname</span> <span class="p" id="mwAak">.</span> <span class="p" id="mwAao">}</span>Unlike relational databases, the object column is heterogeneous: the object data type, if not a URI, is usually implied (or specified in the ontology) by the predicate value. Literal nodes carry type information consistent with the underlying XSD namespace including signed and unsigned short and long integers, single and double precision floats, datetime, penny-precise decimal, Boolean, and string. Triple store implementations on traditional relational databases will typically store the value as a string and a fourth column will identify the real type. Polymorphic databases such as MongoDB and SQLite can store the native value directly into the object field.
Thus, SPARQL provides a full set of analytic query operations such as JOIN, SORT, AGGREGATE for data whose schema is intrinsically part of the data rather than requiring a separate schema definition. However, schema information (the ontology) is often provided externally, to allow joining of different datasets unambiguously. In addition, SPARQL provides specific graph traversal syntax for data that can be thought of as a graph.
The example below demonstrates a simple query that leverages the ontology definition foaf ("friend of a friend").
Specifically, the following query returns names and emails of every person in the dataset:
<span id="mwAb0"></span><span class="k" id="mwAb4">PREFIX</span> <span class="nn" id="mwAb8">foaf</span><span class="p" id="mwAcA">:</span> <span class="nl" id="mwAcE"><http://xmlns.com/foaf/0.1/></span> <span class="k" id="mwAcI">SELECT</span> <span class="nv" id="mwAcM">?name</span> <span class="nv" id="mwAcQ">?email</span> <span class="k" id="mwAcU">WHERE</span> <span class="p" id="mwAcY">{</span> <span class="nv" id="mwAcc">?person</span> <span class="k" id="mwAcg">a</span> <span class="nn" id="mwAck">foaf</span><span class="p" id="mwAco">:</span><span class="nt" id="mwAcs">Person</span> <span class="p" id="mwAcw">.</span> <span class="nv" id="mwAc0">?person</span> <span class="nn" id="mwAc4">foaf</span><span class="p" id="mwAc8">:</span><span class="nt" id="mwAdA">name</span> <span class="nv" id="mwAdE">?name</span> <span class="p" id="mwAdI">.</span> <span class="nv" id="mwAdM">?person</span> <span class="nn" id="mwAdQ">foaf</span><span class="p" id="mwAdU">:</span><span class="nt" id="mwAdY">mbox</span> <span class="nv" id="mwAdc">?email</span> <span class="p" id="mwAdg">.</span> <span class="p" id="mwAdk">}</span>This query joins all of the triples with a matching subject, where the type predicate, "a", is a person (foaf:Person), and the person has one or more names (foaf:name) and mailboxes (foaf:mbox).
For the sake of readability, the author of this query chose to reference the subject using the variable name "?person". Since the first element of the triple is always the subject, the author could have just as easily used any variable name, such as "?subj" or "?x". Whatever name is chosen, it must be the same on each line of the query to signify that the query engine is to join triples with the same subject.
The result of the join is a set of rows – ?person, ?name, ?email. This query returns the ?name and ?email because ?person is often a complex URI rather than a human-friendly string. Note that any ?person may have multiple mailboxes, so in the returned set, a ?name row may appear multiple times, once for each mailbox, duplicating the ?name.
An important consideration in SPARQL is that when lookup conditions are not met in the pipeline for terminal entities like ?email, then the whole row is excluded, unlike SQL where typically a null column is returned. The query above will return only those ?person where both at least one ?name and at least one ?email can be found. If a ?person had no email, they would be excluded. To align the output with that expected from an equivalent SQL query, the OPTIONAL keyword is required:
<span id="mwAfc"></span><span class="k" id="mwAfg">PREFIX</span> <span class="nn" id="mwAfk">foaf</span><span class="p" id="mwAfo">:</span> <span class="nl" id="mwAfs"><http://xmlns.com/foaf/0.1/></span> <span class="k" id="mwAfw">SELECT</span> <span class="nv" id="mwAf0">?name</span> <span class="nv" id="mwAf4">?email</span> <span class="k" id="mwAf8">WHERE</span> <span class="p" id="mwAgA">{</span> <span class="nv" id="mwAgE">?person</span> <span class="k" id="mwAgI">a</span> <span class="nn" id="mwAgM">foaf</span><span class="p" id="mwAgQ">:</span><span class="nt" id="mwAgU">Person</span> <span class="p" id="mwAgY">.</span> <span class="k" id="mwAgc">OPTIONAL</span> <span class="p" id="mwAgg">{</span> <span class="nv" id="mwAgk">?person</span> <span class="nn" id="mwAgo">foaf</span><span class="p" id="mwAgs">:</span><span class="nt" id="mwAgw">name</span> <span class="nv" id="mwAg0">?name</span> <span class="p" id="mwAg4">.</span> <span class="nv" id="mwAg8">?person</span> <span class="nn" id="mwAhA">foaf</span><span class="p" id="mwAhE">:</span><span class="nt" id="mwAhI">mbox</span> <span class="nv" id="mwAhM">?email</span> <span class="p" id="mwAhQ">.</span> <span class="p" id="mwAhU">}</span> <span class="p" id="mwAhY">}</span>This query can be distributed to multiple SPARQL endpoints (services that accept SPARQL queries and return results), computed, and results gathered, a procedure known as federated query.
Whether in a federated manner or locally, additional triple definitions in the query could allow joins to different subject types, such as automobiles, to allow simple queries, for example, to return a list of names and emails for people who drive automobiles with a high fuel efficiency.
Sign in to keep reading
4 more chapters are free with a Lably account.
Query forms
In the case of queries that read data from the database, the SPARQL language specifies four different query variations for different purposes.
SELECTquery- Used to extract raw values from a SPARQL endpoint, the results are returned in a table format.
CONSTRUCTquery- Used to extract information from the SPARQL endpoint and transform the results into valid RDF.
ASKquery- Used to provide a simple True/False result for a query on a SPARQL endpoint.
DESCRIBEquery- Used to extract an RDF graph from the SPARQL endpoint, the content of which is left to the endpoint to decide, based on what the maintainer deems as useful information.
Each of these query forms takes a WHERE block to restrict the query, although, in the case of the DESCRIBE query, the WHERE is optional.
SPARQL 1.1 specifies a language for updating the database with several new query forms.
Example
Another SPARQL query example that models the question "What are all the country capitals in Africa?":
<span id="mwAjY"></span><span class="k" id="mwAjc">PREFIX</span> <span class="nn" id="mwAjg">ex</span><span class="p" id="mwAjk">:</span> <span class="nl" id="mwAjo"><http://example.com/exampleOntology#></span> <span class="k" id="mwAjs">SELECT</span> <span class="nv" id="mwAjw">?capital</span> <span class="nv" id="mwAj0">?country</span> <span class="k" id="mwAj4">WHERE</span> <span class="p" id="mwAj8">{</span> <span class="nv" id="mwAkA">?x</span> <span class="nn" id="mwAkE">ex</span><span class="p" id="mwAkI">:</span><span class="nt" id="mwAkM">cityname</span> <span class="nv" id="mwAkQ">?capital</span> <span class="p" id="mwAkU">;</span> <span class="nn" id="mwAkY">ex</span><span class="p" id="mwAkc">:</span><span class="nt" id="mwAkg">isCapitalOf</span> <span class="nv" id="mwAkk">?y</span> <span class="p" id="mwAko">.</span> <span class="nv" id="mwAks">?y</span> <span class="nn" id="mwAkw">ex</span><span class="p" id="mwAk0">:</span><span class="nt" id="mwAk4">countryname</span> <span class="nv" id="mwAk8">?country</span> <span class="p" id="mwAlA">;</span> <span class="nn" id="mwAlE">ex</span><span class="p" id="mwAlI">:</span><span class="nt" id="mwAlM">isInContinent</span> <span class="nn" id="mwAlQ">ex</span><span class="p" id="mwAlU">:</span><span class="nt" id="mwAlY">Africa</span> <span class="p" id="mwAlc">.</span> <span class="p" id="mwAlg">}</span>Variables are indicated by a ? or $ prefix. Bindings for ?capital and the ?country will be returned. When a triple ends with a semicolon, the subject from this triple will implicitly complete the following pair to an entire triple. So for example ex:isCapitalOf ?y is short for ?x ex:isCapitalOf ?y.
The SPARQL query processor will search for sets of triples that match these four triple patterns, binding the variables in the query to the corresponding parts of each triple. Important to note here is the "property orientation" (class matches can be conducted solely through class-attributes or properties – see Duck typing).
To make queries concise, SPARQL allows the definition of prefixes and base URIs in a fashion similar to Turtle. In this query, the prefix "ex" stands for “http://example.com/exampleOntology#”.
SPARQL has native dateTime operations as well. Here is a query that will return all pieces of software where the EOL date is greater than or equal to 1000 days from the release date and the release year is 2020 or greater:
<span id="mwAms"></span><span class="k" id="mwAmw">SELECT</span> <span class="nv" id="mwAm0">?lbl</span> <span class="nv" id="mwAm4">?version</span> <span class="nv" id="mwAm8">?released</span> <span class="nv" id="mwAnA">?eol</span> <span class="nv" id="mwAnE">?duration</span> <span class="k" id="mwAnI">WHERE</span> <span class="p" id="mwAnM">{</span> <span class="nv" id="mwAnQ">?software</span> <span class="k" id="mwAnU">a</span> <span class="nn" id="mwAnY">ex</span><span class="p" id="mwAnc">:</span><span class="nt" id="mwAng">Software</span> <span class="p" id="mwAnk">;</span> <span class="nn" id="mwAno">rdfs</span><span class="p" id="mwAns">:</span><span class="nt" id="mwAnw">label</span> <span class="nv" id="mwAn0">?lbl</span> <span class="p" id="mwAn4">;</span> <span class="nn" id="mwAn8">ex</span><span class="p" id="mwAoA">:</span><span class="nt" id="mwAoE">EOL</span> <span class="nv" id="mwAoI">?eol</span> <span class="p" id="mwAoM">;</span> <span class="c" id="mwAoQ"># is xsd:dateTime</span> <span class="nn" id="mwAoU">ex</span><span class="p" id="mwAoY">:</span><span class="nt" id="mwAoc">version</span> <span class="nv" id="mwAog">?version</span> <span class="p" id="mwAok">;</span> <span class="c" id="mwAoo"># string</span> <span class="nn" id="mwAos">ex</span><span class="p" id="mwAow">:</span><span class="nt" id="mwAo0">released</span> <span class="nv" id="mwAo4">?released</span> <span class="p" id="mwAo8">;</span> <span class="c" id="mwApA"># is xsd:dateTime</span> <span class="c" id="mwApE"># After this stage, ?duration is bound as xsd:duration type</span> <span class="c" id="mwApI"># (in Java implementations, org.apache.jena.datatypes.xsd.XSDDuration)</span> <span class="c" id="mwApM"># and is available in the pipeline, in the SELECT, and in</span> <span class="c" id="mwApQ"># GROUP or ORDER operators, etc.:</span> <span class="k" id="mwApU">BIND</span><span class="p" id="mwApY">(</span><span class="nv" id="mwApc">?eol</span> <span class="o" id="mwApg">-</span> <span class="nv" id="mwApk">?released</span> <span class="k" id="mwApo">AS</span> <span class="nv" id="mwAps">?duration</span><span class="p" id="mwApw">)</span> <span class="c" id="mwAp0"># toString representation of Duration is of format PnYnMnDTnHnMnS. </span> <span class="c" id="mwAp4"># We must use ^^ casting to tell the engine this is to be treated as a duration.</span> <span class="c" id="mwAp8"># SPARQL (and RDF) literal syntax has built-in numeric shortcuts to simplify </span> <span class="c" id="mwAqA"># expressions without casts:</span> <span class="c" id="mwAqE"># 16 xsd:int java.lang.Integer</span> <span class="c" id="mwAqI"># 16.7 xsd:decimal java.math.BigDecimal preserves precision</span> <span class="c" id="mwAqM"># 16.700 xsd:decimal java.math.BigDecimal preserves precision</span> <span class="c" id="mwAqQ"># 1.0632e6 xsd:double java.lang.Double true double float; be careful</span> <span class="c" id="mwAqU"># 2147483649 xsd:long java.lang.Long >32 bit int automatically detected</span> <span class="c" id="mwAqY">#</span> <span class="c" id="mwAqc"># Most castings work as expected e.g. "16.700"^^xsd:double. </span> <span class="c" id="mwAqg"># Note in the FILTER below we use the shortcut for integer 2020:</span> <span class="k" id="mwAqk">FILTER</span><span class="p" id="mwAqo">(</span><span class="nv" id="mwAqs">?duration</span> <span class="o" id="mwAqw">>=</span> <span class="s" id="mwAq0">"P1000D"</span><span class="o" id="mwAq4">^^</span><span class="nn" id="mwAq8">xsd</span><span class="p" id="mwArA">:</span><span class="nt" id="mwArE">duration</span> <span class="o" id="mwArI">&&</span> <span class="nf" id="mwArM">YEAR</span><span class="p" id="mwArQ">(</span><span class="nv" id="mwArU">?released</span><span class="p" id="mwArY">)</span> <span class="o" id="mwArc">>=</span> <span class="mi" id="mwArg">2020</span><span class="p" id="mwArk">)</span> <span class="p" id="mwAro">}</span> <span class="k" id="mwArs">ORDER BY</span> <span class="k" id="mwArw">DESC</span><span class="p" id="mwAr0">(</span><span class="nv" id="mwAr4">?duration</span><span class="p" id="mwAr8">)</span> <span class="k" id="mwAsA">LIMIT</span> <span class="mi" id="mwAsE">5</span>Extensions
GeoSPARQL defines filter functions for geographic information system (GIS) queries using well-understood OGC standards (GML, WKT, etc.).
SPARUL is another extension to SPARQL. It enables the RDF store to be updated with this declarative query language, by adding INSERT and DELETE methods.
XSPARQL is an integrated query language combining XQuery with SPARQL to query both XML and RDF data sources at once.
Implementations
Open source, reference SPARQL implementations
- Eclipse RDF4J, formerly OpenRDF Sesame
- Apache Jena
- OpenLink Virtuoso
See List of SPARQL implementations for more comprehensive coverage, including triplestore, APIs, and other storages that have implemented the SPARQL standard.

