联合主键

采用联合主键可以解决表中没有唯一主键字段的问题,不过联合主键有如下的缺点:

  • 效率低。在进行数据的添加、删除、查找以及更新的时候数据库系统必须处理两个字段,这样大大降低了数据处理的速度。
  • 使得数据库结构设计变得糟糕。组成联合主键的字段通常都是有业务含义的字段,这与“使用逻辑主键而不是业务主键”的最佳实践相冲突,容易造成系统开发以及维护上的麻烦。
  • 使得创建指向此表的外键关联关系变得非常麻烦甚至无法创建指向此表的外键关联关系。
  • 加大开发难度。很多开发工具以及框架只对单主键有良好的支持,对于联合主键经常需要进行非常复杂的特殊处理。

考虑到这些缺点,我们应该只在兼容遗留系统等特殊场合才使用联合主键,而在其他场
合则应该使用唯一主键。



受限操作的变通解决方案

各个数据库系统中提供的修改表结构的方法是不同的,有的提供了修改表名、修改字段类型、修改字段名称等操作的SQL 语句,而有的则没有提供这些功能,甚至有的数据库系统连删除字段的功能都不支持。但是这些操作有的时候又是必要的,那么有没有变通的手段来实现这些功能呢?答案是有!

在 DB2中如果要在表T中删除一个字段F1,那么可以首先创建一个表T1,这个表T1
的结构和表T 结构一致,唯一区别就是缺少字段F1;接着将表T 中的数据导出到T1 中,然后将表T删除;最后将表T1 重命名为T就可以了。这样就可以达到修改表名的效果了。

在不支持修改字段名称操作的数据库系统上同样可以采用类似策略来解决。比如我们要将表T的F1 字段重命名为F2,那么首先在表T上创建新字段F2,类型和F1 一致,然后将F1 的数据复制到F2 上,最后将字段F1 删除就可以了。这样就可以达到修改字段名称的效果了。



COUNT(*)与COUNT(FName)

COUNT(*)统计的是结果集的总条数,而COUNT(FName)统计的则是除了结果集中FName 不为空值(也就是不等于NULL)的记录的总条数。COUNT(*)结果是9,由于FNumber为IT002 的行的FName 字段是空值,所以COUNT(FName)的计算结果是8。因此在使用聚合函数COUNT 的时候一定要区分两种使用方式的区别,以防止出现数据错误。



 优先使用“BETTWEEN AND”进行范围值检测

数据库系统对“BETTWEEN AND”进行了查询优化,使用它进行范围值检测将会得到比其他方式更好的性能,因此在进行范围值检测的时候应该优先使用“BETTWEEN AND”。需要注意的就是“BETTWEEN AND”在进行检测的时候是包括了范围的边界值的(也就是闭区间),如果需要进行开区间或者半开半闭区间的范围值检测的话就必须使用其他的解决方案了。



低效的“WHERE 1=1”

where 1=1

要实现这种动态的SQL语句拼装,我们可以在宿主语言中建立一个字符串,然后逐个判断各个复选框是否选中来向这个字符串中添加SQL语句片段。这里有一个问题就是当有复选框被选中的时候SQL语句是含有WHERE子句的,而当所有的复选框都没有被选中的时候就没有WHERE子句了,因此在添加每一个过滤条件判断的时候都要判断是否已经存在WHERE语句了,如果没有WHERE语句则添加WHERE语句。
在判断每一个复选框的时候都要去判断,这使得用起来非常麻烦,“聪明的程序员是会偷懒的程序员”,因此开发人员想到了一个捷径:为SQL语句指定一个永远为真的条件语句(比如“1=1”),这样就不用考虑WHERE语句是否存在的问题了。

这看似非常优美的解决了问题,殊不知这样很可能会造成非常大的性能损失,因为使用添加了“1=1”的过滤条件以后数据库系统就无法使用索引等查询优化策略,数据库系统将会被迫对每行数据进行扫描(也就是全表扫描)以比较此行是否满足过滤条件,当表中数据量比较大的时候查询速度会非常慢。因此如果数据检索对性能有比较高的要求就不要使用这种“简便”的方式。下面给出一种参考实现,代码如下:

private void doQuery()
{
  Bool hasWhere = false;
  StringBuilder sql = new StringBuilder(" SELECT * FROM T_Employee");
  if(工号复选框选中)
  {
    hasWhere = appendWhereIfNeed(sql, hasWhere);
    sql.appendLine("FNumber BETWEEN '"+工号文本框1内容+"' AND '"+工号
    文本框2内容+"'");
  }
  if(姓名复选框选中)
  {
    hasWhere = appendWhereIfNeed(sql, hasWhere);
    sql.appendLine("FName LIKE '%"+姓名文本框内容+"%'");
  }
  if(年龄复选框选中)
  {
    hasWhere = appendWhereIfNeed(sql, hasWhere);
    sql.appendLine("FAge BETWEEN "+年龄文本框1内容+" AND "+年龄文本框2
    内容);
  }
  executeSQL(sql);
}

private Bool appendWhereIfNeed(StringBuilder sql,Bool hasWhere)
{
  if(hasWhere==false)
  {
    sql. appendLine("WHERE");
  }
  else
  {
    sql. appendLine("AND");
  }
}