ExecuteNonQuery():执行 SQL 语句并返回受影响的行数
ExecuteReader():执行 SQL 语句并返回一个 SqlDataReader 对象
ExecuteScalar():执行 SQL 语句并返回查询结果的第一行的第一列的值,忽略其他列或行的值
对于 ExecuteNonQuery()方法,我们已经学习过,是用于实现非查询 SQL 命令的执行。另外,两个
方法,其实都用于查询之中。先看 ExecuteScalar()方法,它是用于执行查询 SQL 命令,并可以返回查
询结果第一行第一列的值
以查询数据表中电影的总数量为例,来看 ExecuteScalar()方法的使用,具体代码如下:
// 查询电影数量的SQL语句 string sql = "select count(*) from FilmInfo"; // 连接字符串 string conStr = "server=.;database=FilmDB;uid=sa;pwd=123456;"; // 创建连接对象 SqlConnection conn = new SqlConnection(conStr); // 打开连接 conn.Open(); // 创建Command对象 SqlCommand cmd = new SqlCommand(sql, conn); // 查询首行首列 int number = (int)cmd.ExecuteScalar(); // 输出结果 Console.WriteLine("一共有{0}部电影!",number); // 关闭连接 conn.Close(); Console.ReadKey();
这个案例中,关键代码为:int number = (int)cmd.ExecuteScalar(),通过 Command 对象来调用
ExecuteScalar()方法得到查询结果中首行首列的值。而查询语句 select count(*) from FilmInfo 只
返回了一个表示数量的整数值,所以适合使用该方法。还有一点,这里对查询的结果进行了强制类型转
换。这是因为 ExecuteScalar()方法默认返回结果为 Object 类型,我们根据查询结果可以转成其它相
应类型。需要注意的是,ExecuteScalar()方法执行的 SQL 语句也许并不都能够得到我们想要的结果,
因此在进行类型转换的时候很容易出错,在使用的时候需要尤其小心。
二、DataReader
2.1、简介
DataReader 对象的作用是以流的方式从数据源读取数据,但是在读取的时候它只能够以只进的方
式读取,并且一次只能够读取一行数据。比如数据源中有 10 条数据,它就只能够从前往后读取,如果
读取了第 5 条数据后再想查看第 1 条数据,那就只能够重新创建 DataReader 对象。另外,该对象只能
够读取数据,也就是说是只读的,如果要修改数据,就不能够使用 DataReader 对象了。
DataReader 对象的这种读取方式使得它具有了很多有趣的特性。首先,DataReader 对象读取数据
的操作是一个持续的过程,因此为它提供连接服务的 Connection 对象就无法再执行其他任何操作,除
非将 DataReader 对象关闭,否则这个状态将会一直持续。其次,DataReader 对象并不关心数据行的
多少,它一次只能够读取一行,通过循环就能读取大量的数据。
简单总结起来,DataReader 对象进行查询有如下的特点:
- DataReader 是只读只进的数据流;
- 使用 DataReader 读取数据的过程,始终需要与数据库保持连接;
- 每次只能读取一行数据,通过循环可以读取多行数据。
下面是 ADO.NET 部分对象模型图,图中显示了 DataReader 对象的作用:
趣味理解,应用程序从数据库中查询数据,就像从仓库运送货物到码头一样,两个地方首先要有一
条通路,这是 Connection 连接对象;也要有一辆货车,这就是 DataReader 对象,负责运送货物;货车
上到底装了多少货物,取决于老板给你的指令,这就是 Command 对象。货车要在路上跑的时候,仓库和
码头之间的路要一直通着。
2.2、创建 DataReader 对象
DataReader 对象也会根据数据提供程序的不同而有所区别,而用于 SQL Server 的就是
SqlDataReader 对象,这里我们对 SqlDataReader 进行学习。
SqlDataReader 类所在的命名空间为 System.Data.SqlClient,它本身没有任何的构造函数,所以
不能用new关键字来创建对象了。创建SqlDataReader对象必须调用SqlCommand对象的ExecuteReader()
方法,如下代码所示:
SqlDataReader dr = cmd.ExecuteReader(); // cmd是可用的Command对象
创建出 SqlDataReader 对象后,才能去进行数据的读取。
2.3、常用方法
SqlDataReader 对象是一个轻量级的数据读取对象,常用到的方法有两个,如表 3-2 所示:
Read()方法的作用是读取下一条记录,SqlDataReader 对象的默认位置在第一条记录前面。因此,
必须调用 Read()方法来开始访问数据。Read()方法返回结果为布尔类型,以确定是否还存在数据行,
如果返回值为 true,表示存在数据行;返回值为 false,表示已经没有数据行了。
Close()方法则用于关闭 SqlDataReader 对象,对于每个关联的 SqlConnection 对象,一次只能打
开一个 SqlDataReader 对象,直到调用其 Close()方法之前,打开另一个的任何尝试都将失败,因此
SqlDataReader 对象在使用完毕后一定不要忘记关闭。
由于SqlDataReader对象一次只能够读取一行数据,因此在使用的时候一般都是跟循环结构,尤其
是 while 循环配合一起使用,通过 Read()方法就可以确定是否还有数据行读取,而在循环结构结束后
关闭 SqlDataReader 对象。基本代码如下:
while(dr.Read()) // dr是可用的SqlDataReader对象,通过循环进行数据读取 { // 读取数据 } dr.Close(); // 关闭SqlDataReader对象
如果是特殊情况,查询结果最多只有一行数据时,可以把循环改成 if 语句,只读一次即可。剩下
的问题就是在循环中完成数据读取及数据显示的工作了。
2.4、读取数据
假设执行的查询语句是:select * from FilmInfo,在 SQL Server 中可以得到如下查询结果:
我们在应用程序中如何将数据读取出来呢?SqlDataReader 对象提供了很多用来读取数据的方法,
但是最常用的是通过下标来读取数据。 以图 3-4 的查询结果为例,表示每列数据下标的方法有两种:
1、 使用数字下标:
数字下标从 0 开始,第一列下标为 0,第二列下标为 1,依此类推。使用数字下标读取列数据
的代码如下:
int ID = (int)dr[0]; // 编号 string name = dr[1].ToString(); // 电影名称 string typeName = dr[2].ToString(); // 电影类型 string actors = dr[3].ToString(); // 演员列表 int amount = (int)dr[4]; // 库存数量 decimal price = (decimal)dr[5]; // 价格 string desc = dr[6].ToString(); // 描述
2、使用字段名称下标:
还可以使用字段名称即列名作为下标,代码如下:
int ID = (int)dr["ID"]; // 编号 string name = dr["Name"].ToString(); // 电影名称 string typeName = dr["TypeName"].ToString(); // 电影类型 string actors = dr["Actors"].ToString(); // 演员列表 int amount = (int)dr["Amount"]; // 库存数量 decimal price = (decimal)dr["Price"]; // 价格 string desc = dr["Desc"].ToString(); // 描述
说明:代码中的 dr 为 SqlDataReader 对象。
无论采用哪种方式,其中括号“[]”中所使用的下标或者字段名称都是以SQL查询语句的执行结果
为依据。虽然两种方式的效果是一样的,但是通过字段名称作为下标来读取数据会更加安全一些。还
有,默认时候读取出来的数据是 Object 类型,所以通常需要进行类型转换,一定要注意类型转换是否
正确。
三、案例
3.1、查询全部电影信息
static void Main(string[] args) { // 连接字符串 string conStr = "server=.;database=FilmDB;uid=sa;pwd=123456;"; // 查询所有电影信息的SQL语句 string sql = " select * from FilmInfo "; // 创建Connection对象 SqlConnection conn = new SqlConnection(conStr); // 打开连接 conn.Open(); // 创建Command对象 SqlCommand cmd = new SqlCommand(sql, conn); // 创建DataReader对象 SqlDataReader dr = cmd.ExecuteReader(); Console.WriteLine("============ 所有电影信息如下 ============"); // 通过循环读取所有数据 while (dr.Read()) { // 读取每列的值 int ID = (int)dr[0]; string name = dr[1].ToString(); string typeName = dr[2].ToString(); string actors = dr[3].ToString(); int amount = (int)dr[4]; decimal price = (decimal)dr[5]; string desc = dr[6].ToString(); // 输出 Console.WriteLine("电影编号:{0}", ID); Console.WriteLine("电影名称:{0}", name); Console.WriteLine("电影类型:{0}", typeName); Console.WriteLine("演员列表:{0}", actors); Console.WriteLine("库存数量:{0}", amount); Console.WriteLine("价格:{0}", price); Console.WriteLine("描述:{0}", desc); Console.WriteLine("------------------------------------"); } // 关闭DataReader对象 dr.Close(); // 关闭连接 conn.Close(); Console.WriteLine("\n恭喜,查询完成!"); Console.ReadKey(); }
3.2.根据编号查询某部电影信息
这是查询部分电影信息,我们的 SQL 语句需要加上 where 子句。如果是这样,查询过程又可能有什
么变化呢?下面来看根据电影编号进行查询某部电影信息的案例:
static void Main(string[] args) { Console.Write("输入要查询电影的编号:"); int id = int.Parse(Console.ReadLine()); // 连接字符串 string conStr = "server=.;database=FilmDB;uid=sa;pwd=123456;"; // 根据编号查询电影的SQL语句 string sql = string.Format("select * from FilmInfo where ID='{0}'",id); // 创建Connection对象 SqlConnection conn = new SqlConnection(conStr); // 打开连接 conn.Open(); // 创建Command对象 SqlCommand cmd = new SqlCommand(sql, conn); // 创建DataReader对象 SqlDataReader dr = cmd.ExecuteReader(); Console.WriteLine("============电影信息如下 ============"); // 读取数据 if(dr.Read()) { // 读取每列的值 int ID = (int)dr[0]; string name = dr[1].ToString(); string typeName = dr[2].ToString(); string actors = dr[3].ToString(); int amount = (int)dr[4]; decimal price = (decimal)dr[5]; string desc = dr[6].ToString(); // 输出 Console.WriteLine("电影编号:{0}", ID); Console.WriteLine("电影名称:{0}", name); Console.WriteLine("电影类型:{0}", typeName); Console.WriteLine("演员列表:{0}", actors); Console.WriteLine("库存数量:{0}", amount); Console.WriteLine("价格:{0}", price); Console.WriteLine("描述:{0}", desc); Console.WriteLine("------------------------------------"); } // 关闭DataReader对象 dr.Close(); // 关闭连接 conn.Close(); Console.WriteLine("\n恭喜,查询完成!"); Console.ReadKey(); }
3.3、根据电影名称关键字查询电影信息
我们再来看一个例子,根据电影名称关键字进行模糊查询。模糊查询是查询功能中非常重要的一种,
具体实现的代码如下:
static void Main(string[] args) { Console.Write("输入要查询电影的名称:"); string key = Console.ReadLine(); // 连接字符串 string conStr = "server=.;database=FilmDB;uid=sa;pwd=123456;"; // 根据关键字查询电影的SQL语句 string sql = string.Format(@"select * from FilmInfo where Name like '{0}'","%"+key+"%");; // 创建Connection对象 SqlConnection conn = new SqlConnection(conStr); // 打开连接 conn.Open(); // 创建Command对象 SqlCommand cmd = new SqlCommand(sql, conn); // 创建DataReader对象 SqlDataReader dr = cmd.ExecuteReader(); Console.WriteLine("============电影信息如下 ============"); // 通过循环读取数据 while (dr.Read()) { // 读取每列的值 int ID = (int)dr[0]; string name = dr[1].ToString(); string typeName = dr[2].ToString(); string actors = dr[3].ToString(); int amount = (int)dr[4]; decimal price = (decimal)dr[5]; string desc = dr[6].ToString(); // 输出 Console.WriteLine("电影编号:{0}", ID); Console.WriteLine("电影名称:{0}", name); Console.WriteLine("电影类型:{0}", typeName); Console.WriteLine("演员列表:{0}", actors); Console.WriteLine("库存数量:{0}", amount); Console.WriteLine("价格:{0}", price); Console.WriteLine("描述:{0}", desc); Console.WriteLine("------------------------------------"); } // 关闭DataReader对象 dr.Close(); // 关闭连接 conn.Close(); Console.WriteLine("\n恭喜,查询完成!"); Console.ReadKey(); }
在这里,主要注意进行模糊匹配的查询 SQL 语句写法,在指定值时候要注意写上百分号%。因为查
询结果可能有多个,所以通过循环结构进行数据读取。
数据库:
用户验证A:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data.SqlClient; //引入 namespace ConsoleApplication1 { class Program { static void Main(string[] args) { Console.WriteLine("***************用户登录***************"); Console.Write("用户名:"); String uid = Console.ReadLine(); Console.Write("密码:"); String pwd = Console.ReadLine(); //输入用户名与密码 String connStr = "server=.;uid=sa;pwd=sa;database=School;"; //连接字符串 SqlConnection conn = new SqlConnection(connStr); //连接对象 String sql = String.Format("SELECT [id],[uid],[pwd],[name],[mobile] FROM [users] where uid='{0}'", uid); //将执行的SQL SqlCommand cmd = new SqlCommand(sql, conn); //命令对象 conn.Open(); //打开数据库 SqlDataReader dr = cmd.ExecuteReader(); //执行读取 if (dr.Read()) //下移取数据 { String pwdDb = dr["pwd"].ToString(); //根据用户名获取数据库中的密码 if (pwdDb == pwd) //如果输入的密码与数据库中的密码一样 { Console.WriteLine("登录成功,欢迎您:"+dr["name"].ToString()); //提示登录成功 }else { Console.WriteLine("密码错误,请重试。"); //提示密码错误 } }else //如果下移失败则用户不存在 { Console.WriteLine("用户不存在"); } dr.Close(); conn.Close(); } } }
用户验证B:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data.SqlClient; namespace SchoolMIS { class Program { static void Main(string[] args) { Console.WriteLine("*********************学员管理系统登录*********************"); Console.Write("用户名:"); string uid = Console.ReadLine(); Console.Write("密码:"); string pwd = Console.ReadLine(); String connStr = "server=.;database=School;uid=sa;pwd=sa;"; SqlConnection conn = new SqlConnection(connStr); String sql = string.Format("select COUNT(*) from users where uid='{0}' and pwd='{1}'",uid,pwd); SqlCommand cmd = new SqlCommand(sql, conn); conn.Open(); int count=Convert.ToInt32(cmd.ExecuteScalar()); //取单行单列的值 conn.Close(); if (count > 0) { Console.WriteLine("登录成功,欢迎进入本系统"); } else { Console.WriteLine("验证失败,请重试"); } } } }
注册用户C:
五、SQL辅助工具类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data.SqlClient; using System.Data; namespace DB { public class SQLHelper { //只读的静态数据库连接字符串 public static readonly string connString ="server=.;database=School;uid=sa;pwd=sa"; #region 执行 增 删 改 /// <summary> /// 执行 增 删 改 /// </summary> /// <param name="sql">要执行的SQL</param> /// <param name="param">参数</param> /// <returns>影响行数</returns> public static int Zsg(string sql,params SqlParameter[] param) { //实例化连接对象,并指定连接字符串,自动释放资源,不用关闭 using (SqlConnection conn = new SqlConnection(connString)) { //实例化命令对象,指定sql,与连接对象 using (SqlCommand cmd = new SqlCommand(sql, conn)) { //如果有参数 if (param != null) { //批量添加参数 cmd.Parameters.AddRange(param); } //打开连接 conn.Open(); //执行sql并返回影响行数 return cmd.ExecuteNonQuery(); } } } #endregion #region 执行 查询 /// <summary> /// 执行 查询 /// </summary> /// <param name="sql">要执行的SQL</param> /// <param name="param">参数</param> /// <returns>数据集</returns> public static SqlDataReader Cx(string sql,params SqlParameter[] param) { //实例化连接对象,并指定连接字符串 SqlConnection conn = new SqlConnection(connString); //实例化命令对象,指定sql,与连接对象 using (SqlCommand cmd = new SqlCommand(sql, conn)) { //如果有参数 if (param != null) { //批量添加参数 cmd.Parameters.AddRange(param); } //打开连接 conn.Open(); //执行sql并返回影响行数,如果将返回的SqlDataReader关闭时也将关闭连接 return cmd.ExecuteReader(CommandBehavior.CloseConnection); } } #endregion #region 完成数据的查询,返回DataTable /// <summary> /// 表格 完成数据的查询,返回DataTable /// </summary> /// <param name="sql">要执行的sql</param> /// <param name="param">参数</param> /// <returns>DataTable</returns> public static DataTable Bg(string sql,params SqlParameter[] param) { //实例化连接对象,并指定连接字符串,自动释放资源,不用关闭 using (SqlConnection conn = new SqlConnection(connString)) { SqlDataAdapter adp = new SqlDataAdapter(sql, conn); if (param != null) { adp.SelectCommand.Parameters.AddRange(param); } DataTable dt = new DataTable(); adp.Fill(dt); return dt; } } #endregion } }
增删除改示例
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication7 { class Program { static void Main(string[] args) { String sql = @"INSERT INTO [School].[dbo].[users] ([uid] ,[pwd] ,[name] ,[mobile]) VALUES ('user007' ,'123567' ,'李小军' ,'18890909987')"; //执行并返回影响行数 int row=DB.SQLHelper.Zsg(sql); if(row>0) { Console.WriteLine("执行成功"); } else { Console.WriteLine("执行失败"); } } } }