Cheatsheets:SPARQL: Difference between revisions

From Wikibase
Jump to navigation Jump to search
Show, don't tell: real verified Wikidata queries for every construct (dogs/cats, populations, Nobel laureates, Einstein vs Obama) (via update-page on MediaWiki MCP Server)
Fix: running-example table delimiters; keep inline syntaxhighlight mid-line (line-start tags render as block pre) (via update-page on MediaWiki MCP Server)
Line 11: Line 11:
== Running example ==
== Running example ==


How many dogs and cats are in Wikidata? (returns: dog 553, cat 239)
How many dogs and cats are in Wikidata?


<syntaxhighlight lang="sparql">
<syntaxhighlight lang="sparql">
Line 23: Line 23:
</syntaxhighlight>
</syntaxhighlight>


| Result ||
{| class="wikitable"
! Animal !! Count
|-
| dog || 553
| dog || 553
|-
|-
| cat || 239
| cat || 239
|}


== Query forms ==
== Query forms ==
Line 54: Line 57:


A triple pattern is <syntaxhighlight lang="sparql" inline>subject predicate object .</syntaxhighlight> —
A triple pattern is <syntaxhighlight lang="sparql" inline>subject predicate object .</syntaxhighlight> —
each part may be a variable (<syntaxhighlight lang="sparql" inline>?x</syntaxhighlight>), an IRI
each part may be a variable, an IRI, or a literal.
(<syntaxhighlight lang="sparql" inline">wd:Q144</syntaxhighlight>), or a literal.


<syntaxhighlight lang="sparql">
<syntaxhighlight lang="sparql">
Line 77: Line 79:
| DISTINCT || drop duplicate rows
| DISTINCT || drop duplicate rows
|-
|-
| ORDER BY || sort (<syntaxhighlight lang="sparql" inline>ASC(?)</syntaxhighlight> / <syntaxhighlight lang="sparql" inline>DESC(?)</syntaxhighlight>)
| ORDER BY || sort (ASC(?) / DESC(?))
|-
|-
| LIMIT n || at most n rows
| LIMIT n || at most n rows
Line 104: Line 106:
! Category !! Operators / functions
! Category !! Operators / functions
|-
|-
| comparison || <syntaxhighlight lang="sparql" inline>= < > <= >= !=</syntaxhighlight>
| comparison || = < > <= >= !=
|-
|-
| logical || <syntaxhighlight lang="sparql" inline>&& || !</syntaxhighlight>
| logical || && || !
|-
|-
| string || <syntaxhighlight lang="sparql" inline>STR() CONTAINS() STRSTARTS() REGEX()</syntaxhighlight>
| string || STR() CONTAINS() STRSTARTS() REGEX()
|-
|-
| numeric || <syntaxhighlight lang="sparql" inline>ABS() ROUND() FLOOR() CEIL()</syntaxhighlight>
| numeric || ABS() ROUND() FLOOR() CEIL()
|-
|-
| date/time || <syntaxhighlight lang="sparql" inline>YEAR() MONTH() DAY() NOW()</syntaxhighlight>
| date/time || YEAR() MONTH() DAY() NOW()
|-
|-
| term tests || <syntaxhighlight lang="sparql" inline>isIRI() isBlank() isLiteral() LANG() DATATYPE()</syntaxhighlight>
| term tests || isIRI() isBlank() isLiteral() LANG() DATATYPE()
|}
|}


Line 145: Line 147:
== OPTIONAL, UNION, MINUS ==
== OPTIONAL, UNION, MINUS ==


* <syntaxhighlight lang="sparql" inline">OPTIONAL</syntaxhighlight> — left join: keep the row, leave the variable unbound when absent
{| class="wikitable"
* <syntaxhighlight lang="sparql" inline">UNION</syntaxhighlight> — alternatives (the default between patterns is AND, not OR)
! Keyword !! Meaning
* <syntaxhighlight lang="sparql" inline">MINUS</syntaxhighlight> — remove rows that match the pattern
|-
| OPTIONAL || left join: keep the row, leave the variable unbound when absent
|-
| UNION || alternatives (the default between patterns is AND, not OR)
|-
| MINUS || remove rows that match the pattern
|}


