XQuery/SPARQL Tutorial
SPARQL interface
editThe emp-dept RDF can be queried using SPARQL via an XQuery front end to a store provided by Talis. This script supports SPARQL queries and browsing the RDF graph.
The interface expands a query like
select ?name ?job where { ?emp rdf:type f:emp. ?emp foaf:surname ?name. ?emp f:Job ?job. }
that you can run here [dead link] into
prefix foaf: <http://xmlns.com/foaf/0.1/> prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> prefix f: <http://www.cems.uwe.ac.uk/empdept/concept/> prefix xs: <http://www.w3.org/2001/XMLSchema#> select ?name ?job where { ?emp rdf:type f:emp. ?emp foaf:surname ?name. ?emp f:Job ?job. }
and sends this to the Talis service in a form that can be run that you can run here. The resultant SPARQLQuery Results XML is converted to HTML.
Example Queries
editList all employees
editselect ?emp where { ?emp rdf:type f:emp. }
List the names of all employees in alphabetical order
editselect ?name where { ?emp rdf:type f:emp. ?emp foaf:surname ?name. } ORDER BY ?name
List the employees' name, salary, department number and job
editselect ?name ?sal ?dno ?job where { ?emp rdf:type f:emp; foaf:surname ?name; f:Sal ?sal; f:Dept ?dept; f:Job ?job. ?dept f:DeptNo ?dno. }
Note that ; in place of . repeats the subject.
Try out this and the following queries here [dead link].
List the first 5 employees
editselect ?ename where { ?emp rdf:type f:emp; foaf:surname ?ename. } ORDER BY ?ename LIMIT 5
List the top 5 employees by salary
editselect ?ename ?sal where { ?emp rdf:type f:emp; foaf:surname ?ename; f:Sal ?sal. } ORDER BY DESC(?sal) LIMIT 5
List the departments
editselect ?dept where { ?dept rdf:type f:dept. }
List all departments and all employees
editselect ?dept ?emp where { {?dept rdf:type f:dept } UNION {?emp rdf:type f:emp} }
List the employees with salaries over 1000
editIf the RDF literal is typed, for example as xs:integer as is the case with this generated RDF, then the following query will select employees with a salary greater than 1000:
select ?emp ?sal where { ?emp rdf:type f:emp; f:Sal ?sal. FILTER (?sal > 1000) }
If the RDF literal is not typed, then the variable must be cast:
select ?emp ?sal where { ?emp rdf:type f:emp; f:Sal ?sal. FILTER (xs:integer(?sal) > 1000) }
List employees and their locations
editselect ?emp ?loc where { ?emp rdf:type f:emp. ?emp f:Dept ?dept. ?dept f:Location ?loc. }
List the names of employees and their managers
editselect ?ename ?mname where { ?emp rdf:type f:emp; f:Mgr ?mgr; foaf:surname ?ename. ?mgr foaf:surname ?mname. }
Include employees with no manager
editselect ?ename ?mname where { ?emp rdf:type f:emp; foaf:surname ?ename. OPTIONAL {?emp f:Mgr ?mgr. ?mgr foaf:surname ?mname. } }
List employees with no manager
editselect ?ename where { ?emp rdf:type f:emp; foaf:surname ?ename. OPTIONAL {?emp f:Mgr ?mgr} FILTER (!bound(?mgr)) }
List the distinct locations of staff
editselect distinct ?loc where { ?emp rdf:type f:emp. ?emp f:Dept ?dept. ?dept f:Location ?loc. }
List details of the employees who are ANALYSTs
editselect * where { ?emp rdf:type f:emp. ?emp f:Dept ?dept. ?dept f:Location ?loc. ?emp f:Job ?job. FILTER (?job = "ANALYST") }
List employees who are either ANALYSTs or MANAGERs
editselect ?emp where { ?emp rdf:type f:emp; f:Job ?job. FILTER (?job = "ANALYST" || ?job = "MANAGER") }
List employees who are neither ANALYSTs nor MANAGERs
editselect * where { ?emp rdf:type f:emp; f:Job ?job. FILTER (?job != "ANALYST" && ?job != "MANAGER") }
List employees whose surname begins with "S"
editselect * where { ?emp rdf:type f:emp. ?emp foaf:surname ?ename. FILTER (regex(?ename, "^S")) }
List employees whose surname contains "AR"
editselect * where { ?emp rdf:type f:emp. ?emp foaf:surname ?ename. FILTER (regex(?ename, "AR")) }
List employees whose surname contains M followed by R ignoring case
editselect * where { ?emp rdf:type f:emp. ?emp foaf:surname ?ename. FILTER (regex(?ename, "m.*r","i")) }
Compute the maximum salary
editSPARQL 1.0 lacks min() or max(), although they are added to some implementations. The following recipe, due to Dean Allemang can be used:
select ?maxemp ?maxsal where { ?maxemp rdf:type f:emp. ?maxemp f:Sal ?maxsal. OPTIONAL { ?emp rdf:type f:emp. ?emp f:Sal ?sal. FILTER ( ?sal > ?maxsal) }. FILTER (!bound (?sal)) }
How does this work? We seek a maximum salary of a maximum employee. For such an employee, the OPTIONAL clauses will not match, since there are no employees with a greater salary and thus ?sal will not be bound.
In SPARQL 1.1 max() and min() are allowed so the query to return the maximum salary becomes
select (max(?sal) as ?maxsal) where { ?maxemp rdf:type f:emp. ?maxemp f:Sal ?sal. }
Compute employees with the same salary
editselect * where { ?emp1 f:Sal ?sal. ?emp2 f:Sal ?sal. FILTER (?emp1 != ?emp2) }
Get the department which SMITH works for
editselect ?dname where { ?emp rdf:type f:emp. ?emp f:Dept ?dept. ?emp foaf:surname "SMITH". ?dept f:Dname ?dname. }
List the names of employees in Accounting
editselect ?ename where { ?emp rdf:type f:emp. ?emp f:Dept ?dept. ?emp foaf:surname ?ename. ?dept f:Dname "Accounting". }
Employees hired in this millennium
editselect ?ename ?hire where { ?emp rdf:type f:emp. ?emp f:HireDate ?hire. ?emp foaf:surname ?ename. FILTER (?hire > "2000-01-01"^^xs:date) }
Note that the literal needs to be typed to make this comparison work.
List the names of employees whose manager is in a different department
editselect ?name ?edname ?mdname { ?emp rdf:type f:emp; foaf:surname ?name; f:Dept ?dept; f:Mgr ?mgr. ?mgr f:Dept ?mdept. ?dept f:Dname ?edname. ?mdept f:Dname ?mdname. FILTER (?dept != ?mdept) }
List the grades of employees
editIn relational terms, this is a theta-join between the employee and the salgrade tables:
select ?ename ?grade where { ?emp rdf:type f:emp; foaf:surname ?ename; f:Sal ?sal. ?salgrade rdf:type f:salgrade; f:LoSal ?low; f:HiSal ?high; f:Grade ?grade. FILTER (?sal >= ?low && ?sal <= ?high) }
Abbreviated query syntax
editA new prefix simplifies referencing individual resources by their URI
prefix e: <http://www.cems.uwe.ac.uk/empdept/emp/> select ?sal where { e:7900 f:Sal ?sal. }
is short for
select ?sal where { <http://www.cems.uwe.ac.uk/empdept/emp/7900> f:Sal ?sal. }
We could also introduce a default namespace:
prefix : <http://www.cems.uwe.ac.uk/empdept/concept/> select ?name ?sal ?dno ?job where { ?emp rdf:type :emp; foaf:surname ?name; :Sal ?sal; :Dept ?dept; :Job ?job. ?dept :DeptNo ?dno. }
and use the abbreviation a for rdf:type:
prefix : <http://www.cems.uwe.ac.uk/empdept/concept/> select ?name ?sal ?dno ?job where { ?emp a :emp; foaf:surname ?name; :Sal ?sal; :Dept ?dept; :Job ?job. ?dept :DeptNo ?dno. }
and if we don't need to return the resource itself, it can be anonymous
prefix : <http://www.cems.uwe.ac.uk/empdept/concept/> select ?name ?sal ?dno ?job where { [ a :emp; foaf:surname ?name; :Sal ?sal; :Dept ?dept; :Job ?job ]. ?dept :DeptNo ?dno. }
Aggregate features
editAggregation functions like count() and sum() and the GROUP BY clause are not defined in SPARQL 1.0 although they are available on some services (such as the Talis platform) in advance of standardisation in SPARQL 1.1.
Count the number of departments
editselect (count(?dept) as ?count) where { ?dept rdf:type f:dept. }
Count the number of employees in each department
editselect distinct ?dept (count(?emp) as ?count) where { ?dept a f:dept. ?emp f:Dept ?dept. } group by ?dept
Generic queries
editThe uniformity of the triple data model enable us to query the dataset in very general ways, which are useful if we know nothing about the data.
List all data
editselect * where { ?s ?p ?o }
This would be impracticable on a realistic dataset, but a sample of the triples can be obtained by limiting the number of triples returned.
select * where { ?s ?p ?o } LIMIT 20
List all employee data
editselect ?prop ?val where { ?emp rdf:type f:emp. ?emp ?prop ?val. }
What types are there?
editselect distinct ?type where { ?s a ?type }
This shows that triples defining the emp vocabulary are in the same dataset.
What properties are there?
editselect distinct ?prop where { ?s ?prop ?o }
What is the domain(s) of a property?
editselect distinct ?type where { ?s f:Sal ?v. ?s a ?type. }
What are the ranges of a property?
editselect distinct ?type where { ?s f:Sal ?o. ?o a ?type. }
This query only finds ranges which are instances of a type in the dataset. Sal has a range of xs:integer but it is not easy to discover that with a SPARQL query.
select distinct ?type where { ?s f:Mgr ?o. ?o a ?type. }
What properties have a given type as its domain ?
editselect distinct ?prop where { ?s a f:salgrade. ?s ?prop []. }
Schema queries
editThe presence of schema data enables SPARQL to be used to query this meta-data. The results could be compared with the results by directly querying the data.
What properties have a domain of a given type?
editselect ?prop where { ?prop rdfs:domain f:emp. }
Note that this has only returned the properties in the empdept vocab, not the foaf name property used in the raw data.
What integer properties do employees have?
editselect ?prop where { ?prop rdfs:domain f:emp. ?prop rdfs:range xs:integer. }
What types of resources have salaries?
editselect ?type where { f:Sal rdfs:domain ?type. }
Queries on both the data and the vocab can be made
What literal properties do MANAGERS have?
editselect DISTINCT ?prop where { ?x f:Job "MANAGER". ?x a ?type. ?prop rdfs:domain ?type. ?prop rdfs:range rdfs:literal. }
To do
edit- the example RDF lacks language tags which are required to illustrate lang() function
- all queries to be moved to the codelist together with the SQL and XQuery equivalents