You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by ja...@apache.org on 2023/03/07 01:53:18 UTC

[iotdb] branch master updated: [IOTDB-5631] Add aggregate method time_duration for query

This is an automated email from the ASF dual-hosted git repository.

jackietien pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/master by this push:
     new 91414196aa [IOTDB-5631] Add aggregate  method time_duration for query
91414196aa is described below

commit 91414196aa6a435a83a3b298f7bd2b91ba5c3738
Author: Zhijia Cao <ca...@126.com>
AuthorDate: Tue Mar 7 09:53:12 2023 +0800

    [IOTDB-5631] Add aggregate  method time_duration for query
---
 docs/UserGuide/Operators-Functions/Aggregation.md  |  80 ++++-
 .../UserGuide/Operators-Functions/Aggregation.md   |  80 ++++-
 .../apache/iotdb/itbase/constant/TestConstant.java |   4 +
 .../it/aggregation/IoTDBAggregationOptimizeIT.java |   8 +-
 .../db/it/aggregation/IoTDBTimeDurationIT.java     | 368 +++++++++++++++++++++
 .../udf/builtin/BuiltinAggregationFunction.java    |   4 +-
 .../org/apache/iotdb/db/constant/SqlConstant.java  |   1 +
 .../db/mpp/aggregation/AccumulatorFactory.java     |   2 +
 .../mpp/aggregation/TimeDurationAccumulator.java   | 124 +++++++
 .../SlidingWindowAggregatorFactory.java            |   2 +
 .../iotdb/db/mpp/plan/parser/ASTVisitor.java       |   1 +
 .../plan/parameter/AggregationDescriptor.java      |   4 +
 .../org/apache/iotdb/db/utils/SchemaUtils.java     |   4 +
 .../apache/iotdb/db/utils/TypeInferenceUtils.java  |   3 +
 thrift-commons/src/main/thrift/common.thrift       |   3 +-
 15 files changed, 657 insertions(+), 31 deletions(-)

diff --git a/docs/UserGuide/Operators-Functions/Aggregation.md b/docs/UserGuide/Operators-Functions/Aggregation.md
index c20947908e..0be24a85ec 100644
--- a/docs/UserGuide/Operators-Functions/Aggregation.md
+++ b/docs/UserGuide/Operators-Functions/Aggregation.md
@@ -27,19 +27,20 @@ All aggregate functions except `COUNT()`, `COUNT_IF()` ignore null values and re
 
 The aggregate functions supported by IoTDB are as follows:
 
-| Function Name | Function Description                                                                                                                                 | Allowed Input Data Types | Output Data Types                                                                                                                                                                                                                                                                                        [...]
-| ------------- |------------------------------------------------------------------------------------------------------------------------------------------------------| ------------------------ |--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [...]
-| SUM           | Summation.                                                                                                                                           | INT32 INT64 FLOAT DOUBLE | DOUBLE                                                                                                                                                                                                                                                                                                   [...]
-| COUNT         | Counts the number of data points.                                                                                                                    | All types                | INT                                                                                                                                                                                                                                                                                                      [...]
-| AVG           | Average.                                                                                                                                             | INT32 INT64 FLOAT DOUBLE | DOUBLE                                                                                                                                                                                                                                                                                                   [...]
-| EXTREME       | Finds the value with the largest absolute value. Returns a positive value if the maximum absolute value of positive and negative values is equal.    | INT32 INT64 FLOAT DOUBLE | Consistent with the input data type                                                                                                                                                                                                                                                                      [...]
-| MAX_VALUE     | Find the maximum value.                                                                                                                              | INT32 INT64 FLOAT DOUBLE | Consistent with the input data type                                                                                                                                                                                                                                                                      [...]
-| MIN_VALUE     | Find the minimum value.                                                                                                                              | INT32 INT64 FLOAT DOUBLE | Consistent with the input data type                                                                                                                                                                                                                                                                      [...]
-| FIRST_VALUE   | Find the value with the smallest timestamp.                                                                                                          | All data types           | Consistent with input data type                                                                                                                                                                                                                                                                          [...]
-| LAST_VALUE    | Find the value with the largest timestamp.                                                                                                           | All data types           | Consistent with input data type                                                                                                                                                                                                                                                                          [...]
-| MAX_TIME      | Find the maximum timestamp.                                                                                                                          | All data Types           | Timestamp                                                                                                                                                                                                                                                                                                [...]
-| MIN_TIME      | Find the minimum timestamp.                                                                                                                          | All data Types           | Timestamp                                                                                                                                                                                                                                                                                                [...]
-| COUNT_IF    | Find the number of data points that continuously meet a given condition and the number of data points that meet the condition (represented by keep) meet the specified threshold. | BOOLEAN                  | `[keep >=/>/=/!=/</<=]threshold`:The specified threshold or threshold condition, it is equivalent to `keep >= threshold` if `threshold` is used alone, type of `threshold` is `INT64`<br/> `ignoreNull`:Optional, default value is `true`;If the value is `true`, null values [...]
+| Function Name | Function Description                                                                                                                                 | Allowed Input Data Types | Output Data Types                                                                                                                                                                                                                                                                                        [...]
+|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------| ------------------------ |--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [...]
+| SUM           | Summation.                                                                                                                                           | INT32 INT64 FLOAT DOUBLE | DOUBLE                                                                                                                                                                                                                                                                                                   [...]
+| COUNT         | Counts the number of data points.                                                                                                                    | All types                | INT                                                                                                                                                                                                                                                                                                      [...]
+| AVG           | Average.                                                                                                                                             | INT32 INT64 FLOAT DOUBLE | DOUBLE                                                                                                                                                                                                                                                                                                   [...]
+| EXTREME       | Finds the value with the largest absolute value. Returns a positive value if the maximum absolute value of positive and negative values is equal.    | INT32 INT64 FLOAT DOUBLE | Consistent with the input data type                                                                                                                                                                                                                                                                      [...]
+| MAX_VALUE     | Find the maximum value.                                                                                                                              | INT32 INT64 FLOAT DOUBLE | Consistent with the input data type                                                                                                                                                                                                                                                                      [...]
+| MIN_VALUE     | Find the minimum value.                                                                                                                              | INT32 INT64 FLOAT DOUBLE | Consistent with the input data type                                                                                                                                                                                                                                                                      [...]
+| FIRST_VALUE   | Find the value with the smallest timestamp.                                                                                                          | All data types           | Consistent with input data type                                                                                                                                                                                                                                                                          [...]
+| LAST_VALUE    | Find the value with the largest timestamp.                                                                                                           | All data types           | Consistent with input data type                                                                                                                                                                                                                                                                          [...]
+| MAX_TIME      | Find the maximum timestamp.                                                                                                                          | All data Types           | Timestamp                                                                                                                                                                                                                                                                                                [...]
+| MIN_TIME      | Find the minimum timestamp.                                                                                                                          | All data Types           | Timestamp                                                                                                                                                                                                                                                                                                [...]
+| COUNT_IF      | Find the number of data points that continuously meet a given condition and the number of data points that meet the condition (represented by keep) meet the specified threshold. | BOOLEAN                  | `[keep >=/>/=/!=/</<=]threshold`:The specified threshold or threshold condition, it is equivalent to `keep >= threshold` if `threshold` is used alone, type of `threshold` is `INT64`<br/> `ignoreNull`:Optional, default value is `true`;If the value is `true`, null valu [...]
+| TIME_DURATION | Find the difference between the timestamp of the largest non-null value and the timestamp of the smallest non-null value in a column               |  All data Types   | INT64                                                                                                                                                                                                                                                                                                             [...]
 
 ## COUNT
 
