Chinaunix首页 | 论坛 | 博客
  • 博客访问: 45190
  • 博文数量: 43
  • 博客积分: 1161
  • 博客等级: 少尉
  • 技术积分: 425
  • 用 户 组: 普通用户
  • 注册时间: 2010-04-24 11:14
文章分类
文章存档

2011年(40)

2010年(3)

分类: Python/Ruby

2011-09-16 21:57:57

在DBI模块中,有一个primary_key函数,用于获取表的主键字段,返回一个包含所有主键字段的数组.

primary_key($catalog,$table_schema,$table_name)

常用的使用方式(mysql下)是:


my @pk_array=$dbh->primary_key(undef,‘some_database’,‘some_table’);


从CPAN上的DBI文档中可以看到,primary_key返回的list,是按照字段在主键中的顺序排序的(The list is in primary key column sequence order).


但是,在实际的使用中,发现这个函数的返回值,并没有遵循文档中的解释.


  1. sub primary_key {
  2.         my ($dbh, @args) = @_;
  3.         my $sth = $dbh->primary_key_info(@args) or return;
  4.         my ($row, @col);
  5.         push @col, $row->[3] while ($row = $sth->fetch);
  6.         Carp::croak("primary_key method not called in list context")
  7.                 unless wantarray; # leave us some elbow room
  8.         return @col;
  9.     }


这一段是DBI.pm中的primary_key源代码,可以看到,函数实际上是调用了DBI的另外一个接口---primary_key_info

获取primary_key_info的返回结果后,没有做任何处理,只是提取了其中的COLUMN_NAME信息构造成一个list返回.


查看DBD/mysql.pm中primary_key_info的源代码,可以看到这么一段


  1. my $desc_sth = $dbh->prepare("SHOW KEYS FROM $table_id");
  2. my $desc= $dbh->selectall_arrayref($desc_sth, { Columns=>{} });

可以看到,primary_key_info实际上是使用 “show keys from SOME_TABLE”来获取表的索引信息----包含表的所有索引信息.如



可以看到,存在一个Seq_in_index字段来记录字段中某个索引中的顺序位置.


  1. my %col_info;
  2.   for my $row (grep { $_->{key_name} eq 'PRIMARY'} @$desc)
  3.   {
  4.     $col_info{ $row->{column_name} }= {
  5.       TABLE_CAT => $catalog,
  6.       TABLE_SCHEM => $schema,
  7.       TABLE_NAME => $table,
  8.       COLUMN_NAME => $row->{column_name},
  9.       KEY_SEQ => $row->{seq_in_index},
  10.       PK_NAME => $row->{key_name},
  11.     };
  12.   }

这段代码从原始数据(@$desc)中筛选出主键信息,放入一个以 column_name为 key的哈希(%col_info)中.


  1. $VAR1 = {
  2. 'A' => {
  3. 'PK_NAME' => 'PRIMARY',
  4. 'TABLE_NAME' => 'slic',
  5. 'TABLE_CAT' => 'Momo',
  6. 'COLUMN_NAME' => 'A',
  7. 'TABLE_SCHEM' => 'test',
  8. 'KEY_SEQ' => 1
  9. },
  10. 'B' => {
  11. 'PK_NAME' => 'PRIMARY',
  12. 'TABLE_NAME' => 'slic',
  13. 'TABLE_CAT' => 'Momo',
  14. 'COLUMN_NAME' => 'B',
  15. 'TABLE_SCHEM' => 'test',
  16. 'KEY_SEQ' => 2
  17. }
  18. };

%col_info的数据结构是上面这样一个嵌套哈希:

%col_info以表的字段名为key指向一个匿名哈希

这个匿名hash中以如上信息(PK_NAME,TABLE_NAME.......)为key,值为各信息对应的值.


  1. my $sth= $sponge->prepare("primary_key_info $table", {
  2.       rows => [ map { [ @{$_}{@names} ] } values %col_info ],
  3.       NUM_OF_FIELDS => scalar @names,
  4.       NAME => \@names,
  5.       }) or

在构造了信息哈希之后,上面这段代码把信息压回到$sth中作为primary_key_info的最终返回值(返回一个$sth对象).

这段代码的核心是 map操作.

把每个主键字段的相关信息压入到一个匿名数组中,构造成一个嵌套的list结构,如下所示:


  1. $VAR1 = [
  2. [
  3. 'Momo',
  4. 'test',
  5. 'slic',
  6. 'B',
  7. 2,
  8. 'PRIMARY'
  9. ],
  10. [
  11. 'Momo',
  12. 'test',
  13. 'slic',
  14. 'A',
  15. 1,
  16. 'PRIMARY'
  17. ]
  18. ];

可以看到, $sth->{rows}是一个list引用,这个list每个元素都是一个匿名list.

每个匿名list表示一个主键字段,其中 list[3]为字段名,list[4]为字段中主键中的顺序位置.


从这里可以看到,有一个hash到list的转换操作,而转换时,没有使用字段的位置信息(KEY_SEQ).


Perl中,hash转换为list,无法预计结果list中元素的排序顺序,这个顺序依赖于Perl对hash的内部实现.

所以,$sth->[rows]的元素顺序并不总和字段在主键定义中的顺序完全一样.

从上面的示例可以看到,定义时排在后面的字段B,是$sth->[rows]的一个元素.


再回到primary_key函数的代码中:


  1. sub primary_key {
  2.         my ($dbh, @args) = @_;
  3.         my $sth = $dbh->primary_key_info(@args) or return;
  4.         my ($row, @col);
  5.         push @col, $row->[3] while ($row = $sth->fetch);
  6.         Carp::croak("primary_key method not called in list context")
  7.                 unless wantarray; # leave us some elbow room
  8.         return @col;
  9.     }

primary_key使用fetch直接去获取$sth中的数据,即$sth->[rows],其内部实现是一行一行的顺序读取$sth->[rows]并返回.

所以,primary_key返回值中,字段的顺序就完全依赖于$sth->[rows]中各元素的位置.

上面已经提到,由于做了hash到list的转换,使得$sth->[rows]丢失了字段的位置信息,所以primary_key的返回list中元素的顺序不符合预期.


在需要主键字段位置信息的需求中,规避这个问题很简单,有两个方法.

  1. 只使用primary_key_info来获取数据,自己实现 返回 “按定义顺序排序的主键字段list” 的函数.----primary_key_info的返回值包含了 KEY_SEQ信息.
  2. 修改DBI::primary_key的源代码,使用 KEY_SEQ做排序处理,然后在代码中继续使用DBI::primary_key

对比起来,第一种方案,即实现一个SELF::primary_key版本更好,因为其他人的DBI::primary_key可能并未patch


  1. sub primary_key {
  2.         my ($dbh, @args) = @_;
  3.         my $sth = $dbh->primary_key_info(@args) or return;
  4.         my ($row, @col);
  5.         my $res=$sth->fetchall_arrayref;
  6.         @col=map {$_->[3]} sort {$a->[4]<=>$b->[4]} @$res;
  7.         return @col;
  8.     }

上面这段代码可以是 SELF::primary_key

也可以用来替代DBI::primary_key.


与原版primary_key的差别是,使用了KEY_SEQ对返回结果做了排序处理.


阅读(647) | 评论(0) | 转发(0) |
0

上一篇:LION下编译MySQL 5.5.15

下一篇:没有了

给主人留下些什么吧!~~