You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by qi...@apache.org on 2020/02/27 05:46:55 UTC

[incubator-iotdb] branch master updated: [IOTDB-429] return empty dataset instead of throw exception (#838)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new bcd6c22  [IOTDB-429] return empty dataset instead of throw exception (#838)
bcd6c22 is described below

commit bcd6c22a96c21725c85c18bce862c49ca15b2593
Author: Jialin Qiao <qj...@mails.tsinghua.edu.cn>
AuthorDate: Thu Feb 27 13:46:44 2020 +0800

    [IOTDB-429] return empty dataset instead of throw exception (#838)
    
    * return empty dataset instead of throw exception
---
 .../2-DML (Data Manipulation Language).md          | 23 ++++++++-
 .../5-Operation Manual/4-SQL Reference.md          | 19 +++----
 .../2-DML (Data Manipulation Language).md          | 19 ++++++-
 .../5-Operation Manual/4-SQL Reference.md          |  6 ++-
 .../main/java/org/apache/iotdb/JDBCExample.java    |  2 +-
 .../org/apache/iotdb/jdbc/IoTDBResultMetadata.java |  7 +--
 .../apache/iotdb/jdbc/IoTDBResultMetadataTest.java | 30 ++++++-----
 .../apache/iotdb/db/qp/executor/PlanExecutor.java  |  6 ++-
 .../iotdb/db/qp/strategy/PhysicalGenerator.java    |  6 ---
 .../iotdb/db/integration/IoTDBSimpleQueryTest.java | 59 ++++++++++++++++++++++
 .../tsfile/read/query/dataset/EmptyDataSet.java    | 39 ++++++++++++++
 11 files changed, 176 insertions(+), 40 deletions(-)

diff --git a/docs/Documentation-CHN/UserGuide/5-Operation Manual/2-DML (Data Manipulation Language).md b/docs/Documentation-CHN/UserGuide/5-Operation Manual/2-DML (Data Manipulation Language).md
index 0bfaa96..872df0f 100644
--- a/docs/Documentation-CHN/UserGuide/5-Operation Manual/2-DML (Data Manipulation Language).md	
+++ b/docs/Documentation-CHN/UserGuide/5-Operation Manual/2-DML (Data Manipulation Language).md	
@@ -20,7 +20,9 @@
 -->
 
 # 第5章 IoTDB操作指南
+
 ## DML (数据操作语言)
+
 ## 数据接入
 
 IoTDB为用户提供多种插入实时数据的方式,例如在[Cli/Shell工具](/#/Documents/progress/chap4/sec1)中直接输入插入数据的INSERT语句,或使用Java API(标准[Java JDBC](/#/Documents/progress/chap4/sec2)接口)单条或批量执行插入数据的INSERT语句。
@@ -28,11 +30,13 @@ IoTDB为用户提供多种插入实时数据的方式,例如在[Cli/Shell工
 本节主要为您介绍实时数据接入的INSERT语句在场景中的实际使用示例,有关INSERT SQL语句的详细语法请参见本文[INSERT语句](/#/Documents/progress/chap5/sec4)节。
 
 ### 使用INSERT语句
+
 使用INSERT语句可以向指定的已经创建的一条或多条时间序列中插入数据。对于每一条数据,均由一个时间戳类型的时间戳和一个数值或布尔值、字符串类型的传感器采集值组成。
 
 在本节的场景实例下,以其中的两个时间序列`root.ln.wf02.wt02.status`和`root.ln.wf02.wt02.hardware`为例 ,它们的数据类型分别为BOOLEAN和TEXT。
 
 单列数据插入示例代码如下:
+
 ```
 IoTDB > insert into root.ln.wf02.wt02(timestamp,status) values(1,true)
 IoTDB > insert into root.ln.wf02.wt02(timestamp,hardware) values(1, "v1")
@@ -70,16 +74,21 @@ IoTDB > insert into root.ln.wf02.wt02(timestamp, temperature) values(1,"v1")
 ```
 Msg: The resultDataType or encoding or compression of the last node temperature is conflicting in the storage group root.ln
 ```
+
 若用户插入的数据类型与该Timeseries对应的数据类型不一致,例如执行以下命令:
+
 ```
 IoTDB > insert into root.ln.wf02.wt02(timestamp,hardware) values(1,100)
 ```
+
 系统将会返回以下ERROR告知数据类型有误:
+
 ```
 error: The TEXT data type should be covered by " or '
 ```
 
 ## 数据查询
+
 ### 时间切片查询
 
 本节主要介绍时间切片查询的相关示例,主要使用的是[IoTDB SELECT语句](/#/Documents/progress/chap5/sec4)。同时,您也可以使用[Java JDBC](/#/Documents/progress/chap4/sec2)标准接口来执行相关的查询语句。
@@ -91,6 +100,7 @@ SQL语句为:
 ```
 select temperature from root.ln.wf01.wt01 where time < 2017-11-01T00:08:00.000
 ```
+
 其含义为:
 
 被选择的设备为ln集团wf01子站wt01设备;被选择的时间序列为温度传感器(temperature);该语句要求选择出该设备在“2017-11-01T00:08:00.000”(此处可以使用多种时间格式,详情可参看[2.1节](/#/Documents/progress/chap2/sec1))时间点以前的所有温度传感器的值。
@@ -106,6 +116,7 @@ SQL语句为:
 ```
 select status, temperature from root.ln.wf01.wt01 where time > 2017-11-01T00:05:00.000 and time < 2017-11-01T00:12:00.000;
 ```
+
 其含义为:
 
 被选择的设备为ln集团wf01子站wt01设备;被选择的时间序列为供电状态(status)和温度传感器(temperature);该语句要求选择出“2017-11-01T00:05:00.000”至“2017-11-01T00:12:00.000”之间的所选时间序列的值。
@@ -123,6 +134,7 @@ SQL语句为:
 ```
 select status,temperature from root.ln.wf01.wt01 where (time > 2017-11-01T00:05:00.000 and time < 2017-11-01T00:12:00.000) or (time >= 2017-11-01T16:35:00.000 and time <= 2017-11-01T16:37:00.000);
 ```
+
 其含义为:
 
 被选择的设备为ln集团wf01子站wt01设备;被选择的时间序列为“供电状态(status)”和“温度传感器(temperature)”;该语句指定了两个不同的时间区间,分别为“2017-11-01T00:05:00.000至2017-11-01T00:12:00.000”和“2017-11-01T16:35:00.000至2017-11-01T16:37:00.000”;该语句要求选择出满足任一时间区间的被选时间序列的值。
@@ -138,6 +150,7 @@ select status,temperature from root.ln.wf01.wt01 where (time > 2017-11-01T00:05:
 ```
 select wf01.wt01.status,wf02.wt02.hardware from root.ln where (time > 2017-11-01T00:05:00.000 and time < 2017-11-01T00:12:00.000) or (time >= 2017-11-01T16:35:00.000 and time <= 2017-11-01T16:37:00.000);
 ```
+
 其含义为:
 
 被选择的时间序列为“ln集团wf01子站wt01设备的供电状态”以及“ln集团wf02子站wt02设备的硬件版本”;该语句指定了两个时间区间,分别为“2017-11-01T00:05:00.000至2017-11-01T00:12:00.000”和“2017-11-01T16:35:00.000至2017-11-01T16:37:00.000”;该语句要求选择出满足任意时间区间的被选时间序列的值。
@@ -149,7 +162,7 @@ select wf01.wt01.status,wf02.wt02.hardware from root.ln where (time > 2017-11-01
 
 IoTDB支持另外两种结果返回形式: 按设备时间对齐 'align by device' 和 时序不对齐 'disable align'.
 
-'align by device' 对齐方式下,设备ID会单独作为一列出现。在select 子句中写了多少列,最终结果就会有该列数+2 (时间列和设备名字列)。SQL形如:
+'align by device' 对齐方式下,设备ID会单独作为一列出现。在 select 子句中写了多少列,最终结果就会有该列数+2 (时间列和设备名字列)。SQL形如:
 
 ```
 select s1,s2 from root.sg1.* GROUP BY DEVICE
@@ -168,6 +181,8 @@ select s1,s2 from root.sg1.* GROUP BY DEVICE
 IoTDB支持根据时间间隔和自定义的滑动步长(默认值与时间间隔相同,自定义的值必须大于等于时间间隔)对结果集进行划分,默认结果按照时间升序排列。
 同时,您也可以使用Java JDBC标准接口来执行相关的查询语句。
 
+Group By 语句不支持 limit 和 offset。
+
 GROUP BY语句为用户提供三类指定参数:
 
 * 参数1:时间轴显示时间窗参数
@@ -185,6 +200,7 @@ GROUP BY语句为用户提供三类指定参数:
 **图 5.2 三类参数的实际含义**</center>
 
 #### 未指定滑动步长的降频聚合查询
+
 对应的SQL语句是:
 
 ```
@@ -205,6 +221,7 @@ select count(status), max_value(temperature) from root.ln.wf01.wt01 group by ([2
 <center><img style="width:100%; max-width:800px; max-height:600px; margin-left:auto; margin-right:auto; display:block;" src="https://user-images.githubusercontent.com/16079446/69116068-eed51b00-0ac5-11ea-9731-b5a45c5cd224.png"></center>
 
 #### 指定滑动步长的降频聚合查询
+
 对应的SQL语句是:
 
 ```
@@ -230,11 +247,13 @@ select count(status), max_value(temperature) from root.ln.wf01.wt01 group by ([2
 <center><img style="width:100%; max-width:800px; max-height:600px; margin-left:auto; margin-right:auto; display:block;" src="https://user-images.githubusercontent.com/16079446/69116083-f85e8300-0ac5-11ea-84f1-59d934eee96e.png"></center>
 
 #### 带值过滤条件的降频聚合查询
+
 对应的SQL语句是:
 
 ```
 select count(status), max_value(temperature) from root.ln.wf01.wt01 where time > 2017-11-01T01:00:00 and temperature > 20 group by([2017-11-01T00:00:00, 2017-11-07T23:00:00], 3h, 1d);
 ```
+
 这条查询的含义是:
 
 由于用户指定了滑动步长为`1d`,GROUP BY语句执行时将会每次把时间间隔往后移动一天的步长,而不是默认的3小时。
@@ -283,6 +302,7 @@ delete from root.ln.wf02.wt02.status where time<=2017-11-01T16:26:00;
 ```
 delete from root.ln.wf02.wt02 where time <= 2017-11-01T16:26:00;
 ```
+
 或
 
 ```
@@ -290,6 +310,7 @@ delete from root.ln.wf02.wt02.* where time <= 2017-11-01T16:26:00;
 ```
 
 需要注意的是,当删除的路径不存在时,IoTDB会提示路径不存在,无法删除数据,如下所示。
+
 ```
 IoTDB> delete from root.ln.wf03.wt02.status where time < now()
 Msg: TimeSeries does not exist and its data cannot be deleted
diff --git a/docs/Documentation-CHN/UserGuide/5-Operation Manual/4-SQL Reference.md b/docs/Documentation-CHN/UserGuide/5-Operation Manual/4-SQL Reference.md
index d9d62ce..7e07a88 100644
--- a/docs/Documentation-CHN/UserGuide/5-Operation Manual/4-SQL Reference.md	
+++ b/docs/Documentation-CHN/UserGuide/5-Operation Manual/4-SQL Reference.md	
@@ -249,7 +249,7 @@ Note: In Version 0.7.0, if <WhereClause> includes `OR`, time filter can not be u
 Note: There must be a space on both sides of the plus and minus operator appearing in the time expression 
 ```
 
-* Group By语句
+* Group By 语句
 
 ```
 SELECT <SelectClause> FROM <FromClause> WHERE  <WhereClause> GROUP BY <GroupByClause>
@@ -278,7 +278,7 @@ Note: <TimeUnit> needs to be greater than 0
 Note: Third <TimeUnit> if set shouldn't be smaller than second <TimeUnit>
 ```
 
-* Fill语句
+* Fill 语句
 
 ```
 SELECT <SelectClause> FROM <FromClause> WHERE <WhereClause> FILL <FillClause>
@@ -307,7 +307,7 @@ Note: the statement needs to satisfy this constraint: <PrefixPath>(FromClause) +
 Note: Integer in <TimeUnit> needs to be greater than 0
 ```
 
-* Limit语句
+* Limit & SLimit 语句
 
 ```
 SELECT <SelectClause> FROM <FromClause> [WHERE <WhereClause>] [<LIMITClause>] [<SLIMITClause>]
@@ -336,14 +336,15 @@ Note: The order of <LIMITClause> and <SLIMITClause> does not affect the grammati
 Note: <FillClause> can not use <LIMITClause> but not <SLIMITClause>.
 ```
 
-* Group by device语句
+* Align by device语句
+
 ```
-GroupbyDeviceClause : GROUP BY DEVICE
+AlignbyDeviceClause : ALIGN BY DEVICE
 
 规则:  
 1. 大小写不敏感.  
-正例: select * from root.sg1 align by device  
-正例: select * from root.sg1 GROUP BY DEVICE  
+正例: select * from root.sg1 align by device
+正例: select * from root.sg1 ALIGN BY DEVICE
 
 2. AlignbyDeviceClause 只能放在末尾.  
 正例: select * from root.sg1 where time > 10 align by device  
@@ -407,7 +408,8 @@ root.sg1.d0.s0 is INT32 while root.sg2.d3.s0 is FLOAT.
    - select * from root.vehicle where time = 3 Fill(int32[previous, 5ms]) align by device
 ```
 
-* Disable align语句
+* Disable align 语句
+
 ```
 规则:  
 1. 大小写均可.  
@@ -442,7 +444,6 @@ root.sg1.d0.s0 is INT32 while root.sg2.d3.s0 is FLOAT.
    - select * from root.vehicle slimit 10 soffset 2 disable align
    - select * from root.vehicle where time > 10 disable align
 
-
 ```
 
 ### 数据库管理语句
diff --git a/docs/Documentation/UserGuide/5-Operation Manual/2-DML (Data Manipulation Language).md b/docs/Documentation/UserGuide/5-Operation Manual/2-DML (Data Manipulation Language).md
index 4594908..2986d4a 100644
--- a/docs/Documentation/UserGuide/5-Operation Manual/2-DML (Data Manipulation Language).md	
+++ b/docs/Documentation/UserGuide/5-Operation Manual/2-DML (Data Manipulation Language).md	
@@ -83,6 +83,7 @@ error: The TEXT data type should be covered by " or '
 ```
 
 ## SELECT
+
 ### Time Slice Query
 
 This chapter mainly introduces the relevant examples of time slice query using IoTDB SELECT statements. Detailed SQL syntax and usage specifications can be found in [SQL Documentation](/#/Documents/progress/chap5/sec4). You can also use the [Java JDBC](/#/Documents/progress/chap4/sec2) standard interface to execute related queries.
@@ -117,6 +118,7 @@ The execution result of this SQL statement is as follows:
 <center><img style="width:100%; max-width:800px; max-height:600px; margin-left:auto; margin-right:auto; display:block;" src="https://user-images.githubusercontent.com/23614968/61280328-40a12800-a7ea-11e9-85b9-3b8db67673a3.png"></center>
 
 #### Select Multiple Columns of Data for the Same Device According to Multiple Time Intervals
+
 IoTDB supports specifying multiple time interval conditions in a query. Users can combine time interval conditions at will according to their needs. For example, the SQL statement is:
 
 ```
@@ -131,6 +133,7 @@ The execution result of this SQL statement is as follows:
 
 
 #### Choose Multiple Columns of Data for Different Devices According to Multiple Time Intervals
+
 The system supports the selection of data in any column in a query, i.e., the selected columns can come from different devices. For example, the SQL statement is:
 
 ```
@@ -144,6 +147,7 @@ The execution result of this SQL statement is as follows:
 <center><img style="width:100%; max-width:800px; max-height:600px; margin-left:auto; margin-right:auto; display:block;" src="https://user-images.githubusercontent.com/13203019/51577450-dcfe0800-1ef4-11e9-9399-4ba2b2b7fb73.jpg"></center>
 
 ### Down-Frequency Aggregate Query
+
 This section mainly introduces the related examples of down-frequency aggregation query, 
 using the [GROUP BY clause](/#/Documents/progress/chap5/sec4), 
 which is used to partition the result set according to the user's given partitioning conditions and aggregate the partitioned result set. 
@@ -168,6 +172,7 @@ and value filtering conditions specified.
 **Figure 5.2 The actual meanings of the three types of parameters**</center>
 
 #### Down-Frequency Aggregate Query without Specifying the Sliding Step Length
+
 The SQL statement is:
 
 ```
@@ -188,6 +193,7 @@ Since there is data for each time period in the result range to be displayed, th
 <center><img style="width:100%; max-width:800px; max-height:600px; margin-left:auto; margin-right:auto; display:block;" src="https://user-images.githubusercontent.com/16079446/69116068-eed51b00-0ac5-11ea-9731-b5a45c5cd224.png"></center>
 
 #### Down-Frequency Aggregate Query Specifying the Sliding Step Length
+
 The SQL statement is:
 
 ```
@@ -213,6 +219,7 @@ Since there is data for each time period in the result range to be displayed, th
 <center><img style="width:100%; max-width:800px; max-height:600px; margin-left:auto; margin-right:auto; display:block;" src="https://user-images.githubusercontent.com/16079446/69116083-f85e8300-0ac5-11ea-84f1-59d934eee96e.png"></center>
 
 #### Down-Frequency Aggregate Query Specifying the value Filtering Conditions
+
 The SQL statement is:
 
 ```
@@ -237,6 +244,7 @@ The path after SELECT in GROUP BY statement must be aggregate function, otherwis
 <center><img style="width:100%; max-width:800px; max-height:600px; margin-left:auto; margin-right:auto; display:block;" src="https://user-images.githubusercontent.com/16079446/69116099-0b715300-0ac6-11ea-8074-84e04797b8c7.png"></center>
 
 ### Automated Fill
+
 In the actual use of IoTDB, when doing the query operation of timeseries, situations where the value is null at some time points may appear, which will obstruct the further analysis by users. In order to better reflect the degree of data change, users expect missing values to be automatically filled. Therefore, the IoTDB system introduces the function of Automated Fill.
 
 Automated fill function refers to filling empty values according to the user's specified method and effective time range when performing timeseries queries for single or multiple columns. If the queried point's value is not null, the fill function will not work.
@@ -244,6 +252,7 @@ Automated fill function refers to filling empty values according to the user's s
 > Note: In the current version, IoTDB provides users with two methods: Previous and Linear. The previous method fills blanks with previous value. The linear method fills blanks through linear fitting. And the fill function can only be used when performing point-in-time queries.
 
 #### Fill Function
+
 * Previous Function
 
 When the value of the queried timestamp is null, the value of the previous timestamp is used to fill the blank. The formalized previous method is as follows (see Section 7.1.3.6 for detailed syntax):
@@ -311,6 +320,7 @@ On the [sample data](https://raw.githubusercontent.com/apache/incubator-iotdb/ma
 <center><img style="width:100%; max-width:800px; max-height:600px; margin-left:auto; margin-right:auto; display:block;" src="https://user-images.githubusercontent.com/13203019/51577727-d4f29800-1ef5-11e9-8ff3-3bb519da3993.jpg"></center>
 
 #### Correspondence between Data Type and Fill Method
+
 Data types and the supported fill methods are shown in Table 3-6.
 
 <center>**Table 3-6 Data types and the supported fill methods**
@@ -347,11 +357,17 @@ When the fill method is not specified, each data type bears its own default fill
 
 ### Row and Column Control over Query Results
 
-IoTDB provides [LIMIT/SLIMIT](/#/Documents/progress/chap5/sec4) clause and [OFFSET/SOFFSET](/#/Documents/progress/chap5/sec4) clause in order to make users have more control over query results. The use of LIMIT and SLIMIT clauses allows users to control the number of rows and columns of query results, and the use of OFFSET and SOFSET clauses allows users to set the starting position of the results for display.
+IoTDB provides [LIMIT/SLIMIT](/#/Documents/progress/chap5/sec4) clause and [OFFSET/SOFFSET](/#/Documents/progress/chap5/sec4) 
+clause in order to make users have more control over query results. 
+The use of LIMIT and SLIMIT clauses allows users to control the number of rows and columns of query results, 
+and the use of OFFSET and SOFSET clauses allows users to set the starting position of the results for display.
+
+Note that the LIMIT and OFFSET are not supported in group by query.
 
 This chapter mainly introduces related examples of row and column control of query results. You can also use the [Java JDBC](/#/Documents/progress/chap4/sec2) standard interface to execute queries.
 
 #### Row Control over Query Results
+
 By using LIMIT and OFFSET clauses, users can control the query results in a row-related manner. We will demonstrate how to use LIMIT and OFFSET clauses through the following examples.
 
 * Example 1: basic LIMIT clause
@@ -369,7 +385,6 @@ The result is shown below:
 
 <center><img style="width:100%; max-width:800px; max-height:600px; margin-left:auto; margin-right:auto; display:block;" src="https://user-images.githubusercontent.com/13203019/51577752-efc50c80-1ef5-11e9-9071-da2bbd8b9bdd.jpg"></center>
 
-
 * Example 2: LIMIT clause with OFFSET
 
 The SQL statement is:
diff --git a/docs/Documentation/UserGuide/5-Operation Manual/4-SQL Reference.md b/docs/Documentation/UserGuide/5-Operation Manual/4-SQL Reference.md
index 0f5d4b6..0c467a3 100644
--- a/docs/Documentation/UserGuide/5-Operation Manual/4-SQL Reference.md	
+++ b/docs/Documentation/UserGuide/5-Operation Manual/4-SQL Reference.md	
@@ -343,9 +343,10 @@ Note: The order of <LIMITClause> and <SLIMITClause> does not affect the grammati
 Note: <FillClause> can not use <LIMITClause> but not <SLIMITClause>.
 ```
 
-* Group By Device Statement
+* Align By Device Statement
+
 ```
-GroupbyDeviceClause : GROUP BY DEVICE
+AlignbyDeviceClause : ALIGN BY DEVICE
 
 Rules:  
 1. Both uppercase and lowercase are ok.  
@@ -417,6 +418,7 @@ For example, "select s0,s0,s1 from root.sg.* align by device" is not equal to "s
    - select * from root.vehicle where time = 3 Fill(int32[previous, 5ms]) align by device
 ```
 * Disable Align Statement
+
 ```
 Disable Align Clause: DISABLE ALIGN
 
diff --git a/example/jdbc/src/main/java/org/apache/iotdb/JDBCExample.java b/example/jdbc/src/main/java/org/apache/iotdb/JDBCExample.java
index 99a6193..011fba1 100644
--- a/example/jdbc/src/main/java/org/apache/iotdb/JDBCExample.java
+++ b/example/jdbc/src/main/java/org/apache/iotdb/JDBCExample.java
@@ -46,7 +46,7 @@ public class JDBCExample {
       outputResult(resultSet);
       resultSet = statement.executeQuery("select count(*) from root");
       outputResult(resultSet);
-      resultSet = statement.executeQuery("select count(*) from root where time >= 1 and time <= 100 group by ([0, 100], 20ms, 20ms)");
+      resultSet = statement.executeQuery("select count(*) from root where time >= 1 and time <= 100 group by ([0, 100), 20ms, 20ms)");
       outputResult(resultSet);
     }
   }
diff --git a/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBResultMetadata.java b/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBResultMetadata.java
index 25e88bd..b890428 100644
--- a/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBResultMetadata.java
+++ b/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBResultMetadata.java
@@ -59,11 +59,8 @@ public class IoTDBResultMetadata implements ResultSetMetaData {
   }
 
   @Override
-  public int getColumnCount() throws SQLException {
-    if (columnInfoList == null || columnInfoList.isEmpty()) {
-      throw new SQLException("No column exists");
-    }
-    return columnInfoList.size();
+  public int getColumnCount() {
+    return columnInfoList == null ? 0 : columnInfoList.size();
   }
 
   @Override
diff --git a/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBResultMetadataTest.java b/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBResultMetadataTest.java
index 6e9bbda..0664f8e 100644
--- a/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBResultMetadataTest.java
+++ b/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBResultMetadataTest.java
@@ -19,11 +19,14 @@
 package org.apache.iotdb.jdbc;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 
 import java.sql.SQLException;
 import java.sql.Types;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import org.junit.After;
 import org.junit.Before;
@@ -42,7 +45,7 @@ public class IoTDBResultMetadataTest {
   }
 
   @Test
-  public void testGetColumnCount() throws SQLException {
+  public void testGetColumnCount() {
     metadata = new IoTDBResultMetadata(null, null, false);
     boolean flag = false;
     try {
@@ -50,19 +53,20 @@ public class IoTDBResultMetadataTest {
     } catch (Exception e) {
       flag = true;
     }
-    assertEquals(flag, true);
+    assertFalse(flag);
 
-    List<String> columnInfoList = new ArrayList<>();
     flag = false;
     try {
-      metadata = new IoTDBResultMetadata(columnInfoList, null, false);
+      metadata = new IoTDBResultMetadata(Collections.emptyList(), null, false);
       metadata.getColumnCount();
     } catch (Exception e) {
       flag = true;
     }
-    assertEquals(flag, true);
+    assertFalse(flag);
 
+    List<String> columnInfoList = new ArrayList<>();
     columnInfoList.add("root.a.b.c");
+    metadata = new IoTDBResultMetadata(columnInfoList, null, false);
     assertEquals(metadata.getColumnCount(), 1);
   }
 
@@ -75,7 +79,7 @@ public class IoTDBResultMetadataTest {
     } catch (Exception e) {
       flag = true;
     }
-    assertEquals(flag, true);
+    assertTrue(flag);
 
     List<String> columnInfoList = new ArrayList<>();
     metadata = new IoTDBResultMetadata(columnInfoList, null, false);
@@ -85,7 +89,7 @@ public class IoTDBResultMetadataTest {
     } catch (Exception e) {
       flag = true;
     }
-    assertEquals(flag, true);
+    assertTrue(flag);
 
     String[] colums = {"root.a.b.c1", "root.a.b.c2", "root.a.b.c3"};
     metadata = new IoTDBResultMetadata(Arrays.asList(colums), null, false);
@@ -95,7 +99,7 @@ public class IoTDBResultMetadataTest {
     } catch (Exception e) {
       flag = true;
     }
-    assertEquals(flag, true);
+    assertTrue(flag);
 
     flag = false;
     try {
@@ -103,7 +107,7 @@ public class IoTDBResultMetadataTest {
     } catch (Exception e) {
       flag = true;
     }
-    assertEquals(flag, true);
+    assertTrue(flag);
 
     for (int i = 1; i <= colums.length; i++) {
       assertEquals(metadata.getColumnLabel(i), colums[i - 1]);
@@ -120,7 +124,7 @@ public class IoTDBResultMetadataTest {
     } catch (Exception e) {
       flag = true;
     }
-    assertEquals(flag, true);
+    assertTrue(flag);
 
     List<String> columnInfoList = new ArrayList<>();
     metadata = new IoTDBResultMetadata(columnInfoList, null, false);
@@ -130,7 +134,7 @@ public class IoTDBResultMetadataTest {
     } catch (Exception e) {
       flag = true;
     }
-    assertEquals(flag, true);
+    assertTrue(flag);
 
     String[] columns = {"timestamp", "root.a.b.boolean", "root.a.b.int32", "root.a.b.int64",
         "root.a.b.float",
@@ -145,7 +149,7 @@ public class IoTDBResultMetadataTest {
     } catch (Exception e) {
       flag = true;
     }
-    assertEquals(flag, true);
+    assertTrue(flag);
 
     flag = false;
     try {
@@ -153,7 +157,7 @@ public class IoTDBResultMetadataTest {
     } catch (Exception e) {
       flag = true;
     }
-    assertEquals(flag, true);
+    assertTrue(flag);
 
     assertEquals(metadata.getColumnType(1), Types.TIMESTAMP);
     for (int i = 1; i <= types.length; i++) {
diff --git a/server/src/main/java/org/apache/iotdb/db/qp/executor/PlanExecutor.java b/server/src/main/java/org/apache/iotdb/db/qp/executor/PlanExecutor.java
index 8956d4c..01c72a7 100644
--- a/server/src/main/java/org/apache/iotdb/db/qp/executor/PlanExecutor.java
+++ b/server/src/main/java/org/apache/iotdb/db/qp/executor/PlanExecutor.java
@@ -117,6 +117,7 @@ import org.apache.iotdb.tsfile.read.TsFileSequenceReader;
 import org.apache.iotdb.tsfile.read.common.Field;
 import org.apache.iotdb.tsfile.read.common.Path;
 import org.apache.iotdb.tsfile.read.common.RowRecord;
+import org.apache.iotdb.tsfile.read.query.dataset.EmptyDataSet;
 import org.apache.iotdb.tsfile.read.query.dataset.QueryDataSet;
 import org.apache.iotdb.tsfile.utils.Binary;
 import org.apache.iotdb.tsfile.utils.Pair;
@@ -228,7 +229,10 @@ public class PlanExecutor implements IPlanExecutor {
     if (queryPlan instanceof AlignByDevicePlan) {
       queryDataSet = new AlignByDeviceDataSet((AlignByDevicePlan) queryPlan, context, queryRouter);
     } else {
-      if (queryPlan instanceof GroupByPlan) {
+      if (queryPlan.getPaths() == null || queryPlan.getPaths().isEmpty()) {
+        // no time series are selected, return EmptyDataSet
+        return new EmptyDataSet();
+      } else if (queryPlan instanceof GroupByPlan) {
         GroupByPlan groupByPlan = (GroupByPlan) queryPlan;
         return queryRouter.groupBy(groupByPlan, context);
       } else if (queryPlan instanceof AggregationPlan) {
diff --git a/server/src/main/java/org/apache/iotdb/db/qp/strategy/PhysicalGenerator.java b/server/src/main/java/org/apache/iotdb/db/qp/strategy/PhysicalGenerator.java
index ea1897a..49a21c2 100644
--- a/server/src/main/java/org/apache/iotdb/db/qp/strategy/PhysicalGenerator.java
+++ b/server/src/main/java/org/apache/iotdb/db/qp/strategy/PhysicalGenerator.java
@@ -352,12 +352,6 @@ public class PhysicalGenerator {
         measurements.addAll(measurementSetOfGivenSuffix);
       }
 
-      if (measurements.isEmpty()
-          && alignByDevicePlan.getConstMeasurements().isEmpty()
-          && alignByDevicePlan.getNotExistMeasurements().isEmpty()) {
-        throw new QueryProcessException("do not select any existing series");
-      }
-
       // slimit trim on the measurementColumnList
       if (queryOperator.hasSlimit()) {
         int seriesSlimit = queryOperator.getSeriesLimit();
diff --git a/server/src/test/java/org/apache/iotdb/db/integration/IoTDBSimpleQueryTest.java b/server/src/test/java/org/apache/iotdb/db/integration/IoTDBSimpleQueryTest.java
index d50e785..2fb5acf 100644
--- a/server/src/test/java/org/apache/iotdb/db/integration/IoTDBSimpleQueryTest.java
+++ b/server/src/test/java/org/apache/iotdb/db/integration/IoTDBSimpleQueryTest.java
@@ -18,6 +18,8 @@
  */
 package org.apache.iotdb.db.integration;
 
+import static org.junit.Assert.fail;
+
 import java.sql.Connection;
 import java.sql.DriverManager;
 import java.sql.ResultSet;
@@ -26,6 +28,7 @@ import java.sql.Statement;
 import org.apache.iotdb.db.utils.EnvironmentUtils;
 import org.apache.iotdb.jdbc.Config;
 import org.junit.After;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -42,6 +45,62 @@ public class IoTDBSimpleQueryTest {
   }
 
   @Test
+  public void testEmptyDataSet() throws SQLException, ClassNotFoundException {
+    Class.forName(Config.JDBC_DRIVER_NAME);
+    try(Connection connection = DriverManager
+        .getConnection(Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", "root");
+        Statement statement = connection.createStatement()){
+
+      ResultSet resultSet = statement.executeQuery("select * from root");
+      // has an empty time column
+      Assert.assertEquals(1, resultSet.getMetaData().getColumnCount());
+      while(resultSet.next()) {
+        fail();
+      }
+
+      resultSet = statement.executeQuery(
+          "select count(*) from root where time >= 1 and time <= 100 group by ([0, 100), 20ms, 20ms)");
+      // has an empty time column
+      Assert.assertEquals(1, resultSet.getMetaData().getColumnCount());
+      while (resultSet.next()) {
+        fail();
+      }
+
+      resultSet = statement.executeQuery("select count(*) from root");
+      // has no column
+      Assert.assertEquals(0, resultSet.getMetaData().getColumnCount());
+      while(resultSet.next()) {
+        fail();
+      }
+
+      resultSet = statement.executeQuery("select * from root align by device");
+      // has time and device columns
+      Assert.assertEquals(2, resultSet.getMetaData().getColumnCount());
+      while(resultSet.next()) {
+        fail();
+      }
+
+      resultSet = statement.executeQuery("select count(*) from root align by device");
+      // has device column
+      Assert.assertEquals(1, resultSet.getMetaData().getColumnCount());
+      while(resultSet.next()) {
+        fail();
+      }
+
+      resultSet = statement.executeQuery(
+          "select count(*) from root where time >= 1 and time <= 100 "
+              + "group by ([0, 100), 20ms, 20ms) align by device");
+      // has time and device columns
+      Assert.assertEquals(2, resultSet.getMetaData().getColumnCount());
+      while (resultSet.next()) {
+        fail();
+      }
+
+      resultSet.close();
+    }
+  }
+
+  @Test
   public void testUnseqUnsealedDeleteQuery() throws SQLException, ClassNotFoundException {
     Class.forName(Config.JDBC_DRIVER_NAME);
     try(Connection connection = DriverManager
diff --git a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/query/dataset/EmptyDataSet.java b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/query/dataset/EmptyDataSet.java
new file mode 100644
index 0000000..97c7b31
--- /dev/null
+++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/query/dataset/EmptyDataSet.java
@@ -0,0 +1,39 @@
+/*
+ * 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.tsfile.read.query.dataset;
+
+import java.util.Collections;
+import org.apache.iotdb.tsfile.read.common.RowRecord;
+
+public class EmptyDataSet extends QueryDataSet {
+
+  public EmptyDataSet() {
+    super(Collections.emptyList(), Collections.emptyList());
+  }
+
+  @Override
+  protected boolean hasNextWithoutConstraint() {
+    return false;
+  }
+
+  @Override
+  protected RowRecord nextWithoutConstraint() {
+    return null;
+  }
+}