[ Crtani @ 14.09.2011. 13:28 ] @
Društvo,

Molim za pomoć oko JOIN-ovanja tabela u DataSet-u.

Uprošćen primer:
na bazi postoje dve tabele (to je dato, bez mogućnosti da tu bilo šta promenim) koje imaju za cilj da evidentiraju pripadnost različitih konta raznim grupama.

GRUPE (grupa, odKonta, doKonta,...)
KONTA (konto, naziv,...)

Da bih prikazao sve grupe u kojima se nalazi svaki do konta u SQL-u koristim otprilike ovakav upit:

select K.konto, G.grupa
from KONTA as K
left join GRUPE as G on K.konto between G.odKonta and G.doKonta+'z'

(Ovim se postize to da je prilikom definisanja grupe dovoljno navesti samo prve cifre konta iako su konta osmocifrena, pa npr. u grupu od 1 do 1 ući će sva konta koja počinju na 1)

Moje pitanje je: kako ovakav JOIN izvesti u DataSet-u. Ne poznajem još uvek dovoljno DataRelation klasu, ali mi se za sada čini da se kod nje podrazumeva uspostavljanje relacije kroz jednakost dve kolone.

Siguran sam da postoji neko elegantno rešenje za postavljanje složenijih uslova relacije.
Ako treba još detalja, recite.

Unapred hvala.

[ sallle @ 14.09.2011. 15:24 ] @
s obzirom na prirodu konta (sintetika - analitika), prirodnije je da ti konta budu definisana kao stringovi, a da joinovanje radis koristeci like operator.

A sto se tice zajebancije sa datarelations lakse ti je da napravis datatable koji ce da odgovara izlazu tvog join-a iz baze. a da za eventualno prikazivanje konta odredjene grupe koristis datarow filter.

[ deerbeer @ 14.09.2011. 15:36 ] @
Nema join-ova u dataset-u na taj nacin, jedino postoje metode tabele tj. reda GetChildRows ako u bazi postoji FK constraint za datu tabelu .
Druga opcija je ovo sto ti je @sallle predlozio .
[ pl4stik @ 14.09.2011. 17:53 ] @
A zasto ovaj upit ne moze? Jel si probao da iskopiras u designer ovaj sql?
Meni dataset koristi bas zbog joina i nisam sreo nesto da ne moze sto se toga tice, a posle ga doradis ako treba nesto...

[ Crtani @ 14.09.2011. 19:03 ] @
Hvala na vremenu, ali i dalje nema pravog odgovora, nadam se da ćemo nastaviti diskusiju

@Salle
Konta i jesu stringovi, kako bi inače prošao deo upita DoKonta+'z' ?
Ne može LIKE zato što konto ne mora uopšte da bude nalik granicama grupe. Npr. grupa može da bude odKonta:1 do doKonta:3, a tu treba da uđe i konto 21111111. To nije ni Like 1% ni like 3%, ali zato jeste between '1' and '3z'
Ali to je druga tema. U svakom slučaju upit je dobar, pitanje je kako ga izvesti bez SQL-a, u DataSet-u.

Gotov upit ne mogu da iskoristim pa da napunim DataTable direktno sa baze, zato što, najprostije rečeno, jednu od ove dve tabele neću imati u bazi podataka već samo u DataSet-u.
Čudi me da jezik kao što je SQL, koji je brušen godinama, standardizovan i zaokružen, ne može da se iskoristi za upite nad DataSet-om.

Čini mi se da Linq krije rešenje problema.


[ CallMeSaMaster @ 14.09.2011. 19:11 ] @
Ne ulazi mozda like 1% ali mozda ti pomaze like %1% mada nije bas to sto ti trazis. Eventualno da probas sa substring-om gdje je tvoj kod izmedju prvog karaktera od i do kantona?


select K.konto, G.grupa
from KONTA as K
left join GRUPE as G on K.konto between Substring(G.odKonta0,1) and Substring(G.doKonta,0,1)

