[朝花夕拾]空值的问题

空值的问题[1]


空值的含义

   空值(NULL)在关系模型中可以有两种定义:1、空值表示可以给该列赋值,但是目前还没有赋值;2、空值表示该列不可以被赋值。RDBMS支持的一般都是第一种类型,SQL标准也是这样定义的。

NULL引起的EXISTS子句错误

   
1989年9月,C.J.Date在“Database Programming & Design”上发表了题为“Beware of SQL EXISTS!”的文章,用例子说明了SQL标准定义的EXISTS语句存在问题(大家不妨用这个例子来检查一下目前的数据库系统是否还存在如此的问题)。该例子用到了供应商-供应零件表sp,表中只有三条记录:

  sno pno qty
  --- --- ----
  S1 P1 NULL
  S2 P1 200
  S3 P1 1000

  这个表的意思很明白,就是供应商(SNO)对每种零件(PNO)提供了多少(QTY)。记录(S1, P1, NULL)表明供应商S1提供了零件P1,但是具体的数目不清楚。

  DATE给出了下面的查询:查找供应了零件P1但是供应数不包括1000的供应商。很明显,正确的查询结果应该是一条记录S2,只有供应商S2满足条件。对应的SQL语句可以是:

  SELECT DISTINCT spx.sno 
  FROM sp spx 
  WHERE spx.pno = 'P1' AND
     1000 NOT IN
     ( SELECT spy.qty 
      FROM sp spy 
      WHERE spy.sno = spx.sno AND
         spy.pno = 'P1');

  ---
  注:假如表sp以(sno, pno)作为主键,则用不着上面的DISTINCT,SQL语句如果写成:

  SELECT sno
  FROM sp
  WHERE sno = 'P1' AND qty != 1000 ;

  返回的结果应该是(S1, S2),当然可以将上述的SQL语句修改成:

   WHERE sno = 'P1' AND qty != 1000 AND qty is not NULL ;    

  此时返回结果只有S2。这个例子太特别,不知道有没有更具实践意义的好例子。
   
---

  上述语句可以返回正确的结果S2,但是如果用EXISTS子句改写成“等价”形式:

  SELECT DISTINCT spx.sno
  FROM sp spx
  WHERE spx.pno = 'P1' AND
     NOT EXISTS
     ( SELECT spy.qty
      FROM sp spy
      WHERE spy.sno = spx.sno AND
         spy.pno = 'P1' AND
         spy.qty = 1000);

  根据ANSI SQL的定义,结果应该是S1、S2。请看DATE的分析:假如主查询中spx=(S1, P1, NULL),则子查询的EXISTS子句逻辑上等价于:

  (SELECT spy.qty
   FROM sp spy
   WHERE spy.sno = 'S1' AND
      spy.pno = 'P1' AND
      spy.qty = 1000)

  这个子查询的结果是个空集,因此整个查询相当于:  

  SELECT DISTINCT spx.sno
  FROM sp spx
  WHERE spx.pno = 'P1'
     /* true for spx = (S1,P1,1000) */
  AND true

  于是供应商S1也出现在了查询结果中,从而导致了错误。回顾一下上面的分析过程,容易发现EXISTS子句并不是产生问题的根源,错误源于WHERE子句对(spy.qty = 1000)的判断。存在NULL的逻辑判断应该采用三值逻辑,而ANSI SQL只定义了WHERE子句的二值逻辑,即WHERE子句只可能产生TRUE或者FALSE。在上述的例子中,WHERE子句对记录spy=(S1,P1,NULL)的判断结果应该是“未知”,而不是FALSE。既然NULL参与的比较是问题的源头,RDBMS可能导致的问题就远远不止DATE提出的那些了,比如:

  1、不能正确的处理子查询

  
前面用了IN子句的查询可以产生正确的结果,但也可以将其改成一种错误的形式:
  
  
SELECT DISTINCT sno 
  FROM sp
  WHERE pno = 'P1' AND
     sno NOT IN 
       ( SELECT sno
        FROM sp
        WHERE pno = 'P1' AND qty = 1000) ;

  这个查询和用了EXISTS子句的查询存在同样的问题。因此用了子查询的SQL语句,只要子查询中表的某一列存在空值,都可能导致类似的问题。

  2、即使用户给出了正确的SQL语句,试图避免上述问题,DBMS系统也可能产生错误结果。

   
