设为首页 收藏本站
查看: 859|回复: 0

[经验分享] .NET和SQL Server中“空值”辨析 (DBNull与Null的区别)

[复制链接]

尚未签到

发表于 2015-7-1 11:56:52 | 显示全部楼层 |阅读模式
  对表进行插入操作,如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 方法,来确定联系人数据库中的数据库字段是否具有有效值。如果具有有效值,字段值将被追加到在标签中输出的字符串中。
  

DSC0000.gif DSC0001.gif 代码

private void OutputLabels(DataTable dt)
{
   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都会抛出不能转换的错误。
  如:
   DSC0002.png
  
  

代码

protected void Page_Load(object sender, EventArgs e)
    {
        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[0][0].ToString());//可以正常输出
        Response.Write(dt.Rows[0][0]);//可以正常输出
    }  
  
  
  在 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()); 依然会出错,因为上面的语句不成立时,仍然是不返回任何行。
  
  下面来看一些示例:
  
  
  

代码

using (SqlConnection conn = new SqlConnection(connStr))
        {
            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==0)
            {
                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("alert('Add Success!')");
}
上段程序当然可以添加成功,  
问题是当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("alert('Add Success!')");
}
如果是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都会抛出不能转换的错误。
  示例:
  

代码

if (ds.Tables[0].Rows[0]["click"].ToString() =="")
                {
                    //true
                }
                if (ds.Tables[0].Rows[0]["click"].ToString() == string.Empty)
                {
                    //true
                }

                if (ds.Tables[0].Rows[0]["click"].ToString() == null)
                {
                   //false
                }
                if (ds.Tables[0].Rows[0]["click"]==null)
                {
                    //false
                }
                if (ds.Tables[0].Rows[0]["click"].Equals(DBNull.Value))
                {
                    //true
                }  
  在处理数据库的时候的一个技巧:
  

代码

  if (ds.Tables[0].Rows.Count > 0)    {
      if (ds.Tables[0].Rows[0]["id"].ToString() != "")
       {
           model.id = int.Parse(ds.Tables[0].Rows[0]["id"].ToString());
       }
}
//以上方式这里判断一下查询出来是否存在行,如果不判断的话就需要判断返回值是否为空了,因为查询没有任何行的情况下返回的是Null而不是DBNull,下面这种方式要每行都添加这种判断显然不是好方法.
//如:
if(ds.Tables[0].Rows[0]["id"]!=null && ds.Tables[0].Rows[0]["id"].ToString()!=""){
         model.id = int.Parse(ds.Tables[0].Rows[0]["id"].ToString());
}
//或者
if(ds.Tables[0].Rows[0]["id"]!=null && ! Convert.IsDBNull(ds.Tables[0].Rows[0]["id"]))
         model.id = int.Parse(ds.Tables[0].Rows[0]["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,4)};
            parameters[0].Value = id;
            Holiday.DAL.Model.TouristTrack model = new Holiday.DAL.Model.TouristTrack();
            DataSet ds = SQLLinkDatabase.Query(strSql.ToString(), parameters);
            if (ds.Tables[0].Rows.Count > 0)
            {
                if (ds.Tables[0].Rows[0]["id"].ToString() != "")
                {
                    model.id = int.Parse(ds.Tables[0].Rows[0]["id"].ToString());
                }
                model.routeName = ds.Tables[0].Rows[0]["routeName"].ToString();
                model.routeCharacteristic = ds.Tables[0].Rows[0]["routeCharacteristic"].ToString();
                model.routeIntroductions = ds.Tables[0].Rows[0]["routeIntroductions"].ToString();
                model.costDetail = ds.Tables[0].Rows[0]["costDetail"].ToString();
                model.participate = ds.Tables[0].Rows[0]["participate"].ToString();
                if (ds.Tables[0].Rows[0]["routeCategory"].ToString() != "")
                {
                    model.routeCategory = int.Parse(ds.Tables[0].Rows[0]["routeCategory"].ToString());
                }
                model.dineMenu = ds.Tables[0].Rows[0]["dineMenu"].ToString();
                model.weather = ds.Tables[0].Rows[0]["weather"].ToString();
                model.isEnable = ds.Tables[0].Rows[0]["isEnable"].ToString();
                model.Addperson = ds.Tables[0].Rows[0]["addPerson"].ToString();
                if (ds.Tables[0].Rows[0]["addDate"].ToString() != "")
                {
                    model.addDate = DateTime.Parse(ds.Tables[0].Rows[0]["addDate"].ToString());
                }
                model.Competitiveproducts = ds.Tables[0].Rows[0]["competitiveProducts"].ToString();
                model.luxury = ds.Tables[0].Rows[0]["luxury"].ToString();
                model.onVacation = ds.Tables[0].Rows[0]["onVacation"].ToString();
                model.Characteristic = ds.Tables[0].Rows[0]["characteristic"].ToString();
                model.hotRecommend = ds.Tables[0].Rows[0]["hotRecommend"].ToString();
                if (ds.Tables[0].Rows[0]["referencesPrice"].ToString() != "")
                {
                    model.ReferencesPrice = decimal.Parse(ds.Tables[0].Rows[0]["referencesPrice"].ToString());
                }
                model.SpecialPreference = ds.Tables[0].Rows[0]["specialPreference"].ToString();
                model.ImgShow = ds.Tables[0].Rows[0]["imgShow"].ToString();
                model.ImgName = ds.Tables[0].Rows[0]["imgName"].ToString();
                model.ImgUrl = ds.Tables[0].Rows[0]["imgUrl"].ToString();
                model.NewProduct = ds.Tables[0].Rows[0]["newProduct"].ToString();
                model.Overflow = ds.Tables[0].Rows[0]["overflow"].ToString();
                model.Season = ds.Tables[0].Rows[0]["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"));
  }  
  
  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-82209-1-1.html 上篇帖子: MS SQL Server 2005 物理查询处理中的各个阶段 (二) 下篇帖子: SQL Server 批量生成bcp命令
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表