@@ -123,4 +124,57 @@ Result:
 +------------------------------------------------------------------------+------------------------------------------------------------------------+
 |                                                                       1|                                                                       1|
 +------------------------------------------------------------------------+------------------------------------------------------------------------+
+```
+
+## TIME_DURATION
+### Grammar
+```sql
+    time_duration(Path)
+```
+### Example
+#### raw data
+```sql
++----------+-------------+
+|      Time|root.db.d1.s1|
++----------+-------------+
+|         1|           70|
+|         3|           10|
+|         4|          303|
+|         6|          110|
+|         7|          302|
+|         8|          110|
+|         9|           60|
+|        10|           70|
+|1677570934|           30|
++----------+-------------+
+```
+#### Insert sql
+```sql
+"CREATE DATABASE root.db",
+"CREATE TIMESERIES root.db.d1.s1 WITH DATATYPE=INT32, ENCODING=PLAIN tags(city=Beijing)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(1, 2, 10, true)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(2, null, 20, true)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(3, 10, 0, null)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(4, 303, 30, true)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(5, null, 20, true)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(6, 110, 20, true)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(7, 302, 20, true)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(8, 110, null, true)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(9, 60, 20, true)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(10,70, 20, null)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(1677570934, 30, 0, true)",
+```
+
+SQL:
+```sql
+select time_duration(s1) from root.db.d1.s1
+```
+
+Result:
+```
++----------------------------+
+|time_duration(root.db.d1.s1)|
++----------------------------+
+|                  1677570933|
++----------------------------+
 ```
\ No newline at end of file
diff --git a/docs/zh/UserGuide/Operators-Functions/Aggregation.md b/docs/zh/UserGuide/Operators-Functions/Aggregation.md
index 0e63b6a0d8..2945d4e453 100644
--- a/docs/zh/UserGuide/Operators-Functions/Aggregation.md
+++ b/docs/zh/UserGuide/Operators-Functions/Aggregation.md
@@ -27,19 +27,20 @@
 
 IoTDB 支持的聚合函数如下:
 
-| 函数名      | 功能描述                                          | 允许的输入类型           | 必要的属性参数                                                                                                                                                                                                              | 输出类型      |
-| ----------- |-----------------------------------------------| ------------------------ |----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------|
-| SUM         | 求和。                                           | INT32 INT64 FLOAT DOUBLE | 无                                                                                                                                                                                                                    | DOUBLE    |
-| COUNT       | 计算数据点数。                                       | 所有类型                 | 无                                                                                                                                                                                                                    | INT64     |
-| AVG         | 求平均值。                                         | INT32 INT64 FLOAT DOUBLE | 无                                                                                                                                                                                                                    | DOUBLE    |
-| EXTREME     | 求具有最大绝对值的值。如果正值和负值的最大绝对值相等,则返回正值。             | INT32 INT64 FLOAT DOUBLE | 无                                                                                                                                                                                                                    | 与输入类型一致   |
-| MAX_VALUE   | 求最大值。                                         | INT32 INT64 FLOAT DOUBLE | 无                                                                                                                                                                                                                    | 与输入类型一致   |
-| MIN_VALUE   | 求最小值。                                         | INT32 INT64 FLOAT DOUBLE | 无                                                                                                                                                                                                                    | 与输入类型一致   |
-| FIRST_VALUE | 求时间戳最小的值。                                     | 所有类型                 | 无                                                                                                                                                                                                                    | 与输入类型一致   |
-| LAST_VALUE  | 求时间戳最大的值。                                     | 所有类型                 | 无                                                                                                                                                                                                                    | 与输入类型一致   |
-| MAX_TIME    | 求最大时间戳。                                       | 所有类型                 | 无                                                                                                                                                                                                                    | Timestamp |
-| MIN_TIME    | 求最小时间戳。                                       | 所有类型                 | 无                                                                                                                                                                                                                    | Timestamp |
-| COUNT_IF    | 求数据点连续满足某一给定条件,且满足条件的数据点个数(用keep表示)满足指定阈值的次数。 | BOOLEAN                  | `[keep >=/>/=/!=/</<=]threshold`:被指定的阈值或阈值条件,若只使用`threshold`则等价于`keep >= threshold`,`threshold`类型为`INT64`<br/> `ignoreNull`:可选,默认为`true`;为`true`表示忽略null值,即如果中间出现null值,直接忽略,不会打断连续性;为`false`表示不忽略null值,即如果中间出现null值,会打断连续性 | INT64     |
+| 函数名           | 功能描述                                          | 允许的输入类型                  | 必要的属性参数                                                                                                                                                                                                                    | 输出类型      |
+|---------------|-----------------------------------------------|--------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------|
+| SUM           | 求和。                                           | INT32 INT64 FLOAT DOUBLE | 无                                                                                                                                                                                                                          | DOUBLE    |
+| COUNT         | 计算数据点数。                                       | 所有类型                     | 无                                                                                                                                                                                                                          | INT64     |
+| AVG           | 求平均值。                                         | INT32 INT64 FLOAT DOUBLE | 无                                                                                                                                                                                                                          | DOUBLE    |
+| EXTREME       | 求具有最大绝对值的值。如果正值和负值的最大绝对值相等,则返回正值。             | INT32 INT64 FLOAT DOUBLE | 无                                                                                                                                                                                                                          | 与输入类型一致   |
+| MAX_VALUE     | 求最大值。                                         | INT32 INT64 FLOAT DOUBLE | 无                                                                                                                                                                                                                          | 与输入类型一致   |
+| MIN_VALUE     | 求最小值。                                         | INT32 INT64 FLOAT DOUBLE | 无                                                                                                                                                                                                                          | 与输入类型一致   |
+| FIRST_VALUE   | 求时间戳最小的值。                                     | 所有类型                     | 无                                                                                                                                                                                                                          | 与输入类型一致   |
+| LAST_VALUE    | 求时间戳最大的值。                                     | 所有类型                     | 无                                                                                                                                                                                                                          | 与输入类型一致   |
+| MAX_TIME      | 求最大时间戳。                                       | 所有类型                     | 无                                                                                                                                                                                                                          | Timestamp |
+| MIN_TIME      | 求最小时间戳。                                       | 所有类型                     | 无                                                                                                                                                                                                                          | Timestamp |
+| COUNT_IF      | 求数据点连续满足某一给定条件,且满足条件的数据点个数(用keep表示)满足指定阈值的次数。 | BOOLEAN                  | `[keep >=/>/=/!=/</<=]threshold`:被指定的阈值或阈值条件,若只使用`threshold`则等价于`keep >= threshold`,`threshold`类型为`INT64`<br/> `ignoreNull`:可选,默认为`true`;为`true`表示忽略null值,即如果中间出现null值,直接忽略,不会打断连续性;为`false`表示不忽略null值,即如果中间出现null值,会打断连续性 | INT64     |
+| TIME_DURATION | 求某一列最大一个不为NULL的值所在时间戳与最小一个不为NULL的值所在时间戳的时间戳差  | 所有类型                     | 无                                                                                                                                                                                                                          |   INT64        |
 
 ### COUNT_IF
 
@@ -104,4 +105,57 @@ select count_if(s1=0 & s2=0, 3, 'ignoreNull'='false'), count_if(s1=1 & s2=0, 3,
 +------------------------------------------------------------------------+------------------------------------------------------------------------+
 |                                                                       1|                                                                       1|
 +------------------------------------------------------------------------+------------------------------------------------------------------------+
+```
+
+## TIME_DURATION
+### 语法
+```sql
+    time_duration(Path)
+```
+### 使用示例
+#### 准备数据
+```
++----------+-------------+
+|      Time|root.db.d1.s1|
++----------+-------------+
+|         1|           70|
+|         3|           10|
+|         4|          303|
+|         6|          110|
+|         7|          302|
+|         8|          110|
+|         9|           60|
+|        10|           70|
+|1677570934|           30|
++----------+-------------+
+```
+#### 写入语句
+```sql
+"CREATE DATABASE root.db",
+"CREATE TIMESERIES root.db.d1.s1 WITH DATATYPE=INT32, ENCODING=PLAIN tags(city=Beijing)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(1, 2, 10, true)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(2, null, 20, true)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(3, 10, 0, null)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(4, 303, 30, true)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(5, null, 20, true)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(6, 110, 20, true)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(7, 302, 20, true)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(8, 110, null, true)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(9, 60, 20, true)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(10,70, 20, null)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(1677570934, 30, 0, true)",
+```
+
+查询:
+```sql
+select time_duration(s1) from root.db.d1.s1
+```
+
+输出
+```
++----------------------------+
+|time_duration(root.db.d1.s1)|
++----------------------------+
+|                  1677570933|
++----------------------------+
 ```
