ugrás a tartalomhoz

"IN" hasonlító (MySQL)

laji · 2006. Jún. 23. (P), 21.56
  1. SELECT * FROM t1 WHERE t1.a IN (felsorolás)  
Ha a "felsorolás"-t magam viszem be (php, dump, stb.), minden rendben. Ha a felsorolásként egy (ebből vagy másik táblából származó) szöveges (varchar) mezőt (oszlopot) adok meg, amiben szabályosan vesszővel vannak elválasztva a felsorolás elemei, a MySQL nem fogadja el, egyszerű szövegnek látja, nem felsorolásnak. Kijátszható ugyan az IN helyett a LIKE használatával, de (főként, hogy egész számokról van szó), ez jelentősen lelassítja a keresést. Van erre más megoldás?
 
1

csak némi tipp

Anonymous · 2006. Jún. 24. (Szo), 00.05
Nem értek hozzá, csak tipp:
Esetleg nincs véletlenül mysqlben valami string2felsorolás() vagy string2tömb([elválasztójel]) függvény? ... és ha a "felsorolás"-t előbb egy változóba teszed? @v:=felsorolas ?
2

Sajnos, nem jött be

laji · 2006. Jún. 24. (Szo), 03.51
Függvény nincs (oda-vissza nyálaztam párszor), a változóba tétel meg ugyanúgy szövegként hagyja (ráadásul az előző sorból venné az értéket).
3

lehet, hogy tévedek,

TeeCee · 2006. Jún. 24. (Szo), 08.24
de sokszor fordult elő az ismerőseim között a következőkben vázolt probléma (~tervezési hiba), lehet, hogy Nálad is ilyen lenne?

Én a következőre tippelek (egy "képek behelyezése kategóriákba" problémán keresztül bemutatva):
"Photos" tábla: (photoid, name, categories)
"Categories" tábla: (catid, catname)
és ha jól sejtem, akkor a képek tábla categories mezőjében a catid-ek vannak felsorolva...

Ilyenkor használatos a kapcsolótábla ami után így nézne ki a dolog:
"Photos" tábla: (photoid, name [itt nincs categories mező!])
"Categories" tábla: (catid, catname)
"Cats_conn" tábla: (photoid, catid)

Ekkor ha egy kép több kategórába tartozik, a kategórianeveket megkapod az alábbi lekérdezés eredményeként
SELECT catname
FROM Photos AS p, Cats_conn AS conn, Categories AS c
WHERE p.photoid=conn.photoid AND conn.catid=c.catid
AND photoid=123
4

find_in_set ?

sotetbarna · 2006. Jún. 24. (Szo), 08.44
Hali!

Sajnos mysql hibánál (és általában minden hibakeresésénél) fontos lenne tudni, hogy milyen környezetben jön a hiba. Jó lett volna, ha egy create table, insert megelőzte volna a selectet, akkor talán könnyebb lenne segíteni.

Hoztam létre egy környezetet, hogy ki tudjam próbálni, mi lehet a probléma.
  1. create table test1 (fint int, fchar varchar(30));  
  2.   
  3. insert into test1 values (1,'11,33,44,55,66,77');  
  4. insert into test1 values (2,'11,33,44,55,66,77');  
  5. insert into test1 values (11,'11,33,44,55,66,77');  
  6. insert into test1 values (55,'11,33,44,55,66,77');  
  7. insert into test1 values (77,'11,33,44,55,66,77');  
  8.   
  9. select *, fint in (fchar) as 'in', find_in_set(fint,fchar) as 'find_in_set' from test1;  
  10. +------+-------------------+------+-------------+  
  11. | fint | fchar             | in   | find_in_set |  
  12. +------+-------------------+------+-------------+  
  13. |    1 | 11,33,44,55,66,77 |    0 |           0 |  
  14. |    2 | 11,33,44,55,66,77 |    0 |           0 |  
  15. |   11 | 11,33,44,55,66,77 |    1 |           1 |  
  16. |   55 | 11,33,44,55,66,77 |    0 |           4 |  
  17. |   77 | 11,33,44,55,66,77 |    0 |           6 |  
  18. +------+-------------------+------+-------------+  
A find_in_set esetleg segíthet.

