I'm trying to understand how VALUES behaves regarding the position of it in the query. The idea is to use this for easier parametrization of SPARQL queries that come from a template.
For most queries it worked as expected but I now ran into a case that I don't understand. Given the following query:
SELECT * WHERE {
# Hard coded version:
#BIND( (2020 - 2) AS ?year )
# Works as expected:
#VALUES ?startYear { 2020 }
#BIND( (?startYear -2 ) AS ?year )
# Does not work as expected:
# Year is "1" in Fuseki, empty in Stardog. The "-2" does not seem to have any effect in that scenario
BIND( (?startYear -2 ) AS ?year )
VALUES ?startYear { 2020 }
}
As you can see in the comments, the last one does not work. If the VALUES is before the BIND, everything seems as expected. In the SPARQL 1.1 spec, there is an example where they move VALUES around but I can't find an explanation for what I see here.
Why does it not work when VALUES is at the end of the query when I use it in a BIND? It does work well in FILTER in that case.
It's the position of BIND that matters, not VALUES. BIND in SPARQL always closes the group graph pattern, i.e. it goes on top of all patterns in the same {}
block which precede it, see https://www.w3.org/TR/sparql11-query/#bind
It becomes evident if you look at the query structure (the so-called "algebra") at http://www.sparql.org/query-validator.html:
SELECT *
WHERE {
VALUES ?startYear { 2020 }
BIND( (?startYear -2 ) AS ?year )
}
has this algebra:
9 (extend ((?year (- ?startYear 2)))
10 (table (vars ?startYear)
11 (row [?startYear 2020])
12 ))))
the table
is your VALUES
and the extend
is your BIND
. The BIND
is evaluated on top of VALUES
and thus ?startYear
has a value when you deduct 2.
SELECT *
WHERE {
BIND( (?startYear -2 ) AS ?year )
VALUES ?startYear { 2020 }
}
however, has the following algebra:
9 (join
10 (extend ((?year (- ?startYear 2)))
11 (table unit))
12 (table (vars ?startYear)
13 (row [?startYear 2020])
14 ))))
Here nothing precedes BIND
in its block and thus you see table unit
as its argument (it's the so-called empty group). It doesn't bind any variables so ?startYear
has no value when BIND
is evaluated. The result of BIND is then joined with VALUES
but it's too late since - 2
already happened.
I always recommend using that online tool for checking semantics of your queries.