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 4ab546a00ce [IOTDB-5930] Support OrderBy in Last Query
4ab546a00ce is described below

commit 4ab546a00cecd9e4eed3e3b1edeb492654e08a9a
Author: YangCaiyin <[email protected]>
AuthorDate: Wed May 31 13:55:08 2023 +0800

    [IOTDB-5930] Support OrderBy in Last Query
---
 docs/UserGuide/Query-Data/Last-Query.md            |  26 +++-
 docs/UserGuide/Query-Data/Order-By.md              | 165 ++++++++++++++++++--
 docs/zh/UserGuide/Query-Data/Last-Query.md         |  22 ++-
 docs/zh/UserGuide/Query-Data/Order-By.md           | 168 +++++++++++++++++++--
 .../apache/iotdb/db/it/orderBy/IoTDBOrderByIT.java |  92 +++++++++++
 .../apache/iotdb/db/metadata/utils/MetaUtils.java  |  11 +-
 .../apache/iotdb/db/mpp/plan/analyze/Analysis.java |  23 ++-
 .../iotdb/db/mpp/plan/analyze/AnalyzeVisitor.java  |  27 +---
 .../db/mpp/plan/planner/LogicalPlanBuilder.java    |  21 ++-
 .../db/mpp/plan/planner/LogicalPlanVisitor.java    |  27 ++--
 .../db/mpp/plan/planner/OperatorTreeGenerator.java |  17 +--
 .../plan/planner/distribution/SourceRewriter.java  |  36 ++---
 .../plan/node/process/last/LastQueryMergeNode.java |  48 +++---
 .../plan/node/process/last/LastQueryNode.java      |  56 ++++---
 .../plan/statement/component/OrderByComponent.java |   2 +-
 .../db/mpp/plan/statement/crud/QueryStatement.java |  12 +-
 .../db/mpp/plan/plan/QueryLogicalPlanUtil.java     |   6 +-
 .../mpp/plan/plan/distribution/LastQueryTest.java  |   4 +-
 18 files changed, 591 insertions(+), 172 deletions(-)

diff --git a/docs/UserGuide/Query-Data/Last-Query.md 
b/docs/UserGuide/Query-Data/Last-Query.md
index d3c433c5e42..ea963b20fa4 100644
--- a/docs/UserGuide/Query-Data/Last-Query.md
+++ b/docs/UserGuide/Query-Data/Last-Query.md
@@ -7,9 +7,9 @@
     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
@@ -40,10 +40,10 @@ which means: Query and return the last data points of 
timeseries prefixPath.path
     ```
     | Time | timeseries | value | dataType |
     ```
-    
+
     **Note:** The `value` colum will always return the value as `string` and 
thus also has `TSDataType.TEXT`. Therefore, the column `dataType` is returned 
also which contains the _real_ type how the value should be interpreted.
 
-- We can use `ORDER BY TIMESERIES (DESC | ASC)` to specify that the result set 
is sorted in descending/ascending order by timeseries name.
+- We can use `TIME/TIMESERIES/VALUE/DATATYPE (DESC | ASC)` to specify that the 
result set is sorted in descending/ascending order based on a particular 
column. When the value column contains multiple types of data, the sorting is 
based on the string representation of the values.
 
 **Example 1:** get the last point of root.ln.wf01.wt01.status:
 
@@ -72,7 +72,7 @@ Total line number = 2
 It costs 0.002s
 ```
 
-**Example 3:** get the last points of all sensor in root.ln.wf01.wt01, and 
order the result by the timeseries column desc
+**Example 3:** get the last points of all sensor in root.ln.wf01.wt01, and 
order the result by the timeseries column in descending order
 
 ```
 IoTDB> select last * from root.ln.wf01.wt01 order by timeseries desc;
@@ -84,4 +84,18 @@ IoTDB> select last * from root.ln.wf01.wt01 order by 
timeseries desc;
 
+-----------------------------+-----------------------------+---------+--------+
 Total line number = 2
 It costs 0.002s
-```
\ No newline at end of file
+```
+
+**Example 4:** get the last points of all sensor in root.ln.wf01.wt01, and 
order the result by the dataType column in descending order
+
+```
+IoTDB> select last * from root.ln.wf01.wt01 order by dataType desc;
++-----------------------------+-----------------------------+---------+--------+
+|                         Time|                   timeseries|    
value|dataType|
++-----------------------------+-----------------------------+---------+--------+
+|2017-11-07T23:59:00.000+08:00|root.ln.wf01.wt01.temperature|21.067368|  
DOUBLE|
+|2017-11-07T23:59:00.000+08:00|     root.ln.wf01.wt01.status|    false| 
BOOLEAN|
++-----------------------------+-----------------------------+---------+--------+
+Total line number = 2
+It costs 0.002s
+```
diff --git a/docs/UserGuide/Query-Data/Order-By.md 
b/docs/UserGuide/Query-Data/Order-By.md
index 9f451f85ef2..8f5ccbb2a74 100644
--- a/docs/UserGuide/Query-Data/Order-By.md
+++ b/docs/UserGuide/Query-Data/Order-By.md
@@ -7,9 +7,9 @@
     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
@@ -21,13 +21,13 @@
 
 # Order By
 
-## order by in ALIGN BY TIME mode
+## Order by in ALIGN BY TIME mode
 
 The result set of IoTDB is in ALIGN BY TIME mode by default and `ORDER BY 
TIME` clause can also be used to specify the ordering of timestamp. The SQL 
statement is:
 ```sql
 select * from root.ln.** where time <= 2017-11-01T00:01:00 order by time desc;
 ```
-执行结果:
+Results:
 
 ```
 
+-----------------------------+--------------------------+------------------------+-----------------------------+------------------------+
@@ -38,10 +38,9 @@ select * from root.ln.** where time <= 2017-11-01T00:01:00 
order by time desc;
 |1970-01-01T08:00:00.002+08:00|                        v2|                   
false|                         null|                    null|
 |1970-01-01T08:00:00.001+08:00|                        v1|                    
true|                         null|                    null|
 
+-----------------------------+--------------------------+------------------------+-----------------------------+------------------------+
-Total line number = 4
 ```
 
-## order by in ALIGN BY DEVICE mode
+## Order by in ALIGN BY DEVICE mode
 When querying in ALIGN BY DEVICE mode, `ORDER BY` clause can be used to 
specify the ordering of result set.
 
 ALIGN BY DEVICE mode supports four kinds of clauses with two sort keys which 
are `Device` and `Time`.
@@ -74,7 +73,6 @@ The result shows below:
 |2017-11-01T00:00:00.000+08:00|root.ln.wf01.wt01|    null|  true|      25.96|
 |2017-11-01T00:01:00.000+08:00|root.ln.wf01.wt01|    null|  true|      24.36|
 +-----------------------------+-----------------+--------+------+-----------+
-Total line number = 6
 ```
 When `Time` is the main sort key, the result set is sorted by timestamp first, 
then by device name in data points with the same timestamp. The SQL statement 
is:
 ```sql
@@ -92,7 +90,6 @@ The result shows below:
 |2017-11-01T00:01:00.000+08:00|root.ln.wf02.wt02|      v2|  true|       null|
 |2017-11-01T00:01:00.000+08:00|root.ln.wf01.wt01|    null|  true|      24.36|
 +-----------------------------+-----------------+--------+------+-----------+
-Total line number = 6
 ```
 When `ORDER BY` clause is not used, sort in default way, the SQL statement is:
 ```sql
@@ -111,7 +108,6 @@ The result below indicates `ORDER BY DEVICE ASC,TIME ASC` 
is the clause in defau
 |2017-11-01T00:00:00.000+08:00|root.ln.wf02.wt02|      v2|  true|       null|
 |2017-11-01T00:01:00.000+08:00|root.ln.wf02.wt02|      v2|  true|       null|
 +-----------------------------+-----------------+--------+------+-----------+
-Total line number = 6
 ```
 Besides,`ALIGN BY DEVICE` and `ORDER BY` clauses can be used with aggregate 
query,the SQL statement is:
 ```sql
@@ -129,5 +125,152 @@ The result shows below:
 |2017-11-01T00:02:00.000+08:00|root.ln.wf02.wt02|              0|            
0|              null|
 |2017-11-01T00:03:00.000+08:00|root.ln.wf02.wt02|              0|            
0|              null|
 
