2009-05-13 15 views
5

Mam kwerendy, które znajdują się w wielu metod każdy (kwerendy), które mogą zawierać wiele parametrów. Próbuję zmniejszyć rozmiar pliku i liczbę linii, aby było łatwiejsze w utrzymaniu. Poniżej jest takie zdarzenie:Jak sprawić, by wiele zapytań do bazy danych było bardziej wydajnych w Perlu?

$sql_update = qq { UPDATE database.table 
        SET column = 'UPDATE!' 
        WHERE id = ? 
       }; 

$sth_update = $dbh->prepare($sql_update); 

if ($dbh->err) { 
    my $error = "Could not prepare statement. Error: ". $dbh->errstr ." Exiting at line " . __LINE__; 
    print "$error\n"; 
    die; 
} 

$sth_rnupdate->execute($parameter); 

if ($dbh->err) { 
    my $error = "Could not execute statement. Error: ". $dbh->errstr ." Exiting at line " . __LINE__; 
    print "$error\n"; 
    die; 
} 

Jest to tylko jeden z przykładów, jednak istnieje wiele innych wybranych przykładów, które zawierają tylko jeden parametr, które mają być przekazane w, jednak istnieje również niektóre z dwóch lub większej liczby parametrów. Sądzę, że po prostu zastanawiam się, czy możliwe jest zamknięcie tego wszystkiego w funkcji/metodzie, przekazanie tablicy parametrów, w jaki sposób parametry zostaną wprowadzone do funkcji execute()?

Gdyby było to możliwe, mógłbym napisać metodę, która po prostu przekazuje zapytanie SQL i parametry i zwraca odnośnik do pobranych rekordów. Czy to w ogóle brzmi bezpiecznie?

Odpowiedz

6

Jeśli Twoim jedynym celem jest liczba wierszy i możliwy do utrzymania kod, najlepiej byłoby użyć dowolnego z wielu dostępnych szkieletów/bibliotek ORM. Class::DBI i DBIx::Class to dwa dobre punkty początkowe. Na wszelki wypadek martwisz się, że poświęcisz więcej czasu na naukę tych modułów - nie: zajęło mi to tylko jedno popołudnie, aby zacząć i produkować. Korzystanie Class :: DBI na przykład Twój przykład jest tylko jedna linia:

Table->retrieve(id => $parameter)->column('UPDATE!')->update; 

Jedynym-stronie (jeśli tak) z tych ram jest to, że bardzo skomplikowane SQL wymagane pisanie niestandardowe metody nauczania, które mogą zabrać cię jakiś dodatkowy czas (nie za dużo), aby obejść.

+0

Brzmi bardzo korzystnie. Czy moduł Class :: DBI ma rdzeń perl? (dla unix) –

+0

Nie. Będziesz musiał go zainstalować. Wpisanie "install Class :: DBI" i odpowiedź "y" na wszystkie zależności wewnątrz powłoki CPAN zainstaluje ją dla ciebie. –

2

Funkcja "Wykonaj" przyjmuje akceptację tablicy zawierającej wszystkie parametry.

Po prostu trzeba znaleźć sposób, aby wskazać, które stwierdzenie obsłużyć chcesz wykonać i gotowe ...

Byłoby znacznie lepiej, aby zachować swoje oświadczenie obsługuje gdzieś ponieważ jeśli utworzyć nowy za każdym razem i przygotuj go za każdym razem, gdy naprawdę nie zgrywasz korzyści z "przygotowania" ...

O zwrocie wszystkich wierszy możesz to zrobić (coś takiego jak "while fetchrow_hashref push") uważaj na duże zestawy wyników, które coudl zjem całą twoją pamięć!

+0

Widzę, co mówisz - będę musiał zbadać to trochę. Wszystkie moje stwierdzenia naśladują kod podany powyżej, ale jedyną rzeczą, która jest zróżnicowana, są parametry i samo wyrażenie. –

2

Oto proste podejście za pomocą zamknięć/anonimowy subs przechowywane w hash wg nazwy słowa kluczowego (kompiluje, ale nie testowane inaczej), edytowane do obejmują wykorzystanie RaiseError:

