DBD::mysql allows programs to prepare asynchronous queries. Execution returns immediately, and it's up to the program to fetch results when they're ready. The DBD::mysql documentation explains ways to poll for results, but POE can notify programs when they're ready using a select_read() callback.

This is a tricky technique if a program needs asynchronous results from several queries at once. All the results are funneled into the same callback because they arrive at the same filehandle. The most obvious solution is to open a searate database connection for each parallel pipeline.

#!/usr/bin/env perl

use warnings;
use strict;

use POE;
use DBI;

POE::Session->create(
  inline_states => {
    _start    => \&execute_query,
    got_timer => \&say_something,
    got_data  => \&fetch_and_shutdown,
  }
);

POE::Kernel->run();
exit;

sub execute_query {
  my ($kernel, $heap) = @_[KERNEL, HEAP];

  # Connect to the database and start an asynchronous request.
  # Light on error checking because it's an example.

  my $dbh = DBI->connect(
    'dbi:mysql:',
    undef, undef,
    {
      PrintError => 0,
      RaiseError => 1,
    }
  );

  my $sth = $dbh->prepare('SELECT SLEEP(3), 3', {async => 1});
  $sth->execute();

  # Wait for mysql to send back data.

  open(my ($mysql_fh), "<&=", $dbh->mysql_fd()) or die "dup: $!";
  $kernel->select_read($mysql_fh, 'got_data');

  # Periodically display something, so we know it's working.

  $kernel->delay(got_timer => 1);

  # Don't let the handles go out of scope.
  # We could put these in globals, but that's not good practice.

  $heap->{dbh} = $dbh;
  $heap->{sth} = $sth;
}

sub fetch_and_shutdown {
  my ($kernel, $heap, $mysql_fh) = @_[KERNEL, HEAP, ARG0];

  # We don't need these anymore.  Let them go out of scope.  Not
  # technically necessary since we're going to let the session
  # destruct.

  my $dbh = delete $heap->{dbh};
  my $sth = delete $heap->{sth};

  print "got data from MySQL\n";
  print join(' ', $sth->fetchrow_array), "\n";

  # Stop watching the filehandle, and stop the interval timer.

  $kernel->select_read($mysql_fh => undef);
  $kernel->delay(got_timer => undef);
}

sub say_something {
  print "timer fired!\n";
  $_[KERNEL]->delay(got_timer => 1);
}