OVH Cloud OVH Cloud

Nombre variables de params dans une requete SQL

10 réponses
Avatar
Marc Nadeau
J'ai un script qui interroge une BD mysql (banal) et qui parfois ne doit
tenir compte que d'un champs de recherche, parfois doit faire une recherche
plus serrée en sélectionnant sur 2 ou 3 champs.

Actuellement, je dois faire effectuer la requête par des fonctions
différentes selons le nombre de champs utilisés.

--------------
# fonction1: un seul critère:
snip...
my ($critere, $clef) = @_;
snip...
$sth = $dbh->prepare ("SELECT COUNT(*) FROM bannieres WHERE $critere LIKE
\"$clef\" ");
snip...
----------------
# fonction2: 2 critères
my ($critere01, $clef01, $critere02, $clef02) = @_;
$sth = $dbh->prepare ("SELECT COUNT(*) FROM bannieres WHERE $critere01 LIKE
\"$clef01\" AND $critere02 LIKE \"$clef02\" ");

--------------
# fonction3: Ainsi de suite...
---------------

Je voudrais faire exécuter toutes ces requêtes par une seule et même
fonction qui créerait une requête différente selon le nombre de paramètres
envoyés à la dite fonction.

Ce n'est pas critique pcq le programme fonctionne bien, mais je veux éviter
la redondance dans le code.


Merci d'avance!

--
L'Amiral nous brouille l'écoute avec sa panne de micro

10 réponses

Avatar
root
On Sun, 23 Nov 2003 07:13:35 +0000, Marc Nadeau wrote:


J'ai un script qui interroge une BD mysql (banal) et qui parfois ne doit
tenir compte que d'un champs de recherche, parfois doit faire une recherche
plus serrée en sélectionnant sur 2 ou 3 champs.

Actuellement, je dois faire effectuer la requête par des fonctions
différentes selons le nombre de champs utilisés.