+-----------------------------+-----------------+---------------+-------------+------------------+
-Total line number = 6
-```
\ No newline at end of file
+```
+## Order by arbitrary expressions
+
+In addition to the predefined keywords "Time" and "Device" in IoTDB, `ORDER 
BY` can also be used to sort by any expressions.
+
+When sorting, `ASC` or `DESC` can be used to specify the sorting order, and 
`NULLS` syntax is supported to specify the priority of NULL values in the 
sorting. By default, `NULLS FIRST` places NULL values at the top of the result, 
and `NULLS LAST` ensures that NULL values appear at the end of the result. If 
not specified in the clause, the default order is ASC with NULLS LAST.
+
+Here are several examples of queries for sorting arbitrary expressions using 
the following data:
+```
++-----------------------------+-------------+-------+-------+--------+-------+
+|                         Time|       Device|   base|  score|   bonus|  total| 
   
++-----------------------------+-------------+-------+-------+--------+-------+
+|1970-01-01T08:00:00.000+08:00|     root.one|     12|   50.0|    45.0|  107.0| 
 
+|1970-01-02T08:00:00.000+08:00|     root.one|     10|   50.0|    45.0|  105.0|
+|1970-01-03T08:00:00.000+08:00|     root.one|      8|   50.0|    45.0|  103.0| 
      
+|1970-01-01T08:00:00.010+08:00|     root.two|      9|   50.0|    15.0|   74.0| 
  
+|1970-01-01T08:00:00.020+08:00|     root.two|      8|   10.0|    15.0|   33.0| 
 
+|1970-01-01T08:00:00.010+08:00|   root.three|      9|   null|    24.0|   33.0| 
   
+|1970-01-01T08:00:00.020+08:00|   root.three|      8|   null|    22.5|   30.5| 
  
+|1970-01-01T08:00:00.030+08:00|   root.three|      7|   null|    23.5|   30.5| 
  
+|1970-01-01T08:00:00.010+08:00|    root.four|      9|   32.0|    45.0|   86.0| 
 
+|1970-01-01T08:00:00.020+08:00|    root.four|      8|   32.0|    45.0|   85.0| 
  
+|1970-01-01T08:00:00.030+08:00|    root.five|      7|   53.0|    44.0|  104.0|
+|1970-01-01T08:00:00.040+08:00|    root.five|      6|   54.0|    42.0|  102.0| 
    
++-----------------------------+-------------+-------+-------+--------+-------+
+```
+When you need to sort the results based on the base score score, you can use 
the following SQL:
+```Sql
+select score from root.** order by score desc align by device
+```
+This will give you the following results:
+
+```
++-----------------------------+---------+-----+
+|                         Time|   Device|score|
++-----------------------------+---------+-----+
+|1970-01-01T08:00:00.040+08:00|root.five| 54.0|
+|1970-01-01T08:00:00.030+08:00|root.five| 53.0|
+|1970-01-01T08:00:00.000+08:00| root.one| 50.0|
+|1970-01-02T08:00:00.000+08:00| root.one| 50.0|
+|1970-01-03T08:00:00.000+08:00| root.one| 50.0|
+|1970-01-01T08:00:00.000+08:00| root.two| 50.0|
+|1970-01-01T08:00:00.010+08:00| root.two| 50.0|
+|1970-01-01T08:00:00.010+08:00|root.four| 32.0|
+|1970-01-01T08:00:00.020+08:00|root.four| 32.0|
+|1970-01-01T08:00:00.020+08:00| root.two| 10.0|
++-----------------------------+---------+-----+
+```
+If you want to sort the results based on the total score, you can use an 
expression in the `ORDER BY` clause to perform the calculation:
+```Sql
+select score,total from root.one order by base+score+bonus desc
+```
+This SQL is equivalent to:
+```Sql
+select score,total from root.one order by total desc
+```
+Here are the results:
+```
++-----------------------------+--------------+--------------+
+|                         Time|root.one.score|root.one.total|
++-----------------------------+--------------+--------------+
+|1970-01-01T08:00:00.000+08:00|          50.0|         107.0|
+|1970-01-02T08:00:00.000+08:00|          50.0|         105.0|
+|1970-01-03T08:00:00.000+08:00|          50.0|         103.0|
++-----------------------------+--------------+--------------+
+```
+If you want to sort the results based on the total score and, in case of tied 
scores, sort by score, base, bonus, and submission time in descending order, 
you can specify multiple layers of sorting using multiple expressions:
+
+```Sql
+select base, score, bonus, total from root.** order by total desc NULLS Last,
+                                  score desc NULLS Last,
+                                  bonus desc NULLS Last,
+                                  time desc align by device
+```
+Here are the results:
+```
++-----------------------------+----------+----+-----+-----+-----+
+|                         Time|    Device|base|score|bonus|total|
++-----------------------------+----------+----+-----+-----+-----+
+|1970-01-01T08:00:00.000+08:00|  root.one|  12| 50.0| 45.0|107.0|
+|1970-01-02T08:00:00.000+08:00|  root.one|  10| 50.0| 45.0|105.0|
+|1970-01-01T08:00:00.030+08:00| root.five|   7| 53.0| 44.0|104.0|
+|1970-01-03T08:00:00.000+08:00|  root.one|   8| 50.0| 45.0|103.0|
+|1970-01-01T08:00:00.040+08:00| root.five|   6| 54.0| 42.0|102.0|
+|1970-01-01T08:00:00.010+08:00| root.four|   9| 32.0| 45.0| 86.0|
+|1970-01-01T08:00:00.020+08:00| root.four|   8| 32.0| 45.0| 85.0|
+|1970-01-01T08:00:00.010+08:00|  root.two|   9| 50.0| 15.0| 74.0|
+|1970-01-01T08:00:00.000+08:00|  root.two|   9| 50.0| 15.0| 74.0|
+|1970-01-01T08:00:00.020+08:00|  root.two|   8| 10.0| 15.0| 33.0|
+|1970-01-01T08:00:00.010+08:00|root.three|   9| null| 24.0| 33.0|
+|1970-01-01T08:00:00.030+08:00|root.three|   7| null| 23.5| 30.5|
+|1970-01-01T08:00:00.020+08:00|root.three|   8| null| 22.5| 30.5|
++-----------------------------+----------+----+-----+-----+-----+
+```
+In the `ORDER BY` clause, you can also use aggregate query expressions. For 
example:
+```Sql
+select min_value(total) from root.** order by min_value(total) asc align by 
device
+```
+This will give you the following results:
+```
++----------+----------------+
+|    Device|min_value(total)|
++----------+----------------+
+|root.three|            30.5|
+|  root.two|            33.0|
+| root.four|            85.0|
+| root.five|           102.0|
+|  root.one|           103.0|
++----------+----------------+
+```
+When specifying multiple columns in the query, the unsorted columns will 
change order along with the rows and sorted columns. The order of rows when the 
sorting columns are the same may vary depending on the specific implementation 
(no fixed order). For example:
+```Sql
+select min_value(total),max_value(base) from root.** order by max_value(total) 
desc align by device
+```
+This will give you the following results:
+·
+```
++----------+----------------+---------------+
+|    Device|min_value(total)|max_value(base)|
++----------+----------------+---------------+
+|  root.one|           103.0|             12|
+| root.five|           102.0|              7|
+| root.four|            85.0|              9|
+|  root.two|            33.0|              9|
+|root.three|            30.5|              9|
++----------+----------------+---------------+
+```
+
+You can use both `ORDER BY DEVICE,TIME` and `ORDER BY EXPRESSION` together. 
For example:
+```Sql
+select score from root.** order by device asc, score desc, time asc align by 
device
+```
+This will give you the following results:
+```
++-----------------------------+---------+-----+
+|                         Time|   Device|score|
++-----------------------------+---------+-----+
+|1970-01-01T08:00:00.040+08:00|root.five| 54.0|
+|1970-01-01T08:00:00.030+08:00|root.five| 53.0|
+|1970-01-01T08:00:00.010+08:00|root.four| 32.0|
+|1970-01-01T08:00:00.020+08:00|root.four| 32.0|
+|1970-01-01T08:00:00.000+08:00| root.one| 50.0|
+|1970-01-02T08:00:00.000+08:00| root.one| 50.0|
+|1970-01-03T08:00:00.000+08:00| root.one| 50.0|
+|1970-01-01T08:00:00.000+08:00| root.two| 50.0|
+|1970-01-01T08:00:00.010+08:00| root.two| 50.0|
+|1970-01-01T08:00:00.020+08:00| root.two| 10.0|
++-----------------------------+---------+-----+
+```
diff --git a/docs/zh/UserGuide/Query-Data/Last-Query.md 
b/docs/zh/UserGuide/Query-Data/Last-Query.md
index 86107e8214c..6390804bfc8 100644
--- a/docs/zh/UserGuide/Query-Data/Last-Query.md
+++ b/docs/zh/UserGuide/Query-Data/Last-Query.md
@@ -7,9 +7,9 @@
     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
