Как мога да получа брой редове в DBI, без да изпълнявам две отделни извиквания за обработка?

Изпълнявам DBI в Perl и не мога да разбера как, когато изпълнявам подготвен оператор, мога да разбера дали върнатият брой редове е 0.

Осъзнавам, че мога да задам брояч в моя цикъл while, където извличам редовете си, но се надявах, че има по-малко грозен начин да го направя.


person Yevgeny Simkin    schedule 12.05.2009    source източник


Отговори (12)


За да разберете колко реда има в набор от резултати, имате точно две опции:

  1. select count(*)
  2. Превъртете набора от резултати и пребройте редовете.

Можете да въведете някаква магия чрез съхранена процедура, която връща масив или нещо по-фантастично, но в крайна сметка ще трябва да се случи едно от тези две неща.

Така че няма фантастичен начин да постигнете този резултат. Просто трябва да ги преброите :-)

person Edward Q. Bridges    schedule 13.05.2009
comment
Ключът тук беше неразбирането ми какво прави fetch. Мислех, че върна някакъв хеш и просто го повторих, но всъщност цялото нещо е поток, така че извличате редове направо от DB (а не от някакъв върнат обект), така че, подготвеното изпълнение наистина няма представа колко върнати редове има. - person Yevgeny Simkin; 14.05.2009

Въз основа на бърз поглед тук, изглежда, че след като стартирате

$statement->execute($arg)

можете да получите достъп до броя на редовете чрез

$statement->rows
person Harper Shelby    schedule 12.05.2009
comment
това е наистина досадно... точно там гледах, но напълно го пропуснах, защото поставиха това if($sth-›rows == 0) СЛЕД цикъла, където четат редовете!! благодаря, че го хвана. - person Yevgeny Simkin; 13.05.2009
comment
Обърнете внимание на много важното предупреждение относно редовете в [search.cpan.org /~timb/DBI-1.608/DBI.pm#rows POD]. - person jplindstrom; 13.05.2009
comment
Ъъъ, да, явно така не се правят линкове :) - person jplindstrom; 13.05.2009

уговорката в документацията (свързана в коментар към друг отговор) е важен и дава истинския, правилен отговор:

Като цяло можете да разчитате само на брой редове след изпълнение на non-SELECT (за някои специфични операции като UPDATE и DELETE) или след извличане на всички редове на оператор SELECT.

За операторите SELECT обикновено не е възможно да се знае колко реда ще бъдат върнати, освен като се извлекат всички. Някои драйвери ще върнат броя на редовете, които приложението е извлякло досега, но други може да върнат -1, докато не бъдат извлечени всички редове. Така че не се препоръчва използването на метода rows или $DBI::rows с изрази SELECT.

person John Siracusa    schedule 13.05.2009
comment
така че сега, след като знаем, че -›редове не е отговорът на въпроса... КАКЪВ Е отговорът? В примера, цитиран по-горе (в отговора, който първоначално избрах като правилен), те правят избор и след това виждат дали редове == 0... така че отново съм объркан. - person Yevgeny Simkin; 14.05.2009
comment
Както се казва в документите, за операторите SELECT обикновено не е възможно да се знае колко реда ще бъдат върнати, освен като се извлекат всички. Ключовата дума е като цяло. Някои бази данни, използващи някои машини, може да се държат по различен начин. Но единственият начин да сте 100% сигурни във всички възможни случаи е да извлечете всеки ред и да преброите. - person John Siracusa; 14.05.2009

Малко е късно, но ако някой използва ORACLE, ето решението за изпотяване:

SELECT
  q.*,
  ROWNUM DB_ROWNUM,
  (SELECT max(ROWNUM) FROM ($sql)) DB_COUNT
FROM
  ($sql) q

$sql е вашата заявка, разбира се. Оптимизаторът на Oracles е достатъчно интелигентен, за да не изпълнява всичко два пъти.

Сега всеки извлечен ред съдържа текущия номер на ред (полезно за номериране на редове в решетка за страници) в DB_ROWNUM и пълния брой редове в DB_COUNT. Все още трябва да извлечете поне един ред (така че това не е точно отговорът на въпроса по-горе ;)), но използването на пот идва след това:

Също така е много лесен начин да направите начало и ограничение в Oracle и пак да получите пълния брой редове:

SELECT * FROM (
  SELECT /*+ FIRST_ROWS($limit) */
    q.*,
    ROWNUM DB_ROWNUM,
    (SELECT max(ROWNUM) FROM ($sql)) DB_COUNT
  FROM
    ($sql) q
  WHERE
    ROWNUM <= $limit
)
WHERE
  DB_ROWNUM > $start

