diff --git a/CHANGELOG.md b/CHANGELOG.md
index b6ceac5e4ba32636ef076b018e6a33c150ed76a9..13a20975fb3cea4ebe6a120188e7999e5b59ee25 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
   by setting the server property `EXT_ENTITY_STATE=ENABLED`. See
   [!62](https://gitlab.com/caosdb/caosdb-server/-/merge_requests/62) for more
   information.
+* New query functionality: `ANY VERSION OF` modifier. E.g. `FIND ANY VERSION OF
+  RECORD WITH pname=val` returns all current and old versions of records where
+  `pname=val`. For further information, examples and limitations see the wiki
+  page on [CQL](https://gitlab.com/caosdb/caosdb/-/wikis/manuals/CQL/CaosDB%20Query%20Language)
 * New server property `SERVER_SIDE_SCRIPTING_BIN_DIRS` which accepts a comma or
   space separated list as values. The server looks for scripts in all
   directories in the order or the list and uses the first matching file.
diff --git a/conf/core/server.conf b/conf/core/server.conf
index 1eb96c7bf2dd2a5a216c81222e548a6155636091..f831f074d1904b53b481f1ad9496c6093f7a8eed 100644
--- a/conf/core/server.conf
+++ b/conf/core/server.conf
@@ -68,7 +68,7 @@ MYSQL_USER_NAME=caosdb
 # Password for the user
 MYSQL_USER_PASSWORD=caosdb
 # Schema of mysql procedures and tables which is required by this CaosDB instance
-MYSQL_SCHEMA_VERSION=v3.0.0-rc2
+MYSQL_SCHEMA_VERSION=v4.0.0-rc1
 
 
 # --------------------------------------------------
diff --git a/src/main/java/org/caosdb/server/jobs/core/EntityStateJob.java b/src/main/java/org/caosdb/server/jobs/core/EntityStateJob.java
index 39f4d6754ed665a0590c7a02f1e1882b8a907b64..83ec96f77db4515369fc0d3bce0f5d8146d9649e 100644
--- a/src/main/java/org/caosdb/server/jobs/core/EntityStateJob.java
+++ b/src/main/java/org/caosdb/server/jobs/core/EntityStateJob.java
@@ -18,6 +18,7 @@ import org.caosdb.server.entity.EntityInterface;
 import org.caosdb.server.entity.Message;
 import org.caosdb.server.entity.Message.MessageType;
 import org.caosdb.server.entity.StatementStatus;
+import org.caosdb.server.entity.container.TransactionContainer;
 import org.caosdb.server.entity.wrapper.Property;
 import org.caosdb.server.entity.xml.ToElementable;
 import org.caosdb.server.jobs.EntityJob;
@@ -583,6 +584,7 @@ public abstract class EntityStateJob extends EntityJob {
 
   EntityInterface findStateModel(EntityInterface stateEntity) throws Exception {
     // TODO this should be cached
+    TransactionContainer c = new TransactionContainer();
     Query query =
         new Query(
             "FIND RECORD "
@@ -592,8 +594,8 @@ public abstract class EntityStateJob extends EntityJob {
                 + " WHICH REFERENCES "
                 + Integer.toString(stateEntity.getId()),
             getUser(),
-            null);
+            c);
     query.execute(getTransaction().getAccess());
-    return retrieveValidEntity(query.getResultSet().get(0));
+    return retrieveValidEntity(c.get(0).getId());
   }
 }
diff --git a/src/main/java/org/caosdb/server/query/Backreference.java b/src/main/java/org/caosdb/server/query/Backreference.java
index 5f8105c823a231a14853e1db370649c7a7189649..684beef2e016203f3e6f64fd66ccefb750d11981 100644
--- a/src/main/java/org/caosdb/server/query/Backreference.java
+++ b/src/main/java/org/caosdb/server/query/Backreference.java
@@ -103,9 +103,12 @@ public class Backreference implements EntityFilterInterface, QueryInterface {
     return "@(" + getEntity() + "," + getProperty() + ")";
   }
 
-  /** */
   @Override
   public void apply(final QueryInterface query) throws QueryException {
+    if (query.isVersioned() && hasSubProperty()) {
+      throw new UnsupportedOperationException(
+          "Versioned queries are not supported for subqueries yet. Please file a feature request.");
+    }
     final long t1 = System.currentTimeMillis();
     this.query = query;
     this.targetSet = query.getTargetSet();
@@ -113,7 +116,7 @@ public class Backreference implements EntityFilterInterface, QueryInterface {
       initBackRef(query);
 
       final CallableStatement callApplyBackRef =
-          getConnection().prepareCall("call applyBackReference(?,?,?,?,?)");
+          getConnection().prepareCall("call applyBackReference(?,?,?,?,?,?)");
       callApplyBackRef.setString(1, getSourceSet()); // sourceSet
       this.statistics.put("sourceSet", getSourceSet());
       this.statistics.put(
@@ -145,6 +148,7 @@ public class Backreference implements EntityFilterInterface, QueryInterface {
         callApplyBackRef.setNull(4, VARCHAR);
       }
       callApplyBackRef.setBoolean(5, hasSubProperty()); // subQuery?
+      callApplyBackRef.setBoolean(6, this.isVersioned());
 
       executeStmt(query, callApplyBackRef);
       callApplyBackRef.close();
@@ -210,11 +214,10 @@ public class Backreference implements EntityFilterInterface, QueryInterface {
           this.statistics.put(
               "subPropertySourceSetCount", Utils.countTable(query.getConnection(), this.sourceSet));
           this.targetSet = null;
-          getSubProperty().getFilter().apply(this);
 
           final long t3 = System.currentTimeMillis();
           try (final PreparedStatement callFinishSubProperty =
-              getConnection().prepareCall("call finishSubProperty(?,?,?)")) {
+              getConnection().prepareCall("call finishSubProperty(?,?,?,?)")) {
             callFinishSubProperty.setString(1, query.getSourceSet()); // sourceSet
             if (query.getTargetSet() != null) { // targetSet
               callFinishSubProperty.setString(2, query.getTargetSet());
@@ -222,6 +225,7 @@ public class Backreference implements EntityFilterInterface, QueryInterface {
               callFinishSubProperty.setNull(2, VARCHAR);
             }
             callFinishSubProperty.setString(3, this.sourceSet); // list
+            callFinishSubProperty.setBoolean(4, this.isVersioned());
             final ResultSet rs2 = callFinishSubProperty.executeQuery();
             rs2.next();
             this.statistics.put("finishSubPropertyStmt", rs2.getString("finishSubPropertyStmt"));
@@ -320,4 +324,9 @@ public class Backreference implements EntityFilterInterface, QueryInterface {
   public void addBenchmark(final String str, final long time) {
     this.query.addBenchmark(this.getClass().getSimpleName() + "." + str, time);
   }
+
+  @Override
+  public boolean isVersioned() {
+    return this.query.isVersioned();
+  }
 }
diff --git a/src/main/java/org/caosdb/server/query/CQLLexer.g4 b/src/main/java/org/caosdb/server/query/CQLLexer.g4
index 62cb12c09c00a2f0005a8b9e2b7d5cc8f6b753e5..ec363be59893ad72cf412dbdc9d1f6d6644da5be 100644
--- a/src/main/java/org/caosdb/server/query/CQLLexer.g4
+++ b/src/main/java/org/caosdb/server/query/CQLLexer.g4
@@ -34,6 +34,25 @@ BY:
 	[Bb][Yy]
 ;
 
+fragment
+OF_f:
+	[Oo][Ff]
+;
+
+fragment
+ANY_f:
+	[Aa][Nn][Yy]
+;
+
+fragment
+VERSION_f:
+	[Vv][Ee][Rr][Ss][Ii][Oo][Nn]
+;
+
+ANY_VERSION_OF:
+	(ANY_f EMPTY_SPACE VERSION_f EMPTY_SPACE OF_f)
+;
+
 SELECT:
     [Ss][Ee][Ll][Ee][Cc][Tt] -> pushMode(SELECT_MODE)
 ;
diff --git a/src/main/java/org/caosdb/server/query/CQLParser.g4 b/src/main/java/org/caosdb/server/query/CQLParser.g4
index 699ff2f386878444f4400d1e6e7df75ad53c8d9a..0daeff47a95ff38391ce3f9b8004bec642b0ccb2 100644
--- a/src/main/java/org/caosdb/server/query/CQLParser.g4
+++ b/src/main/java/org/caosdb/server/query/CQLParser.g4
@@ -31,11 +31,12 @@ options { tokenVocab = CQLLexer; }
     import java.util.List;
 }
 
-cq returns [Query.Type t, List<Query.Selection> s, Query.Pattern e, Query.Role r, EntityFilterInterface filter]
+cq returns [Query.Type t, List<Query.Selection> s, Query.Pattern e, Query.Role r, EntityFilterInterface filter, VersionFilter v]
 	@init{
 	    $s = null;
 		$e = null;
 		$r = null;
+		$v = VersionFilter.UNVERSIONED;
 		$filter = null;
 	}
 :
@@ -44,6 +45,7 @@ cq returns [Query.Type t, List<Query.Selection> s, Query.Pattern e, Query.Role r
 	   SELECT prop_sel {$s = $prop_sel.s;} FROM {$t = Query.Type.FIND;}
 	   | FIND {$t = Query.Type.FIND;}
 	   | COUNT {$t = Query.Type.COUNT;})
+	(version {$v = $version.v;})?
 	(
 		(
 			role {$r = $role.r;}
@@ -56,6 +58,14 @@ cq returns [Query.Type t, List<Query.Selection> s, Query.Pattern e, Query.Role r
 	EOF
 ;
 
+version returns [VersionFilter v]
+	@init{
+		$v = null;
+	}
+:
+	ANY_VERSION_OF {$v = VersionFilter.ANY_VERSION;}
+;
+
 prop_sel returns [List<Query.Selection> s]
     @init{
         $s = new LinkedList<Query.Selection>();
diff --git a/src/main/java/org/caosdb/server/query/Conjunction.java b/src/main/java/org/caosdb/server/query/Conjunction.java
index a8fd9cd16296dfdf846bd81d2c25676537cd590c..2e44aea4f64a43ebb2a1c1edb153c6f68c172f8e 100644
--- a/src/main/java/org/caosdb/server/query/Conjunction.java
+++ b/src/main/java/org/caosdb/server/query/Conjunction.java
@@ -63,8 +63,9 @@ public class Conjunction extends EntityFilterContainer implements QueryInterface
       // generate empty temporary targetSet if query.getTargetSet() is not
       // empty anyways.
       final PreparedStatement callInitEmptyTarget =
-          getConnection().prepareStatement("call initEmptyTargetSet(?)");
+          getConnection().prepareStatement("call initEmptyTargetSet(?,?)");
       callInitEmptyTarget.setString(1, query.getTargetSet());
+      callInitEmptyTarget.setBoolean(2, query.isVersioned());
       final ResultSet initEmptyTargetResultSet = callInitEmptyTarget.executeQuery();
       if (initEmptyTargetResultSet.next()) {
         this.targetSet = bytes2UTF8(initEmptyTargetResultSet.getBytes("newTableName"));
@@ -149,4 +150,9 @@ public class Conjunction extends EntityFilterContainer implements QueryInterface
   public void addBenchmark(final String str, final long time) {
     this.query.addBenchmark(this.getClass().getSimpleName() + "." + str, time);
   }
+
+  @Override
+  public boolean isVersioned() {
+    return this.query.isVersioned();
+  }
 }
diff --git a/src/main/java/org/caosdb/server/query/Disjunction.java b/src/main/java/org/caosdb/server/query/Disjunction.java
index 3bd2f32fdd3bfb64ad2a07c527b91136e965c22b..31288f2f169d4ebce1cf3d4da5b2ab369fcd5e1f 100644
--- a/src/main/java/org/caosdb/server/query/Disjunction.java
+++ b/src/main/java/org/caosdb/server/query/Disjunction.java
@@ -51,7 +51,8 @@ public class Disjunction extends EntityFilterContainer implements QueryInterface
         // targetTable
         // which will be used to collect the entities.
         final PreparedStatement callInitDisjunctionFilter =
-            getConnection().prepareStatement("call initDisjunctionFilter()");
+            getConnection().prepareStatement("call initDisjunctionFilter(?)");
+        callInitDisjunctionFilter.setBoolean(1, this.isVersioned());
         final ResultSet initDisjuntionFilterResultSet = callInitDisjunctionFilter.executeQuery();
         if (initDisjuntionFilterResultSet.next()) {
           this.targetSet = bytes2UTF8(initDisjuntionFilterResultSet.getBytes("newTableName"));
@@ -69,11 +70,12 @@ public class Disjunction extends EntityFilterContainer implements QueryInterface
 
       if (query.getTargetSet() == null) {
         // calculate the difference and store to sourceSet
-        final CallableStatement callFinishNegationFilter =
-            getConnection().prepareCall("call calcIntersection(?,?)");
-        callFinishNegationFilter.setString(1, getSourceSet());
-        callFinishNegationFilter.setString(2, this.targetSet);
-        callFinishNegationFilter.execute();
+        final CallableStatement callFinishDisjunctionFilter =
+            getConnection().prepareCall("call calcIntersection(?,?,?)");
+        callFinishDisjunctionFilter.setString(1, getSourceSet());
+        callFinishDisjunctionFilter.setString(2, this.targetSet);
+        callFinishDisjunctionFilter.setBoolean(3, this.isVersioned());
+        callFinishDisjunctionFilter.execute();
       }
       // ELSE: the query.getTargetSet() is identical to targetSet and is
       // not
@@ -136,4 +138,9 @@ public class Disjunction extends EntityFilterContainer implements QueryInterface
   public void addBenchmark(final String str, final long time) {
     this.query.addBenchmark(this.getClass().getSimpleName() + "." + str, time);
   }
+
+  @Override
+  public boolean isVersioned() {
+    return this.query.isVersioned();
+  }
 }
diff --git a/src/main/java/org/caosdb/server/query/IDFilter.java b/src/main/java/org/caosdb/server/query/IDFilter.java
index 26054bb16f0eb39a581a4fa0da6c62a67c6fc385..12f53b44472d5b897c3d8b2a065388969c1edcbc 100644
--- a/src/main/java/org/caosdb/server/query/IDFilter.java
+++ b/src/main/java/org/caosdb/server/query/IDFilter.java
@@ -59,7 +59,7 @@ public class IDFilter implements EntityFilterInterface {
       final Connection connection = query.getConnection();
       // applyIDFilter(sourceSet, targetSet, o, vInt, agg)
       final CallableStatement callIDFilter =
-          connection.prepareCall("call applyIDFilter(?,?,?,?,?)");
+          connection.prepareCall("call applyIDFilter(?,?,?,?,?,?)");
 
       callIDFilter.setString(1, query.getSourceSet()); // sourceSet
       if (query.getTargetSet() != null) { // targetSet
@@ -89,6 +89,8 @@ public class IDFilter implements EntityFilterInterface {
         callIDFilter.setString(5, getAggregate());
       }
 
+      // versioning
+      callIDFilter.setBoolean(6, query.isVersioned());
       callIDFilter.execute();
       callIDFilter.close();
     } catch (final SQLException e) {
diff --git a/src/main/java/org/caosdb/server/query/Negation.java b/src/main/java/org/caosdb/server/query/Negation.java
index d4243580bf52511d05699c7c80e69015ea2a0dac..981b4c8f2ca19a1a6ec47df9e85f108ab7bb01ac 100644
--- a/src/main/java/org/caosdb/server/query/Negation.java
+++ b/src/main/java/org/caosdb/server/query/Negation.java
@@ -90,8 +90,8 @@ public class Negation implements EntityFilterInterface, QueryInterface {
       // generate empty temporary targetSet if query.getTargetSet() is not
       // empty anyways.
       final PreparedStatement callInitEmptyTarget =
-          getConnection().prepareStatement("call initEmptyTargetSet(?)");
-      callInitEmptyTarget.setString(1, query.getTargetSet());
+          getConnection().prepareStatement("call initDisjunctionFilter(?)");
+      callInitEmptyTarget.setBoolean(1, query.isVersioned());
       final ResultSet initEmptyTargetResultSet = callInitEmptyTarget.executeQuery();
       if (initEmptyTargetResultSet.next()) {
         this.targetSet = bytes2UTF8(initEmptyTargetResultSet.getBytes("newTableName"));
@@ -100,19 +100,22 @@ public class Negation implements EntityFilterInterface, QueryInterface {
 
       this.filter.apply(this);
 
-      if (query.getTargetSet() != null && !this.targetSet.equalsIgnoreCase(query.getTargetSet())) {
-        // merge temporary targetSet with query.getTargetSet()
+      if (query.getTargetSet() == null) {
+        // intersect temporary targetSet with query.getSourceSet()
         final CallableStatement callFinishConjunctionFilter =
-            query.getConnection().prepareCall("call calcUnion(?,?)");
-        callFinishConjunctionFilter.setString(1, query.getTargetSet());
+            query.getConnection().prepareCall("call calcDifference(?,?,?)");
+        callFinishConjunctionFilter.setString(1, query.getSourceSet());
         callFinishConjunctionFilter.setString(2, this.targetSet);
+        callFinishConjunctionFilter.setBoolean(3, this.isVersioned());
         callFinishConjunctionFilter.execute();
-      } else if (query.getTargetSet() == null) {
-        // intersect temporary targetSet with query.getSourceSet()
+      } else {
+        // merge temporary targetSet with query.getTargetSet()
         final CallableStatement callFinishConjunctionFilter =
-            query.getConnection().prepareCall("call calcDifference(?,?)");
-        callFinishConjunctionFilter.setString(1, query.getSourceSet());
+            query.getConnection().prepareCall("call calcComplementUnion(?,?,?,?)");
+        callFinishConjunctionFilter.setString(1, query.getTargetSet());
         callFinishConjunctionFilter.setString(2, this.targetSet);
+        callFinishConjunctionFilter.setString(3, query.getSourceSet());
+        callFinishConjunctionFilter.setBoolean(4, this.isVersioned());
         callFinishConjunctionFilter.execute();
       }
     } catch (final SQLException e) {
@@ -167,4 +170,9 @@ public class Negation implements EntityFilterInterface, QueryInterface {
   public void addBenchmark(final String str, final long time) {
     this.query.addBenchmark(this.getClass().getSimpleName() + "." + str, time);
   }
+
+  @Override
+  public boolean isVersioned() {
+    return this.query.isVersioned();
+  }
 }
diff --git a/src/main/java/org/caosdb/server/query/POV.java b/src/main/java/org/caosdb/server/query/POV.java
index 03b603d0df70d1159c1d78f1626e23faab40e033..fcc719e6ff24299d0b5a62a241442e08fa33962f 100644
--- a/src/main/java/org/caosdb/server/query/POV.java
+++ b/src/main/java/org/caosdb/server/query/POV.java
@@ -155,7 +155,7 @@ public class POV implements EntityFilterInterface {
           this.unit = getUnit(unitStr);
         } catch (final ParserException e) {
           e.printStackTrace();
-          throw new UnsupportedOperationException();
+          throw new UnsupportedOperationException("Could not parse the unit.");
         }
 
         this.stdUnitSig = this.unit.normalize().getSignature();
@@ -214,6 +214,10 @@ public class POV implements EntityFilterInterface {
 
   @Override
   public void apply(final QueryInterface query) throws QueryException {
+    if (query.isVersioned() && hasSubProperty()) {
+      throw new UnsupportedOperationException(
+          "Versioned queries are not supported for subqueries yet. Please file a feature request.");
+    }
     final long t1 = System.currentTimeMillis();
     try {
       this.connection = query.getConnection();
@@ -226,9 +230,9 @@ public class POV implements EntityFilterInterface {
       // applyPOV(sourceSet, targetSet, propertiesTable, refIdsTable, o,
       // vText, vInt,
       // vDouble,
-      // vDatetime, vDateTimeDotNotation, agg, pname)
+      // vDatetime, vDateTimeDotNotation, agg, pname, versioned)
       final CallableStatement callPOV =
-          this.connection.prepareCall("call applyPOV(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
+          this.connection.prepareCall("call applyPOV(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
       callPOV.setString(1, query.getSourceSet()); // sourceSet
       this.statistics.put("sourceSet", query.getSourceSet());
       this.statistics.put(
@@ -313,6 +317,10 @@ public class POV implements EntityFilterInterface {
       }
 
       if (getAggregate() != null) { // agg
+        if (query.isVersioned()) {
+          throw new UnsupportedOperationException(
+              "Versioned queries are not supported for aggregate functions like GREATES or SMALLEST in the filters.");
+        }
         callPOV.setString(14, getAggregate());
       } else {
         callPOV.setNull(14, VARCHAR);
@@ -323,6 +331,7 @@ public class POV implements EntityFilterInterface {
       } else {
         callPOV.setNull(15, VARCHAR);
       }
+      callPOV.setBoolean(16, query.isVersioned());
       prefix.add("#executeStmt");
       executeStmt(callPOV, query);
       prefix.pop();
@@ -420,7 +429,9 @@ public class POV implements EntityFilterInterface {
 
     if (hasSubProperty() && this.targetSet != null) {
       try (PreparedStatement stmt =
-          query.getConnection().prepareStatement("call initEmptyTargetSet(NULL)")) {
+          query.getConnection().prepareStatement("call initEmptyTargetSet(?, ?)")) {
+        stmt.setNull(1, VARCHAR);
+        stmt.setBoolean(2, query.isVersioned());
         // generate new targetSet
         final ResultSet rs = stmt.executeQuery();
         if (rs.next()) {
diff --git a/src/main/java/org/caosdb/server/query/Query.java b/src/main/java/org/caosdb/server/query/Query.java
index c3873cf3b4ba2322373dde13f4dfd72e6a94c7e4..695a295da5f64ec788c4dfbb3332734ead44b289 100644
--- a/src/main/java/org/caosdb/server/query/Query.java
+++ b/src/main/java/org/caosdb/server/query/Query.java
@@ -169,12 +169,22 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac
     }
   }
 
+  public static class IdVersionPair {
+    public IdVersionPair(Integer id, String version) {
+      this.id = id;
+      this.version = version;
+    }
+
+    public Integer id;
+    public String version;
+  }
+
   private static boolean filterEntitiesWithoutRetrievePermisions =
       !CaosDBServer.getServerProperty(
               ServerProperties.KEY_QUERY_FILTER_ENTITIES_WITHOUT_RETRIEVE_PERMISSIONS)
           .equalsIgnoreCase("FALSE");
 
-  List<Integer> resultSet = null;
+  List<IdVersionPair> resultSet = null;
   private final String query;
   private Pattern entity = null;
   private Role role = null;
@@ -189,6 +199,7 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac
   private Type type = null;
   private final ArrayList<ToElementable> messages = new ArrayList<>();
   private Access access;
+  private boolean versioned = false;
 
   public Type getType() {
     return this.type;
@@ -216,7 +227,7 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac
    */
   private void initResultSetWithNameIDAndChildren() throws SQLException {
     final CallableStatement callInitEntity =
-        getConnection().prepareCall("call initEntity(?,?,?,?,?)");
+        getConnection().prepareCall("call initEntity(?,?,?,?,?,?)");
 
     try {
       callInitEntity.setInt(1, Integer.parseInt(this.entity.toString()));
@@ -243,6 +254,7 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac
         break;
     }
     callInitEntity.setString(5, this.sourceSet);
+    callInitEntity.setBoolean(6, this.versioned);
     callInitEntity.execute();
     callInitEntity.close();
   }
@@ -260,8 +272,8 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac
         applyQueryTemplates(this, getSourceSet());
       }
 
-      if (this.role != null) {
-        final RoleFilter roleFilter = new RoleFilter(this.role, "=");
+      if (this.role != null && this.role != Role.ENTITY) {
+        final RoleFilter roleFilter = new RoleFilter(this.role, "=", this.versioned);
         roleFilter.apply(this);
       }
 
@@ -312,7 +324,9 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac
         final Query subQuery = new Query(q.getValue(), query.getUser());
         subQuery.setAccess(query.getAccess());
         subQuery.parse();
-        final String subResultSet = subQuery.executeStrategy();
+
+        // versioning for QueryTemplates is not supported and probably never will.
+        final String subResultSet = subQuery.executeStrategy(false);
 
         // ... and merge the resultSets.
         union(query, resultSet, subResultSet);
@@ -384,7 +398,7 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac
 
     // filter by role
     if (this.role != null && this.role != Role.ENTITY) {
-      final RoleFilter roleFilter = new RoleFilter(this.role, "=");
+      final RoleFilter roleFilter = new RoleFilter(this.role, "=", this.versioned);
       roleFilter.apply(this);
     }
 
@@ -402,8 +416,9 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac
     }
   }
 
-  private String initQuery() throws QueryException {
-    try (final CallableStatement callInitQuery = getConnection().prepareCall("call initQuery()")) {
+  private String initQuery(boolean versioned) throws QueryException {
+    String sql = "call initQuery(" + versioned + ")";
+    try (final CallableStatement callInitQuery = getConnection().prepareCall(sql)) {
       ResultSet initQueryResult = null;
       initQueryResult = callInitQuery.executeQuery();
       if (!initQueryResult.next()) {
@@ -431,6 +446,7 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac
     }
 
     this.entity = cq.e;
+    this.versioned = cq.v != VersionFilter.UNVERSIONED;
     this.role = cq.r;
     this.parseTree = cq.toStringTree(parser);
     this.type = cq.t;
@@ -442,23 +458,33 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac
     }
   }
 
-  private String executeStrategy() throws QueryException {
+  private String executeStrategy(boolean versioned) throws QueryException {
     if (this.entity != null) {
-      return sourceStrategy(initQuery());
+      return sourceStrategy(initQuery(versioned));
     } else {
-      return targetStrategy(initQuery());
+      return targetStrategy(initQuery(versioned));
     }
   }
 
-  private LinkedList<Integer> getResultSet(final String resultSetTableName) throws QueryException {
+  private List<IdVersionPair> getResultSet(final String resultSetTableName, boolean versioned)
+      throws QueryException {
     ResultSet finishResultSet = null;
     try {
-      final PreparedStatement finish =
-          getConnection().prepareStatement("Select id from `" + resultSetTableName + "`");
+      final String sql =
+          "Select results.id AS id"
+              + (versioned ? ", ev.version AS version" : "")
+              + " from `"
+              + resultSetTableName
+              + "` AS results"
+              + (versioned
+                  ? " JOIN entity_version AS ev ON (results.id = ev.entity_id AND results._iversion = ev._iversion)"
+                  : "");
+      final PreparedStatement finish = getConnection().prepareStatement(sql);
       finishResultSet = finish.executeQuery();
-      final LinkedList<Integer> rs = new LinkedList<Integer>();
+      final List<IdVersionPair> rs = new LinkedList<>();
       while (finishResultSet.next()) {
-        rs.add(finishResultSet.getInt("id"));
+        final String version = versioned ? finishResultSet.getString("version") : null;
+        rs.add(new IdVersionPair(finishResultSet.getInt("id"), version));
       }
       return rs;
     } catch (final SQLException e) {
@@ -480,15 +506,15 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac
 
     try {
 
-      this.resultSet = getResultSet(executeStrategy());
+      this.resultSet = getResultSet(executeStrategy(this.versioned), this.versioned);
 
       filterEntitiesWithoutRetrievePermission(this.resultSet);
 
       // Fill resulting entities into container
       if (this.container != null && this.type == Type.FIND) {
-        for (final int id : this.resultSet) {
+        for (final IdVersionPair p : this.resultSet) {
 
-          final Entity e = new RetrieveEntity(id);
+          final Entity e = new RetrieveEntity(p.id, p.version);
 
           // if query has select-clause:
           if (this.selections != null && !this.selections.isEmpty()) {
@@ -579,16 +605,16 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac
    * @param entities
    * @throws TransactionException
    */
-  private void filterEntitiesWithoutRetrievePermission(final List<Integer> entities)
+  private void filterEntitiesWithoutRetrievePermission(final List<IdVersionPair> entities)
       throws TransactionException {
     if (!filterEntitiesWithoutRetrievePermisions) {
       return;
     }
-    final Iterator<Integer> iterator = entities.iterator();
+    final Iterator<IdVersionPair> iterator = entities.iterator();
     while (iterator.hasNext()) {
       final long t1 = System.currentTimeMillis();
-      final Integer id = iterator.next();
-      if (!execute(new RetrieveSparseEntity(id, null), getAccess())
+      final IdVersionPair next = iterator.next();
+      if (!execute(new RetrieveSparseEntity(next.id, next.version), getAccess())
           .getEntity()
           .getEntityACL()
           .isPermitted(getUser(), EntityPermission.RETRIEVE_ENTITY)) {
@@ -604,10 +630,6 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac
     return this.query;
   }
 
-  public List<Integer> getResultSet() {
-    return this.resultSet;
-  }
-
   @Override
   public String getSourceSet() {
     return this.sourceSet;
@@ -742,4 +764,9 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac
     }
     return benchmark;
   }
+
+  @Override
+  public boolean isVersioned() {
+    return this.versioned;
+  }
 }
diff --git a/src/main/java/org/caosdb/server/query/QueryInterface.java b/src/main/java/org/caosdb/server/query/QueryInterface.java
index 32cc1c7d8d98b56f80b159fc3f0004a491773563..0f2db9a97094ddc26062a9c9922907c183a26413 100644
--- a/src/main/java/org/caosdb/server/query/QueryInterface.java
+++ b/src/main/java/org/caosdb/server/query/QueryInterface.java
@@ -43,4 +43,6 @@ public interface QueryInterface {
   public Subject getUser();
 
   public void addBenchmark(final String str, final long time);
+
+  public boolean isVersioned();
 }
diff --git a/src/main/java/org/caosdb/server/query/RoleFilter.java b/src/main/java/org/caosdb/server/query/RoleFilter.java
index 9055dccb358dd94ec17dee8b26bac97d2170b016..48c8372014c6e702be8b88113102da030dbc6046 100644
--- a/src/main/java/org/caosdb/server/query/RoleFilter.java
+++ b/src/main/java/org/caosdb/server/query/RoleFilter.java
@@ -33,6 +33,7 @@ public class RoleFilter implements EntityFilterInterface {
 
   private final Role role;
   private final String operator;
+  private boolean versioned;
 
   /**
    * Guarantees that all entities in the result set do have ("=") or do not have ("!=") the role in
@@ -43,7 +44,8 @@ public class RoleFilter implements EntityFilterInterface {
    * @throws NullPointerException If role or operator is null.
    * @throws IllegalArgumentException If operator is not "=" or "!=".
    */
-  public RoleFilter(final Role role, final String operator) {
+  public RoleFilter(final Role role, final String operator, final boolean versioned) {
+    this.versioned = versioned;
     if (role == null) {
       throw new NullPointerException("The role must not be null.");
     }
@@ -72,7 +74,8 @@ public class RoleFilter implements EntityFilterInterface {
             getOperator(),
             getRole(),
             query.getSourceSet(),
-            query.getTargetSet());
+            query.getTargetSet(),
+            this.versioned);
       }
     } catch (final SQLException e) {
       throw new QueryException(e);
@@ -85,20 +88,28 @@ public class RoleFilter implements EntityFilterInterface {
       final String operator,
       final String role,
       final String sourceSet,
-      final String targetSet)
+      final String targetSet,
+      final boolean versioned)
       throws SQLException {
-    final PreparedStatement filterRoleStmt =
-        connection.prepareCall(
-            "INSERT IGNORE INTO `"
-                + targetSet
-                + "` (id) SELECT id FROM `"
-                + sourceSet
-                + (sourceSet.equals("entities")
-                    ? "` AS s WHERE EXISTS (SELECT * FROM entities AS e WHERE e.id=s.id AND e.role"
-                    : "` AS e WHERE NOT (e.role")
-                + operator
-                + "?);");
-    filterRoleStmt.setString(1, role);
+    if (!sourceSet.equals("entities")) {
+      throw new UnsupportedOperationException("SourceSet is supposed to be the `entities` table.");
+    }
+    final String sql =
+        ("INSERT IGNORE INTO `"
+            + targetSet
+            + (versioned
+                ? "` (id, _iversion) SELECT e.id, _get_head_iversion(e.id) FROM `entities` AS e WHERE e.role "
+                    + operator
+                    + " ? UNION SELECT a.id, a._iversion FROM `archive_entities` AS a WHERE a.role"
+                    + operator
+                    + "?"
+                : "` (id) SELECT e.id FROM `entities` AS e WHERE e.role" + operator + "?"));
+    final PreparedStatement filterRoleStmt = connection.prepareCall(sql);
+    int params = (versioned ? 2 : 1);
+    while (params > 0) {
+      filterRoleStmt.setString(params, role);
+      params--;
+    }
     filterRoleStmt.execute();
   }
 
@@ -109,7 +120,7 @@ public class RoleFilter implements EntityFilterInterface {
         connection.prepareCall(
             "DELETE FROM `"
                 + sourceSet
-                + "` WHERE EXISTS (SELECT * FROM entities AS e WHERE e.id=`"
+                + "` WHERE EXISTS (SELECT 1 FROM entities AS e WHERE e.id=`"
                 + sourceSet
                 + "`.id AND NOT e.role"
                 + operator
diff --git a/src/main/java/org/caosdb/server/query/SubProperty.java b/src/main/java/org/caosdb/server/query/SubProperty.java
index f8c22367c70f623903a3ee2f14a0af3cbbefd128..4668935e56f0057d3d0fe927aa0a729669e7e0f6 100644
--- a/src/main/java/org/caosdb/server/query/SubProperty.java
+++ b/src/main/java/org/caosdb/server/query/SubProperty.java
@@ -90,18 +90,15 @@ public class SubProperty implements QueryInterface, EntityFilterInterface {
         getQuery().filterEntitiesWithoutRetrievePermission(this, this.sourceSet);
 
         final CallableStatement callFinishSubProperty =
-            getConnection().prepareCall("call finishSubProperty(?,?,?)");
-        callFinishSubProperty.setString(1, query.getSourceSet()); // sourceSet
-        // of
-        // parent
-        // query
+            getConnection().prepareCall("call finishSubProperty(?,?,?,?)");
+        callFinishSubProperty.setString(1, query.getSourceSet()); // sourceSet of parent query
         if (query.getTargetSet() != null) { // targetSet
           callFinishSubProperty.setString(2, query.getTargetSet());
         } else {
           callFinishSubProperty.setNull(2, VARCHAR);
         }
-        callFinishSubProperty.setString(3, this.sourceSet); // sub query
-        // sourceSet
+        callFinishSubProperty.setString(3, this.sourceSet); // sub query sourceSet
+        callFinishSubProperty.setBoolean(4, this.isVersioned());
         callFinishSubProperty.execute();
         callFinishSubProperty.close();
       } else {
@@ -165,4 +162,9 @@ public class SubProperty implements QueryInterface, EntityFilterInterface {
   public void addBenchmark(final String str, final long time) {
     this.query.addBenchmark(this.getClass().getSimpleName() + "." + str, time);
   }
+
+  @Override
+  public boolean isVersioned() {
+    return this.query.isVersioned();
+  }
 }
diff --git a/src/main/java/org/caosdb/server/query/VersionFilter.java b/src/main/java/org/caosdb/server/query/VersionFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..aa391778ce20c089a9fa6dae88caf77e0f797703
--- /dev/null
+++ b/src/main/java/org/caosdb/server/query/VersionFilter.java
@@ -0,0 +1,7 @@
+package org.caosdb.server.query;
+
+public class VersionFilter {
+
+  public static final VersionFilter ANY_VERSION = new VersionFilter();
+  public static final VersionFilter UNVERSIONED = new VersionFilter();
+}
diff --git a/src/test/java/org/caosdb/server/query/TestCQL.java b/src/test/java/org/caosdb/server/query/TestCQL.java
index acd4946d826b7b0ed00c396c71dd84384e4668a4..2f6651cc81a2614b63f1314f35052fe2096758b0 100644
--- a/src/test/java/org/caosdb/server/query/TestCQL.java
+++ b/src/test/java/org/caosdb/server/query/TestCQL.java
@@ -243,6 +243,8 @@ public class TestCQL {
   String emptyTextValue = "FIND ENTITY WITH prop=''";
   String queryMR56 = "FIND ENTITY WITH ((p0 = v0 OR p1=v1) AND p2=v2)";
 
+  String versionedQuery1 = "FIND ANY VERSION OF ENTITY e1";
+
   @Test
   public void testQuery1()
       throws InterruptedException, SQLException, ConnectionException, QueryException {
@@ -6450,4 +6452,23 @@ public class TestCQL {
     final ParseTree pov = conjunction.getChild(4);
     assertEquals("p2=v2", pov.getText());
   }
+
+  /** String versionedQuery1 = "FIND ANY VERSION OF ENTITY e1"; */
+  @Test
+  public void testVersionedQuery1() {
+    CQLLexer lexer;
+    lexer = new CQLLexer(CharStreams.fromString(this.versionedQuery1));
+    final CommonTokenStream tokens = new CommonTokenStream(lexer);
+
+    final CQLParser parser = new CQLParser(tokens);
+    final CqContext sfq = parser.cq();
+
+    System.out.println(sfq.toStringTree(parser));
+
+    // 4 children: FIND, version, role, entity, EOF
+    assertEquals(5, sfq.getChildCount());
+    assertEquals(VersionFilter.ANY_VERSION, sfq.v);
+    assertEquals(Query.Role.ENTITY, sfq.r);
+    assertEquals("e1", sfq.e.toString());
+  }
 }