Last modified on 26 March 2014, at 11:35

XQuery/SPARQL Tutorial

SPARQL interfaceEdit

The 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 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 QueriesEdit

List all employeesEdit

 select ?emp where {
  ?emp  rdf:type  f:emp.
 }

Run

List the names of all employees in alphabetical orderEdit

 select ?name where {
  ?emp  rdf:type  f:emp.
  ?emp  foaf:surname ?name. 
 }
 ORDER BY ?name

Run

List the employees' name, salary, department number and jobEdit

 select ?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.

List the first 5 employeesEdit

select ?ename where {
  ?emp  rdf:type  f:emp;
        foaf:surname ?ename.
} 
ORDER BY ?ename
LIMIT 5

List the top 5 employees by salaryEdit

select ?ename ?sal where {
 ?emp  rdf:type  f:emp;
       foaf:surname ?ename;
       f:Sal ?sal.
} 
ORDER BY DESC(?sal)
LIMIT 5

List the departmentsEdit

select ?dept where {
  ?dept  rdf:type  f:dept.  
}

List all departments and all employeesEdit

select ?dept ?emp where {
  {?dept  rdf:type  f:dept }
  UNION
  {?emp rdf:type f:emp}
}


List the employees with salaries over 1000Edit

If 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 locationsEdit

select ?emp ?loc where {
  ?emp  rdf:type  f:emp.
  ?emp f:Dept ?dept.
  ?dept f:Location ?loc.
} 

List the names of employees and their managersEdit

select ?ename ?mname where {
  ?emp  rdf:type  f:emp;
        f:Mgr ?mgr;
        foaf:surname ?ename.
  ?mgr foaf:surname ?mname.
} 

Include employees with no managerEdit

select ?ename ?mname where {
  ?emp  rdf:type  f:emp;
        foaf:surname ?ename.
  OPTIONAL {?emp f:Mgr ?mgr.
            ?mgr foaf:surname ?mname.
           }
}

List employees with no managerEdit

select ?ename where {
  ?emp  rdf:type  f:emp;
        foaf:surname ?ename.
  OPTIONAL {?emp f:Mgr ?mgr}
  FILTER (!bound(?mgr))
}

List the distinct locations of staffEdit

select distinct ?loc  where {
  ?emp  rdf:type  f:emp.
  ?emp  f:Dept ?dept.
  ?dept f:Location ?loc.
} 


List details of the employees who are ANALYSTsEdit

select * 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 MANAGERsEdit

select ?emp where {
  ?emp  rdf:type  f:emp;
        f:Job ?job.
  FILTER (?job = "ANALYST"  || ?job = "MANAGER")
}

List employees who are neither ANALYSTs nor MANAGERsEdit

select * where {
   ?emp  rdf:type  f:emp;
         f:Job ?job.
   FILTER (?job != "ANALYST"  && ?job != "MANAGER")
}

List employees whose surname begins with "S"Edit

select * where {
  ?emp  rdf:type  f:emp.
  ?emp foaf:surname ?ename.
  FILTER (regex(?ename, "^S"))
} 

List employees whose surname contains "AR"Edit

select * where {
  ?emp  rdf:type  f:emp.
  ?emp foaf:surname ?ename.
  FILTER (regex(?ename, "AR"))
} 

List employees whose surname contains M followed by R ignoring caseEdit

select * where {
  ?emp  rdf:type  f:emp.
  ?emp foaf:surname ?ename.
  FILTER (regex(?ename, "m.*r","i"))
}

Compute the maximum salaryEdit

SPARQL 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 salaryEdit

 select * where {
    ?emp1 f:Sal ?sal.
    ?emp2 f:Sal ?sal.
    FILTER (?emp1 != ?emp2)
 }

Get the department which SMITH works forEdit

select ?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 AccountingEdit

select ?ename  where {
  ?emp  rdf:type  f:emp.
  ?emp f:Dept ?dept.
  ?emp foaf:surname ?ename.
  ?dept f:Dname "Accounting".
} 


Employees hired in this millenniumEdit

select ?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 departmentEdit

select ?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 employeesEdit

In 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 syntaxEdit

A 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 featuresEdit

Aggregation 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 departmentsEdit

select (count(?dept) as ?count) where {
  ?dept  rdf:type  f:dept.

}

Count the number of employees in each departmentEdit

select distinct ?dept (count(?emp) as ?count) where {
  ?dept a f:dept.
  ?emp f:Dept ?dept.
} group by ?dept

Generic queriesEdit

The 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 dataEdit

select * 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 dataEdit

select ?prop ?val where {
  ?emp  rdf:type  f:emp.
  ?emp ?prop ?val.
}


What types are there?Edit

select distinct ?type where {
  ?s a ?type
}

This shows that triples defining the emp vocabulary are in the same dataset.

What properties are there?Edit

select distinct ?prop where {
   ?s ?prop ?o
}

What is the domain(s) of a property?Edit

select distinct ?type where {
    ?s f:Sal ?v.
    ?s a ?type.
}

What are the ranges of a property?Edit

select 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 ?Edit

select distinct ?prop where {
     ?s a f:salgrade.
     ?s ?prop [].
}

Schema queriesEdit

The 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?Edit

select ?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?Edit

select ?prop  where {
   ?prop rdfs:domain f:emp.
   ?prop rdfs:range xs:integer.
}

What types of resources have salaries?Edit

select ?type where {
  f:Sal rdfs:domain ?type.
}


Queries on both the data and the vocab can be made


What literal properties do MANAGERS have?Edit

select DISTINCT ?prop  where {
   ?x f:Job "MANAGER".
   ?x a ?type. 
   ?prop rdfs:domain ?type.
   ?prop rdfs:range rdfs:literal.
}

To doEdit

  • 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