Nevek keresése Firebird-ben hasonlósággal (D7)

Nevek keresése Firebird-ben hasonlósággal (D7)
2015-01-20T01:01:36+01:00
2015-01-30T15:31:23+01:00
2022-12-02T03:00:37+01:00
mandula
Sziasztok !
 Firebird SQL 2.5.3 alatt lehet valahogy úgy keresni, hogy ne vegye figyelembe a nyelvi sajátosságokat?
Tehát rátaláljon a "Kováts Ági"-ra, ha arra keresünk, hogy "vats"=ékezet nélkül=ekezet nelkul ... netán még jobb, arra is működik, hogy "kovacs" ... cs-vel a végén...stb.
Végeredményben nem fontos, hogy Firebird alatt közvetlenül működjön, elég, ha Delphi kód van hozzá, és majd legenerálok egy második oszlopot...
De mielőtt nekiállok feleslegesen melózni, rákérdezek, mert úgy rémlik, mintha erre lenne már KÉSZ megoldás?
Előre is köszönöm szépen ;)

Ui.: Charset=UTF8, Collate=UNICODE
Mutasd a teljes hozzászólást!
Ezt nem levenshtein-nel kapnád el, hanem a collation-nel. Az adatok alapvető rendezése a táblában maradhat a magyar szerinti, az AI-s collation-t megadhatod magában a lekérdezésben is:

select ... from ... where mező collate unicode_ci_ai like ... order by mező

Szótöredéknél viszont se levenshtein-nel, se soundex-szel sem mész semmire sem. Tehát, ha cs-re keresel, akkor a Kovácsot megkapod, de a Kovátsot már nem. Már csak azért sem, mert a cs=ts működik az előbbi példában, de más esetben ez nem működik.

