Наш веб-сайт использует Perl, чтобы предоставить нашим сотрудникам отдела кадров простой механизм для размещения вакансий на нашем веб-сайте. Он был разработан третьей стороной, но они уже давно были вовлечены в контакт, и, к сожалению, у нас нет собственных навыков Perl. Вот что происходит, когда маркетологи обходят своих штатных ИТ-специалистов!
Мне нужно внести простые изменения в это приложение. В настоящее время на странице вакансий написано «В настоящее время у нас есть следующие вакансии:», независимо от того, есть ли какие-либо вакансии! Поэтому мы хотим изменить его так, чтобы эта строка отображалась только в подходящее время.
Я мог бы, конечно, начать немного изучать 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->fetchrow_hashref()) {. Я полагаю, что это говорит «пока я могу снять еще одну вакансию из возвращенного набора записей ...». Если я помещаю свой оператор печати перед этой строкой, он всегда будет отображаться; после этой строки и повторяется для каждой вакансии.
Как определить, есть ли какие-то вакансии, которые нужно отобразить, без преждевременного просмотра возвращенного набора записей?
Я всегда мог скопировать код внутри цикла while и поместить его в оператор if () (предшествующий циклу while), который также будет включать мой оператор печати. Но я бы предпочел использовать более простой подход If any records then print "We currently have.." line. К сожалению, я понятия не имею, как кодировать даже эту простую строчку.
Видите ли, я сказал вам, что это тривиальная проблема, даже с учетом моих неуклюжих объяснений!
TIA
Крис





Это не столько вопрос Perl, сколько вопрос базы данных, и нет хорошего способа узнать, сколько результатов у вас есть, пока вы их не получите. У вас есть два варианта:
Например, с головы до ног:
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 ({})};
$ # результатов будет 0, если есть один результат. Пожалуйста, не используйте массив $ #, если вы не уверены, что вам это нужно. В большинстве случаев @array в скалярном контексте - это то, что вам нужно.
@ysth - Ну, я же сказал, что это было не в моей голове. Будет ли скаляр (@array) делать то, что я хочу?
Другая проблема заключается в том, что если в результате появляются тысячи (или сотни тысяч) строк, возможно, вы не захотите хранить их все в памяти одновременно.
@Graeme: Если он не работает на monster.com, я сомневаюсь, что у него есть тысячи открытых вакансий.
@Paul: scalar (@array) вернет количество элементов в массиве, да, но в этом случае достаточно просто проверки 'if (@array) ...' и, IMO, более читаемым, чем явная скаляризация. .
@Dave - Мне никогда не нравилась эта "контекстная" особенность Perl. Я думаю, что яснее изложить это прямо.
@ Пол: Да, в данном конкретном случае это не проблема. Я имел в виду общий случай.
@ Пол: Честно говоря, многим это не нравится. Моя точка зрения заключалась скорее в том, что концептуально вы здесь ищете, есть ли что-нибудь в массиве (if (@array)), а не количество элементов, которые он содержит (if (scalar (@array)! = 0) ). Но, как всегда, TIMTOWTDI.
Очень простой способ:
$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
Немного более эффективный способ (избегая условного выражения внутри цикла), если вы не возражаете, чтобы он немного изменил способ вывода страницы (все сразу, а не по строкам), вы можете создать переменную для хранения вывод непосредственно перед циклом:
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;
}
Если они не Google, у них, вероятно, не может быть больше, чем несколько или несколько десятков вакансий в любое время, поэтому стоимость условного объявления ничтожна по сравнению со стоимостью отрисовки страницы. Я все еще голосую за решение @ Graeme.
Конечно, но если код будет повторно использован в другом контексте, хорошо подумать.
Я уверен, что код будет повторно использован во многих местах, но не нами! :) Но я ценю ваше мнение - это было бы разумно как подход «хорошей практики».
Я также не уверен, что выполнение операции конкатенации строк более эффективно, чем сравнение. Это определенно то, что потребует тестирования, если вы действительно хотите нарушить первое правило Optimization Club.
Согласовано. Для большого набора данных я также ожидал бы, что конкатенации (с соответствующими требованиями к выделению памяти и копированию строк) будут существенно менее эффективными, чем выполнение теста.
Просто добавьте еще один запрос .. примерно так:
# 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);
Если вы используете Mysql, метод "строк" работает нормально:
$sth->execute();
if ($sth->rows) {
print "We have data!\n";
}
while(my $ref = $sth->fetchrow_hashref()) {
...
}
Метод и некоторые предостережения подробно описаны в "perldoc DBI". Всегда начинайте с perldoc.
Вы не можете полагаться на $ sth-> rows для запросов SELECT (как рассматриваемый). См .: search.cpan.org/~timb/DBI-1.631/DBI.pm#rows
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) {
...
}
}
Он различает один vacancy и несколько vacancйес.
И я не вижу других решений с использованием fetchall_arrayref.
Поскольку каждый хочет оптимизировать повторные тесты для проверки того, был ли напечатан заголовок в решении Грэма, я представляю его небольшую вариацию:
$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";
}
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.
Поскольку ваш запрос представляет собой ВЫБРАТЬ, вы не можете использовать ряды или значение, возвращаемое самим выполнять.
Однако вы можете заранее подсчитать, сколько строк (т.е. вакансий) выберет ваш запрос, добавив еще один запрос ... примерно так:
# 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();
...
}
Или, аналогично, вместо "подготовить" и "выполнять" запроса, а затем использования "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_array или selectall_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";
Обратите внимание, что require автоматически умирает для вас, если он терпит неудачу, поэтому я немного изменил это, чтобы никто не мог скопировать и вставить это в свой код. :)