--------------
# fonction1: un seul critère:
snip...
my ($critere, $clef) = @_;
snip...
$sth = $dbh->prepare ("SELECT COUNT(*) FROM bannieres WHERE $critere LIKE
"$clef" ");
snip...
----------------
# fonction2: 2 critères
my ($critere01, $clef01, $critere02, $clef02) = @_;
$sth = $dbh->prepare ("SELECT COUNT(*) FROM bannieres WHERE $critere01 LIKE
"$clef01" AND $critere02 LIKE "$clef02" ");

--------------
# fonction3: Ainsi de suite...
---------------



--8<--
#!/usr/bin/perl

use strict;
use warnings;

print requete(
'A' => '1',
'B' => '2',
'C' => '3',
'D' => '4',
);

sub requete {
my %parms = @_;

my $requete = "SELECT COUNT(*) FROM bannieres WHERE ";

my @criteres_like;
foreach (keys %parms) {
push @criteres_like, "$_ LIKE $parms{$_}";
}

$requete .= join(" AND ", @criteres_like);

return $requete;
}

-->8--

SELECT COUNT(*) FROM bannieres WHERE A LIKE 1 AND D LIKE 4 AND C LIKE 3
AND B LIKE 2

Note: l'utilisation d'un hash fait que l'ordre des critères passés en
paramètre n'est pas conservé. Mais c'est tellement plus simple/élégant
avec un hash :)

Avatar
Alex Marandon
In article <z6Zvb.60940$, Marc Nadeau wrote:

J'ai un script qui interroge une BD mysql (banal) et qui parfois ne doit
tenir compte que d'un champs de recherche, parfois doit faire une recherche
plus serrée en sélectionnant sur 2 ou 3 champs.

Actuellement, je dois faire effectuer la requête par des fonctions
différentes selons le nombre de champs utilisés.

--------------
# fonction1: un seul critère:
snip...
my ($critere, $clef) = @_;
snip...
$sth = $dbh->prepare ("SELECT COUNT(*) FROM bannieres WHERE $critere LIKE
"$clef" ");
snip...
----------------
# fonction2: 2 critères
my ($critere01, $clef01, $critere02, $clef02) = @_;
$sth = $dbh->prepare ("SELECT COUNT(*) FROM bannieres WHERE $critere01 LIKE
"$clef01" AND $critere02 LIKE "$clef02" ");


# Tu répupère les parametres dans un hash.
my %criteres = @_;

# Le WHERE TRUE fonctionne avec PostgreSQL. Avec MySQL, je crois me
# rappeler que la syntaxe est différente (WHERE 1, il me semble).
# L'important est d'avoir une première condition toujours vraie
# simplement pour pouvoir empiler les AND derrière.
my $sql = 'SELECT COUNT(*) FROM bannieres WHERE TRUE ';

# On crée les conditions de la clause WHERE
for ( keys %criteres ) {
$sql .= ' AND $_ LIKE ' . $criteres{$_} ;
}

# Et voila :-)
$sth = $dbh->prepare($sql);

__END__

Voila, à tester, adapter selon tes besoins, mais le principe est là. Il
faut caculer ta chaine SQL dynamiquement.

Avatar
Alex Marandon
In article , root wrote:
my @criteres_like;
foreach (keys %parms) {
push @criteres_like, "$_ LIKE $parms{$_}";
}

$requete .= join(" AND ", @criteres_like);


Ah bah oui, c'est encore mieux comme ça tiens :)

Avatar
Nicolas Chuche
root disait le 11/23/03 que :

my @criteres_like;
foreach (keys %parms) {
push @criteres_like, "$_ LIKE $parms{$_}";
}


"map" est tout indiqué dans ce cas là :

my @criteres_like = map { "$_ LIKE $parms{$_}" } keys %parms;

Avatar
jeanpierre.vidal
Marc Nadeau wrote in message news:<z6Zvb.60940$...
J'ai un script qui interroge une BD mysql (banal) et qui parfois ne doit
tenir compte que d'un champs de recherche, parfois doit faire une recherche
plus serrée en sélectionnant sur 2 ou 3 champs.

Actuellement, je dois faire effectuer la requête par des fonctions
différentes selons le nombre de champs utilisés.

--------------
# fonction1: un seul critère:
snip...
my ($critere, $clef) = @_;
snip...
$sth = $dbh->prepare ("SELECT COUNT(*) FROM bannieres WHERE $critere LIKE
"$clef" ");
snip...
----------------
# fonction2: 2 critères
my ($critere01, $clef01, $critere02, $clef02) = @_;
$sth = $dbh->prepare ("SELECT COUNT(*) FROM bannieres WHERE $critere01 LIKE
"$clef01" AND $critere02 LIKE "$clef02" ");

--------------
# fonction3: Ainsi de suite...
---------------

Je voudrais faire exécuter toutes ces requêtes par une seule et même
fonction qui créerait une requête différente selon le nombre de paramètres
envoyés à la dite fonction.

Ce n'est pas critique pcq le programme fonctionne bien, mais je veux éviter
la redondance dans le code.


Merci d'avance!


splice ? ou shift ? quelque chose comme ceci (non testé) :

my $query;
my $first = 1;
my $critere;
my $clef;

while (@_) {
...
($critere, $clef) = splice(@_, 0, 2);

<ou encore>
$critere = shift; $clef = shift;
</ou encore>

...
if (first) {
$query = "SELECT COUNT(*) FROM bannieres WHERE $critere LIKE "$clef";
} else {
$query .= " AND $critere LIKE "$clef";
}
...
$first = 0;
}
$sth = $dbh->prepare ($query);

Jean-Pierre

Avatar
root
On Sun, 23 Nov 2003 15:49:40 +0100, Nicolas Chuche wrote:

root disait le 11/23/03 que :

my @criteres_like;
foreach (keys %parms) {
push @criteres_like, "$_ LIKE $parms{$_}";
}


"map" est tout indiqué dans ce cas là :

my @criteres_like = map { "$_ LIKE $parms{$_}" } keys %parms;


Effectivement, je pensais aussi faire un « truc » en une seule ligne,
mais j'ais raté le `map' :)

On peut donc reduire la fonction à ça :

sub requete {
my %parms = @_;
return "SELECT COUNT(*) FROM bannieres WHERE ".
join(" AND ", map { "$_ LIKE $parms{$_}" } keys %parms );
}


Avatar
Marc Nadeau
Marc Nadeau a écrit:


J'ai un script qui interroge une BD mysql (banal) et qui parfois ne doit
tenir compte que d'un champs de recherche, parfois doit faire une
recherche plus serrée en sélectionnant sur 2 ou 3 champs.

Actuellement, je dois faire effectuer la requête par des fonctions
différentes selons le nombre de champs utilisés.

--------------
# fonction1: un seul critère:
snip...
my ($critere, $clef) = @_;
snip...
$sth = $dbh->prepare ("SELECT COUNT(*) FROM bannieres WHERE $critere
LIKE "$clef" ");
snip...
----------------
# fonction2: 2 critères
my ($critere01, $clef01, $critere02, $clef02) = @_;
$sth = $dbh->prepare ("SELECT COUNT(*) FROM bannieres WHERE $critere01
LIKE
"$clef01" AND $critere02 LIKE "$clef02" ");

--------------
# fonction3: Ainsi de suite...
---------------

Je voudrais faire exécuter toutes ces requêtes par une seule et même
fonction qui créerait une requête différente selon le nombre de paramètres
envoyés à la dite fonction.

Ce n'est pas critique pcq le programme fonctionne bien, mais je veux
éviter la redondance dans le code.


Merci d'avance!



C'est bien la première fois que je vois autant de monde s'empresser à me
rendre service sans déboursé de ma part.

Vos réponses me semblent toutes pleines de sens, fonctionnelles et
élégantes.

Ne me reste qu'à tester.

Je m'abonnes à ce ng. Qui sais, je pourrais être utile moi aussi parfois.

Merci encore.



--
Si l'homme est créé libre, il doit gouverner ;
Si l'homme a des tyrans, il doit les détrôner.

-- Voltaire

Avatar
Jean-Michel Hiver
Desole d'intervenir un peu tard dans le fil...

Essaye d'eviter de mettre les valeurs SQL directement dans la chaine, tu
vas au devant de potentiels problemes de securite.

En lieu et place de 1/

my $sql = "SELECT * FROM FOO WHERE BAR LIKE '$baz'";
my $sth = $dbh->prepare ($sql);
$sth->execute();

Il est possible d'ecrire 2/

my $sql = "SELECT * FROM FOO WHERE BAR LIKE ?";
my $sth = $dbh->prepare ($sql);
$sth->execute ($baz);

Imagine un moment que $baz contiene une valeur un peu bizarre, genre
'; SELECT * FROM CUSTOMER_CREDIT_CARD_NUMBERS; ...

Ce genre d'attaque classique sur les scripts CGI est facilement dejoue
en utilisant la soluce numero 2.

Alternativement, tu peux utiliser $dbh->quote ($val) pour quoter
correctement les valeurs.
Avatar
Marc Nadeau
Jean-Michel Hiver a écrit:

Desole d'intervenir un peu tard dans le fil...

Essaye d'eviter de mettre les valeurs SQL directement dans la chaine, tu
vas au devant de potentiels problemes de securite.


Pour ce programme en particulier le besoins en sécurité sont faibles (une
simple rotation de bannières). Je prend note du conseil.


En lieu et place de 1/

my $sql = "SELECT * FROM FOO WHERE BAR LIKE '$baz'";
my $sth = $dbh->prepare ($sql);
$sth->execute();

Il est possible d'ecrire 2/

my $sql = "SELECT * FROM FOO WHERE BAR LIKE ?";
my $sth = $dbh->prepare ($sql);
$sth->execute ($baz);


Et si il y a plusieurs clauses LIKE?


Marc Nadeau
http://www.pagerie.com

--
Je ne suis pas con,
je fais de la retention d'intelligence !
-- P. Geluck

Avatar
Alex Marandon
In article <ejbwb.62287$, Marc Nadeau wrote:
my $sql = "SELECT * FROM FOO WHERE BAR LIKE ?";
my $sth = $dbh->prepare ($sql);
$sth->execute ($baz);


Et si il y a plusieurs clauses LIKE?


Bin tu met plusieurs "?" puis tu passes plusieurs parametres à
$sth->execute().

perldoc DBI explique tout ça.