Ami te szeretnél, az kb olyan, hogy rákeresel azokra a rekordokra, amiben a cs előfordul, majd az összes találaton végiglépkedve indítasz egy újabb lekérdezést, amiben az ahhoz hasonló (soundex vagy levenshtein vagy bármilyen hasonló algoritmus) szövegeket gyűjtöd le. Ez elég lassú lesz szvsz, ha sok rekordod van.
Mutasd a teljes hozzászólást!

  • Az ékezetek figyelmen kívül hagyásához a lekérdezésben használd a  UNICODE_CI_AI collation-t (AI=accent insensitive).

    A hasonló hangzású szavakra a soundex() függvényt lehet használni, ha van ilyen fb alatt. De vigyázz, mert ez angol kiejtést veszi alapul. Alternatíva lehet saját levenshtein távolság függvény használata, lásd pl itt: Levenshtein-Distanz - Firebird Datenbank: Levenshtein-Distanz mit Bordmitteln Teil 1: Einleitung & M
    Mutasd a teljes hozzászólást!
  • Csinálsz egy udf függvényt

    library compare_UDF; uses SysUtils,classes,Windows ; function OEMCompare(mezo,minta : PAnsiChar) : integer; cdecl; procedure preparestr(var s:ansistring); var i:integer; begin for i:=1 to length(s) do case s[i] of 'á' : s[i]:='a'; 'é' : s[i]:='e'; 'í' : s[i]:='i'; 'ó','ő' : s[i]:='o'; 'ú','ű','ü' : s[i]:='u'; end; end; var mezostr,mintastr : AnsiString; begin mezostr:=ansilowercase(mezo); mintastr:=ansilowercase(minta); preparestr(mezostr); preparestr(mintastr); result:=pos(mintastr,mezostr); end; exports OEMCompare index 1; begin end.
    Beregisztrálod :

    DECLARE EXTERNAL FUNCTION OEM_COMPARE CSTRING(255) CHARACTER SET NONE, CSTRING(255) CHARACTER SET NONE RETURNS INTEGER BY VALUE ENTRY_POINT 'OEMCompare' MODULE_NAME 'compare_UDF.dll';

    és már használhatod is :

    select * from tabla where oem_compare(mezo,'vats') >0
    Mutasd a teljes hozzászólást!
  • Delphi 7-el használja az adatbázist, ezért kicsi a valószínűsége hogy unicode stringeket tárolna benne 
    Mutasd a teljes hozzászólást!
  • Nézd meg a kérdés utolsó mondatát...
    Mutasd a teljes hozzászólást!
  • Árnyék
    Hopp azt nem néztem,

    mandula
    akkor csak annyi a változás hogy PUTF8String lesz a függvény paramétereinek típusa a többit a Delphi automatikus stringkonverziója megoldja
    Mutasd a teljes hozzászólást!
  • library compare_UDF; uses SysUtils,classes,Windows ; function OEMCompare(mezo,minta : PUTF8String) : integer; cdecl; procedure preparestr(var s:ansistring); var i:integer; begin for i:=1 to length(s) do case s[i] of 'á' : s[i]:='a'; 'é' : s[i]:='e'; 'í' : s[i]:='i'; 'ó','ő' : s[i]:='o'; 'ú','ű','ü' : s[i]:='u'; end; end; var mezostr,mintastr : AnsiString ; begin mezostr:=ansilowercase(Utf8ToAnsi(mezo^)); mintastr:=ansilowercase(Utf8ToAnsi(minta^)); preparestr(mezostr); preparestr(mintastr); result:=pos(mintastr,mezostr); end; exports OEMCompare index 1; begin end.
    Mutasd a teljes hozzászólást!
  • De minek írjon erre saját függvényt delphi-ben, ha simán a lekérdezésen belül is meg tudja oldani collation-nel?

    Továbbá a függvényed nem kezeli a hasonló hangzású szavakat sem.
    Mutasd a teljes hozzászólást!
  • A UNICODE_CI_AI ról több helyen is azt írják hogy sok rekord esetén extrém lassúvá teszi a lekérdezést, másrészt a külső függvény előnye hogy később beleépíthet olyan dolgokat is mint például helyesírás ellenőrzés a mezőn és a kereső stringen hogy például  bikicsunáj kereső stringet is megtalálja egy angol szövegben, és ehhez az adatbázison semmit nem kell változtatnia. Akár a soundex algoritmus magyarított változatát is beteheti a delphi függvénybe.
    Mutasd a teljes hozzászólást!
  • A UNICODE_CI_AI-vel kapcsolatos hibát a FB 2.5.3-ban már javították. Lásd itt
    Mutasd a teljes hozzászólást!
  • Szia !
     és az UNICODE_CI_AI egyébként magyar aábeéc sorrendez? Mert azt olvastam, hogy azt a sima UNICODE collation csinálja. (korábban UTF8 volt, és az nem jó, mert ABC..ZZáé módon rangsorolt.)
    Mutasd a teljes hozzászólást!
  • Szia Quetzelcoatl !
     Köszi a tökéletes kis UDF példaprogit :)

    Normális esetben, ha nem volna felmérhetetlenül bonyolult meló az UDF-ek kliensenkénti (külön admin joggal való) feltelepítése, akkor máris ugranék erre a megoldásra.

    De sajnos így csak az a 3 lehetőség marad, hogy:
    1.) Minden recordot letöltök D7 alá, és ott manuálisan összehasonlítgatom
     (Na EZ nagyon nem volna hatékony)
    2.) Vagy a meglévő módon használom tovább az egészet... esetleg kiegészítve +1 oszloppal, amin a keresést végzem, és azt előre lekódolom, illetve változások esetén mentés előtt újra. (Kvázi mintha hash-elném, csak valami más algoritmussal, ami "kisimítja" a keresést)
    3.) stored procedure-t írok, ami az összehasonlítást végzi. (kevésbe sebesség-hatékony, és eléggé macerás megírni szemben a delphivel...)
    _________
    Olvasgattam a Levenshteinés Soundex -ről, és alapvetően ilyesmi kellene, csak félek, hogy fog viselkedni, ha különböző országok nevei keverednek. Mert ezek az algoritmusok két komplett szót hasonlítanak össze hangzásra, egy bizonyos nyelvjárás szerint.
    Nekem viszont hason-szó-részlet-előfordulásra kell keresnem.

    Köszönöm az eddigi hozzászólásokat ! Sok jó támpontot adtatok eddig is már ...  :)
    Mutasd a teljes hozzászólást!
  • A soundex a kiejtést vizsgálja, tehát valóban nyelv függő. A levenshtein viszont nem nyelv függő, mert az a szerkesztési távolságot adja meg 2 sztring között, azaz hány szerkesztési lépésben jutsz el az egyik szövegtől a másikig.
    Mutasd a teljes hozzászólást!
  • Amúgy találtam olyat a Delphi-ben, hogy:
    AnsiResemblesText()
    ... de szerintem ez nekünk magyaroknak nem túl jó. Tévedek?
    Mutasd a teljes hozzászólást!
  • Igen, ezt értem, de hogyan alkalmazható ez szó-előfordulás keresésére? Tehát a "ot"-ra keresés megtalálja a "Tóth"-ot ?
    Mutasd a teljes hozzászólást!
  • Ezt nem levenshtein-nel kapnád el, hanem a collation-nel. Az adatok alapvető rendezése a táblában maradhat a magyar szerinti, az AI-s collation-t megadhatod magában a lekérdezésben is:

    select ... from ... where mező collate unicode_ci_ai like ... order by mező

    Szótöredéknél viszont se levenshtein-nel, se soundex-szel sem mész semmire sem. Tehát, ha cs-re keresel, akkor a Kovácsot megkapod, de a Kovátsot már nem. Már csak azért sem, mert a cs=ts működik az előbbi példában, de más esetben ez nem működik.

    Ami te szeretnél, az kb olyan, hogy rákeresel azokra a rekordokra, amiben a cs előfordul, majd az összes találaton végiglépkedve indítasz egy újabb lekérdezést, amiben az ahhoz hasonló (soundex vagy levenshtein vagy bármilyen hasonló algoritmus) szövegeket gyűjtöd le. Ez elég lassú lesz szvsz, ha sok rekordod van.
    Mutasd a teljes hozzászólást!
  • ... Ez elég lassú lesz...

    Igen, erre jutottam én is. Ergo egyenlőre ez még nem túl járható út.
    Hacsak nem állok neki magam fejleszteni egy saját hallás-elgépelés-alapú "szinonima szótár-átalakító kódot".

    És igen, tudom, hogy az alap-rendezésen kívül tudok "saját collation"-nal lekérni adatokat, csak azt hittem, ez sokat lassít.

    select ... from ... where mező collate unicode_ci_ai like ... order by mez
    (... azért köszi az emlékeztetőt, hogy le van ide is írva, ha más is olvassa ezt majd a jövőben :D )

    Vagy érdemes akkor már 2 indexet létrehozni a mezőhöz?
    -az egyik unicode_ci_ai
    - a másik unicode
    ?
    Mutasd a teljes hozzászólást!
  • Hahó

    csinálsz szótárat, szótár tételt (táblák), a szótárba beirsz minden szót ékezetek nélkül és a keresést is ékezetek nélkül futtatod a szótáron.
    egyszerű,gyors, finom.
    B
    Mutasd a teljes hozzászólást!
  • Ez mennyivel gyorsabb és egyszerűbb, mint a megfelelő collation-t használni?
    Mutasd a teljes hozzászólást!
  • Nem vagyok egy nagy fb guru, de tudtommal a like "%...%" típusú kereséseknél nem tudják a db kezelők az indexeket használni, tehát nem hiszem, hogy a lekérdezés sebességét jelentősen befolyásolná hogyan adod meg a ai-s collation-t.
    Mutasd a teljes hozzászólást!
  • Ebben igazad van, a Like '%valami%' -nál valóban nem tudná használni.
    De szerintem a 'valami%' -nál igen, és többnyire azért (szerencsére) a kezdőbetűkre keresnek.
    Végülis pudding próbája ... úgyhogy majd tesztelgetem indexxel és nélküle.

    Kicsit még azért hagyom nyitva a témát, hátha valaki mégiscsak "bedob" valami varázsötletet / kódot?
    Mutasd a teljes hozzászólást!
  • Kováts Ágnes-ből lesz 2 rekord: kovats, agnes a szótárban.
    Mivel a szótár indexelt, ezért az agnes is az indexben lesz, így sokkal gyorsabb keresni az agnes-re, mint a teljes Kováts Ágnes mezőben (%Ágnes%). Ezen kívül minden szó csak egyszer szerepel, tehát ha a szótáron egy like (%gnes%) Az agnes-t csak egyszer hozza, így nem kell minden Ágnes -en végig menni.
    Nálunk ez a a módszer egy átlag 3 szóból álló kifejezést tartalmazó táblán, mely ~30.000 rekordot tartalmazott, szórészletre kereséskor átlag 45ms válaszidőt hozott, a konkrét rekorddal amit kerestünk. Persze megvan az a hátránya, hogy minél több szó szerepel a keresőkifejezésben, úgy lassul a dolog.
    Messze gyorsabb volt, mint collation meg like.
    B
    Mutasd a teljes hozzászólást!
  • Tekintve, hogy a like-ot nem tudod megspórolni, hiszen a szótáron is like-kal keresel, plusz extra join-okat is kell használnod, hogy visszakapd az eredeti rekordokat, ezért eléggé kétlem, hogy gyorsabb lenne a collation-nél. Igaz, én mysql-en, több mint 2 milliós alaptáblánál teszteltem ezt a megoldást még anno.
    Mutasd a teljes hozzászólást!
  • A trükk az, hogy nem join-nal csináltuk, hanem tárolt eljárással, az csak id-ket adott vissza, és ahhoz ment a join.
    Mutasd a teljes hozzászólást!
  • A trükk az, hogy nem join-nal csináltuk, hanem tárolt eljárással, az csak id-ket adott vissza, és ahhoz ment a join.

    Tehát volt join. Meg valszeg a tárolt eljáráson belül is van join vagy valamilyen más kód, ami összeköti a találatokat a szótár táblából a fő táblával. Max akkor lehet értelme a dolognak, ha a szótár tábla méretét jelentősen a fő tábla mérete alatt lehet tartani, mert ugyanaz a szó rengetegszer fordul elő. Különben egyszerűen nincs értelme, hiszen a like "%...%" ugyanúgy nem tud indexet használni a szótáron sem, míg a like "...%" a fő táblán is tud indexet használni minden további tárolt eljárás vagy join nélkül.
    Mutasd a teljes hozzászólást!
  • ok.
    alaprekordok száma: 24161
    szótár szavak száma: 43766
    szótártételek száma: 184220

    keresés kifjezés eleji egyezésre, nem szórészlet: 0,172s 106 találat
    keresés kifejezés eleji egyezésre szórészlet: 0,282s 158 találat
    keresés nem kifejezés eleji egyezésre, nem szórészlet: 0,234s 125 találat
    keresés nem kifejezés eleji egyezésre, szórészlet: 0,531s 488 találat

    A szerver egy Windows 7 pro-t futtató desktop, nem localhoston.

    nekünk ez tökre elegendő :)
    Mutasd a teljes hozzászólást!
  • A kérdés az, hogy mik a számok, ha collation-nel keresel.
    Mutasd a teljes hozzászólást!
  • Háááát, van egy kis gáz:
     - még a 2.5.3-nál is bug-os az order by X collate unicode :(

    Egy sima 283 record-os select kb. 8 másordperc alatt fut le.
    Mutasd a teljes hozzászólást!
  • ... amúgy csak jelzem, hogy 20 percig kerestem, mi a hiba, mire kiderült, hogy PONT olyan betűre próbálok rákeresni, ami kimaradt a függvényből: az "ö" betű :D
    Tehát Helyesen a kód:

    function OEMCompare(mezo,minta : PAnsiChar) : integer; cdecl; procedure preparestr(var s:ansistring); var i:integer; begin for i:=1 to length(s) do case s[i] of 'á' : s[i]:='a'; 'é' : s[i]:='e'; 'í' : s[i]:='i'; 'ó','ö','ő' : s[i]:='o'; 'ú','ü','ű' : s[i]:='u'; end; end;
    Egyébként nagy hasznát vettem egy másik -nem adatbázis alapú- kereső-függvényben. (lista elemeinél a megfelelőre ugrik)
    Köszi mégegyszer !
    Mutasd a teljes hozzászólást!
Tetszett amit olvastál? Szeretnél a jövőben is értesülni a hasonló érdekességekről?
abcd