Ovo je brzinski ...
[ Crtani @ 14.09.2011. 20:27 ] @
@CallMeSaMaster
Zašto (bezuspešno) pokušavaš/pokušavate da popravite upit koji radi savršeno, i koji uopšte nije tema ove diskusije?
Ni %1% ni Substring neće raditi dobro.




[ sallle @ 15.09.2011. 02:19 ] @
dobra dosetka to za dodavanje karaktera na kraj konta...

uvek mozes i rucno da joinujes te dve tabele (dupla for petlja ). A mozes sto si spomenuo i koriscenjem linq-a.
a.join(...).where(...)
[ Boris B. @ 15.09.2011. 14:50 ] @
SQL BETWEEN prima inkluzivni interval tako da mi nije jasno zasto +'z', treba da radi dobro i samo sa BETWEEN odKonta AND doKonta ?

Ovako bi izgledalo nesto sto tebi treba. Konta je instanca KontoDataTable a Grupe je instanca GrupaDataTable:

Code (csharp):

Konta.Join(Grupe, konto => konto, grupa => grupa, (konto, grupa) => new { Grupa = grupa, Konto = konto }, new KontoEqualityComparer());

public class KontoEqualityComparer : IEqualityComparer<object>
{            
    public bool Equals(object x, object y)
    {
        var grupa = x as Grupa ?? y as Grupa;
        var konto = x as Konto ?? y as Konto;
        if (konto == null || grupa == null)
            throw new Exception("...");
        return konto.Oznaka >= grupa.OdKonta && konto.Oznaka <= grupa.DoKonta;
    }

    public int GetHashCode(object obj)
    {
        return obj.GetHashCode();
    }
}      
 


Rezultat ovog joina je kolekcija anonimnih objekata gde svaki ima propertije Konto i Grupa. Mozes i da projektujes u neki svoj objekat, samo popravi onaj new { ... }.


Edit: Može i daleko prostije, onaj SQL JOIN me usmerio na LINQ JOIN umesto da lepo koristim projekciju :)

Code (csharp):

   Grupe.Select(g => new { Grupa = g, Konta = Konta.Where(k => k.Oznaka >= g.OdKonta && k.Oznaka <= g.DoKonta).ToList() }).ToList();
 



[Ovu poruku je menjao Boris B. dana 15.09.2011. u 16:20 GMT+1]
[ Crtani @ 15.09.2011. 17:00 ] @
@Boris B.
Hvala na predlogu rešenja, proveriću pa javljam kad završim posao.

Što se tiče Between primedba nije na mestu, jer +'z' jeste neophodno.
Opet će biti najlakše kroz primer objasniti.
Ako je grupa konta npr od '1' do '3', to znači da u nju treba da uđe i npr konto '31234567'.
string '312345678' nije manji od '3', ali jeste manji od '3z'.
Ako bih izostavio 'z' ni jedan konto koji počinje na '3' ne bi ušao u grupu.
(Naravno, umesto 'z' može da se koristi bilo koji karakter koji ima ascii kod veći od karaktera '9' tj. od 57)



[ CallMeSaMaster @ 15.09.2011. 17:10 ] @
@Crtani

Izvini, tvoj upit ne radi savrseno kao sto ti tvrdis. Ja nisam rekao da ce ovo moje riejsiti tvoj problem definitivno, ali moram odmah da ti kazem da zo dodavanje 'z' karaktera ne znaci nista. S obzirom da je tvoj kod string tipa sta ce se desiti ako slcuajno nekad iz nekog razloga budes imao sljedeci string u bazi '3za1234'? Hoce li i tada tvoj upit biti savrsen kao sto tvrdis? Ne tvrdim da ce se zaista to desiti, ali to nije logika to je stiklanje da nesto proradi...Izvini ja sam samo htio pomoci, ali eto necu se mijesat vise u tvoju temu...
[ Crtani @ 15.09.2011. 20:14 ] @
@CallMeSaMaster
:)
Žao mi je što te je ljutnja na to naterala, ali je ipak pozitivno što si se ovoga puta ozbiljnije pozabavio problemom.
Iako se ne obistinjuje u praksi, tvoju primedba teoretski stoji.
Čoveku prvo pada na pamet da se osigura sa +'zzzzzzzz',
a onda pomisliš da teoretski u string može da dospe i nešto jače od 'z',
onda pomislis na najveći char a to je char(255),
ali se setiš da postoje i kojekakve ćirilice, kineska slova i svašta drugo,
pa se osiguraš sa nchar(65535)
i to nizom od maksimalnih 7 mesta.
+replicate(nchar(65535),7)

