DbCommand.ExecuteScalar 方法的返回值


DbCommand.ExecuteScalar 方法

MSDN 對 DbCommand.ExecuteScalar 方法是這樣描述的:

執行查詢,並返回查詢所返回的結果集中第一行的第一列。 所有其他的列和行將被忽略。
語法: public abstract Object ExecuteScalar()
返回值: 類型: System.Object,結果集中第一行的第一列。
備注: 使用 ExecuteScalar 方法從數據庫中檢索單個值(例如一個聚合值)。 與使用 ExecuteReader 方法然后使用 DbDataReader 返回的數據執行生成單個值所需的操作相比,此操作需要的代碼較少。如果找不到結果集中第一行的第一列;則返回 null 引用(在 Visual Basic 中為 Nothing)。 如果數據庫中的該值為 null,此查詢將返回 DBNull.Value。

准備測試用例

讓我們這實際測試一下吧,首先准備好以下 create-table-Keywords.sql :

1 CREATE TABLE Keywords (
2   keyword_id SERIAL PRIMARY KEY,
3   keyword    VARCHAR(40) NOT NULL,
4   UNIQUE KEY (keyword)
5 )

上面的 SQL 語句來源於《SQL反模式》一書 第17章 可憐人的搜索引擎 第5節 解決方案:使用正確的工具 (第159頁)。

SQL反模式

然后在 openSUSE 12.1 操作系統的 MySQL 5.5.16 數據庫中執行以下 SQL 命令:

ben@vbox:~/work/SQL-Antipatterns> mysql -u test -ppwd-for-test test
mysql> source create-table-Keywords.sql;
Query OK, 0 rows affected (0.18 sec)

mysql> desc Keywords;
+------------+---------------------+------+-----+---------+----------------+
| Field      | Type                | Null | Key | Default | Extra          |
+------------+---------------------+------+-----+---------+----------------+
| keyword_id | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
| keyword    | varchar(40)         | NO   | UNI | NULL    |                |
+------------+---------------------+------+-----+---------+----------------+
2 rows in set (0.01 sec)

mysql> insert into Keywords (keyword) values ('crash');
Query OK, 1 row affected (0.10 sec)

在 MySQL Client 中測試

接着在 MySQL Client 中執行以下三個 SQL select 語句:

mysql> select keyword_id from Keywords where keyword = 'crash';
+------------+
| keyword_id |
+------------+
|          1 |
+------------+
1 row in set (0.01 sec)

mysql> select keyword_id from Keywords where keyword = 'aborted';
Empty set (0.00 sec)

mysql> select max(keyword_id) from Keywords where keyword = 'aborted';
+-----------------+
| max(keyword_id) |
+-----------------+
|            NULL |
+-----------------+
1 row in set (0.01 sec)

mysql>

第一個 select 語句返回一個確實存在的 keyword_id 值,第二個 select 語句返回空結果集,第三個 select 語句返回的結果集中有一行數據,但是其值是 NULL,這是因為 SQL MAX() 函數在起作用。注意由於 Keywords 表的 keyword 列有 unique 索引,上面三個 select 語句返回的結果集中最多只能有一行,不可能有多行。

在 ADO.NET 中測試

我們使用以下 C# 程序 Tester.cs 來測試上述三個 SQL select 語句:

 1 using System;
 2 using MySql.Data.MySqlClient;
 3 
 4 namespace Skyiv.Test
 5 {
 6   static class Tester
 7   {
 8     static void Main()
 9     {
10       Test("SELECT keyword_id FROM Keywords WHERE keyword = 'crash'");
11       Test("SELECT keyword_id FROM Keywords WHERE keyword = 'aborted'");
12       Test("SELECT MAX(keyword_id) FROM Keywords WHERE keyword = 'aborted'");
13     }
14 
15     static void Test(string sql)
16     {
17       using (var conn = new MySqlConnection("server=localhost;user=test;password=pwd-for-test;database=test"))
18       using (var comm = conn.CreateCommand())
19       {
20         conn.Open();
21         comm.CommandText = sql;
22         var result = comm.ExecuteScalar();
23         Console.WriteLine("Type:{0,-13} DBNull:{1,-5} null:{2,-5} Value:[{3}]",
24            (result == null) ? "(null)" : result.GetType().ToString(),
25            result is DBNull, result == null, result);
26       }
27     }
28   }
29 }

