我的sql语句很简单,如下所示:
if not exists (select col_a from t_a where co_b = 'v_b')
begin
insert into t_a (col_a ,col_b )
VALUES(v_a,v_b)
end
else
begin
update t_a set col_a = v_a, col_b = v_b where col_b = 'v_b'
end
由于我要更新数百行,如何以最低的时间成本在Perl中执行此操作?
如果我使用Prepare + Execute,如何使用占位符?
编写语句?
$dbh->prepare($statement);
是否支持多个复合SQL行,如上所述?或者我是否必须将行保存到sql文件中并使用SQL Server运行它?
为了使问题更清楚,我的Perl行看起来如下:
$statement = "if ... VALUES(?,?)...update t_a set col_a = ?, col_b = ?";
# better to use one binding values(v_a, v_b) couplets mapping
# the 2 placeholders of insert and update both?
foreach (@$va_arr) {
my $values_for_one_row = $_;
$dbh->prepare($statement);
$execute->execute($values_for_one_row->{col_a }, $values_for_one_row->{col_b });
}
我忘记了一件事:'what'也是$ va_arr中每次迭代都要更改的值:如果不存在(从t_a中选择col_a,其中co_b ='v_b')。此外,更新部分应为:update t_a set col_a =?,col_b =?其中col_b =“v_b”。似乎没有更好的方法,然后包括准备进入循环?对不起,我认为这个例子没有完整。但我认为simbabque的答案已经足够好了。
答案 0 :(得分:1)
您可以毫无问题地使用SQL。您需要准备一次声明。我假设您的$va_arr
看起来像这样:
my $va_arr = [
{
col_a => 1,
col_b => 2,
},
{
col_a => 'foo',
col_b => 'bar',
},
];
运行此代码的代码如下。 请注意,您必须将col_n
参数传递两次,因为每次执行时,每次?
需要将它们填充两次。它们按查询中?
的顺序填写,因此col_a
需要col_b
,INSERT
,col_a
需要col_b
,UPDATE
} my $sql = <<'EOSQL';
if not exists (select col_a from t_a where co_b = 'whatever')
begin
insert into t_a (col_a ,col_b )
VALUES(?, ?)
end
else
begin
update t_a set col_a = ?, col_b = ?
end
EOSQL
my $sth = $dbi->prepare($sql);
foreach ($values = @{ $va_arr }) {
$dbh->execute($values->{col_a }, $values->{col_b },
$values->{col_a }, $values->{col_b });
}
。
my @columns = qw( col_a col_b col_c col_n );
my $va_arr = [
{
col_a => 1,
col_b => 2,
col_n => 99,
},
{
col_a => 'foo',
col_b => 'bar',
col_n => 'baz',
},
];
# build the sql dynamically based on columns
my $sql = q{
if not exists (select col_a from t_a where co_b = 'whatever')
begin
insert into t_a (} . join(',' @columns) . q{)
VALUES(} . join(',', map '?', @columns) . q{)
end
else
begin
update t_a set } . join(',' map { "$_ => ?" } @columns) . q{
end
};
my $sth = $dbi->prepare($sql);
foreach ($values = @{ $va_arr }) {
$dbh->execute(@{$values}{@columns}, @{$values}{@columns});
}
如果您有一长串列并且您知道订单,请考虑以下事项:
@columns
让我们来看看它的作用。如果您有很长的列列表,这会很有帮助。
?
。INSERT
添加到UPDATE
,并将两者的组合添加到每个列的{{1}}。请注意,我没有运行此功能,只是在这里进行了黑客攻击。
答案 1 :(得分:0)
您应该将prepare语句放在循环之外并使用事务
例如:
my $sql1 = qq(select col_a from t_a where col_b = ?);
my $sql2 = qq(insert into t_a (col_a, col_b) VALUES(?, ?));
my $sql3 = qq(update t_a set col_a = ? where col_b = ?);
my $query = $dbh->prepare($sql1);
$dbh->begin_work();
foreach (@$va_arr) {
my $values_for_one_row = $_;
$query->execute($values_for_one_row->{col_b});
my @out = $query->fetchrow_array();
$query->finish();
if ( not defined $out[0] )
{
$dbh->do($sql2, undef, $values_for_one_row->{col_a}, $values_for_one_row->{col_b});
}
else
{
$dbh->do($sql3, undef, $values_for_one_row->{col_a}, $values_for_one_row->{col_b});
}
}
$dbh->commit();
答案 2 :(得分:0)
如果没有upsert,我可以这样做:
将数据批量加载到临时表中。
删除所有加入目标表的数据。
将数据从分段插入目标。
或者,您可以从登台更新到目标,从连接的登台数据中删除,然后插入暂存中的内容。
或者,几百行并不多,所以我可能:在循环之外准备一个insert和一个update语句句柄。然后在循环中:
my $rows = $upd_sth->execute(...);
$ins_sth->execute(...) if $rows == 0;