:))


[ CallMeSaMaster @ 15.09.2011. 21:53 ] @
Pa dobro rekao sam da sam brzinski preletio(prvi put)

A s obzirom da se radi o string-ovima moze se polemisati na ovu temu do sutra, tako da se nadam da ces naci "najbolje" rijesenje pa ga mozda podijelis s nama :-))
[ Boris B. @ 16.09.2011. 13:58 ] @
Citat:
Crtani: @Boris B.
Hvala na predlogu rešenja, proveriću pa javljam kad završim posao.

Što se tiče Between primedba nije na mestu, jer +'z' jeste neophodno.
Opet će biti najlakše kroz primer objasniti.
Ako je grupa konta npr od '1' do '3', to znači da u nju treba da uđe i npr konto '31234567'.
string '312345678' nije manji od '3', ali jeste manji od '3z'.
Ako bih izostavio 'z' ni jedan konto koji počinje na '3' ne bi ušao u grupu.
(Naravno, umesto 'z' može da se koristi bilo koji karakter koji ima ascii kod veći od karaktera '9' tj. od 57)



Aha, očigledno je taj Konto nešto što bi slovenci rekli "govoreća šifra", tj. takva šifra koja u sebi nosi informaciju o svim "roditeljima" u "drvetu" (ne znam šta je konto, znam da ima neke veze sa naprednim knjigovodstvom... )
A tvoj primer govori da OdKonta-DoKonta mogu da budu i roditelji i deca, i ako je roditelj onda treba uključiti i svu decu.

U suštini postoje dva domena, domen [OdKonta-DoKonta] + sve što pripada grani DoKonta.
Znači,

(Oznaka Between OdKonta AND DoKonta) OR (Oznaka LIKE DoKonta + '%')

To će da radi i sa onim teoretskim primerom sto je dao @CallMeSaMaster

EDIT:

Evo i popravljen LINQ kod:
Code (csharp):

   Grupe.Select(g => new { Grupa = g, Konta = Konta.Where(k => (k.Oznaka >= g.OdKonta && k.Oznaka <= g.DoKonta) || (k.Oznaka.StartsWith(g.DoKonta))).ToList() }).ToList();
 
[ Crtani @ 17.09.2011. 19:56 ] @
@Boris B.
Najpre, hvala na komentarima i velikoj pomoći, tvoji primeri su mi pomogli da bolje razumem LINQ i lambda izraze.

Evo kako sam na kraju rešio:
Code:

            var GrupeKonta =
                from g in Grupe
                from k in Konta
                where k.konto.CompareTo(g.odKonta) != -1 // vece jednako
                        &&
                     k.konto.CompareTo(g.doKonta + "z") != 1 // manje jednako
                select new
                {
                    grupa = g.grupa,
                    konto = k.konto
                };

Moram da napomenem da se u C#-u stringovi ne mogu porediti operatorima >,>=,<,<= pa sam zato koristio .CompareTo metodu.
Još i ovo:
Navedenom kodu prethodila je mala priprema Grupe i Konta, koja je izgledala ovako:
Code:

            var Konta =
                from k in konta.AsEnumerable()
                select new
                {
                    konto = k.Field<string>("konto"),
                    naziv = k.Field<string>("naziv")
                };

            var Grupe =
                from g in grupe.AsEnumerable()
                select new
                {
                    grupa = g.Field<string>("grupa"),
                    odKonta = g.Field<string>("odKonta"),
                    doKonta = g.Field<string>("doKonta")
                };

gde su konta i grupe tipa DataTable.

Još jednom hvala i pozdrav