@@ -43,7 +43,7 @@ select last <Path> [COMMA <Path>]* from < PrefixPath > [COMMA 
< PrefixPath >]* <
     +----+----------+-----+--------+
     ```
 
-- 可以使用 `ORDER BY TIMESERIES (DESC | ASC)` 指定结果集按照序列名降序/升序排列。
+- 可以使用 `ORDER BY TIME/TIMESERIES/VALUE/DATATYPE (DESC | ASC)` 
指定结果集按照某一列进行降序/升序排列。当值列包含多种类型的数据时,按照字符串类型来排序。
 
 **示例 1:** 查询 root.ln.wf01.wt01.status 的最新数据点
 
@@ -84,4 +84,18 @@ IoTDB> select last * from root.ln.wf01.wt01 order by 
timeseries desc;
 
+-----------------------------+-----------------------------+---------+--------+
 Total line number = 2
 It costs 0.002s
-```
\ No newline at end of file
+```
+
+**示例 4:** 查询 root.ln.wf01.wt01 下所有序列的最新数据点,并按照dataType降序排列。
+
+```
+IoTDB> select last * from root.ln.wf01.wt01 order by dataType desc;
++-----------------------------+-----------------------------+---------+--------+
+|                         Time|                   timeseries|    
value|dataType|
++-----------------------------+-----------------------------+---------+--------+
+|2017-11-07T23:59:00.000+08:00|root.ln.wf01.wt01.temperature|21.067368|  
DOUBLE|
+|2017-11-07T23:59:00.000+08:00|     root.ln.wf01.wt01.status|    false| 
BOOLEAN|
++-----------------------------+-----------------------------+---------+--------+
+Total line number = 2
+It costs 0.002s
+```
diff --git a/docs/zh/UserGuide/Query-Data/Order-By.md 
b/docs/zh/UserGuide/Query-Data/Order-By.md
index 7933072b955..745e9a54242 100644
--- a/docs/zh/UserGuide/Query-Data/Order-By.md
+++ b/docs/zh/UserGuide/Query-Data/Order-By.md
@@ -7,9 +7,9 @@
     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
@@ -19,9 +19,9 @@
 
 -->
 
-## 结果集排序
+# 结果集排序
 
-### 时间对齐模式下的排序
+## 时间对齐模式下的排序
 IoTDB的查询结果集默认按照时间对齐,可以使用`ORDER BY TIME`的子句指定时间戳的排列顺序。示例代码如下:
 ```sql
 select * from root.ln.** where time <= 2017-11-01T00:01:00 order by time desc;
@@ -37,9 +37,8 @@ select * from root.ln.** where time <= 2017-11-01T00:01:00 
order by time desc;
 |1970-01-01T08:00:00.002+08:00|                        v2|                   
false|                         null|                    null|
 |1970-01-01T08:00:00.001+08:00|                        v1|                    
true|                         null|                    null|
 
+-----------------------------+--------------------------+------------------------+-----------------------------+------------------------+
-Total line number = 4
 ```
-### 设备对齐模式下的排序
+## 设备对齐模式下的排序
 当使用`ALIGN BY DEVICE`查询对齐模式下的结果集时,可以使用`ORDER BY`子句对返回的结果集顺序进行规定。
 
 
在设备对齐模式下支持4种排序模式的子句,其中包括两种排序键,`DEVICE`和`TIME`,靠前的排序键为主排序键,每种排序键都支持`ASC`和`DESC`两种排列顺序。
@@ -72,7 +71,6 @@ select * from root.ln.** where time <= 2017-11-01T00:01:00 
order by device desc,
 |2017-11-01T00:00:00.000+08:00|root.ln.wf01.wt01|    null|  true|      25.96|
 |2017-11-01T00:01:00.000+08:00|root.ln.wf01.wt01|    null|  true|      24.36|
 +-----------------------------+-----------------+--------+------+-----------+
-Total line number = 6
 ```
 主排序键为`Time`时,结果集会先按照时间戳进行排序,在时间戳相等时按照设备名排序。
 示例代码如下:
@@ -91,7 +89,6 @@ select * from root.ln.** where time <= 2017-11-01T00:01:00 
order by time asc,dev
 |2017-11-01T00:01:00.000+08:00|root.ln.wf02.wt02|      v2|  true|       null|
 |2017-11-01T00:01:00.000+08:00|root.ln.wf01.wt01|    null|  true|      24.36|
 +-----------------------------+-----------------+--------+------+-----------+
-Total line number = 6
 ```
 当没有显式指定时,主排序键默认为`Device`,排序顺序默认为`ASC`,示例代码如下:
 ```sql
@@ -109,7 +106,6 @@ select * from root.ln.** where time <= 2017-11-01T00:01:00 
align by device;
 |2017-11-01T00:00:00.000+08:00|root.ln.wf02.wt02|      v2|  true|       null|
 |2017-11-01T00:01:00.000+08:00|root.ln.wf02.wt02|      v2|  true|       null|
 +-----------------------------+-----------------+--------+------+-----------+
-Total line number = 6
 ```
 同样,可以在聚合查询中使用`ALIGN BY DEVICE`和`ORDER BY`子句,对聚合后的结果进行排序,示例代码如下所示:
 ```sql