因为DBMS系统一般要对用户给出的查询作出改写和优化,DATE给出的那个使用了IN子句的查询可能被优化器改写成带EXISTS子句的查询,这样又会产生错误。

  3、ANSI SQL对HAVING子句的定义存在同样的问题。

  
同WHERE子句类似,HAVING子句采用的也是二值逻辑,只能取值TRUE或FALSE。而很多DBMS允许HAVING子句出现在子查询或视图定义中,这样也会导致类似的错误。

  要解决上述的问题理论上看来并不是很复杂,只要对SQL的处理采用三值逻辑即可,但是这在工程实践中是需要认真对待的,并不是想象的那么简单。

NULL引起的争论

  
既然NULL会引起比较严重的问题,C.J.Date等人就认为数据库中应该取消NULL,而代以某个确切的值。反对者包括E.F.Codd等,他们认为NULL是数据库中必不可少的特性,它很好地反映了现实。这方面是不是有点象Dijkstra发起的编程语言中有关GOTO语句的争论?[2][3]

  数据库中确实存在未知信息:在实际工作中存在很多情况,不能获得某些信息。例如单位的职工统计表中可能会存在某个职工(比如说是foo)的生日未知,在报表中可以用空白、短横线或者直接写上“未知”代替。在数据库系统中可能用空格等代替,这样应用程序就需要根据实际采用的替代值进行判断,首先这样做增加了程序的复杂性,其次还不能保证系统提供统一的解决方案。关系数据库解决这种问题的方法是用“NULL”表示未知值。即便如此,用SQL语句在数据库中查询foo的生日是否为1900年10月10日,返回的是“FALSE”而不是“未知”。

  为了彻底解决上述的问题,关系模型引入了三值逻辑。一般的数据库条件操作结果只可能是“TRUE”或“FALSE”,但是对于存在NULL值的问题只能回答“未知”,而不是给出确切的回答。这种解决方案让人觉得有些不舒服,于是C.J.Date和David McGoveran等就建议用缺省值代替空值,但和三值逻辑相比,这种方法的表达能力还是欠佳。而且实际系统还没有哪个实现了他们的方案。倒是FirstSQL正确地实现了三值逻辑(这是FirstSQL自己的陈述,我没有证实此事,但也倾向于采用三值逻辑)。别的很多DBMS对NULL的实现,甚至ANSI SQL标准本身对NULL的定义都存在缺陷,没事时可以用上述的例子测试一下。

参考文献:

  1. http://www.firstsql.com
  2. http://www.dbdebunk.com
  3. C. J. Date, An Introduction to Database (Eighth Edition), Addison-Wesley
正向代理是一种网络代理服务,它充当客户端和服务器之间的中间人,将客户端的请求转发到目标服务器。在正向代理服务中,有时候需要对请求进行排序,其中可能会遇到空值问题空值问题是指在排序过程中,存在一些值为空的情况。这可能是由于客户端或中间服务器未能正确提供请求所需的数据,或是一些其他原因导致的。在正向代理服务中,我们需要解决这个空值问题,以确保排序结果的准确性和完整性。 对于空值问题,一种常见的解决方法是在排序算法中添加对空值的处理逻辑。在排序过程中,可以将空值视为一个特殊的情况,使得排序算法能够正确处理空值的情况。例如,在排序算法中可以设定空值的优先级为最低,使得空值在排序结果中排在最后。 另一个解决空值问题的方法是在正向代理服务的前端进行数据验证和清理。在接收到客户端的请求之后,可以对请求中的数据进行检查,如果存在空值,则可以进行相应的处理,例如删除空值或者给予默认值。这样可以确保传递给排序算法的数据是符合要求的,避免空值问题对排序结果的影响。 总的来说,正向代理服务在排序空值问题上可以通过在排序算法中处理空值,或者在前端对数据进行验证和清理来解决这个问题。这样可以保证排序结果的准确性和完整性,提高正向代理服务的质量和稳定性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值