Barna
5

A teljes felállás

laji · 2006. Jún. 24. (Szo), 10.43
Elnézést, hogy nem adtam meg pontosabban a felállást. Mielőtt kipróbálom a javaslatokat, itt van (csak a lényeg):
  1. # MySQL betöltés (dump)  
  2. SET NAMES utf8;  
  3. CREATE TABLE `aru` (  
  4.  `SORSZAM` int(5) unsigned NOT NULL default '0',  
  5.  `LEIRAS` text CHARACTER SET utf8 COLLATE utf8_general_ci,  
  6.  PRIMARY KEY (`SORSZAM`),  
  7.  UNIQUE KEY `SORSZAM` (`SORSZAM`)  
  8. ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE utf8_general_ci;  
  9.   
  10. CREATE TABLE `vevo` (  
  11.  `SORSZAM` int(5) unsigned NOT NULL auto_increment,  
  12.  `AZON` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL default '',  
  13.  `NEV` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci,  
  14.  `KOSAR` text CHARACTER SET utf8 COLLATE utf8_general_ci,  
  15.  PRIMARY KEY (`SORSZAM`),  
  16.  UNIQUE KEY `SORSZAM` (`SORSZAM`),  
  17.  UNIQUE KEY `AZON` (`AZON`),  
  18. ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE utf8_general_ci;  
  1. <?php  
  2. // Kosarba tesz  
  3. $azon=$sor_eredmenyBelepett['AZON'];  
  4. $eredmenyKosar=mysql_query(sprintf("  
  5.    SELECT KOSAR FROM vevo WHERE AZON='%s'  
  6.    ", $azon));  
  7. $sor_eredmenyKosar=mysql_fetch_assoc($eredmenyKosar);  
  8. // Mar van vmi a kosarban (pl. "17,91,821").  
  9. $kosarTomb=explode(","$sor_eredmenyKosar['KOSAR']);  
  10. $kosarTomb[]=411; // Peldaul.  
  11. sort($kosarTomb);  
  12. $kosarFelsorolas=implode(","$kosarTomb);  
  13. $modositasKosarba=mysql_query(sprintf("  
  14.    UPDATE vevo SET KOSAR='%s'  
  15.    ", $kosarFelsorolas));  
  16. ?>  
MySQL lekérdezés, ami nem működik. Csak szövegnek látja a felsorolást, tehát úgy alakítja számmá, hogy törli az első vessző utáni részt. Példámban így abból, hogy "17,91,411,821", marad 17 mint egész szám, s így természetesen a COUNT(*) az 1. Ha kézzel viszem be (vagy php-vel), hogy "17,91,411,821", kifogástalan: COUNT(*)=4.
  1. SET NAMES utf8;  
  2. SELECT  vevo.*,  
  3.             (  
  4.             SELECT  COUNT(*)  
  5.             FROM        aru  
  6.             WHERE       aru.SORSZAM IN (vevo.KOSAR)  
  7.             )           AS KOSAR_DB  
  8. FROM        vevo  
  9. ORDER BY    vevo;  
Ami működik (de lassú):
  1. SELECT  vevo.*,  
  2.             (  
  3.             SELECT  COUNT(*)  
  4.             FROM        aru  
  5.             WHERE       CONCAT(',', vevo.KOSAR, ','LIKE CONCAT('%,', aru.SORSZAM, ',%')  
  6.             )           AS KOSAR_DB  
  7. FROM        vevo  
  8. ORDER BY    vevo;  
Felmerülhet, hogy miért bonyolítom így, s nem számoltatom meg egyszerűen a php-vel a kosár darabszámát.
Válasz: mert egy összetettebb lekérdezésről van szó, több allekérdezéssel, s ha egybe fogom az egészet, akkor tudok rendezni a MySQL-ban igény szerint, s már eleve így kapom meg az adatokat. Sajnos, a php rendezőképessége igen gyenge.
Tkp. elég lett volna egy olyan MySQL függvény is, ami egy vesszőkkel tagolt szövegből képes visszaadni a darabszámot - de ilyen nincs.

UI
A FIND_IN_SET-et megnéztem. Valószínűleg működne is, hiszen eleve szöveghez készült, de sajnos, nem darabszámot, hanem találati helyet ad vissza.

UI 2.
Ha más tanács nem lesz, akkor úgy tűnik, TeeCee ajánlata lesz a megoldás. Kár, hogy ez eggyel több táblát kíván.
6

mit is szeretnél?

sotetbarna · 2006. Jún. 24. (Szo), 12.35
az in logikai értéket ad vissza, a find_in_set pedig egy számot, ami ha talált, akkor ugye nagyobb, mint nulla, azaz ha find_in_set(x,y)>0, akkor igaz.

szerintem belekeveredtél dolgokba.

amikor azt írtad, hogy nem php-vel akarod szétdobni, mert a mysql rendez csak jól, akkor nem gondoltál bele, hogy csak a vevőre rendezel (az idézett mysql kód szerint), amiatt a kódrészlet miatt nyugodtan lehet php-vel számoltatni (arról nem is beszélve, hogy a php igenis tud jól rendezni utf8-at)

aztánmeg sztem azt sem gondoltad végig, ha lesz mondjuk 1000 árud és 2000 vevőd, akkor a fenti sql vajon milyen sebességgel fut majd le?

próbálj ki más megközelítést és táblaszerkezetet (például TeeCee-ét)

Barna
8

Agyamra ment a hőség :))

laji · 2006. Jún. 24. (Szo), 16.10
...tényleg ideillik a FIND_IN_SET:
WHERE find_in_set(aru.SORSZAM,vevo.KOSAR)>0

Másrészt végiggondoltam, sebességügyileg kellenek azok a kapcsoló táblák - jól gondolta TeeCee (azaz mindhárman), hogy miben hibáztam.

(A rendezésről: ez csak egy leegyszerűsített részlet, valójában vagy 10-15 oszlop alapján lehet rendezni és szűrni. S ha mindezt egy lekérdezésben oldom meg, akkor POST értékekkel megy MySQL-on belül.)

Sokat segített mindkét dolog, köszönöm mindenkinek! További kellemes hétvégét.
7

WHERE IN nem működik paraméterrel

Anonymous · 2006. Jún. 24. (Szo), 13.01
WHERE-ben levő IN-t nem lehet paraméterezni, azaz statikusan kell felsorolni az értékeket. Ez nem a MySQL hibája.

Egyébként pont azt a hibát követed el, amit TeeCee ír a 3. hsz-ban.
Relációs adatbázisban a relációkat ne felsorolásként tárold, vagy ha mégis, akkor viseld a következményeit (extra feldolgozás, lassúság, nehéz kezelhetőség).

A rendes megoldás az lenne, hogy létrehozol egy kosár táblát (csak példa):
user_id
product_id
quantity
add_date

Ha kiváncsi vagy egy felhasználó termékeire:
select count(*) from cart where user_id = current_user_id
vagy
select sum(quantity) from cart where user_id = current_user_id

Azt gondolom most is figyeled, hogy ha ugyanazt a terméket adják hozzá mégegyszer, akkor vagy nem adja hozzá, vagy a mennyiséget hozzáadja a már bentlévő mennyiséghez.

A dátum azért kell, hogy néha lehessen takarítani, pl. az egy hónapja bent lévő termékeket. Előtte küldhetsz egy figyelmeztetést, így plusz esélyed van, hogy meg is rendelik a termékeket.

Egyáltalán nem kár, hogy plusz egy táblát igényel, az a kár, hogy bárkinek eszébe jut ezen spórolni (spórolni? inkább plusz munkát csinál).
9

Igen, belátom...

laji · 2006. Jún. 24. (Szo), 16.14
...mint eggyel fentebb írtam, s így fogom átalakítani a táblákat. Köszönöm!
10

nem a táblák száma számít

Hodicska Gergely · 2006. Jún. 27. (K), 02.24
Kár, hogy ez eggyel több táblát kíván.

Ezen kár spórolnod, inkább azt nézd, hogy mit nyersz általa. Egyrészt amit a többiek is mondtak, plusz lehetőséged lesz például külső kulcsok használatára, ami nagyon jól jöhet adatbázis integritásának megőrzéséhez, plusz általa egy csomó funkciót nem kell PHP-ban megoldanod.


Felhő