复杂数据类型是指一个数据对象(实体)涉及多个数据库表,表之间的逻辑关系可以用一个树形结构表示。
在该树形结构中,每个表是其中的一个节点。
.仅有一个根
.除根以外的节点,有且仅有一个父节点.
主从等价父子。以A-->B表示A是B的主表,A是父节点,B为子节点.
复杂数据类型的支持实际上通过以下2种结构的支持实现。
(1)A-->B-->C
A: select f1,f2,f3 from t1
主键列:f1,f2
B: select t2.f1,t2.f2,t2.f3 from t2,t1 where t1.f1=t2.f1 and t1.f2=t2.f2 and t2.f1=@f1 and t2.f2=@f2
---@f1,@f2为A抽取的对应的f1,f2字段的值
C: select t3.f1,t3.f2,t3.f3 from t3,t2 where t2.f1=t3.f1 and t2.f1=@f1
---@f1为B抽取的对应的f1的字段值.
声明主键列的作用只是用来记录已抽取单据(对象)。
主表查询列必须包含从表关联的字段,如果是表达式则必须指定别名.
抽取规则中的参数来自父表.
(2)A-->B,A-->C:
A: select f1,f2,f3 from t1
B: select t2.f1,t2.f2,t2.f3 from t2,t1 where t1.f1=t2.f1 and t1.f2=t2.f2 and t2.f1=@f1 and t2.f2=@f2
C: select t2.f1,t2.f2,t2.f3 from t3,t1 where t1.f1=t3.f1 and t1.f2=t3.f2 and t3.f1=@f1 and t3.f2=@f2
多个表在未指定关联关系时,默认是一主多从的情况.
系统对参数的处理最早来源于C处理printf的方法,使用"%s"作为参数占位符。
这种方式有2个缺陷:
(1)当抽取命令需要使用to_date等数据库函数时,无法支持,只能采用烦琐和低效的变通办法处理
(2)同一个参数出现多次时无法支持
改用"@参数名"方式后可以解决上述问题.
为了保证兼容性,抽取规则中引入以下参数:
<parameterize_version>2</parameterize> <!-- 抽取命令参数化版本 1-%s 2-@参数名 默认:1 -->
对于<parameterize_version>为1的情况,先把%s占位符用字段列表名称作为参数替换。
如主键字段列为:f1,f2.
则把参数化版本为1的抽取命令:
select f1,f2,f3 from t2 where f1='%s' and f2='%s'
替换为:
select f1,f2,f3 from t2 where f1='@f1' and f2='@f2'.
这样,可以把2种版本的配置统一处理.
以下是参数化提升的代码:
int CRule::UpParam() {
if (cmd_parameterize_version_!=1)
return 0;
CAutoVector<RuleCommand*>::iterator iter = cmds_.begin();
while(iter!=cmds_.end()) {
RuleCommand *rc = *iter;
vector<int> v_pos; ///< 每个参数的开始位置
size_t offset = 0;
const int hold_len = 2;///< %s长度
do {
size_t pos = sql.find("%s",offset);
if (pos==string::npos)
break;
v_pos.push_back(pos);
offset = pos+hold_len;
} while(1);
if (keys_.size()<v_pos.size()) { ///< 严格意义上,两者应该相同.如果主键列数少于配置的%s个数,则属于配置错误.
return -1;
}
if (v_pos.size()==0) {
iter++;
continue;
}
offset = 0;
string sql;
for (int i=0;i<v_pos.size();i++) {
int pos = v_pos[i];
sql += rc->sql.substr(offset,pos-offset);
sql += keys_[i]->name_;
offset = pos+hold_len;
}
sql += sql.substr(offset);
rc->sql = sql;
iter++;
}
return 0;
}