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页)。
然后在 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 的有一行的查询结果。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· [AI/GPT/综述] AI Agent的设计模式综述
2010-04-30 [翻译] Programming F#