@@ -127,5 +123,155 @@ select count(*) from root.ln.** group by 
((2017-11-01T00:00:00.000+08:00,2017-11
 |2017-11-01T00:02:00.000+08:00|root.ln.wf02.wt02|              0|            
0|              null|
 |2017-11-01T00:03:00.000+08:00|root.ln.wf02.wt02|              0|            
0|              null|
 
+-----------------------------+-----------------+---------------+-------------+------------------+
-Total line number = 6
-```
\ No newline at end of file
+```
+
+## 任意表达式排序
+除了IoTDB中规定的Time,Device关键字外,还可以通过`ORDER BY`子句对指定时间序列中任意列的表达式进行排序。
+
+排序在通过`ASC`,`DESC`指定排序顺序的同时,可以通过`NULLS`语法来指定NULL值在排序中的优先级,`NULLS 
FIRST`默认NULL值在结果集的最上方,`NULLS LAST`则保证NULL值在结果集的最后。如果没有在子句中指定,则默认顺序为`ASC`,`NULLS 
LAST`。
+
+对于如下的数据,将给出几个任意表达式的查询示例供参考:
+```
++-----------------------------+-------------+-------+-------+--------+-------+
+|                         Time|       Device|   base|  score|   bonus|  total| 
   
++-----------------------------+-------------+-------+-------+--------+-------+
+|1970-01-01T08:00:00.000+08:00|     root.one|     12|   50.0|    45.0|  107.0| 
 
+|1970-01-02T08:00:00.000+08:00|     root.one|     10|   50.0|    45.0|  105.0|
+|1970-01-03T08:00:00.000+08:00|     root.one|      8|   50.0|    45.0|  103.0| 
      
+|1970-01-01T08:00:00.010+08:00|     root.two|      9|   50.0|    15.0|   74.0| 
  
+|1970-01-01T08:00:00.020+08:00|     root.two|      8|   10.0|    15.0|   33.0| 
 
+|1970-01-01T08:00:00.010+08:00|   root.three|      9|   null|    24.0|   33.0| 
   
+|1970-01-01T08:00:00.020+08:00|   root.three|      8|   null|    22.5|   30.5| 
  
+|1970-01-01T08:00:00.030+08:00|   root.three|      7|   null|    23.5|   30.5| 
  
+|1970-01-01T08:00:00.010+08:00|    root.four|      9|   32.0|    45.0|   86.0| 
 
+|1970-01-01T08:00:00.020+08:00|    root.four|      8|   32.0|    45.0|   85.0| 
  
+|1970-01-01T08:00:00.030+08:00|    root.five|      7|   53.0|    44.0|  104.0|
+|1970-01-01T08:00:00.040+08:00|    root.five|      6|   54.0|    42.0|  102.0| 
    
++-----------------------------+-------------+-------+-------+--------+-------+
+```
+
+当需要根据基础分数score对结果进行排序时,可以直接使用
+```Sql
+select score from root.** order by score desc align by device
+```
+会得到如下结果
+
+```
++-----------------------------+---------+-----+
+|                         Time|   Device|score|
++-----------------------------+---------+-----+
+|1970-01-01T08:00:00.040+08:00|root.five| 54.0|
+|1970-01-01T08:00:00.030+08:00|root.five| 53.0|
+|1970-01-01T08:00:00.000+08:00| root.one| 50.0|
+|1970-01-02T08:00:00.000+08:00| root.one| 50.0|
+|1970-01-03T08:00:00.000+08:00| root.one| 50.0|
+|1970-01-01T08:00:00.000+08:00| root.two| 50.0|
+|1970-01-01T08:00:00.010+08:00| root.two| 50.0|
+|1970-01-01T08:00:00.010+08:00|root.four| 32.0|
+|1970-01-01T08:00:00.020+08:00|root.four| 32.0|
+|1970-01-01T08:00:00.020+08:00| root.two| 10.0|
++-----------------------------+---------+-----+
+```
+
+当想要根据总分对结果进行排序,可以在order by子句中使用表达式进行计算
+```Sql
+select score,total from root.one order by base+score+bonus desc
+```
+该sql等价于
+```Sql
+select score,total from root.one order by total desc
+```
+得到如下结果
+
+```
++-----------------------------+--------------+--------------+
+|                         Time|root.one.score|root.one.total|
++-----------------------------+--------------+--------------+
+|1970-01-01T08:00:00.000+08:00|          50.0|         107.0|
+|1970-01-02T08:00:00.000+08:00|          50.0|         105.0|
+|1970-01-03T08:00:00.000+08:00|          50.0|         103.0|
++-----------------------------+--------------+--------------+
+```
+而如果要对总分进行排序,且分数相同时依次根据score, base, bonus和提交时间进行排序时,可以通过多个表达式来指定多层排序
+
+```Sql
+select base, score, bonus, total from root.** order by total desc NULLS Last,
+                                  score desc NULLS Last,
+                                  bonus desc NULLS Last,
+                                  time desc align by device
+```
+得到如下结果
+```
++-----------------------------+----------+----+-----+-----+-----+
+|                         Time|    Device|base|score|bonus|total|
++-----------------------------+----------+----+-----+-----+-----+
+|1970-01-01T08:00:00.000+08:00|  root.one|  12| 50.0| 45.0|107.0|
+|1970-01-02T08:00:00.000+08:00|  root.one|  10| 50.0| 45.0|105.0|
+|1970-01-01T08:00:00.030+08:00| root.five|   7| 53.0| 44.0|104.0|
+|1970-01-03T08:00:00.000+08:00|  root.one|   8| 50.0| 45.0|103.0|
+|1970-01-01T08:00:00.040+08:00| root.five|   6| 54.0| 42.0|102.0|
+|1970-01-01T08:00:00.010+08:00| root.four|   9| 32.0| 45.0| 86.0|
+|1970-01-01T08:00:00.020+08:00| root.four|   8| 32.0| 45.0| 85.0|
+|1970-01-01T08:00:00.010+08:00|  root.two|   9| 50.0| 15.0| 74.0|
+|1970-01-01T08:00:00.000+08:00|  root.two|   9| 50.0| 15.0| 74.0|
+|1970-01-01T08:00:00.020+08:00|  root.two|   8| 10.0| 15.0| 33.0|
+|1970-01-01T08:00:00.010+08:00|root.three|   9| null| 24.0| 33.0|
+|1970-01-01T08:00:00.030+08:00|root.three|   7| null| 23.5| 30.5|
+|1970-01-01T08:00:00.020+08:00|root.three|   8| null| 22.5| 30.5|
++-----------------------------+----------+----+-----+-----+-----+
+```
+在order by中同样可以使用聚合查询表达式
+```Sql
+select min_value(total) from root.** order by min_value(total) asc align by 
device
+```
+得到如下结果
+```
++----------+----------------+
+|    Device|min_value(total)|
++----------+----------------+
+|root.three|            30.5|
+|  root.two|            33.0|
+| root.four|            85.0|
+| root.five|           102.0|
+|  root.one|           103.0|
++----------+----------------+
+```
+当在查询中指定多列,未被排序的列会随着行和排序列一起改变顺序,当排序列相同时行的顺序和具体实现有关(没有固定顺序)
+```Sql
+select min_value(total),max_value(base) from root.** order by max_value(total) 
desc align by device
+```
+得到结果如下
+·
+```
++----------+----------------+---------------+
+|    Device|min_value(total)|max_value(base)|
++----------+----------------+---------------+
+|  root.one|           103.0|             12|
+| root.five|           102.0|              7|
+| root.four|            85.0|              9|
+|  root.two|            33.0|              9|
+|root.three|            30.5|              9|
++----------+----------------+---------------+
+```
+
+Order by device, time可以和order by expression共同使用
+```Sql
+select score from root.** order by device asc, score desc, time asc align by 
device
+```
+会得到如下结果
+```
++-----------------------------+---------+-----+
+|                         Time|   Device|score|
++-----------------------------+---------+-----+
+|1970-01-01T08:00:00.040+08:00|root.five| 54.0|
+|1970-01-01T08:00:00.030+08:00|root.five| 53.0|
+|1970-01-01T08:00:00.010+08:00|root.four| 32.0|
+|1970-01-01T08:00:00.020+08:00|root.four| 32.0|
+|1970-01-01T08:00:00.000+08:00| root.one| 50.0|
+|1970-01-02T08:00:00.000+08:00| root.one| 50.0|
+|1970-01-03T08:00:00.000+08:00| root.one| 50.0|
+|1970-01-01T08:00:00.000+08:00| root.two| 50.0|
+|1970-01-01T08:00:00.010+08:00| root.two| 50.0|
+|1970-01-01T08:00:00.020+08:00| root.two| 10.0|
++-----------------------------+---------+-----+
+```
diff --git 
a/integration-test/src/test/java/org/apache/iotdb/db/it/orderBy/IoTDBOrderByIT.java
 
b/integration-test/src/test/java/org/apache/iotdb/db/it/orderBy/IoTDBOrderByIT.java
index bbc859b0255..762b0bb1203 100644
--- 
a/integration-test/src/test/java/org/apache/iotdb/db/it/orderBy/IoTDBOrderByIT.java
+++ 
b/integration-test/src/test/java/org/apache/iotdb/db/it/orderBy/IoTDBOrderByIT.java
@@ -1249,4 +1249,96 @@ public class IoTDBOrderByIT {
       assertEquals("701: Raw data and aggregation hybrid query is not 
supported.", e.getMessage());
     }
   }
+
+  // last query
+  public void testLastQueryOrderBy(String sql, String[][] ans) {
+    try (Connection connection = EnvFactory.getEnv().getConnection();
+        Statement statement = connection.createStatement()) {
+      try (ResultSet resultSet = statement.executeQuery(sql)) {
+        int i = 0;
+        while (resultSet.next()) {
+          String time = resultSet.getString(1);
+          String num = resultSet.getString(2);
+          String value = resultSet.getString(3);
+          String dataType = resultSet.getString(4);
+
+          assertEquals(time, ans[0][i]);
+          assertEquals(num, ans[1][i]);
+          assertEquals(value, ans[2][i]);
+          assertEquals(dataType, ans[3][i]);
+
+          i++;
+        }
+        assertEquals(i, 4);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail();
+    }
+  }
+
+  @Test
+  public void lastQueryOrderBy() {
+    String ans[][] =
+        new String[][] {
+          {"51536000000", "51536000000", "51536000000", "51536000000"},
+          {"root.sg.d.num", "root.sg.d2.num", "root.sg.d.bigNum", 
"root.sg.d2.bigNum"},
+          {"15", "15", "3147483648", "3147483648"},
+          {"INT32", "INT32", "INT64", "INT64"}
+        };
+    String sql = "select last bigNum,num from root.** order by value";
+    testLastQueryOrderBy(sql, ans);
+  }
+
+  @Test
+  public void lastQueryOrderBy2() {
+    String ans[][] =
+        new String[][] {
+          {"51536000000", "51536000000", "51536000000", "51536000000"},
+          {"root.sg.d2.num", "root.sg.d2.bigNum", "root.sg.d.num", 
"root.sg.d.bigNum"},
+          {"15", "3147483648", "15", "3147483648"},
+          {"INT32", "INT64", "INT32", "INT64"}
+        };
+    String sql = "select last bigNum,num from root.** order by timeseries 
desc";
+    testLastQueryOrderBy(sql, ans);
+  }
+
+  @Test
+  public void lastQueryOrderBy3() {
+    String ans[][] =
+        new String[][] {
+          {"51536000000", "51536000000", "51536000000", "51536000000"},
+          {"root.sg.d2.num", "root.sg.d2.bigNum", "root.sg.d.num", 
"root.sg.d.bigNum"},
+          {"15", "3147483648", "15", "3147483648"},
+          {"INT32", "INT64", "INT32", "INT64"}
+        };
+    String sql = "select last bigNum,num from root.** order by timeseries 
desc, value asc";
+    testLastQueryOrderBy(sql, ans);
+  }
+
+  @Test
+  public void lastQueryOrderBy4() {
+    String ans[][] =
+        new String[][] {
+          {"51536000000", "51536000000", "51536000000", "51536000000"},
+          {"root.sg.d2.num", "root.sg.d.num", "root.sg.d2.bigNum", 
"root.sg.d.bigNum"},
+          {"15", "15", "3147483648", "3147483648"},
+          {"INT32", "INT32", "INT64", "INT64"}
+        };
+    String sql = "select last bigNum,num from root.** order by value, 
timeseries desc";
+    testLastQueryOrderBy(sql, ans);
+  }
+
+  @Test
+  public void lastQueryOrderBy5() {
+    String ans[][] =
+        new String[][] {
+          {"51536000000", "51536000000", "51536000000", "51536000000"},
+          {"root.sg.d2.num", "root.sg.d.num", "root.sg.d2.bigNum", 
"root.sg.d.bigNum"},
+          {"15", "15", "3147483648", "3147483648"},
+          {"INT32", "INT32", "INT64", "INT64"}
+        };
+    String sql = "select last bigNum,num from root.** order by datatype, 
timeseries desc";
+    testLastQueryOrderBy(sql, ans);
+  }
 }
diff --git 
a/server/src/main/java/org/apache/iotdb/db/metadata/utils/MetaUtils.java 
b/server/src/main/java/org/apache/iotdb/db/metadata/utils/MetaUtils.java
index 18f58bdb30a..a1b977b8aeb 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/utils/MetaUtils.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/utils/MetaUtils.java
@@ -27,7 +27,6 @@ import org.apache.iotdb.commons.path.PartialPath;
 import org.apache.iotdb.commons.schema.node.IMNode;
 import org.apache.iotdb.commons.utils.TestOnly;
 import 
org.apache.iotdb.db.mpp.plan.planner.plan.parameter.AggregationDescriptor;
-import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.OrderByParameter;
 import org.apache.iotdb.db.mpp.plan.statement.component.Ordering;
 import org.apache.iotdb.tsfile.read.common.Path;
 import org.apache.iotdb.tsfile.utils.Binary;
@@ -118,18 +117,14 @@ public class MetaUtils {
   }
 
   public static List<PartialPath> groupAlignedSeriesWithOrder(
-      List<PartialPath> fullPaths, OrderByParameter orderByParameter) {
+      List<PartialPath> fullPaths, Ordering timeseriesOrdering) {
     Map<String, AlignedPath> deviceToAlignedPathMap = new HashMap<>();
     List<PartialPath> res = groupAlignedSeries(fullPaths, 
deviceToAlignedPathMap);
     res.sort(
-        orderByParameter.getSortItemList().get(0).getOrdering() == Ordering.ASC
-            ? Comparator.naturalOrder()
-            : Comparator.reverseOrder());
+        timeseriesOrdering == Ordering.ASC ? Comparator.naturalOrder() : 
Comparator.reverseOrder());
     // sort the measurements of AlignedPath
     Comparator<Binary> comparator =
-        orderByParameter.getSortItemList().get(0).getOrdering() == Ordering.ASC
-            ? Comparator.naturalOrder()
-            : Comparator.reverseOrder();
+        timeseriesOrdering == Ordering.ASC ? Comparator.naturalOrder() : 
Comparator.reverseOrder();
     for (AlignedPath alignedPath : deviceToAlignedPathMap.values()) {
       alignedPath.sortMeasurement(comparator);
     }
diff --git 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analysis.java 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analysis.java
index db2ae86e9d6..7249d232bfa 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analysis.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analysis.java
@@ -40,6 +40,7 @@ import 
org.apache.iotdb.db.mpp.plan.planner.plan.parameter.GroupByTimeParameter;
 import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.IntoPathDescriptor;
 import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.OrderByParameter;
 import org.apache.iotdb.db.mpp.plan.statement.Statement;
+import org.apache.iotdb.db.mpp.plan.statement.component.Ordering;
 import org.apache.iotdb.db.mpp.plan.statement.component.SortItem;
 import org.apache.iotdb.db.mpp.plan.statement.crud.QueryStatement;
 import org.apache.iotdb.db.mpp.plan.statement.sys.ShowQueriesStatement;
@@ -182,7 +183,7 @@ public class Analysis {
   // one.
   private Set<Expression> orderByExpressions;
 
-  private boolean orderByExpressionInDeviceView = false;
+  private boolean hasSort = false;
 
   // parameter of `FILL` clause
   private FillDescriptor fillDescriptor;
@@ -195,6 +196,10 @@ public class Analysis {
 
   private OrderByParameter mergeOrderParameter;
 
+  // This field will be set and used when the order by in last query only 
indicates the ordering of
+  // timeseries, otherwise it will be null
+  private Ordering timeseriesOrderingForLastQuery = null;
+
   // header of result dataset
   private DatasetHeader respDatasetHeader;
 
@@ -660,12 +665,12 @@ public class Analysis {
     this.deviceToOrderByExpressions = deviceToOrderByExpressions;
   }
 
-  public void setOrderByExpressionInDeviceView(boolean 
orderByExpressionInDeviceView) {
-    this.orderByExpressionInDeviceView = orderByExpressionInDeviceView;
+  public void setHasSort(boolean hasSort) {
+    this.hasSort = hasSort;
   }
 
-  public boolean isOrderByExpressionInDeviceView() {
-    return orderByExpressionInDeviceView;
+  public boolean isHasSort() {
+    return hasSort;
   }
 
   public Map<String, List<SortItem>> getDeviceToSortItems() {
@@ -695,4 +700,12 @@ public class Analysis {
   public List<Pair<Expression, String>> getOutputExpressions() {
     return this.outputExpressions;
   }
+
+  public Ordering getTimeseriesOrderingForLastQuery() {
+    return timeseriesOrderingForLastQuery;
+  }
+
+  public void setTimeseriesOrderingForLastQuery(Ordering 
timeseriesOrderingForLastQuery) {
+    this.timeseriesOrderingForLastQuery = timeseriesOrderingForLastQuery;
+  }
 }
diff --git 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/AnalyzeVisitor.java 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/AnalyzeVisitor.java
index ae5a30ac329..e9c6d7440c9 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/AnalyzeVisitor.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/AnalyzeVisitor.java
@@ -91,7 +91,6 @@ import 
org.apache.iotdb.db.mpp.plan.statement.component.GroupBySessionComponent;
 import org.apache.iotdb.db.mpp.plan.statement.component.GroupByTimeComponent;
 import 
org.apache.iotdb.db.mpp.plan.statement.component.GroupByVariationComponent;
 import org.apache.iotdb.db.mpp.plan.statement.component.IntoComponent;
-import org.apache.iotdb.db.mpp.plan.statement.component.OrderByKey;
 import org.apache.iotdb.db.mpp.plan.statement.component.Ordering;
 import org.apache.iotdb.db.mpp.plan.statement.component.ResultColumn;
 import org.apache.iotdb.db.mpp.plan.statement.component.SortItem;
@@ -182,7 +181,6 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.TimeZone;
-import java.util.TreeSet;
 import java.util.stream.Collectors;
 
 import static com.google.common.base.Preconditions.checkState;
@@ -427,23 +425,7 @@ public class AnalyzeVisitor extends 
StatementVisitor<Analysis, MPPQueryContext>
       Analysis analysis, List<Expression> selectExpressions, ISchemaTree 
schemaTree) {
     Set<Expression> sourceExpressions;
 
-    OrderByParameter orderByParameter = analysis.getMergeOrderParameter();
-    if (orderByParameter != null && 
!orderByParameter.getSortItemList().isEmpty()) {
-      List<SortItem> sortItemList = orderByParameter.getSortItemList();
-      checkState(
-          sortItemList.size() == 1
-              && 
sortItemList.get(0).getSortKey().equals(OrderByKey.TIMESERIES),
-          "Last queries only support sorting by timeseries now.");
-      boolean isAscending = sortItemList.get(0).getOrdering() == Ordering.ASC;
-      sourceExpressions =
-          new TreeSet<>(
-              (e1, e2) ->
-                  isAscending
-                      ? e1.toString().compareTo(e2.toString())
-                      : e2.toString().compareTo(e1.toString()));
-    } else {
-      sourceExpressions = new LinkedHashSet<>();
-    }
+    sourceExpressions = new LinkedHashSet<>();
 
     for (Expression selectExpression : selectExpressions) {
       sourceExpressions.addAll(
@@ -1276,7 +1258,12 @@ public class AnalyzeVisitor extends 
StatementVisitor<Analysis, MPPQueryContext>
 
   // For last query
   private void analyzeOrderBy(Analysis analysis, QueryStatement 
queryStatement) {
-    analysis.setMergeOrderParameter(new 
OrderByParameter(queryStatement.getSortItemList()));
+    if (!queryStatement.hasOrderBy()) return;
+
+    if (queryStatement.onlyOrderByTimeseries()) {
+      analysis.setTimeseriesOrderingForLastQuery(
+          queryStatement.getOrderByComponent().getTimeseriesOrder());
+    }
   }
 
   private void analyzeOrderBy(
diff --git 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanBuilder.java
 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanBuilder.java
index 3bc06b12940..ee6aac29077 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanBuilder.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanBuilder.java
@@ -203,9 +203,7 @@ public class LogicalPlanBuilder {
   }
 
   public LogicalPlanBuilder planLast(
-      Set<Expression> sourceExpressions,
-      Filter globalTimeFilter,
-      OrderByParameter mergeOrderParameter) {
+      Set<Expression> sourceExpressions, Filter globalTimeFilter, Ordering 
timeseriesOrdering) {
     List<PlanNode> sourceNodeList = new ArrayList<>();
     List<PartialPath> selectedPaths =
         sourceExpressions.stream()
@@ -213,9 +211,9 @@ public class LogicalPlanBuilder {
             .collect(Collectors.toList());
 
     List<PartialPath> groupedPaths =
-        mergeOrderParameter.getSortItemList().isEmpty()
+        timeseriesOrdering == null
             ? MetaUtils.groupAlignedSeries(selectedPaths)
-            : MetaUtils.groupAlignedSeriesWithOrder(selectedPaths, 
mergeOrderParameter);
+            : MetaUtils.groupAlignedSeriesWithOrder(selectedPaths, 
timeseriesOrdering);
     for (PartialPath path : groupedPaths) {
       if (path instanceof MeasurementPath) { // non-aligned series
         sourceNodeList.add(
@@ -233,7 +231,7 @@ public class LogicalPlanBuilder {
             context.getQueryId().genPlanNodeId(),
             sourceNodeList,
             globalTimeFilter,
-            mergeOrderParameter);
+            timeseriesOrdering);
     ColumnHeaderConstant.lastQueryColumnHeaders.forEach(
         columnHeader ->
             context
@@ -1215,6 +1213,17 @@ public class LogicalPlanBuilder {
     return this;
   }
 
+  public LogicalPlanBuilder planOrderBy(List<SortItem> sortItemList) {
+    if (sortItemList.isEmpty()) {
+      return this;
+    }
+
+    this.root =
+        new SortNode(
+            context.getQueryId().genPlanNodeId(), root, new 
OrderByParameter(sortItemList));
+    return this;
+  }
+
   public LogicalPlanBuilder planOrderBy(
       Set<Expression> orderByExpressions, List<SortItem> sortItemList) {
 
diff --git 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanVisitor.java
 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanVisitor.java
index cdc17b753b9..14a49611613 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanVisitor.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanVisitor.java
@@ -48,7 +48,6 @@ import 
org.apache.iotdb.db.mpp.plan.planner.plan.node.write.InsertRowsNode;
 import 
org.apache.iotdb.db.mpp.plan.planner.plan.node.write.InsertRowsOfOneDeviceNode;
 import org.apache.iotdb.db.mpp.plan.planner.plan.node.write.InsertTabletNode;
 import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.AggregationStep;
-import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.OrderByParameter;
 import org.apache.iotdb.db.mpp.plan.statement.StatementNode;
 import org.apache.iotdb.db.mpp.plan.statement.StatementVisitor;
 import org.apache.iotdb.db.mpp.plan.statement.component.Ordering;
@@ -117,14 +116,19 @@ public class LogicalPlanVisitor extends 
StatementVisitor<PlanNode, MPPQueryConte
     LogicalPlanBuilder planBuilder = new LogicalPlanBuilder(analysis, context);
 
     if (queryStatement.isLastQuery()) {
-      return planBuilder
-          .planLast(
-              analysis.getSourceExpressions(),
-              analysis.getGlobalTimeFilter(),
-              analysis.getMergeOrderParameter())
-          .planOffset(queryStatement.getRowOffset())
-          .planLimit(queryStatement.getRowLimit())
-          .getRoot();
+      planBuilder =
+          planBuilder
+              .planLast(
+                  analysis.getSourceExpressions(),
+                  analysis.getGlobalTimeFilter(),
+                  analysis.getTimeseriesOrderingForLastQuery())
+              .planOffset(queryStatement.getRowOffset())
+              .planLimit(queryStatement.getRowLimit());
+
+      if (queryStatement.hasOrderBy() && 
!queryStatement.onlyOrderByTimeseries()) {
+        planBuilder = 
planBuilder.planOrderBy(queryStatement.getSortItemList());
+      }
+      return planBuilder.getRoot();
     }
 
     if (queryStatement.isAlignByDevice()) {
@@ -524,10 +528,7 @@ public class LogicalPlanVisitor extends 
StatementVisitor<PlanNode, MPPQueryConte
         && 0 != analysis.getDataPartitionInfo().getDataPartitionMap().size()) {
       PlanNode lastPlanNode =
           new LogicalPlanBuilder(analysis, context)
-              .planLast(
-                  analysis.getSourceExpressions(),
-                  analysis.getGlobalTimeFilter(),
-                  new OrderByParameter())
+              .planLast(analysis.getSourceExpressions(), 
analysis.getGlobalTimeFilter(), null)
               .getRoot();
       planBuilder = planBuilder.planSchemaQueryOrderByHeat(lastPlanNode);
     }
diff --git 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/OperatorTreeGenerator.java
 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/OperatorTreeGenerator.java
index e98855927e4..baf40fa1ec7 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/OperatorTreeGenerator.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/OperatorTreeGenerator.java
@@ -2211,13 +2211,6 @@ public class OperatorTreeGenerator extends 
PlanVisitor<Operator, LocalExecutionP
   @Override
   public Operator visitLastQuery(LastQueryNode node, LocalExecutionPlanContext 
context) {
 
-    List<SortItem> sortItemList = 
node.getMergeOrderParameter().getSortItemList();
-    checkArgument(
-        sortItemList.isEmpty()
-            || (sortItemList.size() == 1
-                && Objects.equals(sortItemList.get(0).getSortKey(), 
OrderByKey.TIMESERIES)),
-        "Last query only support order by timeseries asc/desc");
-
     context.setLastQueryTimeFilter(node.getTimeFilter());
     
context.setNeedUpdateLastCache(LastQueryUtil.needUpdateCache(node.getTimeFilter()));
 
@@ -2232,8 +2225,8 @@ public class OperatorTreeGenerator extends 
PlanVisitor<Operator, LocalExecutionP
         context.getCachedLastValueAndPathList();
 
     int initSize = cachedLastValueAndPathList != null ? 
cachedLastValueAndPathList.size() : 0;
-    // no order by clause
-    if (sortItemList.isEmpty()) {
+    // no need to order by timeseries at first
+    if (!node.needOrderByTimeseries()) {
       TsBlockBuilder builder = LastQueryUtil.createTsBlockBuilder(initSize);
       for (int i = 0; i < initSize; i++) {
         TimeValuePair timeValuePair = cachedLastValueAndPathList.get(i).left;
@@ -2256,7 +2249,7 @@ public class OperatorTreeGenerator extends 
PlanVisitor<Operator, LocalExecutionP
     } else {
       // order by timeseries
       Comparator<Binary> comparator =
-          sortItemList.get(0).getOrdering() == Ordering.ASC
+          node.getTimeseriesOrdering() == Ordering.ASC
               ? ASC_BINARY_COMPARATOR
               : DESC_BINARY_COMPARATOR;
       // sort values from last cache
@@ -2301,9 +2294,9 @@ public class OperatorTreeGenerator extends 
PlanVisitor<Operator, LocalExecutionP
                 node.getPlanNodeId(),
                 LastQueryMergeOperator.class.getSimpleName());
 
-    List<SortItem> items = node.getMergeOrderParameter().getSortItemList();
+    Ordering timeseriesOrdering = node.getTimeseriesOrdering();
     Comparator<Binary> comparator =
-        (items.isEmpty() || items.get(0).getOrdering() == Ordering.ASC)
+        (timeseriesOrdering == null || timeseriesOrdering == Ordering.ASC)
             ? ASC_BINARY_COMPARATOR
             : DESC_BINARY_COMPARATOR;
 
diff --git 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/distribution/SourceRewriter.java
 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/distribution/SourceRewriter.java
index 03cc0fba950..5b4cd4312be 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/distribution/SourceRewriter.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/distribution/SourceRewriter.java
@@ -61,10 +61,7 @@ import 
org.apache.iotdb.db.mpp.plan.planner.plan.node.source.SourceNode;
 import 
org.apache.iotdb.db.mpp.plan.planner.plan.parameter.AggregationDescriptor;
 import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.AggregationStep;
 import 
org.apache.iotdb.db.mpp.plan.planner.plan.parameter.CrossSeriesAggregationDescriptor;
-import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.OrderByParameter;
-import org.apache.iotdb.db.mpp.plan.statement.component.OrderByKey;
 import org.apache.iotdb.db.mpp.plan.statement.component.Ordering;
-import org.apache.iotdb.db.mpp.plan.statement.component.SortItem;
 import org.apache.iotdb.db.mpp.plan.statement.crud.QueryStatement;
 
 import java.util.ArrayList;
@@ -196,7 +193,7 @@ public class SourceRewriter extends 
SimplePlanNodeRewriter<DistributionPlanConte
       return deviceViewNodeList;
     }
 
-    if (analysis.isOrderByExpressionInDeviceView()) {
+    if (analysis.isHasSort()) {
       return deviceViewNodeList;
     }
 
@@ -272,9 +269,7 @@ public class SourceRewriter extends 
SimplePlanNodeRewriter<DistributionPlanConte
   @Override
   public List<PlanNode> visitSort(SortNode node, DistributionPlanContext 
context) {
 
-    if (node.getChild() instanceof DeviceViewNode) {
-      analysis.setOrderByExpressionInDeviceView(true);
-    }
+    analysis.setHasSort(true);
 
     List<PlanNode> children = rewrite(node.getChild(), context);
     if (children.size() == 1) {
@@ -422,9 +417,7 @@ public class SourceRewriter extends 
SimplePlanNodeRewriter<DistributionPlanConte
       LastQueryScanNode node, DistributionPlanContext context) {
     LastQueryNode mergeNode =
         new LastQueryNode(
-            context.queryContext.getQueryId().genPlanNodeId(),
-            node.getPartitionTimeFilter(),
-            new OrderByParameter());
+            context.queryContext.getQueryId().genPlanNodeId(), 
node.getPartitionTimeFilter(), null);
     return processRawSeriesScan(node, context, mergeNode);
   }
 
@@ -433,9 +426,7 @@ public class SourceRewriter extends 
SimplePlanNodeRewriter<DistributionPlanConte
       AlignedLastQueryScanNode node, DistributionPlanContext context) {
     LastQueryNode mergeNode =
         new LastQueryNode(
-            context.queryContext.getQueryId().genPlanNodeId(),
-            node.getPartitionTimeFilter(),
-            new OrderByParameter());
+            context.queryContext.getQueryId().genPlanNodeId(), 
node.getPartitionTimeFilter(), null);
     return processRawSeriesScan(node, context, mergeNode);
   }
 
@@ -570,11 +561,8 @@ public class SourceRewriter extends 
SimplePlanNodeRewriter<DistributionPlanConte
     if (context.queryMultiRegion) {
       PlanNode newRoot = genLastQueryRootNode(node, context);
       // add sort op for each if we add LastQueryMergeNode as root
-      if (newRoot instanceof LastQueryMergeNode && 
node.getMergeOrderParameter().isEmpty()) {
-        OrderByParameter orderByParameter =
-            new OrderByParameter(
-                Collections.singletonList(new SortItem(OrderByKey.TIMESERIES, 
Ordering.ASC)));
-        addSortForEachLastQueryNode(root, orderByParameter);
+      if (newRoot instanceof LastQueryMergeNode && 
!node.needOrderByTimeseries()) {
+        addSortForEachLastQueryNode(root, Ordering.ASC);
       }
       root.getChildren().forEach(newRoot::addChild);
       return Collections.singletonList(newRoot);
@@ -583,12 +571,12 @@ public class SourceRewriter extends 
SimplePlanNodeRewriter<DistributionPlanConte
     }
   }
 
-  private void addSortForEachLastQueryNode(PlanNode root, OrderByParameter 
orderByParameter) {
+  private void addSortForEachLastQueryNode(PlanNode root, Ordering 
timeseriesOrdering) {
     if (root instanceof LastQueryNode
         && (root.getChildren().get(0) instanceof LastQueryScanNode
             || root.getChildren().get(0) instanceof AlignedLastQueryScanNode)) 
{
       LastQueryNode lastQueryNode = (LastQueryNode) root;
-      lastQueryNode.setMergeOrderParameter(orderByParameter);
+      lastQueryNode.setTimeseriesOrdering(timeseriesOrdering);
       // sort children node
       lastQueryNode.setChildren(
           lastQueryNode.getChildren().stream()
@@ -617,15 +605,17 @@ public class SourceRewriter extends 
SimplePlanNodeRewriter<DistributionPlanConte
               });
     } else {
       for (PlanNode child : root.getChildren()) {
-        addSortForEachLastQueryNode(child, orderByParameter);
+        addSortForEachLastQueryNode(child, timeseriesOrdering);
       }
     }
   }
 
   private PlanNode genLastQueryRootNode(LastQueryNode node, 
DistributionPlanContext context) {
     PlanNodeId id = context.queryContext.getQueryId().genPlanNodeId();
-    if (context.oneSeriesInMultiRegion || 
!node.getMergeOrderParameter().isEmpty()) {
-      return new LastQueryMergeNode(id, node.getMergeOrderParameter());
+    // if the series is from multi regions or order by clause only refer to 
timeseries, use
+    // LastQueryMergeNode
+    if (context.oneSeriesInMultiRegion || node.needOrderByTimeseries()) {
+      return new LastQueryMergeNode(id, node.getTimeseriesOrdering());
     }
     return new LastQueryCollectNode(id);
   }
diff --git 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/process/last/LastQueryMergeNode.java
 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/process/last/LastQueryMergeNode.java
index 08829e3c92b..7cca2dd7038 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/process/last/LastQueryMergeNode.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/process/last/LastQueryMergeNode.java
@@ -23,7 +23,8 @@ import 
org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId;
 import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeType;
 import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanVisitor;
 import 
org.apache.iotdb.db.mpp.plan.planner.plan.node.process.MultiChildProcessNode;
-import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.OrderByParameter;
+import org.apache.iotdb.db.mpp.plan.statement.component.Ordering;
+import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils;
 
 import java.io.DataOutputStream;
 import java.io.IOException;
@@ -37,17 +38,16 @@ public class LastQueryMergeNode extends 
MultiChildProcessNode {
 
   // The result output order, which could sort by sensor and time.
   // The size of this list is 2 and the first SortItem in this list has higher 
priority.
-  private final OrderByParameter mergeOrderParameter;
+  private final Ordering timeseriesOrdering;
 
-  public LastQueryMergeNode(PlanNodeId id, OrderByParameter 
mergeOrderParameter) {
+  public LastQueryMergeNode(PlanNodeId id, Ordering timeseriesOrdering) {
     super(id);
-    this.mergeOrderParameter = mergeOrderParameter;
+    this.timeseriesOrdering = timeseriesOrdering;
   }
 
-  public LastQueryMergeNode(
-      PlanNodeId id, List<PlanNode> children, OrderByParameter 
mergeOrderParameter) {
+  public LastQueryMergeNode(PlanNodeId id, List<PlanNode> children, Ordering 
timeseriesOrdering) {
     super(id, children);
-    this.mergeOrderParameter = mergeOrderParameter;
+    this.timeseriesOrdering = timeseriesOrdering;
   }
 
   @Override
@@ -62,7 +62,7 @@ public class LastQueryMergeNode extends MultiChildProcessNode 
{
 
   @Override
   public PlanNode clone() {
-    return new LastQueryMergeNode(getPlanNodeId(), mergeOrderParameter);
+    return new LastQueryMergeNode(getPlanNodeId(), timeseriesOrdering);
   }
 
   @Override
@@ -78,7 +78,7 @@ public class LastQueryMergeNode extends MultiChildProcessNode 
{
   @Override
   public String toString() {
     return String.format(
-        "LastQueryMergeNode-%s:[OrderByParameter: %s]", this.getPlanNodeId(), 
mergeOrderParameter);
+        "LastQueryMergeNode-%s:[OrderByParameter: %s]", this.getPlanNodeId(), 
timeseriesOrdering);
   }
 
   @Override
@@ -93,12 +93,12 @@ public class LastQueryMergeNode extends 
MultiChildProcessNode {
       return false;
     }
     LastQueryMergeNode that = (LastQueryMergeNode) o;
-    return mergeOrderParameter.equals(that.mergeOrderParameter);
+    return timeseriesOrdering.equals(that.timeseriesOrdering);
   }
 
   @Override
   public int hashCode() {
-    return Objects.hash(super.hashCode(), mergeOrderParameter);
+    return Objects.hash(super.hashCode(), timeseriesOrdering);
   }
 
   @Override
@@ -109,19 +109,33 @@ public class LastQueryMergeNode extends 
MultiChildProcessNode {
   @Override
   protected void serializeAttributes(ByteBuffer byteBuffer) {
     PlanNodeType.LAST_QUERY_MERGE.serialize(byteBuffer);
-    mergeOrderParameter.serializeAttributes(byteBuffer);
+    if (timeseriesOrdering == null) {
+      ReadWriteIOUtils.write((byte) 0, byteBuffer);
+    } else {
+      ReadWriteIOUtils.write((byte) 1, byteBuffer);
+      ReadWriteIOUtils.write(timeseriesOrdering.ordinal(), byteBuffer);
+    }
   }
 
   @Override
   protected void serializeAttributes(DataOutputStream stream) throws 
IOException {
     PlanNodeType.LAST_QUERY_MERGE.serialize(stream);
-    mergeOrderParameter.serializeAttributes(stream);
+    if (timeseriesOrdering == null) {
+      ReadWriteIOUtils.write((byte) 0, stream);
+    } else {
+      ReadWriteIOUtils.write((byte) 1, stream);
+      ReadWriteIOUtils.write(timeseriesOrdering.ordinal(), stream);
+    }
   }
 
   public static LastQueryMergeNode deserialize(ByteBuffer byteBuffer) {
-    OrderByParameter mergeOrderParameter = 
OrderByParameter.deserialize(byteBuffer);
+    byte needOrderByTimeseries = ReadWriteIOUtils.readByte(byteBuffer);
+    Ordering timeseriesOrdering = null;
+    if (needOrderByTimeseries == 1) {
+      timeseriesOrdering = 
Ordering.values()[ReadWriteIOUtils.readInt(byteBuffer)];
+    }
     PlanNodeId planNodeId = PlanNodeId.deserialize(byteBuffer);
-    return new LastQueryMergeNode(planNodeId, mergeOrderParameter);
+    return new LastQueryMergeNode(planNodeId, timeseriesOrdering);
   }
 
   @Override
@@ -129,7 +143,7 @@ public class LastQueryMergeNode extends 
MultiChildProcessNode {
     this.children = children;
   }
 
-  public OrderByParameter getMergeOrderParameter() {
-    return mergeOrderParameter;
+  public Ordering getTimeseriesOrdering() {
+    return timeseriesOrdering;
   }
 }
diff --git 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/process/last/LastQueryNode.java
 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/process/last/LastQueryNode.java
index 16a2eab96ca..2128a866806 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/process/last/LastQueryNode.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/node/process/last/LastQueryNode.java
@@ -23,7 +23,7 @@ import 
org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeId;
 import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanNodeType;
 import org.apache.iotdb.db.mpp.plan.planner.plan.node.PlanVisitor;
 import 
org.apache.iotdb.db.mpp.plan.planner.plan.node.process.MultiChildProcessNode;
-import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.OrderByParameter;
+import org.apache.iotdb.db.mpp.plan.statement.component.Ordering;
 import org.apache.iotdb.tsfile.read.filter.basic.Filter;
 import org.apache.iotdb.tsfile.read.filter.factory.FilterFactory;
 import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils;
@@ -42,24 +42,24 @@ public class LastQueryNode extends MultiChildProcessNode {
 
   private final Filter timeFilter;
 
-  // The result output order, which could sort by sensor and time.
-  // The size of this list is 2 and the first SortItem in this list has higher 
priority.
-  private OrderByParameter mergeOrderParameter;
+  // the ordering of timeseries in the result of last query
+  // which is set to null if there is no need to sort
+  private Ordering timeseriesOrdering;
 
-  public LastQueryNode(PlanNodeId id, Filter timeFilter, OrderByParameter 
mergeOrderParameter) {
+  public LastQueryNode(PlanNodeId id, Filter timeFilter, @Nullable Ordering 
timeseriesOrdering) {
     super(id);
     this.timeFilter = timeFilter;
-    this.mergeOrderParameter = mergeOrderParameter;
+    this.timeseriesOrdering = timeseriesOrdering;
   }
 
   public LastQueryNode(
       PlanNodeId id,
       List<PlanNode> children,
       Filter timeFilter,
-      OrderByParameter mergeOrderParameter) {
+      @Nullable Ordering timeseriesOrdering) {
     super(id, children);
     this.timeFilter = timeFilter;
-    this.mergeOrderParameter = mergeOrderParameter;
+    this.timeseriesOrdering = timeseriesOrdering;
   }
 
   @Override
@@ -74,7 +74,7 @@ public class LastQueryNode extends MultiChildProcessNode {
 
   @Override
   public PlanNode clone() {
-    return new LastQueryNode(getPlanNodeId(), timeFilter, mergeOrderParameter);
+    return new LastQueryNode(getPlanNodeId(), timeFilter, timeseriesOrdering);
   }
 
   @Override
@@ -105,12 +105,12 @@ public class LastQueryNode extends MultiChildProcessNode {
     }
     LastQueryNode that = (LastQueryNode) o;
     return Objects.equals(timeFilter, that.timeFilter)
-        && mergeOrderParameter.equals(that.mergeOrderParameter);
+        && timeseriesOrdering.equals(that.timeseriesOrdering);
   }
 
   @Override
   public int hashCode() {
-    return Objects.hash(super.hashCode(), timeFilter, mergeOrderParameter);
+    return Objects.hash(super.hashCode(), timeFilter, timeseriesOrdering);
   }
 
   @Override
@@ -127,7 +127,12 @@ public class LastQueryNode extends MultiChildProcessNode {
       ReadWriteIOUtils.write((byte) 1, byteBuffer);
       timeFilter.serialize(byteBuffer);
     }
-    mergeOrderParameter.serializeAttributes(byteBuffer);
+    if (timeseriesOrdering == null) {
+      ReadWriteIOUtils.write((byte) 0, byteBuffer);
+    } else {
+      ReadWriteIOUtils.write((byte) 1, byteBuffer);
+      ReadWriteIOUtils.write(timeseriesOrdering.ordinal(), byteBuffer);
+    }
   }
 
   @Override
@@ -139,7 +144,12 @@ public class LastQueryNode extends MultiChildProcessNode {
       ReadWriteIOUtils.write((byte) 1, stream);
       timeFilter.serialize(stream);
     }
-    mergeOrderParameter.serializeAttributes(stream);
+    if (timeseriesOrdering == null) {
+      ReadWriteIOUtils.write((byte) 0, stream);
+    } else {
+      ReadWriteIOUtils.write((byte) 1, stream);
+      ReadWriteIOUtils.write(timeseriesOrdering.ordinal(), stream);
+    }
   }
 
   public static LastQueryNode deserialize(ByteBuffer byteBuffer) {
@@ -147,9 +157,13 @@ public class LastQueryNode extends MultiChildProcessNode {
     if (ReadWriteIOUtils.readByte(byteBuffer) == 1) {
       timeFilter = FilterFactory.deserialize(byteBuffer);
     }
-    OrderByParameter mergeOrderParameter = 
OrderByParameter.deserialize(byteBuffer);
+    byte needOrderByTimeseries = ReadWriteIOUtils.readByte(byteBuffer);
+    Ordering timeseriesOrdering = null;
+    if (needOrderByTimeseries == 1) {
+      timeseriesOrdering = 
Ordering.values()[ReadWriteIOUtils.readInt(byteBuffer)];
+    }
     PlanNodeId planNodeId = PlanNodeId.deserialize(byteBuffer);
-    return new LastQueryNode(planNodeId, timeFilter, mergeOrderParameter);
+    return new LastQueryNode(planNodeId, timeFilter, timeseriesOrdering);
   }
 
   @Override
@@ -162,11 +176,15 @@ public class LastQueryNode extends MultiChildProcessNode {
     return timeFilter;
   }
 
-  public OrderByParameter getMergeOrderParameter() {
-    return mergeOrderParameter;
+  public Ordering getTimeseriesOrdering() {
+    return timeseriesOrdering;
+  }
+
+  public void setTimeseriesOrdering(Ordering timeseriesOrdering) {
+    this.timeseriesOrdering = timeseriesOrdering;
   }
 
-  public void setMergeOrderParameter(OrderByParameter mergeOrderParameter) {
-    this.mergeOrderParameter = mergeOrderParameter;
+  public boolean needOrderByTimeseries() {
+    return timeseriesOrdering != null;
   }
 }
diff --git 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/OrderByComponent.java
 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/OrderByComponent.java
index 3606b93c024..cd5b4a59cb9 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/OrderByComponent.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/OrderByComponent.java
@@ -106,7 +106,7 @@ public class OrderByComponent extends StatementNode {
   }
 
   public Ordering getTimeseriesOrder() {
-    checkState(timeOrderPriority != -1, "The timeseries order is not 
specified.");
+    checkState(timeseriesOrderPriority != -1, "The timeseries order is not 
specified.");
     return sortItemList.get(timeseriesOrderPriority).getOrdering();
   }
 
diff --git 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/crud/QueryStatement.java
 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/crud/QueryStatement.java
index 2dd2a129f8b..3b7fe31c517 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/crud/QueryStatement.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/crud/QueryStatement.java
@@ -38,7 +38,6 @@ import 
org.apache.iotdb.db.mpp.plan.statement.component.GroupByTimeComponent;
 import org.apache.iotdb.db.mpp.plan.statement.component.HavingCondition;
 import org.apache.iotdb.db.mpp.plan.statement.component.IntoComponent;
 import org.apache.iotdb.db.mpp.plan.statement.component.OrderByComponent;
-import org.apache.iotdb.db.mpp.plan.statement.component.OrderByKey;
 import org.apache.iotdb.db.mpp.plan.statement.component.Ordering;
 import org.apache.iotdb.db.mpp.plan.statement.component.ResultColumn;
 import org.apache.iotdb.db.mpp.plan.statement.component.ResultSetFormat;
@@ -356,6 +355,10 @@ public class QueryStatement extends Statement {
     return orderByComponent != null && orderByComponent.isOrderByTimeseries();
   }
 
+  public boolean onlyOrderByTimeseries() {
+    return isOrderByTimeseries() && orderByComponent.getSortItemList().size() 
== 1;
+  }
+
   public boolean isOrderByDevice() {
     return orderByComponent != null && orderByComponent.isOrderByDevice();
   }
@@ -585,10 +588,6 @@ public class QueryStatement extends Statement {
     }
 
     if (isLastQuery()) {
-      if (getSortItemList().size() == 1
-          && 
!getSortItemList().get(0).getSortKey().equals(OrderByKey.TIMESERIES)) {
-        throw new SemanticException("Last query only support sorting by 
timeseries now.");
-      }
       if (isAlignByDevice()) {
         throw new SemanticException("Last query doesn't support align by 
device.");
       }
@@ -605,9 +604,6 @@ public class QueryStatement extends Statement {
         throw new SemanticException(
             "Sorting by device is only supported in ALIGN BY DEVICE queries.");
       }
-      if (isOrderByTime()) {
-        throw new SemanticException("Sorting by time is not yet supported in 
last queries.");
-      }
       if (seriesLimit != 0 || seriesOffset != 0) {
         throw new SemanticException("SLIMIT and SOFFSET can not be used in 
LastQuery.");
       }
diff --git 
a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/QueryLogicalPlanUtil.java
 
b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/QueryLogicalPlanUtil.java
index 5571dbb2d33..f2b670a0b0a 100644
--- 
a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/QueryLogicalPlanUtil.java
+++ 
b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/QueryLogicalPlanUtil.java
@@ -141,11 +141,7 @@ public class QueryLogicalPlanUtil {
 
     LastQueryNode lastQueryNode =
         new LastQueryNode(
-            queryId.genPlanNodeId(),
-            sourceNodeList,
-            TimeFilter.gt(100),
-            new OrderByParameter(
-                Collections.singletonList(new SortItem(OrderByKey.TIMESERIES, 
Ordering.ASC))));
+            queryId.genPlanNodeId(), sourceNodeList, TimeFilter.gt(100), 
Ordering.ASC);
 
     querySQLs.add(sql);
     sqlToPlanMap.put(sql, lastQueryNode);
diff --git 
a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/distribution/LastQueryTest.java
 
b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/distribution/LastQueryTest.java
index 390314205cf..a32b673c801 100644
--- 
a/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/distribution/LastQueryTest.java
+++ 
b/server/src/test/java/org/apache/iotdb/db/mpp/plan/plan/distribution/LastQueryTest.java
@@ -35,7 +35,6 @@ import 
org.apache.iotdb.db.mpp.plan.planner.plan.node.process.last.LastQueryMerg
 import 
org.apache.iotdb.db.mpp.plan.planner.plan.node.process.last.LastQueryNode;
 import 
org.apache.iotdb.db.mpp.plan.planner.plan.node.source.AlignedLastQueryScanNode;
 import org.apache.iotdb.db.mpp.plan.planner.plan.node.source.LastQueryScanNode;
-import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.OrderByParameter;
 
 import org.junit.Assert;
 import org.junit.Test;
@@ -209,8 +208,7 @@ public class LastQueryTest {
     }
 
     PlanNode root =
-        new LastQueryNode(
-            context.getQueryId().genPlanNodeId(), sourceNodeList, null, new 
OrderByParameter());
+        new LastQueryNode(context.getQueryId().genPlanNodeId(), 
sourceNodeList, null, null);
     return new LogicalQueryPlan(context, root);
   }
 }

Reply via email to