使用 Mono 2.10.6 編譯和運行,結果如下所示:

ben@vbox:~/work/SQL-Antipatterns> dmcs Tester.cs -r:/home/ben/repo/dll/MySql.Data.dll && mono Tester.exe
Type:System.UInt64 DBNull:False null:False Value:[1]
Type:(null)        DBNull:False null:True  Value:[]
Type:System.DBNull DBNull:True  null:False Value:[]
ben@vbox:~/work/SQL-Antipatterns>

從上述運行結果中,我們可以看出:

  • 對於第二個 select 語句,DbCommand.ExecuteScalar 方法的返回值是 null,調用者需要使用 result == null 來判斷。
  • 對於第三個 select 語句,DbCommand.ExceuteScalar 方法的返回值是 DBNull.Value,調用者需要用 result is DBNull 或者 result == DBNull.Value 來判斷。

對 ADO.NET 的 DbCommand.ExecuteScalar 方法的調用者來說,select keyword_id 和 select MAX(keyword_id) 都是一樣方便的,只不過要注意根據所使用的 SQL 語句來選擇使用 result == null 還是使用 result is DBNull 來判斷查詢結果是否為空。所以我建議使用 select keyword_id 這種 SQL 語句,以減少一個 SQL MAX() 調用,提高運行效率。

當然,如果使用 result == null || result is DBNull 來判斷查詢結果是否為空就更保險了,能夠適應這兩種 SQL 語句的寫法,但是運行效率就稍微低了一點。

如果是使用 DbCommand.ExcuteReader 方法來獲得查詢結果:

  • 對於第二個 select 語句,需要判斷 DbDataReader.Read 方法的返回值來決定查詢結果是否為空。
  • 對於第三個 select 語句,DbDataReader.Read 方法的返回值總是 true,而是通過 DbDataReader.IsDBNull 方法來判斷查詢結果是否為空。

當然,對於查詢結果最多只有一行一列的情況是不推薦使用 DbCommand.ExcuteReader 方法的。只有查詢結果有可能有多行,或者查詢結果有多列的情況下,才需要使用 DbCommand.ExcuteReader 方法。

《SQL反模式》中的用法

在《SQL反模式》一書第159頁是在以下存儲過程中使用 MAX() 函數的:

 1 CREATE PROCEDURE BugsSearch(keyword VARCHAR(40))
 2 BEGIN
 3   SET @keyword = keyword;
 4   PREPARE s1 FROM 'SELECT MAX(keyword_id) INTO @k FROM Keywords WHERE keyword = ?';
 5   EXECUTE s1 USING @keyword;
 6   DEALLOCATE PREPARE S1;
 7   IF (@k IS NULL) THEN
 8     -- (這里省略若干語句)
 9   END IF;
10   -- (這里再次省略若干語句)
11 END

看來這里的 MAX() 函數是不能省略的,雖然 Keyswords 表的 keyword 列上有 unique 索引,查詢結果中不可能有多行,但是查詢結果可能為空集,所以需要使用 MAX() 函數將空的查詢結果轉換為值為 NULL 的有一行的查詢結果。

參考資料

  1. MSDN: DbCommand.ExecuteScalar 方法 (System.Data.Common)
  2. MSDN: DbCommand.ExecuteReader 方法 (System.Data.Common)
  3. MSDN: DbDataReader.Read 方法 (System.Data.Common)
  4. MSDN: DbDataReader.IsDBNull 方法 (System.Data.Common)
  5. MSDN: DBNull.Value 字段 (System)
  6. 豆瓣: SQL反模式


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM