【问题标题】:How can I check if a database query will return results?如何检查数据库查询是否会返回结果?
【发布时间】:2010-09-22 08:28:06
【问题描述】:

我们的网站使用 Perl 为我们的人力资源人员提供了一种简单的机制,可以在我们的网站上发布职位空缺。它是由第三方开发的,但他们早就开始接触了,遗憾的是我们内部没有任何 Perl 技能。当营销人员绕过他们的内部 IT 团队时,就会发生这种情况!

我需要对此应用程序进行简单的更改。目前,职位空缺页面显示“我们目前有以下职位空缺:”,无论是否有职位空缺!所以我们想改变它,使这条线只在适当的时候显示。

显然,我可以开始学习一点 Perl,但我们已经在计划一个替代站点,而且它肯定不会使用 Perl。因此,由于解决方案对于具有这些技能的人来说是微不足道的,所以我想我会寻求一些有针对性的帮助。

下面是列出职位空缺的程序的开始。

sub list {
  require HTTP::Date;
  import HTTP::Date;

  my $date = [split /\s+/, HTTP::Date::time2iso(time())]->[0];

  my $dbh = DBI->connect($dsn, $user, $password)
    || die "cannot connect to $database: $!\n";

  my $sql = <<EOSQL;
SELECT * FROM $table where expiry >= '$date' order by expiry
EOSQL

  my $sth = $dbh->prepare($sql);
  $sth->execute();


  while (my $ref = $sth->fetchrow_hashref()) {
    my $temp  = $template;
    $temp     =~ s#__TITLE__#$ref->{'title'}#;

    my $job_spec = $ref->{'job_spec'};

...etc...

关键行是while (my $ref = $sth-&gt;fetchrow_hashref()) {。我想这是在说'虽然我可以从返回的记录集中拉出另一个空缺......'。如果我将打印语句放在这一行之前,它将始终显示;在这条线之后,每个空缺都会重复。

如何确定有一些空缺要显示,而不会过早移动返回的记录集?

我总是可以在 while 循环中复制代码,并将其放在 if() 语句中(在 while 循环之前),该语句也将包括我的 print 语句。但我更喜欢使用If any records then print "We currently have.." line 更简单的方法。不幸的是,我连这个简单的行都没有编写代码的线索。

看,我告诉过你这是一个微不足道的问题,即使考虑到我笨拙的解释!

TIA

克里斯

【问题讨论】:

  • 请注意,如果 require 失败,它会自动为您而死,所以我稍微调整了一下,这样就没有人可以将其复制并粘贴到他们的代码中。 :)

标签: perl dbi rows


【解决方案1】:

一个非常简单的方法是:

$sth->execute();

my $first = 1;
while (my $ref = $sth->fetchrow_hashref()) {
    if( $first ) {
        print "We currently have the following vacancies:\n";
        $first = 0;
    }
    my $temp  = $template;
    ...
}
if( $first ) {
    print "No vacancies found\n";
}

【讨论】:

  • 或者你可以检查$sth->execute()的返回值,它会提前告诉你是否会有任何数据。
  • @SamKington:$sth->execute() 的返回值仅返回非 SELECT 语句受影响的行数。见:search.cpan.org/~timb/DBI-1.631/DBI.pm#execute
【解决方案2】:

如果你使用的是 Mysql,“rows”方法就可以了:

$sth->execute();

if($sth->rows) {
  print "We have data!\n";
}

while(my $ref = $sth->fetchrow_hashref()) {
...
}

“perldoc DBI”中详细记录了该方法和一些注意事项。始终以“perldoc”开头。

【讨论】:

【解决方案3】:

这与其说是一个 Perl 问题,不如说是一个数据库问题,而且在获得结果之前,没有什么好方法可以知道您有多少结果。这里有两个选择:

  1. 执行“select count(*)”查询以查看有多少行,然后执行另一个查询以获取实际行或
  2. 执行查询并将结果存储到散列中,然后计算散列中有多少条目,然后遍历散列并打印出结果。

例如,在我的脑海中:

my @results = ();
while (my $ref = $sth->fetchrow_hashref()) {
   push @results, $ref;
}

if ($#results == 0) {
  ... no results
} else {
  foreach $ref (@results) {
    my $temp = $template;
    ....
 }

【讨论】:

  • 如果你要这样做,你还不如把读取循环替换为@results = @{ $sth->fetchall_arrayref( {} ) };
  • $#results 在只有一个结果时将为 0。除非您确定需要,否则请不要使用 $#array。大多数时候,标量上下文中的 @array 是您想要的。
  • @ysth - 好吧,我确实说过这不是我的想法。 scalar(@array) 会做我想做的事吗?
  • 另一个问题是,如果结果中有数千(或数十万)行,您可能不想同时将它们全部保存在内存中。
  • @Graeme:除非他在 Monster.com 工作,否则我怀疑他有数千个空缺职位。
【解决方案4】:

由于每个人都想优化格雷姆的解决方案中是否打印标题的重复测试,所以我提出了这个小的变化:

$sth->execute();

my $ref = $sth->fetchrow_hashref();
if ($ref) {
  print "We currently have the following vacancies:\n";
  while ($ref) {
    my $temp  = $template;
    ...
    $ref = $sth->fetchrow_hashref();
  }
} else {
    print "No vacancies found\n";
}

【讨论】:

    【解决方案5】:

    由于您的查询是 SELECT,因此您无法利用 rowsexecute 本身返回的值。

    但是,您可以通过添加另一个查询来预先计算您的查询将选择多少行(即空缺)...如下所示:

    # Retrieve how many vacancies are currently offered:
    my $query = "SELECT COUNT(*) AS rows FROM $table WHERE expiry >= ?";
    $sth = $dbh->prepare($query);
    $sth->execute($date);
    $numVacancies = $numinfo->fetchrow_arrayref()->[0];
    
    # Debug:
    print "Number of vacancies: " . $numVacancies . "\n";
    
    if ( $numVacancies == 0 ) { # no vacancy found...
        print "No vacancies found!\n";
    }
    else { # at least a vacancy has been found...
        print "We currently have the following vacancies:\n";
    
        # Retrieve the vacancies:
        my $sql = "SELECT * FROM $table where expiry >= '$date' ORDER BY expiry";
        my $sth = $dbh->prepare($sql);
        $sth->execute();
    
        ...
    }
    

    或者,类似地,代替 "prepare""execute" 查询然后使用 "fetchrow_array",您可以做任何事情在使用selectrow_array 的单个呼叫中:

    # Retrieve how many vacancies are currently offered:
    my $query = "SELECT COUNT(*) AS rows FROM $table WHERE expiry >= ?"; 
    my $numVacancies = $dbh->selectrow_array($query, undef, $date);
    
    # Debug:
    print "Number of vacancies: " . $numVacancies . "\n";
    

    selectall_arrayref 也是如此:

    # Retrieve how many vacancies are currently offered:
    my $query = "SELECT COUNT(*) AS rows FROM $table WHERE expiry >= ?";
    my $numVacancies = $dbh->selectall_arrayref($query, {Slice => {}}, $date);
    
    # Debug:
    print "Number of vacancies: " . @$numVacancies[0]->{rows} . "\n";
    

    但是,如果你使用selectrow_arrayselectall_arrayref,你也可以直接从原始查询的结果中检索空缺数:

    # Retrieve the vacancies:
    my $sql = "SELECT * FROM $table where expiry >= ? ORDER BY expiry";
    my $vacancies = $dbh->selectall_arrayref($sql, {Slice => {}}, $date);
    
    # Debug:
    print "Number of vacancies: " . scalar @{$vacancies} . "\n";
    

    【讨论】:

      【解决方案6】:

      一种更有效的方式(避免循环内的条件),如果您不介意它稍微改变页面的输出方式(一次全部而不是一次一行),您可以创建一个变量在循环之前保持输出:

      my $output = '';
      

      然后在循环内部,将任何打印语句更改为如下所示:

      $output .= "whatever we would have printed";
      

      然后在循环之后:

      if ($output eq '')
      {
        print 'We have no vacancies.';
      }
      else
      {
        print "We currently have the following vacancies:\n" . $output;
      }
      

      【讨论】:

      • 除非他们是谷歌,否则他们在任何时候都可能没有超过几个或几十个空缺,因此与呈现页面的成本相比,条件的成本是微不足道的。我仍然投票支持@Graeme 的解决方案。
      • 当然可以,但是如果代码恰好在另一个上下文中被重用,那么考虑一下。
      • 我确信代码会在很多地方重复使用,但我们不会! :) 但我很欣赏你的观点 - 作为一种“良好实践”的方法是有意义的。
      • 我也不相信执行字符串连接操作比比较更有效。如果您真的想违反优化俱乐部的第一条规则,那绝对是需要进行基准测试的事情。
      • 同意。对于大型数据集,我还希望连接(及其相关的内存分配和字符串复制要求)比进行测试的效率要低得多。
      【解决方案7】:

      只需添加另一个查询.. 像这样:

      # count the vacancies    
      $numinfo = $dbh->prepare("SELECT COUNT(*) FROM $table WHERE EXPIRY >= ?");
      $numinfo->execute($date);
      $count = $numinfo->fetchrow_arrayref()->[0];
      
      # print a message
      my $msg = '';
      if   ($count == 0) $msg = 'We do not have any vacancies right now';
      else               $msg = 'We have the following vacancies';
      print($msg);
      

      【讨论】:

        【解决方案8】:
        use Lingua::EN::Inflect 'PL';
        
        $sth->execute();
        my $results = $sth->fetchall_arrayref( {}, $max_rows );
        
        if (@$results) {
            print "We currently have the following ", PL("vacancy",scalar @$results), ":\n";
        
            for my $ref (@$results) {
                ...
            }
        }
        

        【讨论】:

        • 区分单个空位y和多个空位ies
        • 我没有看到任何其他使用 fetchall_arrayref 的解决方案。
        【解决方案9】:

        perldoc DBI 说:

         For a non-"SELECT" statement, "execute" returns the number of rows
         affected, if known. If no rows were affected, then "execute"
         returns "0E0", which Perl will treat as 0 but will regard as true.
        

        所以答案是检查$sth->execute()的返回值:

         my $returnval = $sth->execute;
         if (defined $returnval && $returnval == 0) {
             carp "Query executed successfully but returned nothing";
             return;
         }
        

        【讨论】:

        • perldoc 专门说“对于非 SELECT 语句”,并且问题中有一个 SELECT 语句。
        猜你喜欢
        • 1970-01-01
        • 2023-04-10
        • 1970-01-01
        • 2020-11-13
        • 2016-07-23
        • 2023-04-01
        • 2012-10-21
        • 1970-01-01
        • 2016-09-03
        相关资源
        最近更新 更多