.NET和SQL Server中“空值”辨析 (DBNull与Null的区别)
对表进行插入操作,如datetime和int类型的两个字段,都允许为null,
用“sqlcmd.Parameters.Add("@t12",tb12.Text)”参数绑定时。datetime类型时,tb12.Text为空,插入成功,不报错,查看该值,却为1900-01-01;int类型时,用同样语句,tb12.Text为空,插入成功,不报错,查看该值,却为0;用“sqlcmd.Parameters.Add(new SqlParameter("@t12",SqlDbType.DateTime));sqlcmd.Parameters["@t12"].Value=tb12.Text;”语句时,tb12.Text为空插入则报错。
sqlcmd.Parameters.Add(new SqlParameter("@t12",SqlDbType.DateTime));
if (tb12.Text.Length>0)
{
sqlcmd.Parameters["@t12"].Value=tb12.Text
}
else
{
sqlcmd.Parameters["@t12"].Value = System.DBNull.Value;
}
MSDN:
DBNull 类表示一个不存在的值。例如,在数据库的表中,某一行的某列中可能不包含任何数据。即,该列被视为根本不存在,而不只是没有值。一个表示不存在的列的 DBNull 对象。此外,COM 互操作使用 DBNull 类来区分 VT_NULL 变量(指示不存在的值)和 VT_EMPTY 变量(指示未指定的值)。
DBNull 类型是一个单独的类,这意味着只有一个 DBNull 对象存在。DBNull..::.Value 成员表示唯一的 DBNull 对象。DBNull..::.Value 可用于将不存在的值显式分配给数据库字段,但大多数 ADO.NET 数据提供程序在字段没有有效值时会自动分配 DBNull 值。您可以通过将从数据库字段检索到的值传递给 DBNull.Value.Equals 方法,确定该字段值是否为 DBNull 值。然而,有些语言和数据库对象提供一些方法,可以更容易地确定数据库字段值是否为 DBNull..::.Value。这些方法包括 Visual Basic 的 IsDBNull 函数、Convert..::.IsDBNull 方法、 DataTableReader..::.IsDBNull 方法和 IDataRecord..::.IsDBNull 方法。
请勿将面向对象的编程语言中的 nullNothingnullptrnull 引用(在 Visual Basic 中为 Nothing) 概念与 DBNull 对象混淆。在面向对象的编程语言中,nullNothingnullptrnull 引用(在 Visual Basic 中为 Nothing) 表示不存在对某个对象的引用。DBNull 则表示未初始化的变量或不存在的数据库列。
实际上就是数据库中的Null.数据库中如果要插入null值,如:像有些日期字段,如果用户没有选择日期,我们希望他保持NULL状态。
下面的示例调用 DBNull.Value.Equals 方法,来确定联系人数据库中的数据库字段是否具有有效值。如果具有有效值,字段值将被追加到在标签中输出的字符串中。
代码
{
string label;
// Iterate rows of table
foreach (DataRow row in dt.Rows)
{
int labelLen;
label = String.Empty;
label += AddFieldValue(label, row, "Title");
label += AddFieldValue(label, row, "FirstName");
label += AddFieldValue(label, row, "MiddleInitial");
label += AddFieldValue(label, row, "LastName");
label += AddFieldValue(label, row, "Suffix");
label += "\n";
label += AddFieldValue(label, row, "Address1");
label += AddFieldValue(label, row, "AptNo");
label += "\n";
labelLen = label.Length;
label += AddFieldValue(label, row, "Address2");
if (label.Length != labelLen)
label += "\n";
label += AddFieldValue(label, row, "City");
label += AddFieldValue(label, row, "State");
label += AddFieldValue(label, row, "Zip");
Console.WriteLine(label);
Console.WriteLine();
}
}
private string AddFieldValue(string label, DataRow row,
string fieldName)
{
if (! DBNull.Value.Equals(row[fieldName]))
return (string) row[fieldName] + " ";
else
return String.Empty;
}
DBNull在DotNet是单独的一个类型 System.DBNull 。它只有一个值 DBNull.Value 。DBNull 直接继承 Object ,所以 DBNull 不是 string , 不是 int , 也不是 DateTime 。。。
但是为什么 DBNull 可以表示数据库中的字符串,数字,或日期呢?原因是DotNet储存这些数据的类(DataRow等)都是以 object 的形式来储存数据的。
对于 DataRow , 它的 row[column] 返回的值永远不为 null , 要么就是具体的为column 的类型的值 。 要么就是 DBNull 。 所以 row[column].ToString() 这个写法永远不会在ToString那里发生NullReferenceException。
DBNull 实现了 IConvertible 。 但是,除了 ToString 是正常的外,其他的ToXXX都会抛出不能转换的错误。
如:
代码
{
DataTable dt = new DataTable();
dt.Columns.Add(new DataColumn("name",typeof(string)));
DataRow dr=dt.NewRow();
dr["name"] = DBNull.Value;
dt.Rows.Add(dr);
Response.Write(dt.Rows[][].ToString());//可以正常输出
Response.Write(dt.Rows[][]);//可以正常输出
}
在 IDbCommand(OleDbCommand,SqlCommand...) 的ExecuteScalar的返回值中,情况可以这样分析:
select 1 这样返回的object是 1
select null 这样返回的是DBNull.Value
select isnull(null,1) 返回的是 1
select top 0 id from table1 这样返回的值是null -->一行数据也没有
select isnull(id,0) from table1 where 1=0 返回的值是null -->一行数据也没有
这里 ExecuteScalar 的规则就是,返回第一列,第一行的数据。如果第一列第一行不为空,那么ExecuteScalar就直接对应的DotNet的值。如果有第一行,但是第一列为空,那么返回的是 DBNull 。如果一行都没有,那么ExecuteScalar就返回null
规则就是这样的。这里容易犯的一个错误是,把ExecuteScalar返回DBNull与null的情况混淆,例如:
string username=cmd.ExecuteScalar().ToString();//没有任何一行数据时
除非你认为cmd执行后,肯定至少有一行数据,否则这里就会出错。
又或者 select id from usertable where username=@name 这样的sql语句,如果找不到记录,那么ExecuteScalar则会返回null,所以千万不要
int userid=Convert.ToInt32(cmd.ExecuteScalar());
或者你会这样写 SQL 语句:select isnull(id,0) from usertable where username=@name
但是 int userid=Convert.ToInt32(cmd.ExecuteScalar()); 依然会出错,因为上面的语句不成立时,仍然是不返回任何行。
下面来看一些示例:
{
conn.Open();
SqlCommand comm = new SqlCommand("select id from Tab_Article where 1=0", conn);
if (comm.ExecuteScalar() == null)
{
Response.Write("返回的是null");//output:返回的是null
}
}
using (SqlConnection conn = new SqlConnection(connStr))
{
conn.Open();
//这里假设第一行第一列为null
SqlCommand comm = new SqlCommand("select null", conn);
if (Convert.IsDBNull(comm.ExecuteScalar()))
{
Response.Write("返回的是DBNull"); //output:返回的是DBNull
}
}
using (SqlConnection conn = new SqlConnection(connStr))
{
conn.Open();
SqlCommand comm = new SqlCommand("select * from Tab_Article where 1=0", conn);
SqlDataReader read = comm.ExecuteReader();
if (read.Read())
{
Response.Write("有数据");
}
else
{
Response.Write("无数据");//output:无数据
}
}
using (SqlConnection conn = new SqlConnection(connStr))
{
DataTable dt = new DataTable();
conn.Open();
SqlCommand comm = new SqlCommand("select * from Tab_Article where 1=0", conn);
SqlDataAdapter sda = new SqlDataAdapter(comm);
sda.Fill(dt);
if (dt.Rows.Count==)
{
Response.Write("无数据");//output:无数据
}
}
//所以在进行ExecuteScalar处理的时候一定要判断返回的值是否为空
using (SqlConnection conn = new SqlConnection(connStr))
{
conn.Open();
SqlCommand comm = new SqlCommand("select id from Tab_Article where 1=0", conn);
object o=comm.ExecuteScalar();
if (o!=null)
{
//...
}
else if(Convet.isDBNull(o))
{
//判断是否为DBNull,做相关处理,因为如果返回无任何行返回的是null而不是DBNull,使用ToString();会报NullReferencesException异常.
}else{
}
简化写法:
对于IDbDataParameter(OleDDbParameter,SqlParameter..)的Value,如果为null,则代表该参数没有指定,或者是代表DEFAULT。(如果数据库中设置了默认值,就可以使用null),如果为DBNull.Value,则代表SQL中的NULL
所以,如果你要调用存储过程,里面有参数 @val nvarchar(20)="AABB" ,
那么cmd.Parameters["@val"].Value=null 代表使用这个默认的 "AABB"-->这里为null,则代表该参数没有指定,会使用存储过程的默认值:@val nvarchar(20)="AABB"
而cmd.Parameters["@val"].Value=DBNull.Value 代表使用NULL来传给 @val
你可以用Convert.IsDBNull来判断一个值是否DBNull。注意Convert.IsDBNull(null)是false。
备注:以上的SQL语句全是指SQLSERVER2000的。其他的数据库是否为同样的行为,我不确定。
在我的一个程序里遇到这样一个问题?
在数据库中的某个字段类型为 datetime
页面上对应该字段的为一个text文本输入框,意思是输入时间。
string strId =txtId.Text.Trim();
string strName=txtName.Text.Trim();
string strPwd=txtPwd.Text.Trim();
string strExpiry=txtTime.Text.Trim(); //时间
System.Data.SqlClient.SqlParameter []parmCustomers = new SqlParameter[3];
parmCustomers[0] = new SqlParameter( "@C_Id", strId );
parmCustomers[1] = new SqlParameter( "@Name", strName );
parmCustomers[2] = new SqlParameter( "@Pwd", strPwd );
parmCustomers[3] = new SqlParameter("@Date",strExpiry);//如果现文本里没有输入时间
SqlServerDatabase obj = new SqlServerDatabase();
if ( obj.RunProc( "proc_AddUser", parmCustomers ) == 1 ) // 添加成功
{
Response.Write("<script type='text/javascript'>alert('Add Success!')</script>");
}
上段程序当然可以添加成功,
问题是当txtTime.Text什么都没输入的时候,数据库中的这个字段仍然会存储 1900-01-01 00:00:00.000
于是我就在parmCustomers[3] = new SqlParameter("@Date", " " )写入空字符串 或是 null ,可问题插入后数据库里还是显示1900-01-01
以下是解决办法:
于是加了判断: //注数据库里时间字段要设置永许为空
string strExpiry=this.txtTime.Text.Trim();
System.Data.SqlClient.SqlParameter []parmCustomers = new SqlParameter[3];
parmCustomers[0] = new SqlParameter( "@C_Id", strId );
parmCustomers[1] = new SqlParameter( "@Name", strName );
parmCustomers[2] = new SqlParameter( "@Pwd", strPwd );
if(strExpiry.ToString()=="")
{
parmCustomers[3] = new SqlParameter("@Date",DBNull.Value);//如果文本框的时间为空的话就吧 strExpiry 改为 DBNull.Value 就OK了
}
else
{
parmCustomers[3] = new SqlParameter("@Date",strExpiry);//有值时
}
SqlServerDatabase obj = new SqlServerDatabase();
if ( obj.RunProc( "proc_AddUser", parmCustomers ) == 1 ) // 添加成功
{
Response.Write("<script type='text/javascript'>alert('Add Success!')</script>");
}
如果是Sql语句直接插入的话
insert into AddUser (name,pwd)values('test','123')
date字段 就不要写入到插入的Sql语句里 这样数据库里的值就为空了。。。
(1)NULL
null 关键字是表示不引用任何对象的空引用的文字值。null 是引用类型变量的默认值。那么也只有引用型的变量可以为NULL,如果 int i=null,的话,是不可以的,因为Int是值类型的。
(2)DBNULL
DBNull在DotNet是单独的一个类型,该类只能存在唯一的实例,DBNULL.Value,DBNull唯一作用是 可以表示数据库中的字符串,数字,或日期,为什么可以表示原因是DotNet储存这些数据的类(DataRow等)都是以 object 的形式来储存数据的。对于 DataRow , 它的 row[column] 返回的值永远不为 null , 要么就是具体的为column 的类型的值 。 要么就是 DBNull 。 所以 row[column].ToString() 这个写法永远不会在ToString那里发生NullReferenceException。DBNull 实现了 IConvertible 。 但是,除了 ToString 是正常的外,其他的ToXXX都会抛出不能转换的错误。
示例:
{
//true
}
if (ds.Tables[].Rows[]["click"].ToString() == string.Empty)
{
//true
}
if (ds.Tables[].Rows[]["click"].ToString() == null)
{
//false
}
if (ds.Tables[].Rows[]["click"]==null)
{
//false
}
if (ds.Tables[].Rows[]["click"].Equals(DBNull.Value))
{
//true
}
在处理数据库的时候的一个技巧:
if (ds.Tables[].Rows[]["id"].ToString() != "")
{
model.id = int.Parse(ds.Tables[].Rows[]["id"].ToString());
}
}
//以上方式这里判断一下查询出来是否存在行,如果不判断的话就需要判断返回值是否为空了,因为查询没有任何行的情况下返回的是Null而不是DBNull,下面这种方式要每行都添加这种判断显然不是好方法.
//如:
if(ds.Tables[].Rows[]["id"]!=null && ds.Tables[].Rows[]["id"].ToString()!=""){
model.id = int.Parse(ds.Tables[].Rows[]["id"].ToString());
}
//或者
if(ds.Tables[].Rows[]["id"]!=null && ! Convert.IsDBNull(ds.Tables[].Rows[]["id"]))
model.id = int.Parse(ds.Tables[].Rows[]["id"].ToString());
}
//所以我们在做数据库操作的时候会以以下示例方法来取数据库中的数据,也就是判断一下if (ds.Tables[0].Rows.Count > 0){//...}的方式:
public Holiday.DAL.Model.TouristTrack GetModel(int id)
{
StringBuilder strSql = new StringBuilder();
strSql.Append("select top 0 id,routeName,routeCharacteristic,routeIntroductions,costDetail,participate,click,routeCategory,dineMenu,weather,isEnable,addPerson,addDate,competitiveProducts,luxury,onVacation,characteristic,hotRecommend,referencesPrice,specialPreference,imgShow,imgName,imgUrl,newProduct,overflow,season from Tab_TouristTrack ");
strSql.Append(" where id=@id ");
SqlParameter[] parameters = {
new SqlParameter("@id", SqlDbType.Int,)};
parameters[].Value = id;
Holiday.DAL.Model.TouristTrack model = new Holiday.DAL.Model.TouristTrack();
DataSet ds = SQLLinkDatabase.Query(strSql.ToString(), parameters);
if (ds.Tables[].Rows.Count > )
{
if (ds.Tables[].Rows[]["id"].ToString() != "")
{
model.id = int.Parse(ds.Tables[].Rows[]["id"].ToString());
}
model.routeName = ds.Tables[].Rows[]["routeName"].ToString();
model.routeCharacteristic = ds.Tables[].Rows[]["routeCharacteristic"].ToString();
model.routeIntroductions = ds.Tables[].Rows[]["routeIntroductions"].ToString();
model.costDetail = ds.Tables[].Rows[]["costDetail"].ToString();
model.participate = ds.Tables[].Rows[]["participate"].ToString();
if (ds.Tables[].Rows[]["routeCategory"].ToString() != "")
{
model.routeCategory = int.Parse(ds.Tables[].Rows[]["routeCategory"].ToString());
}
model.dineMenu = ds.Tables[].Rows[]["dineMenu"].ToString();
model.weather = ds.Tables[].Rows[]["weather"].ToString();
model.isEnable = ds.Tables[].Rows[]["isEnable"].ToString();
model.Addperson = ds.Tables[].Rows[]["addPerson"].ToString();
if (ds.Tables[].Rows[]["addDate"].ToString() != "")
{
model.addDate = DateTime.Parse(ds.Tables[].Rows[]["addDate"].ToString());
}
model.Competitiveproducts = ds.Tables[].Rows[]["competitiveProducts"].ToString();
model.luxury = ds.Tables[].Rows[]["luxury"].ToString();
model.onVacation = ds.Tables[].Rows[]["onVacation"].ToString();
model.Characteristic = ds.Tables[].Rows[]["characteristic"].ToString();
model.hotRecommend = ds.Tables[].Rows[]["hotRecommend"].ToString();
if (ds.Tables[].Rows[]["referencesPrice"].ToString() != "")
{
model.ReferencesPrice = decimal.Parse(ds.Tables[].Rows[]["referencesPrice"].ToString());
}
model.SpecialPreference = ds.Tables[].Rows[]["specialPreference"].ToString();
model.ImgShow = ds.Tables[].Rows[]["imgShow"].ToString();
model.ImgName = ds.Tables[].Rows[]["imgName"].ToString();
model.ImgUrl = ds.Tables[].Rows[]["imgUrl"].ToString();
model.NewProduct = ds.Tables[].Rows[]["newProduct"].ToString();
model.Overflow = ds.Tables[].Rows[]["overflow"].ToString();
model.Season = ds.Tables[].Rows[]["season"].ToString();
return model;
}
else
{
return null;
}
}
(3)""和String.Empty
这两个都是表示空字符串,其中有一个重点是string str1="" 和 string str2=null 的区别,这样定义后,str1是一个空字符串,空字符串是一个特殊的字符串,只不过这个字符串的值为空,在内存中是有准确的指向的,string str2=null,这样定义后,只是定义了一个string 类的引用,str2并没有指向任何地方,在使用前如果不实例化的话,都将抱错。
(4)Convert.IsDBNull()
Convert.IsDBNull()返回有关指定对象是否为 DBNull 类型的指示,即是用来判断对象是否为DBNULL的。其返回值是True或Flase。
要判断空值,有如下这些方法:
1:e.Row.DataItem!=Null
对于可以取e的,直接用obj和Null比较
2:drg["column"]==System.DBNull.Value 或者Convert.IsDBNull(string)
这种情况用于判断数据库中的数据是否为空
3:string.IsNullOrEmpty(string )
判断字符串是否为空的万能方法
取数据时怕数据库的数据为空,还可以这样写:
if ((dr.Get("MessageID") != null) && !object.Equals(dr.Get("MessageID"), DBNull.Value))
{
wen1.MessageID = int.Parse(dr.Get("MessageID"));
}
.NET和SQL Server中“空值”辨析 (DBNull与Null的区别)的更多相关文章
- SQL Server中内连接和外连接的区别
SQL Server中内连接和外连接的区别 假设一个数据库中有两张表,一张是学生表StudentInfo,一张是班级表ClassInfo,两张表之间用ClassId字段进行关联. 如果用内连接,正常的 ...
- SQL SERVER中强制类型转换cast和convert的区别
在SQL SERVER中,cast和convert函数都可用于类型转换,其功能是相同的, 只是语法不同. cast一般更容易使用,convert的优点是可以格式化日期和数值. 代码 select CO ...
- sql server中的varchar和Nvarchar有什么区别?
很多开发者进行数据库设计的时候往往并没有太多的考虑char, varchar类型,有的是根本就没注意,因为存储价格变得越来越便宜了,忘记了最开始的一些基本设计理论和原则,这点让我想到了现在的年轻人,大 ...
- sql server中index的REBUILD和REORGANIZE的区别及工作方式
sql server中index的REBUILD和REORGANIZE 转自:https://www.cnblogs.com/flysun0311/archive/2013/12/05/3459451 ...
- SQL Server中 SET 和 SELECT 赋值有什么区别?
SQL Server 中对已经定义的变量赋值的方式用两种,分别是 SET 和 SELECT.对于这两种方式的区别,SQL Server 联机丛书中已经有详细的说明,但很多时候我们并没有注意,其实这两种 ...
- SQL SERVER中的sys.objects和sysobjects的区别
这三个视图都是存在于SQL Server的每个数据库中.在SQL Server 2000中,它们都是系统表,而不是视图. 关于两个版本中系统表和系统的视图的对应关系,参考:http://technet ...
- SQL SERVER中查询参数为空(null)时默认查询所有的实现
最近在项目中碰到一个比较有意思的问题,网上查找了一些方法,在这里总结分享一下. 我们经常会碰到这样的场景:需要查询数据,有一些查询条件,但是查询的时候,我们希望在某个条件为空的时候,则不筛选这个条件, ...
- SQL Server中常用的SQL语句(转):
SQL Server中常用的SQL语句 转自:http://www.cnblogs.com/rainman/archive/2013/05/04/3060428.html 1.概述 名词 笛卡尔积.主 ...
- SQL Server中常用的SQL语句
1.概述 名词 笛卡尔积.主键.外键 数据完整性 实体完整性:主属性不能为空值,例如选课表中学号和课程号不能为空 参照完整性:表中的外键取值为空或参照表中的主键 用户定义完整性:取值范围或非空限制,例 ...
随机推荐
- DELPHI7在WIN8和WIN10下安装和运行
DELPHI7在WIN8下安装后可以打开运行,但发现设置断点DEBUG运行DLL工程时会卡死(IDE长时间无反应,不报错). DELPHI7在WIN10下安装后打开的时候会报错,无法运行. 以上两种情 ...
- 咏南C/S开发框架支持最新的DELPHI XE8开发
特大好消息:咏南C/S开发框架支持最新的DELPHI XE8开发!咏南开发框架让你再无开发工具升级后顾之忧! 购买咏南开发框架送项目源码!
- No package identifier when getting name for resource number 0x00000000
貌似在新版本的SDK下有时会出现标题的这个warning~ 如下: 思前想后也不知道这个到底是说的哪里的问题,在逛谷歌的时候无意中发现有人说是因为xml中color的参数直接写成: android: ...
- 读数据库表填充DataTable
我一般用的有2中方法: 1.数据填充 string sqlcmd="select * from table"; SqlDataAdapter adapder = new SqlDa ...
- Windows x86 x64使用SetThreadContext注入shellcode的方式加载DLL
一.前言 注入DLL的方式有很多,在R3就有远程线程CreateRemoteThread.SetWindowsHookEx.QueueUserApc.SetThreadContext 在R0可以使用a ...
- Android 图标上面添加提醒使用开源UI类库 Viewbadger
Viewbadger 1.BadgeView主要是继承了TextView,所以实际上就是一个TextView,底层放了一个label,可以自定义背景图,自定义背景颜色,是否显示,显示进入的动画效果以及 ...
- ASP.NET设置404页面返回302HTTP状态码的解决方法
在配置文件中配置404页面如下: .代码如下: <customErrors mode="On" defaultRedirect="404.aspx"> ...
- gulp安装和使用简介
一. gulp和grunt对比 grunt目前的工作流程:读文件.修改文件.写文件——读文件.修改文件.写文件——... gulp目前的工作流程:读取文件——修改文件——修改文件...——写文件 二. ...
- 在VS2012中使用GDI+
首先说明,在VS的较高版本中,已经包含GDI+的SDK,不用再次下载,只需要使用前应用相应的头文件,添加些代码即可.但是VC6.0中,没有GDI+SDK,需要同志们下载才行. 步骤: 1.在stdaf ...
- DISCUZ X2更换域名注意事项
国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html内部邀请码:C8E245J (不写邀请码,没有现金送)国内私 ...