# define cached SQL in hash, to access by keyword 
# 
sub genCachedSQL { 
    my $dbh = shift; 
    my $sqls = shift; # hashref for keyword => sql query 
    my %SQL_CACHE; 
    while (my($name,$sql) = each %$sqls) { 
    my $sth = $dbh->prepare($sql); 
    $SQL_CACHE{$name}->{sth} = $sth; 

    $SQL_CACHE{$name}->{exec} = sub { # closure for execute(s) 
     my @parameters = @_; 
     $SQL_CACHE{$name}->{sth}->execute(@parameters); 

     return sub { # closure for resultset iterator - check for undef 
      my $row; eval { $row = $SQL_CACHE{$name}->{sth}->fetchrow_arrayref(); }; 
      return $row; 
     } # end resultset closure 

    } # end exec closure 

    } # end while each %$sqls 

    return \%SQL_CACHE; 

} # end genCachedSQL 


my $dbh = DBI->connect('dbi:...', { RaiseError => 1 }); 

# initialize cached SQL statements 
# 
my $sqlrun = genCachedSQL($dbh, 
{'insert_table1' => qq{ INSERT INTO database.table1 (id, column) VALUES (?,?) }, 
    'update_table1' => qq{ UPDATE database.table1 SET column = 'UPDATE!' WHERE id = ? }, 
    'select_table1' => qq{ SELECT column FROM database.table1 WHERE id = ? }}); 

# use cached SQL 
# 
my $colid1 = 1; 
$sqlrun->{'insert_table1'}->{exec}->($colid1,"ORIGINAL"); 
$sqlrun->{'update_table1'}->{exec}->($colid1); 
my $result = $sqlrun->{'select_table1'}->{exec}->($colid1); 
print join("\t", @$_),"\n" while(&$result()); 

my $colid2 = 2; 
$sqlrun->{'insert_table1'}->{exec}->($colid2,"ORIGINAL"); 

# ... 
+0

To jest doskonałe. Spróbuję wdrożyć to rozwiązanie nieco później i dam ci znać, jak sobie radzę. –

+1

Cieszę się, że mogę przyczynić się - zdecydowanie sprawdź, jak eval zachowuje się w przypadku złych przypadków testowych. – bubaker

+0

Szybkie pytanie Bubaker, To zapytanie wykonało fetchall_arrayref(), które powoduje pobranie WSZYSTKICH rekordów, co jest problematyczne, ponieważ moje zapytanie powraca nadmiar 300 000 rekordów ze średnią około 9 kolumn. Czy istnieje sposób, aby to zapytanie wykorzystało fetchrow_arrayref() lub fetchrow_hashref do iterowania poszczególnych rekordów? –

1

Jestem pod wrażeniem bubaker użytkownika przykład zastosowania do tego zamknięcia.

To samo, jeśli pierwotnym celem było uczynienie bazy kodu mniejszą i łatwiejszą w utrzymaniu, nie mogę powstrzymać się od myślenia, że ​​hałas błagający o usunięcie z oryginalnego kodu, zanim ktokolwiek wyruszy na konwersję do CDBI, DBIC itp. (niezależnie od wielkich bibliotek, w których obaj są).)

Jeśli $dbh był tworzony z RaiseError w zestawie atrybutów, większość tego kodu odchodzi:

$sql_update = qq { UPDATE database.table 
        SET column = 'UPDATE!' 
        WHERE id = ? 
       }; 
$sth_update = $dbh->prepare($sql_update); 
$sth_update->execute($parameter); 

nie widzę, że obsługa błędów w oryginalnym kodzie jest dodanie bardzo, że nie można uzyskać z wanilii die wyprodukowanej przez RaiseError, ale jeśli jest to ważne, spójrz na atrybut HandleError na stronie podręcznika DBI.

Ponadto, jeśli takie instrukcje nie są ponownie wykorzystywane (co często jest głównym celem ich przygotowywania, buforowanie sposobu ich optymalizacji, innym powodem jest złagodzenie przeciwskórnej iniekcji SQL za pomocą symboli zastępczych), to dlaczego nie użyć do?

$dbh->do($sql_update, \%attrs, @parameters); 
4

Nie ma sensu sprawdzanie błędów po każdym wywołaniu bazy danych. Jak nudne!

Zamiast tego, po połączeniu się z bazą danych, ustaw opcję RaiseError na true. Jeśli wystąpi błąd bazy danych, zostanie zgłoszony wyjątek. Jeśli go nie złapiesz (w bloku eval {}), twój program zginie z komunikatem, podobnym do tego, co robiłeś ręcznie powyżej.

Powiązane problemy