<syntaxhighlight lang="sparql">
<syntaxhighlight lang="sparql">
Line 181: Line 189:
! Path !! Meaning
! Path !! Meaning
|-
|-
| <syntaxhighlight lang="sparql" inline>wdt:P40/wdt:P40</syntaxhighlight> || two hops (sequence)
| wdt:P40/wdt:P40 || two hops (sequence)
|-
|-
| <syntaxhighlight lang="sparql" inline>wdt:P40+</syntaxhighlight> || one or more hops
| wdt:P40+ || one or more hops
|-
|-
| <syntaxhighlight lang="sparql" inline>wdt:P40*</syntaxhighlight> || zero or more hops
| wdt:P40* || zero or more hops
|-
|-
| <syntaxhighlight lang="sparql" inline>wdt:P40?</syntaxhighlight> || zero or one hop
| wdt:P40? || zero or one hop
|-
|-
| <syntaxhighlight lang="sparql" inline>wdt:P40|wdt:P41</syntaxhighlight> || either property
| wdt:P40|wdt:P41 || either property
|-
|-
| <syntaxhighlight lang="sparql" inline>^wdt:P40</syntaxhighlight> || inverse direction
| ^wdt:P40 || inverse direction
|-
|-
| <syntaxhighlight lang="sparql" inline>!wdt:P40</syntaxhighlight> || any property except P40
| !wdt:P40 || any property except P40
|}
|}


Line 288: Line 296:
* Blank-node labels (<syntaxhighlight lang="sparql" inline>_:x</syntaxhighlight>) are local to one query — they are not IRIs.
* Blank-node labels (<syntaxhighlight lang="sparql" inline>_:x</syntaxhighlight>) are local to one query — they are not IRIs.
* Aggregates need GROUP BY for every non-aggregated variable; forgetting one mixes unrelated rows.
* Aggregates need GROUP BY for every non-aggregated variable; forgetting one mixes unrelated rows.
* <syntaxhighlight lang="sparql" inline">FILTER NOT EXISTS</syntaxhighlight> ≠ <syntaxhighlight lang="sparql" inline">MINUS</syntaxhighlight> when variables are unbound — prefer MINUS for set-difference semantics.
* <syntaxhighlight lang="sparql" inline>FILTER NOT EXISTS</syntaxhighlight> differs from <syntaxhighlight lang="sparql" inline>MINUS</syntaxhighlight> when variables are unbound — prefer MINUS for set-difference semantics.
* Endpoint prefixes are not universal — <syntaxhighlight lang="sparql" inline>wd:</syntaxhighlight>/<syntaxhighlight lang="sparql" inline>wdt:</syntaxhighlight> are Wikidata's; other instances define their own (see [[Help:Contributing/query]]).
* Endpoint prefixes are not universal — <syntaxhighlight lang="sparql" inline>wd:</syntaxhighlight>/<syntaxhighlight lang="sparql" inline>wdt:</syntaxhighlight> are Wikidata's; other instances define their own (see [[Help:Contributing/query]]).



Revision as of 11:32, 19 August 2026

Languages: English · français · Esperanto

Quick reference for smart people — part of our dev cheatsheets collection.

SPARQL 1.1 quick syntax reference — for people who already know what SPARQL is and need reminders on syntax. All examples run as-is against the public Wikidata endpoint https://query.wikidata.org/sparql. For a proper tutorial, take the Wikidata SPARQL tutorial. For querying a different Wikibase instance (endpoint, prefixes, label service), see Help:Contributing/query.

Running example

How many dogs and cats are in Wikidata?

# wd: = entity (Q…), wdt: = property value (P…), SERVICE = label lookup
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
Animal Count
dog 553
cat 239

Query forms

Form Returns Use when
SELECT table of variable bindings you want rows of data (the rest of this sheet)
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
# Is there at least one dog in Wikidata?
ASK WHERE { ?x wdt:P31 wd:Q144 . }          # → true

# Every dog as an RDF graph (first two)
CONSTRUCT { ?x wdt:P31 wd:Q144 . }
WHERE { ?x wdt:P31 wd:Q144 . } LIMIT 2

Triple patterns & literals

A triple pattern is subject predicate object . — each part may be a variable, an IRI, or a literal.

# What is the population of France? (→ 68,605,616)
SELECT ?population WHERE {
  wd:Q142 wdt:P1082 ?population .
}

# Literals can carry datatypes or language tags:
#   "42"^^xsd:integer      "2026-08-19"^^xsd:date
#   "dog"@en               "chien"@fr
# A blank node means "some unnamed thing":
#   wd:Q144 wdt:depicted-by [] .     # dog depicted by something

Solution modifiers

