Diskuze: Provádění série navazujících LINQ dotazů - Jak to zrychlit?
V předchozím kvízu, Test znalostí C# .NET online, jsme si ověřili nabyté zkušenosti z kurzu.

Člen

Zobrazeno 22 zpráv z 22.
//= Settings::TRACKING_CODE_B ?> //= Settings::TRACKING_CODE ?>
V předchozím kvízu, Test znalostí C# .NET online, jsme si ověřili nabyté zkušenosti z kurzu.
Ahoj, nevim jestli se mi to podařilo správně lousknout... kdyžtak budiž
mi prominuto Jen dotaz, co je
špatnýho zavolat ToList() (např jako List<RfcRow> IEdata) a uložit
lokálně výsledky v listu a pak v dalších linq dotazech pracovat s tím
listem? Vše bude probíhat nad výsledky uloženými v paměti.
Ahoj, potíž je v tom, že to trvá asi okolo 10 hodin. Ale nějak začínám chápat, že to stejně jinak nepůjde.
Hádám že 10hod trvá zpracování dotazu v SAP, odeslání dat potom pár desitek sec max.. a lokální dotaz linq v paměti (cca 300 k znamů) pak řádově do 10 sec...
No minimálně jednu sadu výsledků budeš muset prostě uložit do paměti. Tady bych se tedy zamyslel nad omezením základního dotazu v SAP. Hádám voláš RFC, pokud si můžeš RFC přizpůsobit, pak bych to volání udělal s parametry a nějakým základním omezením aby sada těch výsledků ze SAP byla už co nejmenší....
Co je pro mě trošku neznámou je, jak je ten jeden řádek vlastně datově velký. Ze SAP bývá občas výstup fakt velký, tabulky mívají 255 sloupců atd. a člověk z toho potřebuje celkem 3 např. I tohle by šlo předzpracovat přímo v SAP
Jsem si jistý, že sis změřil, které části celého procesu trvají neočekávaně dlouho. Pak už můžu jen obecně doporučit se zaměřit na možné optimalizace těchto částí (viz předchozí komentář od @PetrJílek).
Ahoj, data ze SAP mám naopak velmi rychle, dejme tomu pár sekund. Naopak
neúměrně dlouho trvá zpracování dat u mě v PC. Velikost řádku taky
netuším, protože nevím, jak to zjistit. Do objektu RFCRow se jen ve Visual
Studiu nepodívám. Optimalizoval bych, kdybych věděl, co mám optimalizovat.
Bohužel s tím nemám moc zkušeností a jeto pro mě celkem velké sousto. Už
začínám chápat, že se přechodu do Listu stejně nevyhnu. Díky za další náměty.
Popravdě, to se mi nějak nezdá... Když ty data ze SAP prostě načteš do listu, jak dlouho to trvá? Ty linq dotazy by naopak v paměti měly běžet rychle. Leda:
A, Máš tam rekurze
B, Ty dotazy načteš až později díky "deffered execution" a tudíž
skutečný zpracování dat probíhá až později.
Je potřeba dbát na to, že sestavení linq dotazu != jeho spuštění.
Ještě si můžeš ověřit jak dlouho zabere daný dotaz spuštěný přímo v
prostředí SAP. Nicméně, divil bych se že během několika sec vyjede a
pošle data pro 300k řádků...
Jinak mi to nedalo a kouknul jsem u sebe, sice objekt RFCrow mi moc neříká, ale po načtení dat ze SAP přes connector se dá ve VS normálně celá struktura načteného objektu a dat v něm prozkoumat. Tedy pokud to ve VS nejde, pak je to krajně podivné, nebo nestandard implementace či finálně ty data nejsou načtený v době kdy se na to snažíš podívat.
Ahoj, omlouvám se, dříve jsem se k tomu nedostal. Já jsem v tuto chvíli
přesvědčen, že se mi data ze SAP natáhnou opravdu velmi rychle a dlouho
trvá zpracování. Možná něco používám špatně, ale nevím, jak na to
přijít. Úplně původně jsem data převáděl přímo do pole, pak jsem z
toho tvořil list. Pak mě a kolegům přišlo na mysl, že bychom to mohli
dělat pomocí dotazů, protože je to elegantnější a mohlo by to fungovat
rychleji.
Rekurze nepoužíváme a deffered execution, pokud vím, taky ne(alespoň ve
svém kódu to nikde nemám). Do SAP prostředí přístup nemám, o tom naše
IT nechce ani slyšet .
O rfcRow je něco zde Rfcrow
a poradili mi jeho použití kdysi na StackOferflov: zde
Prozkoumat strukturu načtenou ze SAP mi nelze. Možná je to spojeno s
chybou, viz přiložený obrázek.
Otázka zní: Jak tutově zjistit, jestli se data natáhnou hned nebo to jejich
načtení trvá dlouho?
Děkuji za čas všech zúčastněných!
Jestli je vůle, mohu poskytnout i další zdrojový kód.
Ahoj, v pohodě. Je to téma, který může být užitečný pro víc lidí a taky se mi může hodit vědět co je za problém.
Asi bych tedy rozdělil analýzu na několik postupných kroků... a začal bych samotným načítáním ze SAP.
1. Ověření načtených dat ze SAP a změřit rychlost načtení do "použitelné podoby" pro další zpracování. Tzn. načíst pouze základní dataset ze SAP aniž bych s ním dále pracoval pomocí linq dotazů a zároveň jsem ho byl schopen číst (list, kolekce, atd...). Tim bych ověřil, že skutečně data jsou zpracovány, uloženy a připraveny pro další použití v paměti lokálního klienta. Nevim jak vypadá volání RFC a výstup, ale běžne používám viz níže a nikdy nebyl problém ani s enumerací pro debug ve VS. Následně pak používám konvertor na List nebo DataTable. Aby byla práce s linq snazší, používám předefinované "strong typed" datatable (XSD). Ať už zvolíš jakýkoliv způsob, nepokračoval bych dále dokud nemám plně funkční a jasně ověřitelný načtení dat ze SAP. Taky je otázkou jak Linq dále pracuje (jak efektivně) s těmi custom SAP objekty... tady nemám tušení. Udělám konverzi a dál mě nezajímají.
e.g.
public SapTables.KanbanMonitorDataTable GetKanbanMonitor(string material)
{
SapTables.KanbanMonitorDataTable GetKanbanMonitorRet = default;
try
{
var RfcApi = Conn.RfcDest.Repository.CreateFunction("Z_KANBAN_LIST");
RfcApi.SetValue("I_MATNR", material);
RfcApi.Invoke(Conn.RfcDest);
var argrfcTable = RfcApi.GetTable("T_KANMON", false);
GetKanbanMonitorRet = (SapTables.KanbanMonitorDataTable)Helper.ConvertRfcToAdoTable(ref argrfcTable, new SapTables.KanbanMonitorDataTable());
}
catch
{
throw new Exception("SAP connection error");
}
return GetKanbanMonitorRet;
}
Ahoj, já používám toto:
private Object Connecting(string functionName, List<KeyValuePair<string, string>> inputParameters, string source)
{
Object data = new { };
SetRFC(source); //Set login data
if (!Session.IsConnected)
{
Session.Connect(); //Connection to SAP
if (Session.IsConnected)
{
FunctionCall fn = Session.ImportCall(functionName); //Set call
foreach (KeyValuePair<string, string> param in inputParameters)
{
fn.Importing[param.Key].value = param.Value; //Inserting input parametters of the SAP function
}
Stopwatch sw = new Stopwatch();
sw.Start();
Session.CallFunction(fn, true); //Calling the function
data = fn.Tables["RESULT_TABLE"]; //Getting data from SAP
sw.Stop();
}
else WasConnected = false;
}
if (WasConnected)
Session.Disconnect();
return data;
}
Pomocí Stopwatch jsem naměřil 51,1 s. Pravda - Tvrdil jsem, že to trvá
několik sekund. Možná je to tím, že jsem teď na Homeoffice. Pořád to
nejsou hodiny.
Na přiloženém obrázku je výsledek po pokusu o rozbalení výsledků.
Popravdě nejsem schopen použít tvůj kód neboť jsou pro mě neznámé
třídy.
Aha, tak to je fajn, že tohle funguje. Tak k tomu linq Nechce mi to zobrazit ten tvůj
původní kód.. tedy máš hotový "hrubý SAP" dataset a tam začínáš
pomocí linq "předvýběr". Mají ty dotazy nějaký společný kritéria podle
nichž by ten předvýběr šel jít udělat a opět uložit do paměti? Tzn.
aby následný linq dotazy skutečně dotazovaly jen rozdíly vůči
základnímu datasetu a jen v nezbytně nutný míře. Tím omezíš
vícenásobný dotazování neustále toho samýho...
Např. bych tedy zvážil se zbavit objektu Rfcrow a převedl to na List<třída> kde <třída> má už jen data, nezbytně nutná pro další práci. Tzn. jen sloupce co jsou potřeba a nezbytné údaje.. veškerý další balast zahodit.
Je mi pořád divný, že viz ten přiložený obrázek při enumeraci ty data prostě nejsou vidět....
teď jsem zkoušel nějaký data (ne ze SAP ale z SQL DB) a 400k řádků je celkem prd... rozhodně to trvá desítky sec max podle složitosti dotazů a výkonu lokálního stroje.
Možná bych udělal pokus a vytvořil si třídu, s definicí vlastností (properties) tak, aby odpovídaly sloupcům a jejich typu proměnný v tom výstupu ze SAP. A pak nahulváta pomocí foreach (...) načetl hodnotu z každýho toho rfcrow řádku do odpovídající třídy a tu pak pomocí ADD přidal do listu... Jak dlouho ta operace zabere. Iterace tohoto typu je jedna z nejsnazších a měla by být relativně rychlá. Výsledkem je pak jednoduchá List<třída> kolekce na který se linq dotazy tvoří velice snadno a efektivně.
Ahoj, aktuální kód vypadá asi takto (trochu jsem ho zjednodušil, protože to mám rozdělené do více metod):
Object objectData= new { };
Session.CallFunction(fn, true); //Calling the function
objectData= fn.Tables["RESULT_TABLE"]; //Getting data from SAP
RfcData data = (RfcData)objectData;
IEnumerable<RfcRow> IEdata = from RfcRow dataRow in data.Rows
where dataRow["DEL_STAT"].value.ToString() != "X"
&& dataRow["DEL_STAT_WERK"].value.ToString() != "X"
&& dataRow["DEL_DISPO_99"].value.ToString() != "X"
&& dataRow["DEL_STAT_LV"].value.ToString() != "X"
select dataRow;
IEnumerable<MLFBCode> mLFBs_A = (from RfcRow dataRow in IEdata
where regex_A.IsMatch(dataRow["MLFB"].value.ToString())
&& listOfRegexFromDatabase.Where(x => x.IsMatch(dataRow["MLFB"].value.ToString())).Any()
select new MLFBCode()
{
MLFB = dataRow["MLFB"].value.ToString(),
Number = dataRow["MATNR"].value.ToString(),
Active = true,
})/*.ToList()*/;
IEnumerable<MLFBCode> mLFBs_B = (from RfcRow dataRow in IEdata
where regex_B.IsMatch(dataRow["MLFB"].value.ToString()) //valid according to Regex
&& listOfRegexFromDatabase.Where(x => x.IsMatch(dataRow["MAKTG"].value.ToString())).Any()
select new MLFBCode()
{
MLFB = dataRow["MAKTG"].value.ToString(),
Number = dataRow["MATNR"].value.ToString(),
Active = true,
})/*.ToList()*/;
IEnumerable<MLFBCode> mLFBs = mLFBs_A.Concat(mLFBs_B);
Předvýběr, který se ptáš, je podle tvořen dotazem "IEnumerable<RfcRow> IEdata", kde se prvotně zjišťuje, jestli není záznam nějakým způsobem vymazán. Zde docházím k nedostatku, který mě trápí a to sice, že tento IEnumerable objekt chci převést do Listu, tak už to trvá dlouho a co víc, provádění se po 1-2 hodinách samo ukončí bez jakéhokoli varování, jako bych ho ve VS ani nespustil. Takto mi to začalo blbnout nedávno. V prohlížeči událostí se zobrazí, viz. příloha.
Co se týče tebou popisovaných pokusů: dříve jsem místo objektů rfcRow
a RfcData používal vnořený for (jeden tahal sloupce, druhý řádky) a tím
jsem skládal do pole(později do listu) instance mojí třídy (tj. MLFBCode).
Od toho jsme utekli ze stejného důvodu, tj. časová náročnost, v domnění,
že toto bude rychlejší .
Podle mě nic přebytečného jsem netahal ani tenkrát, ani teď. Ale prosím o
posouzení přiloženého kódu. Každopádně pokusy mohu provést, ale zatím
jsem moc nepobral, jak by se pokusná třída měla lišit od mého
MLFBCode.
Moc děkuji!
Ještě jsem se dozvěděl od IT, že můžeme zkusit jiný konektor než RFC. Nejdříve bych však zkusil ještě zaexperimentovat.
Ahoj kouknu, nicméně podle příznaků mi přijde, že ty data se
skutečně tahají pomalu tahají ze SAP, případně ten dotaz se zpracovává
pomalu. Taky bych se na tenhle typ pokusů vyhnul ASP (sem něvěděl a napsal to jako samostatnou např.
konzolovou app s výstupem do konzole.
Než nad tím pobádáme, můžeš ten konkrétní RFC spustit lokálně v SAP klientovi a zjistit jak dlouho to trvá (případně ten daný dotaz co ta RFC používá..)? Přijde mi, že se dotaz spustí rychle, ale protože ti nejde ta enumerace ani převod do listu jak jsi psal, tak se mi to jeví že to čeká na data ze SAP. Tedy je podle mě nutný ověřit jak dlouho ten dotaz vlastně běží v SAP vč. ověření že výsledky skutečně "vidím"...
Jinak na 4. řádku RfcData data = (RfcData)objectData; bys měl už data po načtení ve VS vidět ne?
Ahoj, je tam asi nějaká principiální chyba. Nevidím tam nic, viz.
obr.
Co se týče pomalého tahání ze SAP: IT mi dokáže zjistit, jak dlouho trvá
zpracování mého požadavku, ale jak dlouho mi to posílám prý musím
zjistit sám. Zkusil jsem ve Správci úloh spustit Sledování prostředků.
Tahání ze SAP trvá opravdu pouze při provádění příkazu
Session.CallFunction(fn, true); a pak aktivita mizí a nebjeví se ani při
provádění Tolist().
Konzolovou aplikaci určitě začnu používat, už jsem o tom přemýšlel
před tím.
Jediné, co mě napadá, je opravdu zkusit jiný konektor.
Ahoj, no obávám se, že tady jsme prozatím skončili a Tvůj problém bude připojení k SAP a načtení dat lokálně do paměti. Linq s tím podle mě moc nesouvisí a domnívám se, že jakmile máš vše načtené v paměti tak to dotazování proběhne rychle. Jinak je něco vidět na tom řádku před tím než proběhne přetypování? viz objectData= fn.Tables["RESULT_TABLE"]; když použiješ
var objectData= fn.Tables["RESULT_TABLE"];
Vypadá to takto, viz. obrázek. No zkusím to přebouchat na jiný konektor.
jj, asi už jsme uhnuli od linq Každopádně kdyžtak založ nový téma SAP connector jestli
chceš.
Zobrazeno 22 zpráv z 22.