\ No newline at end of file
diff --git a/integration-test/src/main/java/org/apache/iotdb/itbase/constant/TestConstant.java b/integration-test/src/main/java/org/apache/iotdb/itbase/constant/TestConstant.java
index 86fcefc4a0..1ae9ca784a 100644
--- a/integration-test/src/main/java/org/apache/iotdb/itbase/constant/TestConstant.java
+++ b/integration-test/src/main/java/org/apache/iotdb/itbase/constant/TestConstant.java
@@ -111,6 +111,10 @@ public class TestConstant {
     return String.format("min_value(%s)", path);
   }
 
+  public static String timeDuration(String path) {
+    return String.format("time_duration(%s)", path);
+  }
+
   public static String recordToInsert(TSRecord record) {
     StringBuilder measurements = new StringBuilder();
     StringBuilder values = new StringBuilder();
diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/aggregation/IoTDBAggregationOptimizeIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/aggregation/IoTDBAggregationOptimizeIT.java
index f7573a9163..9f01ab179f 100644
--- a/integration-test/src/test/java/org/apache/iotdb/db/it/aggregation/IoTDBAggregationOptimizeIT.java
+++ b/integration-test/src/test/java/org/apache/iotdb/db/it/aggregation/IoTDBAggregationOptimizeIT.java
@@ -43,6 +43,7 @@ import static org.apache.iotdb.itbase.constant.TestConstant.maxValue;
 import static org.apache.iotdb.itbase.constant.TestConstant.minTime;
 import static org.apache.iotdb.itbase.constant.TestConstant.minValue;
 import static org.apache.iotdb.itbase.constant.TestConstant.sum;
+import static org.apache.iotdb.itbase.constant.TestConstant.timeDuration;
 
 @RunWith(IoTDBTestRunner.class)
 @Category({LocalStandaloneIT.class, ClusterIT.class})
@@ -167,14 +168,15 @@ public class IoTDBAggregationOptimizeIT {
           lastValue("root.test.1region_d1.s1"),
           maxTime("root.test.1region_d1.s1"),
           minTime("root.test.1region_d1.s1"),
-          count("root.test.2region_d1.s1")
+          count("root.test.2region_d1.s1"),
+          timeDuration("root.test.2region_d1.s1")
         };
-    String[] retArray = new String[] {"2,3.0,1.5,2,2,1,1,2,2,1,2,"};
+    String[] retArray = new String[] {"2,3.0,1.5,2,2,1,1,2,2,1,2,999999999999,"};
     resultSetEqualWithDescOrderTest(
         "select count(1region_d1.s1),sum(1region_d1.s1),avg(1region_d1.s1),"
             + "extreme(1region_d1.s1),max_value(1region_d1.s1),min_value(1region_d1.s1),"
             + "first_value(1region_d1.s1),last_value(1region_d1.s1),max_time(1region_d1.s1),"
-            + "min_time(1region_d1.s1),count(2region_d1.s1) from root.test",
+            + "min_time(1region_d1.s1),count(2region_d1.s1),time_duration(2region_d1.s1) from root.test",
         expectedHeader,
         retArray);
   }
diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/aggregation/IoTDBTimeDurationIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/aggregation/IoTDBTimeDurationIT.java
new file mode 100644
index 0000000000..0be9aa7410
--- /dev/null
+++ b/integration-test/src/test/java/org/apache/iotdb/db/it/aggregation/IoTDBTimeDurationIT.java
@@ -0,0 +1,368 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.db.it.aggregation;
+
+import org.apache.iotdb.it.env.EnvFactory;
+import org.apache.iotdb.it.framework.IoTDBTestRunner;
+import org.apache.iotdb.itbase.category.ClusterIT;
+import org.apache.iotdb.itbase.category.LocalStandaloneIT;
+import org.apache.iotdb.rpc.TSStatusCode;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import static org.apache.iotdb.db.it.utils.TestUtils.assertTestFail;
+import static org.apache.iotdb.db.it.utils.TestUtils.prepareData;
+import static org.apache.iotdb.db.it.utils.TestUtils.resultSetEqualTest;
+
+@RunWith(IoTDBTestRunner.class)
+@Category({LocalStandaloneIT.class, ClusterIT.class})
+public class IoTDBTimeDurationIT {
+  // 2 devices 4 regions
+  protected static final String[] SQLs =
+      new String[] {
+        "CREATE DATABASE root.db",
+        "CREATE DATABASE root.db1",
+        "CREATE TIMESERIES root.db.d1.s1 WITH DATATYPE=INT32, ENCODING=PLAIN tags(city=Beijing)",
+        "CREATE TIMESERIES root.db.d1.s2 WITH DATATYPE=INT32, ENCODING=PLAIN tags(city=Nanjing)",
+        "CREATE TIMESERIES root.db.d1.s3 WITH DATATYPE=BOOLEAN, ENCODING=PLAIN tags(city=Nanjing)",
+        "CREATE TIMESERIES root.db.d2.s1 WITH DATATYPE=INT32, ENCODING=PLAIN tags(city=Nanjing)",
+        "CREATE TIMESERIES root.db.d2.s2 WITH DATATYPE=INT32, ENCODING=PLAIN tags(city=Nanjing)",
+        "CREATE TIMESERIES root.db.d2.s3 WITH DATATYPE=BOOLEAN, ENCODING=PLAIN tags(city=Nanjing)",
+        "CREATE TIMESERIES root.db1.d1.s1 WITH DATATYPE=INT32, ENCODING=PLAIN tags(city=Nanjing)",
+        "CREATE TIMESERIES root.db1.d1.s2 WITH DATATYPE=INT32, ENCODING=PLAIN tags(city=Nanjing)",
+        "CREATE TIMESERIES root.db1.d1.s3 WITH DATATYPE=BOOLEAN, ENCODING=PLAIN tags(city=Nanjing)",
+        "CREATE TIMESERIES root.db1.d2.s1 WITH DATATYPE=INT32, ENCODING=PLAIN tags(city=Nanjing)",
+        "CREATE TIMESERIES root.db1.d2.s2 WITH DATATYPE=INT32, ENCODING=PLAIN tags(city=Nanjing)",
+        "CREATE TIMESERIES root.db1.d2.s3 WITH DATATYPE=BOOLEAN, ENCODING=PLAIN tags(city=Nanjing)",
+        "INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(1, 2, 10, true)",
+        "INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(2, null, 20, true)",
+        "INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(3, 10, 0, null)",
+        "INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(4, 303, 30, true)",
+        "INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(5, null, 20, true)",
+        "INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(6, 110, 20, true)",
+        "INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(7, 302, 20, true)",
+        "INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(8, 110, null, true)",
+        "INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(9, 60, 20, true)",
+        "INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(10,70, 20, null)",
+        "INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(1677570934, 30, 0, true)",
+        "INSERT INTO root.db.d2(timestamp,s1,s2,s3) values(1, 80, 30, true)",
+        "INSERT INTO root.db.d2(timestamp,s1,s2,s3) values(2, null, 30, true)",
+        "INSERT INTO root.db.d2(timestamp,s1,s2,s3) values(3, 60, 30, null)",
+        "INSERT INTO root.db.d2(timestamp,s1,s2,s3) values(4, 40, 20, true)",
+        "INSERT INTO root.db.d2(timestamp,s1,s2,s3) values(5, null, 40, true)",
+        "INSERT INTO root.db.d2(timestamp,s1,s2,s3) values(6, 40, 50, true)",
+        "INSERT INTO root.db.d2(timestamp,s1,s2,s3) values(7, 40, 60, true)",
+        "INSERT INTO root.db.d2(timestamp,s1,s2,s3) values(8, 40, null, true)",
+        "INSERT INTO root.db.d2(timestamp,s1,s2,s3) values(9, 50, 70, true)",
+        "INSERT INTO root.db.d2(timestamp,s1,s2,s3) values(10, 60, 80, null)",
+        "INSERT INTO root.db.d2(timestamp,s1,s2,s3) values(1677570934, 90, 90, true)",
+        "INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(1, 70, 90, true)",
+        "INSERT INTO root.db1.d1(timestamp,s1,s2,s3) values(2, null, 80, true)",
+        "INSERT INTO root.db1.d1(timestamp,s1,s2,s3) values(3, 80, 70, null)",
+        "INSERT INTO root.db1.d1(timestamp,s1,s2,s3) values(4, 90, 70, true)",
+        "INSERT INTO root.db1.d1(timestamp,s1,s2,s3) values(5, null, 60, true)",
+        "INSERT INTO root.db1.d1(timestamp,s1,s2,s3) values(6, 20, 100, true)",
+        "INSERT INTO root.db1.d1(timestamp,s1,s2,s3) values(7, 10, 20, true)",
+        "INSERT INTO root.db1.d1(timestamp,s1,s2,s3) values(8, 20, null, true)",
+        "INSERT INTO root.db1.d1(timestamp,s1,s2,s3) values(9, 30, 20, true)",
+        "INSERT INTO root.db1.d1(timestamp,s1,s2,s3) values(10, 10, 20, null)",
+        "INSERT INTO root.db1.d1(timestamp,s1,s2,s3) values(1677570934, 0, 20, true)",
+        "INSERT INTO root.db1.d2(timestamp,s1,s2,s3) values(1, 10, 20, true)",
+        "INSERT INTO root.db1.d2(timestamp,s1,s2,s3) values(2, null, null, true)",
+        "INSERT INTO root.db1.d2(timestamp,s1,s2,s3) values(3, null, 20, null)",
+        "INSERT INTO root.db1.d2(timestamp,s1,s2,s3) values(4, 0, 0, true)",
+        "INSERT INTO root.db1.d2(timestamp,s1,s2,s3) values(5, null, 0, true)",
+        "INSERT INTO root.db1.d2(timestamp,s1,s2,s3) values(6, 0, 0, true)",
+        "INSERT INTO root.db1.d2(timestamp,s1,s2,s3) values(7, 1, 0, true)",
+        "INSERT INTO root.db1.d2(timestamp,s1,s2,s3) values(8, 1, null, true)",
+        "INSERT INTO root.db1.d2(timestamp,s1,s2,s3) values(9, null, 20, true)",
+        "INSERT INTO root.db1.d2(timestamp,s1,s2,s3) values(10, 1, 0, null)",
+        "INSERT INTO root.db1.d2(timestamp,s1,s2,s3) values(1677570939, 1, 0, true)",
+        "flush"
+      };
+
+  @BeforeClass
+  public static void setUp() throws Exception {
+    EnvFactory.getEnv().getConfig().getCommonConfig().setPartitionInterval(1000);
+    EnvFactory.getEnv().initClusterEnvironment();
+    prepareData(SQLs);
+  }
+
+  @AfterClass
+  public static void tearDown() throws Exception {
+    EnvFactory.getEnv().cleanClusterEnvironment();
+  }
+
+  @Test
+  public void testTimeDurationSingleColumnOnOneDataRegion() {
+    // normal
+    String[] expectedHeader = new String[] {"time_duration(root.db.d1.s1)"};
+    String[] retArray = new String[] {"8,"};
+    resultSetEqualTest(
+        "select time_duration(s1) from root.db.d1 where time < 10", expectedHeader, retArray);
+
+    // order by time desc
+    resultSetEqualTest(
+        "select time_duration(s1) from root.db.d1  where time < 10 order by time desc",
+        expectedHeader,
+        retArray);
+
+    // order by time asc
+    resultSetEqualTest(
+        "select time_duration(s1) from root.db.d1  where time < 10 order by time asc",
+        expectedHeader,
+        retArray);
+
+    // limit
+    resultSetEqualTest(
+        "select time_duration(s1) from root.db.d1  where time < 10 limit 3",
+        expectedHeader,
+        retArray);
+
+    // align by device
+    expectedHeader = new String[] {"Device", "time_duration(s1)"};
+    retArray = new String[] {"root.db.d1,8,"};
+    resultSetEqualTest(
+        "select time_duration(s1) from root.db.d1  where time < 10 align by device",
+        expectedHeader,
+        retArray);
+
+    // align by time
+    expectedHeader = new String[] {"time_duration(root.db.d1.s1)"};
+    retArray = new String[] {"8,"};
+    resultSetEqualTest(
+        "select time_duration(s1) from root.db.d1  where time < 10 align by time",
+        expectedHeader,
+        retArray);
+
+    // group by level=1(database)
+    expectedHeader = new String[] {"time_duration(root.db1.*.s1),time_duration(root.db.*.s1)"};
+    retArray = new String[] {"8,8,"};
+    resultSetEqualTest(
+        "select time_duration(s1) from root.** where time < 10 group by level=1",
+        expectedHeader,
+        retArray);
+
+    // group by level=2(device)
+    expectedHeader = new String[] {"time_duration(root.*.d1.s1)", "time_duration(root.*.d2.s1)"};
+    retArray = new String[] {"8,8,"};
+    resultSetEqualTest(
+        "select time_duration(s1) from root.** where time < 10 group by level=2",
+        expectedHeader,
+        retArray);
+
+    // group by session
+    expectedHeader = new String[] {"Time", "time_duration(root.db1.d1.s1)"};
+    retArray = new String[] {"3,6,"};
+    // ignore = true
+    resultSetEqualTest(
+        "select time_duration(s1) from root.db1.d1 where time<10 group by session(100ms)",
+        expectedHeader,
+        retArray);
+
+    // group by variation
+    expectedHeader = new String[] {"Time", "time_duration(root.db1.d2.s1)"};
+    retArray = new String[] {"1,0,", "3,null,", "4,0,", "6,0,", "7,0,", "8,0,", "9,null,"};
+    resultSetEqualTest(
+        "select time_duration(s1) from root.db1.d2 where time<10 group by variation(root.db.d1.s1)",
+        expectedHeader,
+        retArray);
+
+    // group by condition
+    expectedHeader = new String[] {"Time", "time_duration(root.db1.d2.s1)"};
+    retArray = new String[] {"1,9,"};
+    resultSetEqualTest(
+        "select time_duration(s1) from root.db1.d2 group by condition(root.db.d2.s1 > 10,KEEP>=1,ignoreNull=true)",
+        expectedHeader,
+        retArray);
+
+    // group by tags
+    expectedHeader = new String[] {"city", "time_duration(s1)"};
+    retArray = new String[] {"Nanjing,1677570938,"};
+    resultSetEqualTest(
+        "select time_duration(s1) from root.db1.d2 group by tags(city)", expectedHeader, retArray);
+
+    // group by time
+    expectedHeader = new String[] {"Time", "time_duration(root.db1.d2.s1)"};
+    retArray = new String[] {"1,3,", "6,2,"};
+    resultSetEqualTest(
+        "select time_duration(s1) from root.db1.d2 group by time([1,10),5ms)",
+        expectedHeader,
+        retArray);
+  }
+
+  @Test
+  public void testTimeDurationManyColumnOnManyDataRegion() {
+    // normal
+    String[] expectedHeader =
+        new String[] {
+          "time_duration(root.db1.d1.s1),time_duration(root.db1.d2.s1),time_duration(root.db.d1.s1),time_duration(root.db.d2.s1),time_duration(root.db1.d1.s2),time_duration(root.db1.d2.s2),time_duration(root.db.d1.s2),time_duration(root.db.d2.s2)"
+        };
+    String[] retArray =
+        new String[] {
+          "1677570931,1677570938,1677570933,1677570933,1677570932,1677570938,1677570933,1677570933,"
+        };
+    resultSetEqualTest(
+        "select time_duration(s1),time_duration(s2) from root.**", expectedHeader, retArray);
+
+    // order by time desc
+    resultSetEqualTest(
+        "select time_duration(s1),time_duration(s2) from root.** order by time desc",
+        expectedHeader,
+        retArray);
+
+    // order by time asc
+    resultSetEqualTest(
+        "select time_duration(s1),time_duration(s2) from root.** order by time asc",
+        expectedHeader,
+        retArray);
+
+    // limit
+    resultSetEqualTest(
+        "select time_duration(s1),time_duration(s2) from root.** limit 3",
+        expectedHeader,
+        retArray);
+
+    // align by device
+    expectedHeader = new String[] {"Device", "time_duration(s1)", "time_duration(s2)"};
+    retArray =
+        new String[] {
+          "root.db.d1,1677570933,1677570933,",
+          "root.db.d2,1677570933,1677570933,",
+          "root.db1.d1,1677570931,1677570932,",
+          "root.db1.d2,1677570938,1677570938,"
+        };
+    resultSetEqualTest(
+        "select time_duration(s1),time_duration(s2) from root.** align by device",
+        expectedHeader,
+        retArray);
+
+    // align by time
+    expectedHeader =
+        new String[] {
+          "time_duration(root.db1.d1.s1),time_duration(root.db1.d2.s1),time_duration(root.db.d1.s1),time_duration(root.db.d2.s1),time_duration(root.db1.d1.s2),time_duration(root.db1.d2.s2),time_duration(root.db.d1.s2),time_duration(root.db.d2.s2)"
+        };
+    retArray =
+        new String[] {
+          "1677570931,1677570938,1677570933,1677570933,1677570932,1677570938,1677570933,1677570933,"
+        };
+    resultSetEqualTest(
+        "select time_duration(s1),time_duration(s2) from root.** align by time",
+        expectedHeader,
+        retArray);
+
+    // group by level=1(database)
+    expectedHeader =
+        new String[] {
+          "time_duration(root.db1.*.s1),time_duration(root.db.*.s1),time_duration(root.db1.*.s2),time_duration(root.db.*.s2)"
+        };
+    retArray = new String[] {"1677570938,1677570933,1677570938,1677570933,"};
+    resultSetEqualTest(
+        "select time_duration(s1),time_duration(s2) from root.** group by level=1",
+        expectedHeader,
+        retArray);
+
+    // group by level=2(device)
+    expectedHeader =
+        new String[] {
+          "time_duration(root.*.d1.s1),time_duration(root.*.d2.s1),time_duration(root.*.d1.s2),time_duration(root.*.d2.s2)"
+        };
+    retArray = new String[] {"1677570933,1677570938,1677570933,1677570938,"};
+    resultSetEqualTest(
+        "select time_duration(s1),time_duration(s2) from root.** group by level=2",
+        expectedHeader,
+        retArray);
+
+    // group by session
+    expectedHeader =
+        new String[] {
+          "Time,time_duration(root.db1.d1.s1),time_duration(root.db1.d2.s1),time_duration(root.db.d1.s1),time_duration(root.db.d2.s1),time_duration(root.db1.d1.s2),time_duration(root.db1.d2.s2),time_duration(root.db.d1.s2),time_duration(root.db.d2.s2)"
+        };
+    retArray = new String[] {"1,7,9,9,9,8,9,9,9,", "1677570934,0,0,0,0,0,0,0,0,"};
+    // ignore = true
+    resultSetEqualTest(
+        "select time_duration(s1),time_duration(s2) from root.** group by session(100ms)",
+        expectedHeader,
+        retArray);
+
+    // group by variation
+    expectedHeader =
+        new String[] {
+          "Time,time_duration(root.db1.d1.s1),time_duration(root.db1.d2.s1),time_duration(root.db.d1.s1),time_duration(root.db.d2.s1),time_duration(root.db1.d1.s2),time_duration(root.db1.d2.s2),time_duration(root.db.d1.s2),time_duration(root.db.d2.s2)"
+        };
+    retArray =
+        new String[] {
+          "1,null,0,0,0,null,0,0,0,",
+          "3,0,null,0,0,0,0,0,0,",
+          "4,0,0,0,0,0,0,0,0,",
+          "6,0,0,0,0,0,0,0,0,",
+          "7,0,0,0,0,0,0,0,0,",
+          "8,0,0,0,0,null,null,null,null,",
+          "9,0,null,0,0,0,0,0,0,",
+          "10,0,0,0,0,0,0,0,0,",
+          "1677570934,0,null,0,0,0,null,0,0,"
+        };
+    resultSetEqualTest(
+        "select time_duration(s1),time_duration(s2) from root.** group by variation(root.db.d1.s1)",
+        expectedHeader,
+        retArray);
+
+    // group by condition
+    expectedHeader =
+        new String[] {
+          "Time,time_duration(root.db1.d1.s1),time_duration(root.db1.d2.s1),time_duration(root.db.d1.s1),time_duration(root.db.d2.s1),time_duration(root.db1.d1.s2),time_duration(root.db1.d2.s2),time_duration(root.db.d1.s2),time_duration(root.db.d2.s2)"
+        };
+    retArray =
+        new String[] {"1,1677570931,9,1677570933,1677570933,1677570931,9,1677570933,1677570933,"};
+    resultSetEqualTest(
+        "select time_duration(s1),time_duration(s2) from root.** group by condition(root.db.d2.s1 > 10,KEEP>=1,ignoreNull=true)",
+        expectedHeader,
+        retArray);
+
+    // group by tags
+    expectedHeader = new String[] {"city", "time_duration(s1)"};
+    retArray = new String[] {"Beijing,1677570933,", "Nanjing,1677570938,"};
+    resultSetEqualTest(
+        "select time_duration(s1) from root.** group by tags(city)", expectedHeader, retArray);
+
+    // group by time
+    expectedHeader =
+        new String[] {
+          "Time,time_duration(root.db1.d1.s1),time_duration(root.db1.d2.s1),time_duration(root.db.d1.s1),time_duration(root.db.d2.s1),time_duration(root.db1.d1.s2),time_duration(root.db1.d2.s2),time_duration(root.db.d1.s2),time_duration(root.db.d2.s2)"
+        };
+    retArray = new String[] {"1,1,3,3,3,3,4,4,4,", "6,3,2,3,3,3,3,3,3,"};
+    resultSetEqualTest(
+        "select time_duration(s1),time_duration(s2) from root.** group by time([1,10),5ms)",
+        expectedHeader,
+        retArray);
+  }
+
+  @Test
+  public void testTimeDurationWithSlidingWindow() {
+    assertTestFail(
+        "select time_duration(s2) from root.db1.d2 group by ([1,10),5ms,2ms)",
+        TSStatusCode.EXECUTE_STATEMENT_ERROR.getStatusCode()
+            + ": TIME_DURATION with slidingWindow is not supported now");
+  }
+}
diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/BuiltinAggregationFunction.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/BuiltinAggregationFunction.java
index e0977a7eb9..302750fe8f 100644
--- a/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/BuiltinAggregationFunction.java
+++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/BuiltinAggregationFunction.java
@@ -36,7 +36,7 @@ public enum BuiltinAggregationFunction {
   AVG("avg"),
   SUM("sum"),
   COUNT_IF("count_if"),
-  ;
+  TIME_DURATION("time_duration");
 
   private final String functionName;
 
@@ -72,6 +72,7 @@ public enum BuiltinAggregationFunction {
       case "count":
       case "avg":
       case "sum":
+      case "time_duration":
         return true;
       case "count_if":
         return false;
@@ -96,6 +97,7 @@ public enum BuiltinAggregationFunction {
       case "count":
       case "avg":
       case "sum":
+      case "time_duration":
         return true;
       case "count_if":
         return false;
diff --git a/server/src/main/java/org/apache/iotdb/db/constant/SqlConstant.java b/server/src/main/java/org/apache/iotdb/db/constant/SqlConstant.java
index 42404c86c7..d721244f50 100644
--- a/server/src/main/java/org/apache/iotdb/db/constant/SqlConstant.java
+++ b/server/src/main/java/org/apache/iotdb/db/constant/SqlConstant.java
@@ -59,6 +59,7 @@ public class SqlConstant {
   public static final String DIFF = "diff";
 
   public static final String LAST = "last";
+  public static final String TIME_DURATION = "time_duration";
 
   public static String[] getSingleRootArray() {
     return SINGLE_ROOT_ARRAY;
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/AccumulatorFactory.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/AccumulatorFactory.java
index b806962f2d..18e18a47b8 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/AccumulatorFactory.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/AccumulatorFactory.java
@@ -67,6 +67,8 @@ public class AccumulatorFactory {
         return new CountIfAccumulator(
             initKeepEvaluator(inputExpressions.get(1)),
             Boolean.parseBoolean(inputAttributes.getOrDefault("ignoreNull", "true")));
+      case TIME_DURATION:
+        return new TimeDurationAccumulator();
       default:
         throw new IllegalArgumentException("Invalid Aggregation function: " + aggregationType);
     }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/TimeDurationAccumulator.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/TimeDurationAccumulator.java
new file mode 100644
index 0000000000..47633e0dbb
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/TimeDurationAccumulator.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.db.mpp.aggregation;
+
+import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
+import org.apache.iotdb.tsfile.file.metadata.statistics.Statistics;
+import org.apache.iotdb.tsfile.read.common.block.column.Column;
+import org.apache.iotdb.tsfile.read.common.block.column.ColumnBuilder;
+import org.apache.iotdb.tsfile.utils.BitMap;
+
+public class TimeDurationAccumulator implements Accumulator {
+  protected long minTime = Long.MAX_VALUE;
+  protected long maxTime = Long.MIN_VALUE;
+  protected boolean initResult = false;
+
+  @Override
+  public void addInput(Column[] column, BitMap bitMap, int lastIndex) {
+    for (int i = 0; i <= lastIndex; i++) {
+      if (bitMap != null && !bitMap.isMarked(i)) {
+        continue;
+      }
+      if (!column[1].isNull(i)) {
+        initResult = true;
+        updateMaxTime(column[0].getLong(i));
+        updateMinTime(column[0].getLong(i));
+      }
+    }
+  }
+
+  @Override
+  public void addIntermediate(Column[] partialResult) {
+    if (partialResult[0].isNull(0)) {
+      return;
+    }
+    initResult = true;
+    updateMaxTime(partialResult[0].getLong(0));
+    updateMinTime(partialResult[1].getLong(0));
+  }
+
+  @Override
+  public void addStatistics(Statistics statistics) {
+    updateMaxTime(statistics.getEndTime());
+    updateMinTime(statistics.getStartTime());
+  }
+
+  @Override
+  public void setFinal(Column finalResult) {
+    if (finalResult.isNull(0)) {
+      return;
+    }
+    initResult = true;
+    maxTime = finalResult.getLong(0);
+    minTime = 0L;
+  }
+
+  @Override
+  public void outputIntermediate(ColumnBuilder[] tsBlockBuilder) {
+    if (!initResult) {
+      tsBlockBuilder[0].appendNull();
+      tsBlockBuilder[1].appendNull();
+    } else {
+      tsBlockBuilder[0].writeLong(maxTime);
+      tsBlockBuilder[1].writeLong(minTime);
+    }
+  }
+
+  @Override
+  public void outputFinal(ColumnBuilder tsBlockBuilder) {
+    if (!initResult) {
+      tsBlockBuilder.appendNull();
+    } else {
+      tsBlockBuilder.writeLong(maxTime - minTime);
+    }
+  }
+
+  @Override
+  public void reset() {
+    initResult = false;
+    this.maxTime = 0L;
+    this.minTime = Long.MAX_VALUE;
+  }
+
+  @Override
+  public boolean hasFinalResult() {
+    return false;
+  }
+
+  @Override
+  public TSDataType[] getIntermediateType() {
+    return new TSDataType[] {TSDataType.INT64, TSDataType.INT64};
+  }
+
+  @Override
+  public TSDataType getFinalType() {
+    return TSDataType.INT64;
+  }
+
+  protected void updateMaxTime(long curTime) {
+    initResult = true;
+    maxTime = Math.max(maxTime, curTime);
+  }
+
+  protected void updateMinTime(long curTime) {
+    initResult = true;
+    minTime = Math.min(minTime, curTime);
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/slidingwindow/SlidingWindowAggregatorFactory.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/slidingwindow/SlidingWindowAggregatorFactory.java
index 63ea59167c..3ce57b1c1b 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/slidingwindow/SlidingWindowAggregatorFactory.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/slidingwindow/SlidingWindowAggregatorFactory.java
@@ -152,6 +152,8 @@ public class SlidingWindowAggregatorFactory {
             : new EmptyQueueSlidingWindowAggregator(accumulator, inputLocationList, step);
       case COUNT_IF:
         throw new SemanticException("COUNT_IF with slidingWindow is not supported now");
+      case TIME_DURATION:
+        throw new SemanticException("TIME_DURATION with slidingWindow is not supported now");
       default:
         throw new IllegalArgumentException("Invalid Aggregation Type: " + aggregationType);
     }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java
index b54854825f..07aa25ef6e 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java
@@ -2398,6 +2398,7 @@ public class ASTVisitor extends IoTDBSqlParserBaseVisitor<Statement> {
       case SqlConstant.EXTREME:
       case SqlConstant.AVG:
       case SqlConstant.SUM:
+      case SqlConstant.TIME_DURATION:
         checkFunctionExpressionInputSize(
             functionExpression.getExpressionString(),
             functionExpression.getExpressions().size(),
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/parameter/AggregationDescriptor.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/parameter/AggregationDescriptor.java
index 2a09ecc0ae..06a53e16c2 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/parameter/AggregationDescriptor.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/parameter/AggregationDescriptor.java
@@ -141,6 +141,10 @@ public class AggregationDescriptor {
           outputAggregationNames.add(SqlConstant.LAST_VALUE);
           outputAggregationNames.add(SqlConstant.MAX_TIME);
           break;
+        case TIME_DURATION:
+          outputAggregationNames.add(SqlConstant.MAX_TIME);
+          outputAggregationNames.add(SqlConstant.MIN_TIME);
+          break;
         default:
           outputAggregationNames.add(aggregationFuncName);
       }
diff --git a/server/src/main/java/org/apache/iotdb/db/utils/SchemaUtils.java b/server/src/main/java/org/apache/iotdb/db/utils/SchemaUtils.java
index fc8c29ffa0..ce3c341a4f 100644
--- a/server/src/main/java/org/apache/iotdb/db/utils/SchemaUtils.java
+++ b/server/src/main/java/org/apache/iotdb/db/utils/SchemaUtils.java
@@ -124,6 +124,7 @@ public class SchemaUtils {
       case SqlConstant.MIN_TIME:
       case SqlConstant.MAX_TIME:
       case SqlConstant.COUNT:
+      case SqlConstant.TIME_DURATION:
         return TSDataType.INT64;
       case SqlConstant.AVG:
       case SqlConstant.SUM:
@@ -157,6 +158,7 @@ public class SchemaUtils {
       case EXTREME:
       case COUNT:
       case AVG:
+      case TIME_DURATION:
         return true;
       default:
         throw new IllegalArgumentException(
@@ -180,6 +182,8 @@ public class SchemaUtils {
         return Collections.singletonList(TAggregationType.MAX_TIME);
       case AVG:
         return Arrays.asList(TAggregationType.COUNT, TAggregationType.SUM);
+      case TIME_DURATION:
+        return Arrays.asList(TAggregationType.MAX_TIME, TAggregationType.MIN_TIME);
       case SUM:
       case MIN_VALUE:
       case MAX_VALUE:
diff --git a/server/src/main/java/org/apache/iotdb/db/utils/TypeInferenceUtils.java b/server/src/main/java/org/apache/iotdb/db/utils/TypeInferenceUtils.java
index c295d519cc..e655f9b158 100644
--- a/server/src/main/java/org/apache/iotdb/db/utils/TypeInferenceUtils.java
+++ b/server/src/main/java/org/apache/iotdb/db/utils/TypeInferenceUtils.java
@@ -130,6 +130,7 @@ public class TypeInferenceUtils {
       case SqlConstant.MAX_TIME:
       case SqlConstant.COUNT:
       case SqlConstant.COUNT_IF:
+      case SqlConstant.TIME_DURATION:
         return TSDataType.INT64;
       case SqlConstant.MIN_VALUE:
       case SqlConstant.LAST_VALUE:
@@ -166,6 +167,7 @@ public class TypeInferenceUtils {
       case SqlConstant.MAX_TIME:
       case SqlConstant.FIRST_VALUE:
       case SqlConstant.LAST_VALUE:
+      case SqlConstant.TIME_DURATION:
         return;
       case SqlConstant.COUNT_IF:
         if (dataType != TSDataType.BOOLEAN) {
@@ -201,6 +203,7 @@ public class TypeInferenceUtils {
       case SqlConstant.MAX_TIME:
       case SqlConstant.FIRST_VALUE:
       case SqlConstant.LAST_VALUE:
+      case SqlConstant.TIME_DURATION:
         return;
       case SqlConstant.COUNT_IF:
         Expression keepExpression = inputExpressions.get(1);
diff --git a/thrift-commons/src/main/thrift/common.thrift b/thrift-commons/src/main/thrift/common.thrift
index 994316271f..1f3e335d31 100644
--- a/thrift-commons/src/main/thrift/common.thrift
+++ b/thrift-commons/src/main/thrift/common.thrift
@@ -139,7 +139,8 @@ enum TAggregationType {
   MAX_VALUE,
   MIN_VALUE,
   EXTREME,
-  COUNT_IF
+  COUNT_IF,
+  TIME_DURATION
 }
 
 // for MLNode