Clause What it does
DISTINCT drop duplicate rows
ORDER BY sort (ASC(?) / DESC(?))
LIMIT n at most n rows
OFFSET n skip n rows (paging)
GROUP BY group rows for aggregation (see Aggregates)
# The 3 most populous countries (ORDER BY + LIMIT)
SELECT ?countryLabel ?population WHERE {
  ?country wdt:P31 wd:Q6256 .              # instance of: sovereign state
  ?country wdt:P1082 ?population .
  SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}
ORDER BY DESC(?population)
LIMIT 3

Result: China 1,404,890,000 · India 1,326,093,247 · United States 340,110,988

FILTER

Category Operators / functions
comparison = < > <= >= !=
logical && !
string STR() CONTAINS() STRSTARTS() REGEX()
numeric ABS() ROUND() FLOOR() CEIL()
date/time YEAR() MONTH() DAY() NOW()
term tests isIRI() isBlank() isLiteral() LANG() DATATYPE()
# Countries with more than 1 billion people (→ China, India)
SELECT ?countryLabel ?population WHERE {
  ?country wdt:P31 wd:Q6256 .
  ?country wdt:P1082 ?population .
  FILTER(?population > 1000000000)
  SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}

# France's population in millions (FILTER + BIND arithmetic)
SELECT ?population ?millions WHERE {
  wd:Q142 wdt:P1082 ?population .
  BIND(?population / 1000000 AS ?millions)
}                                          # → 68.605616

VALUES & BIND

# Restrict ?animal to a fixed list (see the running example)
VALUES ?animal { wd:Q144 wd:Q146 }

# Compute a new variable from existing ones
BIND(?population / 1000000 AS ?millions)

OPTIONAL, UNION, MINUS

Keyword Meaning
OPTIONAL left join: keep the row, leave the variable unbound when absent
UNION alternatives (the default between patterns is AND, not OR)
MINUS remove rows that match the pattern
# Einstein (died 1955) vs Obama (alive): OPTIONAL leaves ?deathLabel unbound
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". }
}
# → Albert Einstein  1955-04-18
# → Barack Obama     (no death row)

# Nobel laureates minus the French ones (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

# Dogs AND cats in one result set (UNION + DISTINCT + GROUP BY)
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

Property paths

Path Meaning
wdt:P40/wdt:P40 two hops (sequence)
wdt:P40+ one or more hops
wdt:P40* zero or more hops
wdt:P40? zero or one hop
wdt:P41 either property
^wdt:P40 inverse direction
!wdt:P40 any property except P40
# Named dogs, including breeds and other subclasses of dog
# (wdt:P31/wdt:P279* = "instance of something that is a dog or a subclass of dog")
SELECT ?thing ?thingLabel WHERE {
  ?thing wdt:P31/wdt:P279* wd:Q144 .
  SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}
LIMIT 3

Result: Theo · Tich · Tickle Em Jock

Aggregates

COUNT, SUM, AVG, MIN, MAX, SAMPLE (pick one arbitrary value), GROUP_CONCAT.

# Species with more than 400 direct instances (the running example with HAVING)
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)                # → only dog: 553

# Collect labels into 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

Subqueries

A query inside a query — useful for "the X with the max Y" patterns.

# The most populous country, computed via a subquery (→ China)
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". }
}

SERVICE (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")
  }
}

Common tasks

  • Count rows: SELECT (COUNT(*) AS ?n) WHERE { … }
  • Check existence: ASK WHERE { … }
  • Deduplicate: add DISTINCT
  • Reverse a relation: ?child ^wdt:P40 ?parent
  • Page through results: LIMIT 100 OFFSET 100
  • JSON output: append &format=json to the endpoint URL

Gotchas

  • The default between triple patterns is AND (join) — use UNION for alternatives.
  • An unbound variable in FILTER makes the row fail (unbound ≠ false) — guard with BOUND() or restructure with OPTIONAL.
  • Variables used only inside a property path (e.g. ?s wdt:P40/wdt:P40 ?o) cannot be selected.
  • Blank-node labels (_:x) are local to one query — they are not IRIs.
  • Aggregates need GROUP BY for every non-aggregated variable; forgetting one mixes unrelated rows.
  • FILTER NOT EXISTS differs from MINUS when variables are unbound — prefer MINUS for set-difference semantics.
  • Endpoint prefixes are not universal — wd:/wdt: are Wikidata's; other instances define their own (see Help:Contributing/query).

Further reading