Cheatsheets:SPARQL: Difference between revisions
Property paths: replace confusing prose with step-by-step table (P31 vs P279 vs P31/P279*), live-verified examples (via update-page on MediaWiki MCP Server) |
Standardize: add copy button to block code snippets |
||
| (18 intermediate revisions by 2 users not shown) | |||
| Line 1: | Line 1: | ||
{{Cheatsheet}} | {{Cheatsheet}} | ||
SPARQL | If you are new to SPARQL, see [[FOSS:SPARQL|SPARQL101]]. | ||
see [[ | |||
= | <blockquote> | ||
Examples featured in this cheatsheet run as-is against the public Wikidata endpoint <syntaxhighlight lang="text" inline>https://query.wikidata.org/sparql</syntaxhighlight>. Querying another Wikibase instance is similar, but small query adjustments may be necessary depending on how each instance is set up. See the documentation for your target instance for more info. | |||
</blockquote> | |||
== Basic example == | |||
<syntaxhighlight lang="sparql"> | Natural language question: | ||
<syntaxhighlight lang="text" copy> | |||
How many dogs and cats are featured in Wikidata? | |||
</syntaxhighlight> | |||
SPARQL query: | |||
<syntaxhighlight lang="sparql" copy> | |||
SELECT ?animal ?animalLabel (COUNT(?item) AS ?count) WHERE { | SELECT ?animal ?animalLabel (COUNT(?item) AS ?count) WHERE { | ||
VALUES ?animal { wd:Q144 wd:Q146 } # dog, cat | VALUES ?animal { wd:Q144 wd:Q146 } # dog, cat | ||
| Line 22: | Line 26: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Result: | Result as of 2026-08-19: | ||
<syntaxhighlight lang="text"> | |||
<syntaxhighlight lang="text" copy> | |||
dog 553 | dog 553 | ||
cat 239 | cat 239 | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== Building a query == | == Building a query == | ||
A query is a list of '''triple patterns''' | A query is a list of '''triple patterns'''( | ||
<syntaxhighlight lang="sparql" inline>subject predicate object | <syntaxhighlight lang="sparql" inline>subject predicate object</syntaxhighlight>). | ||
Knowing one or two elements, you can query the rest: | |||
{| class="wikitable" | {| class="wikitable" | ||
| Line 48: | Line 48: | ||
| "What is known about Einstein?" || subject Einstein (wd:Q937) || <syntaxhighlight lang="sparql" inline>wd:Q937 ?predicate ?value .</syntaxhighlight> | | "What is known about Einstein?" || subject Einstein (wd:Q937) || <syntaxhighlight lang="sparql" inline>wd:Q937 ?predicate ?value .</syntaxhighlight> | ||
|} | |} | ||
== Query forms == | == Query forms == | ||
| Line 134: | Line 65: | ||
'''SELECT''' — what is the population of France? | '''SELECT''' — what is the population of France? | ||
<syntaxhighlight lang="sparql"> | <syntaxhighlight lang="sparql" copy> | ||
SELECT ?population WHERE { | SELECT ?population WHERE { | ||
wd:Q142 wdt:P1082 ?population . | wd:Q142 wdt:P1082 ?population . | ||
| Line 141: | Line 72: | ||
Result: | Result: | ||
<syntaxhighlight lang="text"> | <syntaxhighlight lang="text" copy> | ||
68605616 | 68605616 | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 147: | Line 78: | ||
'''ASK''' — is there at least one dog in Wikidata? | '''ASK''' — is there at least one dog in Wikidata? | ||
<syntaxhighlight lang="sparql"> | <syntaxhighlight lang="sparql" copy> | ||
ASK WHERE { ?x wdt:P31 wd:Q144 . } | ASK WHERE { ?x wdt:P31 wd:Q144 . } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Result: | Result: | ||
<syntaxhighlight lang="text"> | <syntaxhighlight lang="text" copy> | ||
true | true | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 159: | Line 90: | ||
triple <syntaxhighlight lang="sparql" inline>?x wdt:P31 wd:Q144</syntaxhighlight>: | triple <syntaxhighlight lang="sparql" inline>?x wdt:P31 wd:Q144</syntaxhighlight>: | ||
<syntaxhighlight lang="sparql"> | <syntaxhighlight lang="sparql" copy> | ||
CONSTRUCT { ?x wdt:P31 wd:Q144 . } | CONSTRUCT { ?x wdt:P31 wd:Q144 . } | ||
WHERE { ?x wdt:P31 wd:Q144 . } LIMIT 2 | WHERE { ?x wdt:P31 wd:Q144 . } LIMIT 2 | ||
| Line 165: | Line 96: | ||
Result (visualised, N-Triples style): | Result (visualised, N-Triples style): | ||
<syntaxhighlight lang="text"> | <syntaxhighlight lang="text" copy> | ||
Q186486 wdt:P31 wd:Q144 | Q186486 wdt:P31 wd:Q144 | ||
Q280571 wdt:P31 wd:Q144 | Q280571 wdt:P31 wd:Q144 | ||
| Line 173: | Line 104: | ||
subject or object): | subject or object): | ||
<syntaxhighlight lang="sparql"> | <syntaxhighlight lang="sparql" copy> | ||
DESCRIBE wd:Q937 | DESCRIBE wd:Q937 | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Result (visualised, N-Triples style, first few of thousands): | Result (visualised, N-Triples style, first few of thousands): | ||
<syntaxhighlight lang="text"> | <syntaxhighlight lang="text" copy> | ||
Q937 rdfs:label "Albert Einstein"@en | Q937 rdfs:label "Albert Einstein"@en | ||
Q937 wdt:P569 1879-03-14 | Q937 wdt:P569 1879-03-14 | ||
| Line 187: | Line 118: | ||
== Solution modifiers == | == Solution modifiers == | ||
'''DISTINCT''' — drop duplicate rows. Einstein and Obama are both male, so the | '''DISTINCT''' — drop duplicate rows. Einstein and Obama are both male, so the | ||
| Line 197: | Line 126: | ||
|- | |- | ||
| | | | ||
<syntaxhighlight lang="sparql"> | <syntaxhighlight lang="sparql" copy> | ||
SELECT ?genderLabel WHERE { | SELECT ?genderLabel WHERE { | ||
VALUES ?person { wd:Q937 wd:Q76 } | VALUES ?person { wd:Q937 wd:Q76 } | ||
| Line 206: | Line 135: | ||
Result: | Result: | ||
<syntaxhighlight lang="text"> | <syntaxhighlight lang="text" copy> | ||
male | male | ||
male | male | ||
</syntaxhighlight> | </syntaxhighlight> | ||
|| | || | ||
<syntaxhighlight lang="sparql" line highlight="1"> | <syntaxhighlight lang="sparql" line highlight="1" copy> | ||
SELECT DISTINCT ?genderLabel WHERE { | SELECT DISTINCT ?genderLabel WHERE { | ||
VALUES ?person { wd:Q937 wd:Q76 } | VALUES ?person { wd:Q937 wd:Q76 } | ||
| Line 220: | Line 149: | ||
Result: | Result: | ||
<syntaxhighlight lang="text"> | <syntaxhighlight lang="text" copy> | ||
male | male | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 231: | Line 160: | ||
|- | |- | ||
| | | | ||
<syntaxhighlight lang="sparql"> | <syntaxhighlight lang="sparql" copy> | ||
SELECT ?countryLabel ?population WHERE { | SELECT ?countryLabel ?population WHERE { | ||
VALUES ?country { wd:Q148 wd:Q30 wd:Q668 } # China, US, India | VALUES ?country { wd:Q148 wd:Q30 wd:Q668 } # China, US, India | ||
| Line 240: | Line 169: | ||
Result: | Result: | ||
<syntaxhighlight lang="text"> | <syntaxhighlight lang="text" copy> | ||
United States 340110988 | United States 340110988 | ||
China 1404890000 | China 1404890000 | ||
| Line 246: | Line 175: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
|| | || | ||
<syntaxhighlight lang="sparql" line highlight="6"> | <syntaxhighlight lang="sparql" line highlight="6" copy> | ||
SELECT ?countryLabel ?population WHERE { | SELECT ?countryLabel ?population WHERE { | ||
VALUES ?country { wd:Q148 wd:Q30 wd:Q668 } | VALUES ?country { wd:Q148 wd:Q30 wd:Q668 } | ||
| Line 256: | Line 185: | ||
Result: | Result: | ||
<syntaxhighlight lang="text"> | <syntaxhighlight lang="text" copy> | ||
China 1404890000 | China 1404890000 | ||
India 1326093247 | India 1326093247 | ||
| Line 269: | Line 198: | ||
|- | |- | ||
| | | | ||
<syntaxhighlight lang="sparql"> | <syntaxhighlight lang="sparql" copy> | ||
SELECT ?countryLabel WHERE { | SELECT ?countryLabel WHERE { | ||
VALUES ?country { wd:Q148 wd:Q30 wd:Q668 } | VALUES ?country { wd:Q148 wd:Q30 wd:Q668 } | ||
| Line 278: | Line 207: | ||
Result: | Result: | ||
<syntaxhighlight lang="text"> | <syntaxhighlight lang="text" copy> | ||
United States | United States | ||
China | China | ||
| Line 284: | Line 213: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
|| | || | ||
<syntaxhighlight lang="sparql" line highlight="6"> | <syntaxhighlight lang="sparql" line highlight="6" copy> | ||
SELECT ?countryLabel WHERE { | SELECT ?countryLabel WHERE { | ||
VALUES ?country { wd:Q148 wd:Q30 wd:Q668 } | VALUES ?country { wd:Q148 wd:Q30 wd:Q668 } | ||
| Line 294: | Line 223: | ||
Result: | Result: | ||
<syntaxhighlight lang="text"> | <syntaxhighlight lang="text" copy> | ||
United States | United States | ||
China | China | ||
| Line 306: | Line 235: | ||
|- | |- | ||
| | | | ||
<syntaxhighlight lang="sparql"> | <syntaxhighlight lang="sparql" copy> | ||
SELECT ?countryLabel ?population WHERE { | SELECT ?countryLabel ?population WHERE { | ||
?country wdt:P31 wd:Q6256 . | ?country wdt:P31 wd:Q6256 . | ||
| Line 317: | Line 246: | ||
Result: | Result: | ||
<syntaxhighlight lang="text"> | <syntaxhighlight lang="text" copy> | ||
China 1404890000 | China 1404890000 | ||
</syntaxhighlight> | </syntaxhighlight> | ||
|| | || | ||
<syntaxhighlight lang="sparql" line highlight="7"> | <syntaxhighlight lang="sparql" line highlight="7" copy> | ||
SELECT ?countryLabel ?population WHERE { | SELECT ?countryLabel ?population WHERE { | ||
?country wdt:P31 wd:Q6256 . | ?country wdt:P31 wd:Q6256 . | ||
| Line 332: | Line 261: | ||
Result: | Result: | ||
<syntaxhighlight lang="text"> | <syntaxhighlight lang="text" copy> | ||
India 1326093247 | India 1326093247 | ||
</syntaxhighlight> | </syntaxhighlight> | ||
|} | |} | ||
== VALUES & BIND == | |||
VALUES restricts a variable to a list; BIND computes a new variable. | |||
<syntaxhighlight lang="sparql" copy> | |||
SELECT ?personLabel ?birthYear WHERE { | |||
VALUES ?person { wd:Q937 wd:Q76 } # Einstein, Obama | |||
?person wdt:P569 ?birth . | |||
BIND(YEAR(?birth) AS ?birthYear) | |||
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". } | |||
} | |||
</syntaxhighlight> | |||
Result: | |||
<syntaxhighlight lang="text" copy> | |||
Albert Einstein 1879 | |||
Barack Obama 1961 | |||
</syntaxhighlight> | |||
== FILTER == | == FILTER == | ||
| Line 359: | Line 305: | ||
'''comparison''' — countries with more than 1 billion people: | '''comparison''' — countries with more than 1 billion people: | ||
<syntaxhighlight lang="sparql"> | <syntaxhighlight lang="sparql" copy> | ||
SELECT ?countryLabel ?population WHERE { | SELECT ?countryLabel ?population WHERE { | ||
?country wdt:P31 wd:Q6256 . | ?country wdt:P31 wd:Q6256 . | ||
| Line 369: | Line 315: | ||
Result: | Result: | ||
<syntaxhighlight lang="text"> | <syntaxhighlight lang="text" copy> | ||
China 1404890000 | China 1404890000 | ||
India 1326093247 | India 1326093247 | ||
| Line 376: | Line 322: | ||
'''logical (AND)''' — countries with 300M–500M people: | '''logical (AND)''' — countries with 300M–500M people: | ||
<syntaxhighlight lang="sparql"> | <syntaxhighlight lang="sparql" copy> | ||
SELECT ?countryLabel ?population WHERE { | SELECT ?countryLabel ?population WHERE { | ||
?country wdt:P31 wd:Q6256 . | ?country wdt:P31 wd:Q6256 . | ||
| Line 386: | Line 332: | ||
Result: | Result: | ||
<syntaxhighlight lang="text"> | <syntaxhighlight lang="text" copy> | ||
United States 340110988 | United States 340110988 | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 392: | Line 338: | ||
'''string (STRSTARTS)''' — countries whose English label starts with "S": | '''string (STRSTARTS)''' — countries whose English label starts with "S": | ||
<syntaxhighlight lang="sparql"> | <syntaxhighlight lang="sparql" copy> | ||
SELECT ?countryLabel WHERE { | SELECT ?countryLabel WHERE { | ||
?country wdt:P31 wd:Q6256 . | ?country wdt:P31 wd:Q6256 . | ||
| Line 402: | Line 348: | ||
Result: | Result: | ||
<syntaxhighlight lang="text"> | <syntaxhighlight lang="text" copy> | ||
Saint Kitts and Nevis | Saint Kitts and Nevis | ||
Somaliland | Somaliland | ||
| Line 410: | Line 356: | ||
'''numeric (ROUND)''' — France's population in millions, rounded: | '''numeric (ROUND)''' — France's population in millions, rounded: | ||
<syntaxhighlight lang="sparql"> | <syntaxhighlight lang="sparql" copy> | ||
SELECT ?millions (ROUND(?millions) AS ?rounded) WHERE { | SELECT ?millions (ROUND(?millions) AS ?rounded) WHERE { | ||
wd:Q142 wdt:P1082 ?population . | wd:Q142 wdt:P1082 ?population . | ||
| Line 418: | Line 364: | ||
Result: | Result: | ||
<syntaxhighlight lang="text"> | <syntaxhighlight lang="text" copy> | ||
68.605616 69 | 68.605616 69 | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 424: | Line 370: | ||
'''date/time (YEAR)''' — Einstein's birth year: | '''date/time (YEAR)''' — Einstein's birth year: | ||
<syntaxhighlight lang="sparql"> | <syntaxhighlight lang="sparql" copy> | ||
SELECT ?personLabel (YEAR(?birth) AS ? | SELECT ?personLabel (YEAR(?birth) AS ?birthyear) WHERE { | ||
wd:Q937 wdt:P569 ?birth . | wd:Q937 wdt:P569 ?birth . | ||
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". } | SERVICE wikibase:label { bd:serviceParam wikibase:language "en". } | ||
| Line 432: | Line 378: | ||
Result: | Result: | ||
<syntaxhighlight lang="text"> | <syntaxhighlight lang="text" copy> | ||
Albert Einstein 1879 | Albert Einstein 1879 | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 439: | Line 385: | ||
are entities (IRIs), not literals: | are entities (IRIs), not literals: | ||
<syntaxhighlight lang="sparql"> | <syntaxhighlight lang="sparql" copy> | ||
SELECT ?o WHERE { | SELECT ?o WHERE { | ||
wd:Q144 wdt:P31 ?o . | wd:Q144 wdt:P31 ?o . | ||
| Line 447: | Line 393: | ||
Result: | Result: | ||
<syntaxhighlight lang="text"> | <syntaxhighlight lang="text" copy> | ||
http://www.wikidata.org/entity/Q55983715 | http://www.wikidata.org/entity/Q55983715 | ||
http://www.wikidata.org/entity/Q136772238 | http://www.wikidata.org/entity/Q136772238 | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<blockquote>'''Gotcha:''' an unbound variable in a FILTER makes the row fail (unbound ≠ false) — guard with <syntaxhighlight lang="sparql" inline>BOUND()</syntaxhighlight> or restructure with OPTIONAL.</ | <blockquote>'''Gotcha:''' an unbound variable in a FILTER makes the row fail (unbound ≠ false) — guard with <syntaxhighlight lang="sparql" inline>BOUND()</syntaxhighlight> or restructure with OPTIONAL. | ||
The unbound FILTER fails silently. This returns '''no rows''', although | |||
Einstein exists and was born in 1879: | |||
<syntaxhighlight lang="sparql" copy> | |||
SELECT ?personLabel WHERE { | |||
VALUES ?person { wd:Q937 } | |||
?person rdfs:label ?personLabel . | |||
FILTER(LANG(?personLabel) = "en" && ?birthYear = 1879) # ?birthYear never bound | |||
} | |||
</syntaxhighlight> | |||
= | Result: | ||
<syntaxhighlight lang="text" copy> | |||
(no rows — the FILTER on an unbound variable dropped Einstein) | |||
</syntaxhighlight> | |||
Fix: bind the variable first, then filter: | |||
<syntaxhighlight lang="sparql"> | <syntaxhighlight lang="sparql" copy> | ||
SELECT ?personLabel | SELECT ?personLabel WHERE { | ||
VALUES ?person { wd:Q937 | VALUES ?person { wd:Q937 } | ||
?person wdt:P569 ?birth . | ?person wdt:P569 ?birth . | ||
BIND(YEAR(?birth) AS ?birthYear) | BIND(YEAR(?birth) AS ?birthYear) | ||
?person rdfs:label ?personLabel . | |||
FILTER(LANG(?personLabel) = "en" && ?birthYear = 1879) | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Result: | Result: | ||
<syntaxhighlight lang="text"> | <syntaxhighlight lang="text" copy> | ||
Albert Einstein | Albert Einstein | ||
</syntaxhighlight> | </syntaxhighlight> | ||
</blockquote> | |||
== OPTIONAL | == UNION, OPTIONAL, MINUS == | ||
{| class="wikitable" | {| class="wikitable" | ||
! Keyword !! Meaning | ! Keyword !! Meaning | ||
|- | |- | ||
| | | UNION || pattern A '''OR''' pattern B (the default between patterns is AND, not OR) | ||
|- | |- | ||
| | | OPTIONAL || declare a statement optional: do not omit a result entity because it has no value for statement | ||
|- | |- | ||
| MINUS || | | MINUS || negation: search for things that do '''NOT''' satisfy a given pattern | ||
|} | |} | ||
'''OPTIONAL''' — Einstein (died 1955) vs Obama (alive); ?deathLabel is unbound | The default between triple patterns is '''AND (join)''': the same variable must | ||
satisfy all patterns. The following query returns only dogs that also have an image: | |||
<syntaxhighlight lang="sparql" copy> | |||
SELECT ?itemLabel WHERE { | |||
?item wdt:P31 wd:Q144 . # ?item is a dog | |||
?item wdt:P18 ?image . # AND has an image (same ?item!) | |||
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". } | |||
} | |||
LIMIT 3 | |||
</syntaxhighlight> | |||
Result: | |||
<syntaxhighlight lang="text" copy> | |||
Laika | |||
Hachikō | |||
Pickles | |||
</syntaxhighlight> | |||
Because the default is AND, when you would like to find something that is/has either A "or" B, you need to use '''UNION''' | |||
{| class="wikitable" | |||
! Without UNION (AND) !! With UNION | |||
|- | |||
| | |||
<syntaxhighlight lang="sparql" copy> | |||
SELECT ?animalLabel (COUNT(?item) AS ?n) WHERE { | |||
?item wdt:P31 wd:Q144 . # AND: ?item must be a dog | |||
?item wdt:P31 wd:Q146 . # AND also a cat — impossible! | |||
BIND("dog" AS ?animalLabel) | |||
} | |||
GROUP BY ?animalLabel | |||
</syntaxhighlight> | |||
Result: | |||
<syntaxhighlight lang="text" copy> | |||
(no rows — nothing is both a dog and a cat) | |||
</syntaxhighlight> | |||
|| | |||
<syntaxhighlight lang="sparql" copy> | |||
SELECT ?animalLabel (COUNT(?item) AS ?n) WHERE { | |||
{ ?item wdt:P31 wd:Q144 . BIND("dog" AS ?animalLabel) } | |||
UNION | |||
{ ?item wdt:P31 wd:Q146 . BIND("cat" AS ?animalLabel) } | |||
} | |||
GROUP BY ?animalLabel | |||
</syntaxhighlight> | |||
Result: | |||
<syntaxhighlight lang="text" copy> | |||
dog 553 | |||
cat 239 | |||
</syntaxhighlight> | |||
|} | |||
Use '''OPTIONAL''' when you would like to find out something when data is available, but don't care if the data is missing — Einstein (died 1955) vs Obama (alive); ?deathLabel is unbound | |||
for Obama: | for Obama: | ||
<syntaxhighlight lang="sparql"> | <syntaxhighlight lang="sparql" copy> | ||
SELECT ?personLabel ?deathLabel WHERE { | SELECT ?personLabel ?deathLabel WHERE { | ||
VALUES ?person { wd:Q937 wd:Q76 } # Einstein, Barack Obama | VALUES ?person { wd:Q937 wd:Q76 } # Einstein, Barack Obama | ||
| Line 498: | Line 512: | ||
Result: | Result: | ||
<syntaxhighlight lang="text"> | <syntaxhighlight lang="text" copy> | ||
Albert Einstein 1955-04-18 | Albert Einstein 1955-04-18 | ||
Barack Obama (unbound) | Barack Obama (unbound) | ||
</syntaxhighlight> | </syntaxhighlight> | ||
''' | If you do not declare '''OPTIONAL''', Barack Obama will not appear in the results, as there is logically no date of death on his record. | ||
<syntaxhighlight lang="sparql"> | Use '''MINUS''' when you would like to find out about things that are/have NOT something | ||
E.g., '''non-French''' Nobel laureates: | |||
<syntaxhighlight lang="sparql" copy> | |||
SELECT ?personLabel WHERE { | SELECT ?personLabel WHERE { | ||
?person wdt:P166 wd:Q7191 . | ?person wdt:P166 wd:Q7191 . | ||
| Line 515: | Line 533: | ||
Result: | Result: | ||
<syntaxhighlight lang="text"> | <syntaxhighlight lang="text" copy> | ||
Nobel Prize winner | Nobel Prize winner | ||
Adam Bernau | Adam Bernau | ||
| Line 521: | Line 539: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
''' | <blockquote>'''Gotcha:''' <syntaxhighlight lang="sparql" inline>FILTER NOT EXISTS</syntaxhighlight> and <syntaxhighlight lang="sparql" inline>MINUS</syntaxhighlight> usually give the same result, but differ when a variable is unbound — prefer MINUS for set-difference semantics. | ||
<syntaxhighlight lang="sparql"> | Both express "non-French Nobel laureates"; the results are identical: | ||
SELECT ? | |||
<syntaxhighlight lang="sparql" copy> | |||
# FILTER NOT EXISTS | |||
{ ? | SELECT ?personLabel WHERE { | ||
?person wdt:P166 wd:Q7191 . | |||
FILTER NOT EXISTS { ?person wdt:P27 wd:Q142 . } | |||
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". } | |||
} | } | ||
LIMIT 3 | |||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="sparql" copy> | |||
<syntaxhighlight lang=" | # MINUS | ||
SELECT ?personLabel WHERE { | |||
?person wdt:P166 wd:Q7191 . | |||
MINUS { ?person wdt:P27 wd:Q142 . } | |||
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". } | |||
} | |||
LIMIT 3 | |||
</syntaxhighlight> | </syntaxhighlight> | ||
< | Result (both): | ||
<syntaxhighlight lang="text" copy> | |||
Nobel Prize winner | |||
Adam Bernau | |||
Santiago Carril | |||
</syntaxhighlight> | |||
The difference appears only when a variable inside the negated pattern is | |||
unbound: FILTER NOT EXISTS tests per-row (an unbound variable never matches), | |||
while MINUS subtracts whole patterns regardless of unbound variables. | |||
</blockquote> | |||
== Property paths == | == Property paths == | ||
| Line 547: | Line 580: | ||
! Path !! Meaning | ! Path !! Meaning | ||
|- | |- | ||
| wdt: | | wdt:P279/wdt:P279 || two hops (sequence) | ||
|- | |- | ||
| wdt: | | wdt:P279+ || one or more hops | ||
|- | |- | ||
| wdt: | | wdt:P279* || zero or more hops (transitive) | ||
|- | |- | ||
| wdt: | | wdt:P279? || zero or one hop | ||
|- | |- | ||
| wdt: | | wdt:P279|wdt:P31 || either property | ||
|- | |- | ||
| ^wdt: | | ^wdt:P279 || inverse direction | ||
|- | |- | ||
| !wdt: | | !wdt:P279 || any property except P279 | ||
|} | |} | ||
A path chains several properties into one pattern. The dog example | A path chains several properties into one pattern. The dog example uses | ||
'''subclass of''' (wdt:P279). Compare the direct pattern with the transitive | |||
(<syntaxhighlight lang="sparql" inline>*</syntaxhighlight> = zero or more hops): | |||
{| class="wikitable" | {| class="wikitable" | ||
! Pattern !! What it matches !! Live example !! Returns | ! Pattern !! What it matches !! Live example !! Returns | ||
|- | |- | ||
| <syntaxhighlight lang="sparql" inline>?x wdt: | | <syntaxhighlight lang="sparql" inline>?x wdt:P279 wd:Q144</syntaxhighlight> || classes that are a '''direct''' kind of dog (one hop) || <syntaxhighlight lang="sparql" inline>?x wdt:P279 wd:Q144</syntaxhighlight> || Rottweiler, Swedish Vallhund — 314 | ||
|- | |- | ||
| <syntaxhighlight lang="sparql" inline>?x wdt:P279* wd:Q144</syntaxhighlight> || classes that are dog or any kind-of-kind-of-dog (any number of hops) || <syntaxhighlight lang="sparql" inline>?x wdt:P279* wd:Q144</syntaxhighlight> || adds sub-breeds like Norman Hound → scent hound → dog — 511 | |||
| <syntaxhighlight lang="sparql" inline>?x wdt:P279* wd:Q144</syntaxhighlight> || classes that are dog or any kind-of-kind-of-dog ( | |||
|} | |} | ||
The | The <syntaxhighlight lang="sparql" inline>*</syntaxhighlight> finds things several hops away that a | ||
single triple cannot: | |||
<syntaxhighlight lang="sparql"> | <syntaxhighlight lang="sparql" copy> | ||
SELECT ?thing ?thingLabel WHERE { | SELECT ?thing ?thingLabel WHERE { | ||
?thing | ?thing wdt:P279* wd:Q144 . | ||
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". } | SERVICE wikibase:label { bd:serviceParam wikibase:language "en". } | ||
} | } | ||
| Line 591: | Line 619: | ||
Result: | Result: | ||
<syntaxhighlight lang="text"> | <syntaxhighlight lang="text" copy> | ||
Lapponian Herder | |||
autism assistance dog | |||
diabetic alert dog | |||
</syntaxhighlight> | |||
(These are sub-subclasses: Lapponian Herder → herding dog → dog.) | |||
<blockquote>'''Gotcha:''' a variable that appears only inside a path is not bound, so it cannot be selected. | |||
A two-hop path (<syntaxhighlight lang="sparql" inline>wdt:P279/wdt:P279</syntaxhighlight>) finds dogs two levels down (bloodhound, beagle) — but the intermediate hop is anonymous, so there is nothing to select for it: | |||
<syntaxhighlight lang="sparql" copy> | |||
# Path form: intermediate hop is anonymous — ?mid is not available | |||
SELECT ?xLabel WHERE { | |||
?x wdt:P279/wdt:P279 wd:Q144 . | |||
?x rdfs:label ?xLabel . FILTER(LANG(?xLabel) = "en") | |||
} | |||
LIMIT 3 | |||
</syntaxhighlight> | |||
Result: | |||
<syntaxhighlight lang="text" copy> | |||
bloodhound | |||
beagle | |||
Chesapeake Bay Retriever | |||
</syntaxhighlight> | |||
To select the intermediate value, write two explicit triples with a variable: | |||
<syntaxhighlight lang="sparql" copy> | |||
# Explicit form: ?mid is selectable | |||
SELECT ?xLabel ?midLabel WHERE { | |||
?x wdt:P279 ?mid . | |||
?mid wdt:P279 wd:Q144 . | |||
?x rdfs:label ?xLabel . FILTER(LANG(?xLabel) = "en") | |||
?mid rdfs:label ?midLabel . FILTER(LANG(?midLabel) = "en") | |||
} | |||
LIMIT 3 | |||
</syntaxhighlight> | </syntaxhighlight> | ||
Result: | |||
<syntaxhighlight lang="text" copy> | |||
shepherd dog pastoral dog | |||
livestock guardian dog pastoral dog | |||
Canadian Eskimo Dog inuit sledge dog | |||
</syntaxhighlight> | |||
</blockquote> | |||
== Aggregates == | == Aggregates == | ||
| Line 607: | Line 676: | ||
animal; here: values of <syntaxhighlight lang="sparql" inline>wdt:P1082</syntaxhighlight> for the three countries): | animal; here: values of <syntaxhighlight lang="sparql" inline>wdt:P1082</syntaxhighlight> for the three countries): | ||
<syntaxhighlight lang="sparql"> | <syntaxhighlight lang="sparql" copy> | ||
SELECT (COUNT(?population) AS ?n) WHERE { | SELECT (COUNT(?population) AS ?n) WHERE { | ||
VALUES ?country { wd:Q148 wd:Q30 wd:Q668 } | VALUES ?country { wd:Q148 wd:Q30 wd:Q668 } | ||
| Line 615: | Line 684: | ||
Result: | Result: | ||
<syntaxhighlight lang="text"> | <syntaxhighlight lang="text" copy> | ||
3 | 3 | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 621: | Line 690: | ||
'''SUM''' — combined population of the three: | '''SUM''' — combined population of the three: | ||
<syntaxhighlight lang="sparql"> | <syntaxhighlight lang="sparql" copy> | ||
SELECT (SUM(?population) AS ?total) WHERE { | SELECT (SUM(?population) AS ?total) WHERE { | ||
VALUES ?country { wd:Q148 wd:Q30 wd:Q668 } | VALUES ?country { wd:Q148 wd:Q30 wd:Q668 } | ||
| Line 629: | Line 698: | ||
Result: | Result: | ||
<syntaxhighlight lang="text"> | <syntaxhighlight lang="text" copy> | ||
3071094235 | 3071094235 | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 635: | Line 704: | ||
'''AVG''' — mean population of the three: | '''AVG''' — mean population of the three: | ||
<syntaxhighlight lang="sparql"> | <syntaxhighlight lang="sparql" copy> | ||
SELECT (AVG(?population) AS ?avg) WHERE { | SELECT (AVG(?population) AS ?avg) WHERE { | ||
VALUES ?country { wd:Q148 wd:Q30 wd:Q668 } | VALUES ?country { wd:Q148 wd:Q30 wd:Q668 } | ||
| Line 643: | Line 712: | ||
Result: | Result: | ||
<syntaxhighlight lang="text"> | <syntaxhighlight lang="text" copy> | ||
1023698078.33 | 1023698078.33 | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 649: | Line 718: | ||
'''MIN / MAX''' — smallest and largest of the three: | '''MIN / MAX''' — smallest and largest of the three: | ||
<syntaxhighlight lang="sparql"> | <syntaxhighlight lang="sparql" copy> | ||
SELECT (MIN(?population) AS ?min) (MAX(?population) AS ?max) WHERE { | SELECT (MIN(?population) AS ?min) (MAX(?population) AS ?max) WHERE { | ||
VALUES ?country { wd:Q148 wd:Q30 wd:Q668 } | VALUES ?country { wd:Q148 wd:Q30 wd:Q668 } | ||
| Line 657: | Line 726: | ||
Result: | Result: | ||
<syntaxhighlight lang="text"> | <syntaxhighlight lang="text" copy> | ||
340110988 1404890000 | 340110988 1404890000 | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 663: | Line 732: | ||
'''SAMPLE''' — one arbitrary value (useful to squash duplicates): | '''SAMPLE''' — one arbitrary value (useful to squash duplicates): | ||
<syntaxhighlight lang="sparql"> | <syntaxhighlight lang="sparql" copy> | ||
SELECT (SAMPLE(?countryLabel) AS ?anyCountry) WHERE { | SELECT (SAMPLE(?countryLabel) AS ?anyCountry) WHERE { | ||
VALUES ?country { wd:Q148 wd:Q30 wd:Q668 } | VALUES ?country { wd:Q148 wd:Q30 wd:Q668 } | ||
| Line 673: | Line 742: | ||
Result: | Result: | ||
<syntaxhighlight lang="text"> | <syntaxhighlight lang="text" copy> | ||
United States | United States | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 679: | Line 748: | ||
'''GROUP_CONCAT''' — Einstein's awards in one cell: | '''GROUP_CONCAT''' — Einstein's awards in one cell: | ||
<syntaxhighlight lang="sparql"> | <syntaxhighlight lang="sparql" copy> | ||
SELECT ?personLabel (GROUP_CONCAT(?awardLabel; SEPARATOR=", ") AS ?awards) WHERE { | SELECT ?personLabel (GROUP_CONCAT(?awardLabel; SEPARATOR=", ") AS ?awards) WHERE { | ||
VALUES ?person { wd:Q937 } | VALUES ?person { wd:Q937 } | ||
| Line 689: | Line 758: | ||
Result: | Result: | ||
<syntaxhighlight lang="text"> | <syntaxhighlight lang="text" copy> | ||
Albert Einstein Nobel Prize in Physics, … | Albert Einstein Nobel Prize in Physics, … | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 696: | Line 765: | ||
than 400 instances: | than 400 instances: | ||
<syntaxhighlight lang="sparql"> | <syntaxhighlight lang="sparql" copy> | ||
SELECT ?animalLabel (COUNT(?item) AS ?n) WHERE { | SELECT ?animalLabel (COUNT(?item) AS ?n) WHERE { | ||
VALUES ?animal { wd:Q144 wd:Q146 } | VALUES ?animal { wd:Q144 wd:Q146 } | ||
| Line 707: | Line 776: | ||
Result: | Result: | ||
<syntaxhighlight lang="text"> | <syntaxhighlight lang="text" copy> | ||
dog 553 | dog 553 | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<blockquote>'''Gotcha:''' aggregates need GROUP BY for every non-aggregated variable | <blockquote>'''Gotcha:''' aggregates need GROUP BY for every non-aggregated variable. Forgetting one is a '''query error''', not a wrong result. | ||
Missing GROUP BY for <syntaxhighlight lang="sparql" inline>?animalLabel</syntaxhighlight>: | |||
<syntaxhighlight lang="sparql" copy> | |||
SELECT ?animalLabel (COUNT(?item) AS ?n) WHERE { | |||
VALUES ?animal { wd:Q144 wd:Q146 } | |||
?item wdt:P31 ?animal . | |||
?animal rdfs:label ?animalLabel . | |||
FILTER(LANG(?animalLabel) = "en") | |||
} | |||
</syntaxhighlight> | |||
Result: | |||
<syntaxhighlight lang="text" copy> | |||
ERROR: Bad aggregate — every non-aggregated variable needs GROUP BY | |||
</syntaxhighlight> | |||
</blockquote> | |||
== Subqueries == | == Subqueries == | ||
| Line 719: | Line 805: | ||
'''The most populous country, computed via a subquery:''' | '''The most populous country, computed via a subquery:''' | ||
<syntaxhighlight lang="sparql"> | <syntaxhighlight lang="sparql" copy> | ||
SELECT ?countryLabel ?max WHERE { | SELECT ?countryLabel ?max WHERE { | ||
{ | { | ||
| Line 734: | Line 820: | ||
Result: | Result: | ||
<syntaxhighlight lang="text"> | <syntaxhighlight lang="text" copy> | ||
China 1404890000 | China 1404890000 | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Line 743: | Line 829: | ||
queries above: | queries above: | ||
<syntaxhighlight lang="sparql"> | <syntaxhighlight lang="sparql" copy> | ||
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". } | SERVICE wikibase:label { bd:serviceParam wikibase:language "en". } | ||
# then reference ?xLabel for every ?x in the query | # then reference ?xLabel for every ?x in the query | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="sparql"> | <syntaxhighlight lang="sparql" copy> | ||
# Federating to another endpoint (needs the remote's own IRIs, e.g. via owl:sameAs) | # Federating to another endpoint (needs the remote's own IRIs, e.g. via owl:sameAs) | ||
SELECT ?cityLabel WHERE { | SELECT ?cityLabel WHERE { | ||
Latest revision as of 09:45, 23 August 2026
Quick reference for smart people — part of our dev cheatsheets collection.
If you are new to SPARQL, see SPARQL101.
Examples featured in this cheatsheet run as-is against the public Wikidata endpoint
https://query.wikidata.org/sparql. Querying another Wikibase instance is similar, but small query adjustments may be necessary depending on how each instance is set up. See the documentation for your target instance for more info.
Basic example
Natural language question:
How many dogs and cats are featured in Wikidata?
SPARQL query:
SELECT ?animal ?animalLabel (COUNT(?item) AS ?count) WHERE {
VALUES ?animal { wd:Q144 wd:Q146 } # dog, cat
?item wdt:P31 ?animal . # ?item is an instance of ?animal
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}
GROUP BY ?animal ?animalLabel
Result as of 2026-08-19:
dog 553
cat 239
Building a query
A query is a list of triple patterns(
subject predicate object).
Knowing one or two elements, you can query the rest:
| Question | Known parts | Pattern |
|---|---|---|
| "What is the population of France?" | subject France (wd:Q142) + predicate population (wdt:P1082) | wd:Q142 wdt:P1082 ?population .
|
| "Which things are dogs?" | predicate instance-of (wdt:P31) + object dog (wd:Q144) | ?x wdt:P31 wd:Q144 .
|
| "What is known about Einstein?" | subject Einstein (wd:Q937) | wd:Q937 ?predicate ?value .
|
Query forms
| Form | Returns | Use when |
|---|---|---|
| SELECT | table of variable bindings | you want rows of data |
| ASK | true/false | you only need to know whether something exists |
| CONSTRUCT | an RDF graph (triples) | you want the result as RDF, not a table |
| DESCRIBE | a graph describing the resource | you want everything known about an entity |
SELECT — what is the population of France?
SELECT ?population WHERE {
wd:Q142 wdt:P1082 ?population .
}
Result:
68605616
ASK — is there at least one dog in Wikidata?
ASK WHERE { ?x wdt:P31 wd:Q144 . }
Result:
true
CONSTRUCT — every dog as RDF (first two). Each result row becomes a
triple ?x wdt:P31 wd:Q144:
CONSTRUCT { ?x wdt:P31 wd:Q144 . }
WHERE { ?x wdt:P31 wd:Q144 . } LIMIT 2
Result (visualised, N-Triples style):
Q186486 wdt:P31 wd:Q144
Q280571 wdt:P31 wd:Q144
DESCRIBE — everything known about Einstein (all triples with wd:Q937 as subject or object):
DESCRIBE wd:Q937
Result (visualised, N-Triples style, first few of thousands):
Q937 rdfs:label "Albert Einstein"@en
Q937 wdt:P569 1879-03-14
Q937 wdt:P570 1955-04-18
Q937 wdt:P21 wd:Q6581097 # male
Solution modifiers
DISTINCT — drop duplicate rows. Einstein and Obama are both male, so the gender repeats:
| Without | With DISTINCT |
|---|---|
SELECT ?genderLabel WHERE {
VALUES ?person { wd:Q937 wd:Q76 }
?person wdt:P21 ?gender .
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}
Result: male
male
|
SELECT DISTINCT ?genderLabel WHERE {
VALUES ?person { wd:Q937 wd:Q76 }
?person wdt:P21 ?gender .
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}
Result: male
|
ORDER BY + LIMIT — the 3 most populous countries, sorted descending:
| Without (arbitrary order) | With ORDER BY DESC + LIMIT |
|---|---|
SELECT ?countryLabel ?population WHERE {
VALUES ?country { wd:Q148 wd:Q30 wd:Q668 } # China, US, India
?country wdt:P1082 ?population .
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}
Result: United States 340110988
China 1404890000
India 1326093247
|
SELECT ?countryLabel ?population WHERE {
VALUES ?country { wd:Q148 wd:Q30 wd:Q668 }
?country wdt:P1082 ?population .
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}
ORDER BY DESC(?population)
Result: China 1404890000
India 1326093247
United States 340110988
|
LIMIT — at most n rows:
| Without (all 3) | With LIMIT 2 |
|---|---|
SELECT ?countryLabel WHERE {
VALUES ?country { wd:Q148 wd:Q30 wd:Q668 }
?country wdt:P1082 ?population .
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}
Result: United States
China
India
|
SELECT ?countryLabel WHERE {
VALUES ?country { wd:Q148 wd:Q30 wd:Q668 }
?country wdt:P1082 ?population .
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}
LIMIT 2
Result: United States
China
|
OFFSET — skip rows (paging). The 2nd most populous country:
| With LIMIT 1 (first) | With LIMIT 1 OFFSET 1 (second) |
|---|---|
SELECT ?countryLabel ?population WHERE {
?country wdt:P31 wd:Q6256 .
?country wdt:P1082 ?population .
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}
ORDER BY DESC(?population)
LIMIT 1
Result: China 1404890000
|
SELECT ?countryLabel ?population WHERE {
?country wdt:P31 wd:Q6256 .
?country wdt:P1082 ?population .
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}
ORDER BY DESC(?population)
LIMIT 1 OFFSET 1
Result: India 1326093247
|
VALUES & BIND
VALUES restricts a variable to a list; BIND computes a new variable.
SELECT ?personLabel ?birthYear WHERE {
VALUES ?person { wd:Q937 wd:Q76 } # Einstein, Obama
?person wdt:P569 ?birth .
BIND(YEAR(?birth) AS ?birthYear)
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}
Result:
Albert Einstein 1879
Barack Obama 1961
FILTER
| Category | Operators / functions |
|---|---|
| comparison | = < > <= >= != |
| logical | && || !
|
| string | STR() CONTAINS() STRSTARTS() STRENDS() REGEX() |
| numeric | ABS() ROUND() FLOOR() CEIL() |
| date/time | YEAR() MONTH() DAY() NOW() |
| term tests | isIRI() isBlank() isLiteral() LANG() DATATYPE() |
comparison — countries with more than 1 billion people:
SELECT ?countryLabel ?population WHERE {
?country wdt:P31 wd:Q6256 .
?country wdt:P1082 ?population .
FILTER(?population > 1000000000)
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}
Result:
China 1404890000
India 1326093247
logical (AND) — countries with 300M–500M people:
SELECT ?countryLabel ?population WHERE {
?country wdt:P31 wd:Q6256 .
?country wdt:P1082 ?population .
FILTER(?population > 300000000 && ?population < 500000000)
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}
Result:
United States 340110988
string (STRSTARTS) — countries whose English label starts with "S":
SELECT ?countryLabel WHERE {
?country wdt:P31 wd:Q6256 .
?country rdfs:label ?countryLabel .
FILTER(LANG(?countryLabel) = "en" && STRSTARTS(?countryLabel, "S"))
}
LIMIT 3
Result:
Saint Kitts and Nevis
Somaliland
Saint Vincent and the Grenadines
numeric (ROUND) — France's population in millions, rounded:
SELECT ?millions (ROUND(?millions) AS ?rounded) WHERE {
wd:Q142 wdt:P1082 ?population .
BIND(?population / 1000000 AS ?millions)
}
Result:
68.605616 69
date/time (YEAR) — Einstein's birth year:
SELECT ?personLabel (YEAR(?birth) AS ?birthyear) WHERE {
wd:Q937 wdt:P569 ?birth .
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}
Result:
Albert Einstein 1879
term test (isIRI) — the values of wdt:P31 on a dog
are entities (IRIs), not literals:
SELECT ?o WHERE {
wd:Q144 wdt:P31 ?o .
FILTER(isIRI(?o))
}
Result:
http://www.wikidata.org/entity/Q55983715
http://www.wikidata.org/entity/Q136772238
Gotcha: an unbound variable in a FILTER makes the row fail (unbound ≠ false) — guard with
BOUND()or restructure with OPTIONAL.The unbound FILTER fails silently. This returns no rows, although Einstein exists and was born in 1879:
SELECT ?personLabel WHERE { VALUES ?person { wd:Q937 } ?person rdfs:label ?personLabel . FILTER(LANG(?personLabel) = "en" && ?birthYear = 1879) # ?birthYear never bound }Result:
(no rows — the FILTER on an unbound variable dropped Einstein)Fix: bind the variable first, then filter:
SELECT ?personLabel WHERE { VALUES ?person { wd:Q937 } ?person wdt:P569 ?birth . BIND(YEAR(?birth) AS ?birthYear) ?person rdfs:label ?personLabel . FILTER(LANG(?personLabel) = "en" && ?birthYear = 1879) }Result:
Albert Einstein
UNION, OPTIONAL, MINUS
| Keyword | Meaning |
|---|---|
| UNION | pattern A OR pattern B (the default between patterns is AND, not OR) |
| OPTIONAL | declare a statement optional: do not omit a result entity because it has no value for statement |
| MINUS | negation: search for things that do NOT satisfy a given pattern |
The default between triple patterns is AND (join): the same variable must satisfy all patterns. The following query returns only dogs that also have an image:
SELECT ?itemLabel WHERE {
?item wdt:P31 wd:Q144 . # ?item is a dog
?item wdt:P18 ?image . # AND has an image (same ?item!)
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}
LIMIT 3
Result:
Laika
Hachikō
Pickles
Because the default is AND, when you would like to find something that is/has either A "or" B, you need to use UNION
| Without UNION (AND) | With UNION |
|---|---|
SELECT ?animalLabel (COUNT(?item) AS ?n) WHERE {
?item wdt:P31 wd:Q144 . # AND: ?item must be a dog
?item wdt:P31 wd:Q146 . # AND also a cat — impossible!
BIND("dog" AS ?animalLabel)
}
GROUP BY ?animalLabel
Result: (no rows — nothing is both a dog and a cat)
|
SELECT ?animalLabel (COUNT(?item) AS ?n) WHERE {
{ ?item wdt:P31 wd:Q144 . BIND("dog" AS ?animalLabel) }
UNION
{ ?item wdt:P31 wd:Q146 . BIND("cat" AS ?animalLabel) }
}
GROUP BY ?animalLabel
Result: dog 553
cat 239
|
Use OPTIONAL when you would like to find out something when data is available, but don't care if the data is missing — Einstein (died 1955) vs Obama (alive); ?deathLabel is unbound for Obama:
SELECT ?personLabel ?deathLabel WHERE {
VALUES ?person { wd:Q937 wd:Q76 } # Einstein, Barack Obama
OPTIONAL { ?person wdt:P570 ?death . }
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}
Result:
Albert Einstein 1955-04-18
Barack Obama (unbound)
If you do not declare OPTIONAL, Barack Obama will not appear in the results, as there is logically no date of death on his record.
Use MINUS when you would like to find out about things that are/have NOT something
E.g., non-French Nobel laureates:
SELECT ?personLabel WHERE {
?person wdt:P166 wd:Q7191 .
MINUS { ?person wdt:P27 wd:Q142 . }
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}
LIMIT 3
Result:
Nobel Prize winner
Adam Bernau
Santiago Carril
Gotcha:
FILTER NOT EXISTSandMINUSusually give the same result, but differ when a variable is unbound — prefer MINUS for set-difference semantics.Both express "non-French Nobel laureates"; the results are identical:
# FILTER NOT EXISTS SELECT ?personLabel WHERE { ?person wdt:P166 wd:Q7191 . FILTER NOT EXISTS { ?person wdt:P27 wd:Q142 . } SERVICE wikibase:label { bd:serviceParam wikibase:language "en". } } LIMIT 3# MINUS SELECT ?personLabel WHERE { ?person wdt:P166 wd:Q7191 . MINUS { ?person wdt:P27 wd:Q142 . } SERVICE wikibase:label { bd:serviceParam wikibase:language "en". } } LIMIT 3Result (both):
Nobel Prize winner Adam Bernau Santiago CarrilThe difference appears only when a variable inside the negated pattern is unbound: FILTER NOT EXISTS tests per-row (an unbound variable never matches), while MINUS subtracts whole patterns regardless of unbound variables.
Property paths
| Path | Meaning |
|---|---|
| wdt:P279/wdt:P279 | two hops (sequence) |
| wdt:P279+ | one or more hops |
| wdt:P279* | zero or more hops (transitive) |
| wdt:P279? | zero or one hop |
| wdt:P31 | either property |
| ^wdt:P279 | inverse direction |
| !wdt:P279 | any property except P279 |
A path chains several properties into one pattern. The dog example uses
subclass of (wdt:P279). Compare the direct pattern with the transitive
(* = zero or more hops):
| Pattern | What it matches | Live example | Returns |
|---|---|---|---|
?x wdt:P279 wd:Q144 |
classes that are a direct kind of dog (one hop) | ?x wdt:P279 wd:Q144 |
Rottweiler, Swedish Vallhund — 314 |
?x wdt:P279* wd:Q144 |
classes that are dog or any kind-of-kind-of-dog (any number of hops) | ?x wdt:P279* wd:Q144 |
adds sub-breeds like Norman Hound → scent hound → dog — 511 |
The * finds things several hops away that a
single triple cannot:
SELECT ?thing ?thingLabel WHERE {
?thing wdt:P279* wd:Q144 .
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}
LIMIT 3
Result:
Lapponian Herder
autism assistance dog
diabetic alert dog
(These are sub-subclasses: Lapponian Herder → herding dog → dog.)
Gotcha: a variable that appears only inside a path is not bound, so it cannot be selected.
A two-hop path (
wdt:P279/wdt:P279) finds dogs two levels down (bloodhound, beagle) — but the intermediate hop is anonymous, so there is nothing to select for it:# Path form: intermediate hop is anonymous — ?mid is not available SELECT ?xLabel WHERE { ?x wdt:P279/wdt:P279 wd:Q144 . ?x rdfs:label ?xLabel . FILTER(LANG(?xLabel) = "en") } LIMIT 3Result:
bloodhound beagle Chesapeake Bay RetrieverTo select the intermediate value, write two explicit triples with a variable:
# Explicit form: ?mid is selectable SELECT ?xLabel ?midLabel WHERE { ?x wdt:P279 ?mid . ?mid wdt:P279 wd:Q144 . ?x rdfs:label ?xLabel . FILTER(LANG(?xLabel) = "en") ?mid rdfs:label ?midLabel . FILTER(LANG(?midLabel) = "en") } LIMIT 3Result:
shepherd dog pastoral dog livestock guardian dog pastoral dog Canadian Eskimo Dog inuit sledge dog
Aggregates
Aggregates collapse many rows into one per group. All examples use the same three countries (China, US, India).
COUNT — how many values (the running example counts instances per
animal; here: values of wdt:P1082 for the three countries):
SELECT (COUNT(?population) AS ?n) WHERE {
VALUES ?country { wd:Q148 wd:Q30 wd:Q668 }
?country wdt:P1082 ?population .
}
Result:
3
SUM — combined population of the three:
SELECT (SUM(?population) AS ?total) WHERE {
VALUES ?country { wd:Q148 wd:Q30 wd:Q668 }
?country wdt:P1082 ?population .
}
Result:
3071094235
AVG — mean population of the three:
SELECT (AVG(?population) AS ?avg) WHERE {
VALUES ?country { wd:Q148 wd:Q30 wd:Q668 }
?country wdt:P1082 ?population .
}
Result:
1023698078.33
MIN / MAX — smallest and largest of the three:
SELECT (MIN(?population) AS ?min) (MAX(?population) AS ?max) WHERE {
VALUES ?country { wd:Q148 wd:Q30 wd:Q668 }
?country wdt:P1082 ?population .
}
Result:
340110988 1404890000
SAMPLE — one arbitrary value (useful to squash duplicates):
SELECT (SAMPLE(?countryLabel) AS ?anyCountry) WHERE {
VALUES ?country { wd:Q148 wd:Q30 wd:Q668 }
?country wdt:P1082 ?population .
?country rdfs:label ?countryLabel .
FILTER(LANG(?countryLabel) = "en")
}
Result:
United States
GROUP_CONCAT — Einstein's awards in one cell:
SELECT ?personLabel (GROUP_CONCAT(?awardLabel; SEPARATOR=", ") AS ?awards) WHERE {
VALUES ?person { wd:Q937 }
?person wdt:P166 ?award .
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}
GROUP BY ?personLabel
Result:
Albert Einstein Nobel Prize in Physics, …
GROUP BY + HAVING — the running example, filtered to groups with more than 400 instances:
SELECT ?animalLabel (COUNT(?item) AS ?n) WHERE {
VALUES ?animal { wd:Q144 wd:Q146 }
?item wdt:P31 ?animal .
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}
GROUP BY ?animal ?animalLabel
HAVING (COUNT(?item) > 400)
Result:
dog 553
Gotcha: aggregates need GROUP BY for every non-aggregated variable. Forgetting one is a query error, not a wrong result.
Missing GROUP BY for
?animalLabel:SELECT ?animalLabel (COUNT(?item) AS ?n) WHERE { VALUES ?animal { wd:Q144 wd:Q146 } ?item wdt:P31 ?animal . ?animal rdfs:label ?animalLabel . FILTER(LANG(?animalLabel) = "en") }Result:
ERROR: Bad aggregate — every non-aggregated variable needs GROUP BY
Subqueries
A query inside a query — useful for "the X with the max Y" patterns.
The most populous country, computed via a subquery:
SELECT ?countryLabel ?max WHERE {
{
SELECT (MAX(?population) AS ?max) WHERE {
?c wdt:P31 wd:Q6256 .
?c wdt:P1082 ?population .
}
}
?country wdt:P31 wd:Q6256 .
?country wdt:P1082 ?max .
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}Result:
China 1404890000SERVICE (labels, federation)
The label service turns entity IDs into human-readable labels — used in most queries above:
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
# then reference ?xLabel for every ?x in the query# Federating to another endpoint (needs the remote's own IRIs, e.g. via owl:sameAs)
SELECT ?cityLabel WHERE {
wd:Q64 wdt:P36 ?city .
?city owl:sameAs ?dbpediaCity .
SERVICE <https://dbpedia.org/sparql> {
?dbpediaCity rdfs:label ?cityLabel .
FILTER(LANG(?cityLabel) = "en")
}
}Further reading
- Wikidata SPARQL tutorial — the recommended tutorial
- SPARQL 1.1 Query Language — official spec
- Wikidata Query Service — the example endpoint used here
- Help:Contributing/query — querying a different instance (endpoint, prefixes, label service)