perl regexp

Udv!

Egy mysql query reszt kellene atalakitanom, de nem igazan boldogulok vele hatekony modon - meg mashogy sem :- ).

Az eredeti query igy nez ki:


 rep_platform IN ('VALAMI1', 'VALAMI2', 'VALAMI3')

es valami ilyesmit szeretnek kapni belole:


 rep_platform LIKE '%VALAMI1%' or rep_platform LIKE '%VALAMI2%' or rep_platform LIKE 'VALAMI3'

.

Nyilvan az IN utani tombben levo elemek szama 1- tol n- ig valtozik (valami mindenkeppen van benne), es az aposztrofok kozotti resz NEM tartalmaz aposztrofot.

Koszi a valaszokat.

Hozzászólások


my $query = < DATA >;  # szóközöket vedd ki

# SPARTAAA style
chomp $query;                           # levágja az újsort a végéről
chop $query;                            # levágja a zárójelet a végéről
my @fields = split / IN \(|, /, $query; # szétszedi a sort
my $key = shift @fields;                # az első mező külön változóba, a tömbben csak a VALAMIk maradnak
s/'//g for @fields;                     # leszedi a '-ket

# összeállítja az új lekérdezést
my $new_query = join ' or ', map {$key." LIKE '\%$_\%'"} @fields;
print $new_query.$/;

__DATA__
rep_platform IN ('VALAMI1', 'VALAMI2', 'VALAMI3')

Sőt, ha úgy sincsenek '-k a VALAMIk belsejében, akkor:


my $query = < DATA >;

# double SPARTAAA style
chomp $query;
chop $query;
$query =~ tr/'//d;
my @fields = split / IN \(|, /, $query;
my $key = shift @fields;

my $new_query = join ' or ', map {$key." LIKE '\%$_\%'"} @fields;
print $new_query.$/;

__DATA__
rep_platform IN ('VALAMI1', 'VALAMI2', 'VALAMI3')

Ebben is igazad van, de valahol meg kell húzni a határt, nem lehet az összes elképzelhető hibalehetőséget lekezelni - előbb-utóbb a teljesítmény és az átláthatóság rovására menne.

Különben is, az OP-ban lévő specifikációt, ami áll egy darab példából ;), a megoldásom teljesíti.

Ha mondjuk ez egy éles alkalmazás lenne, ahol lehetne mintát venni az előforduló query-kből és az azokban előforduló hibás formázásokból, akkor be lehetne lőni, hogy meddig érdemes elmenni a megoldás általánosításával.

Na meg az se mindegy, hogy ezeket a stringeket a felhasználó viszi be, vagy eleve már valami generálja. Az előbbi eset a bonyolultabb, mert a felhasználók tényleg ördögien ügyesek, hogy a programozó által lekezelt n lehetséges rossz bevitelen felüli n+1-ediket is megtalálják (ez n tetszőleges értékére igaz).

Tessek itt egy robosztusabb ;-)
Persze az ilyet szepen sql parserrel kene es nem regexppel parse-olni.


#! /usr/bin/perl

use strict;
use warnings;

while ( my $q = <DATA> ) {
  $q =~ m/\A \s* rep_platform \s+ IN \s* \( \s* ([^)]*) \) \s* \z/xmsi;
  my @args = map { substr $_, 1, -1 } split /\s*,\s*/, $1;
  warn "warning: no args given" if ( not @args );
  my $out = join q{ OR }, map {"rep_platform LIKE '\%$_\%'"} @args;
  print $out, "\n";
}

__DATA__
rep_platform IN ('VALAMI1', 'VALAMI2', 'VALAMI3')

Szerintem jobban jársz, ha nem regexp-pel próbálkozol, hanem find-dal keresed meg az aposztrófokat, és két-két aposztróf közötti szövegből összerakosz részquery-ket, amit listába raksz és a végén összejoinolod.

Kicsit megerőszakolás, kicsit lassú, de nem lehetetlen:


$str = q/rep_platform IN ('VALAMI1', 'VALAMI2', 'VALAMI3')/;
$str =~ s/(\w+)\s+in\s*//i;
$column = $1;
while (
$str =~ s/\(\s*'([^']+)'\)?/$column LIKE '%$1%'/i or
$str =~ s/\s*,\s*'([^']+)'\s*\)?/ or $column LIKE '%$1%'/g
) { }
print "$str\n";

Persze, mindezen tualjdonságokat meg is előlekeztem a "megerőszakolás" és "lassú" kitételekkel, de ez utóbbinak most utána is jártam.

10, 100, ..., 100000 elemű in-listákat tartalmazó fájlokat konvertáltam át like-ossá a fenti kóddal. Az eredmény nem sokkoló, ill. annyiban, hogy bár a futási idő kb. egyenes arányban nő a 10-szerezésekkor, de még így is bakfitty egy nem túl friss (T43p) notebookon:

100000.sql:

real 0m0.236s
user 0m0.212s
sys 0m0.024s
-------
10000.sql:

real 0m0.026s
user 0m0.028s
sys 0m0.000s
-------
1000.sql:

real 0m0.005s
user 0m0.004s
sys 0m0.000s
-------
100.sql:

real 0m0.003s
user 0m0.000s
sys 0m0.004s
-------
10.sql:

real 0m0.002s
user 0m0.000s
sys 0m0.000s
-------