С това всъщност можете да извлечете само редове от 51 до 100 за втората страница във вашата мрежа, но все пак да имате реалния номер на ред (започвайки от 1) и пълния брой (без начало и ограничение) във всеки извлечен ред.

person Neo van Goth    schedule 17.04.2013
comment
имаш предвид пот или сладко? - person Yevgeny Simkin; 17.04.2013

опитайте това SQL решение, комбинирайте своя SQL за данните с отчет за преброяване.

select null, null, null, count(*) from tablex
union
select foo, bar, foobar, null from tablex

първото изявление ще има броя и трябва да бъде първият ред (ако не можете да го поръчате, за да го заобиколите), след което можете да преминете през останалата част от набора от записи в свободното си време.

person Ward Kaatz    schedule 04.01.2012

CPAN казва:

[...] или след извличане на всички редове на оператор SELECT.

Така че правенето на нещо подобно вероятно ще работи:

$sth->execute or die $sth->errstr;

say (scalar keys %{$sth->fetchall_hashref('id')}) . ' row(s).';
person Yoann Le Garff    schedule 23.03.2014

Динамично генерирах SQL и го изпълнявам. За мен select count(*) не изглежда като опция, защото трябва отново да генерирам отново заявката. Следващият подход изглеждаше чист за мен. Но трябва да издадете s $h->execute() още веднъж, за да извлечете данни от реда.

$h->execute() or die "ERROR: Couldn't execute SQL statement";
$rowcount_ref = $h->fetchall_arrayref(0);
$rowcount = scalar (@{$rowcount_ref});

-- Шаакунтала

person Shaakunthala    schedule 07.02.2013

Редовете определено варират в зависимост от версията на базата данни/драйвера. Определено бих потърсил част от логиката там, която се занимава с неочаквани резултати.

person hpavc    schedule 12.05.2009

Ако искате да знаете колко реда има, преди да преминете през всички тях, специфично за MySQL решение може да бъде FOUND_ROWS().

В първата си заявка добавете SQL_CALC_FOUND_ROWS веднага след SELECT. След това направете SELECT FOUND_ROWS(); и имате достъп до броя на редовете веднага. Сега можете да решите дали искате да преминете през всички редове или най-добрия начин да го направите.

Обърнете внимание, че наличието на LIMIT в заявката ще ви даде общото число, което заявката би върнала без LIMIT.

person Richlv    schedule 11.08.2017

Както други вече казаха, наистина трябва да получите редовете, за да разберете дали има такива. Ако трябва да знаете, преди да започнете цикъл на всеки ред и ако очакваните резултати не са големи, можете да получите всички резултати в масив и след това да проверите това.

Наскоро използвах нещо подобно:

foreach my $table ( qw(ACTOR DIRECTOR PRODUCER WRITER) ) {
    my $sth = $dbi->prepare(qq{SELECT * FROM $table WHERE DESCRIPTION != TRIM(DESCRIPTION)})
        or die $dbi->errstr;
    $sth->execute or die $sth->errstr;

    my @rows = @{ $sth->fetchall_arrayref() };

    next unless @rows;

    foreach my $row (@rows) {
        print join(", ", map {qq("$_")} @{$row}), "\n";
    }
}
person mivk    schedule 06.12.2017

Вместо това просто бих оставил базата данни да преброи. Ако всичко, което искате, е броят на редовете. Намерете всички редове за януари 2018 г.

$year='2018';
my $qry = "SELECT COUNT(`my_key`) FROM `mtable` WHERE `my_date` LIKE '$year-01-%';";
my ($count) = $dbc->selectrow_array($qry);
person agrov8    schedule 09.08.2018
comment
всъщност това брои само броя редове, където полето my_key не е null - което може или не може да е това, което искате. - person theMage; 14.11.2020

Мисля, че това може да се постигне доста лесно с помощта на препратки. Кодовият удар по-долу показва как не е необходимо да се преминава към повторно изпълнение на заявката за извличане на редовете след преброяване.

my $sth = $dbh->prepare("SELECT NAME, LOCATION
                        FROM Employees"); 
$sth->execute() or die $DBI::errstr;

$nrows =  $sth->fetchall_arrayref() ;

print "Number of rows found :" . @$nrows . "\n";

#while (my @row = $sth->fetchrow_array()) {

foreach my $row(@{$nrows}) {
   my ($name, $location ) = @$row;
   print " Name = $name, Location = $location\n";
}

Надявам се, че това отговаря на зададения тук въпрос.

person ossuser    schedule 13.01.2021