Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Diskuze: Provádění série navazujících LINQ dotazů - Jak to zrychlit?

Aktivity
Avatar
Tomáš M.
Člen
Avatar
Tomáš M.:4.3.2021 11:03

Nedaří se mi zrychlit provádění série navazujících LINQ dotazů. Dotazy mají za úkol vytáhnout ze SAP data (asi 350 000 záznamů), vyfiltrovat pro mě potřebné záznamy (cca 150 000 z.) a ty pak uložit do mojí místní SQL databáze.

Zkusil jsem: Původně jsem vše prováděl v listu. Jak jsem ale pochopil přecházení z IEnumerable je velice pomalé, takže moje poslední teorie je, že se celá procedura bude odehrávat v IEnumerable. Prošel jsem si odpovídající tutoriály zde na ITnetworku i jinde, ale odpověď na toto téma jsem nenašel.

Chci docílit: V tuto chvíli potřebuji vyřešit tuto otázku: Jak opakovaně NEPROVÁDĚT dotazy, které jsou ve zmiňované sérii jako první? Tato série vypadá takto: zde je první dotaz IEdata, pak následují mLFBsA a mLFBsB, které z něj čerpají. Pomocí Concatenate se oba spojují do objektu mLFBs. Zde jsou obsaženy další dotazy listToAdd, listToUpdate a listToDeactivate, které tahají z mLFBs. Dlě mě, se tedy musí IEdata provést celkem 6x, mLFBsA a mLFBsB 3x a poslední tři každý 1x. Jak to celé zrychlit? Nějaké tipy a triky? Dělám nějaké zásadní programátorské chyby? Omlouvám se, jsem amatér.

 
Odpovědět
4.3.2021 11:03
Avatar
Petr Jílek
Člen
Avatar
Petr Jílek:4.3.2021 20:53

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.

 
Nahoru Odpovědět
4.3.2021 20:53
Avatar
Tomáš M.
Člen
Avatar
Odpovídá na Petr Jílek
Tomáš M.:5.3.2021 6:30

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.

 
Nahoru Odpovědět
5.3.2021 6:30
Avatar
Petr Jílek
Člen
Avatar
Petr Jílek:5.3.2021 8:43

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

 
Nahoru Odpovědět
5.3.2021 8:43
Avatar
zelvicek
Člen
Avatar
Odpovídá na Tomáš M.
zelvicek:5.3.2021 9:53

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).

 
Nahoru Odpovědět
5.3.2021 9:53
Avatar
Tomáš M.
Člen
Avatar
Odpovídá na Petr Jílek
Tomáš M.:8.3.2021 7:35

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 Díky za další náměty.

 
Nahoru Odpovědět
8.3.2021 7:35
Avatar
Petr Jílek
Člen
Avatar
Petr Jílek:8.3.2021 9:02

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ů...

 
Nahoru Odpovědět
8.3.2021 9:02
Avatar
Petr Jílek
Člen
Avatar
Petr Jílek:8.3.2021 9:08

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.

Editováno 8.3.2021 9:08
 
Nahoru Odpovědět
8.3.2021 9:08
Avatar
Tomáš M.
Člen
Avatar
Odpovídá na Petr Jílek
Tomáš M.:10.3.2021 9:45

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 :-D.
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!

 
Nahoru Odpovědět
10.3.2021 9:45
Avatar
Tomáš M.
Člen
Avatar
Odpovídá na Petr Jílek
Tomáš M.:10.3.2021 9:50

Jestli je vůle, mohu poskytnout i další zdrojový kód. :-)

 
Nahoru Odpovědět
10.3.2021 9:50
Avatar
Petr Jílek
Člen
Avatar
Petr Jílek:10.3.2021 11:27

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;
 }
Editováno 10.3.2021 11:27
 
Nahoru Odpovědět
10.3.2021 11:27
Avatar
Tomáš M.
Člen
Avatar
Odpovídá na Petr Jílek
Tomáš M.:11.3.2021 13:16

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. :-(

 
Nahoru Odpovědět
11.3.2021 13:16
Avatar
Petr Jílek
Člen
Avatar
Petr Jílek:11.3.2021 14:50

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....

Editováno 11.3.2021 14:52
 
Nahoru Odpovědět
11.3.2021 14:50
Avatar
Petr Jílek
Člen
Avatar
Petr Jílek:11.3.2021 14:59

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ě.

 
Nahoru Odpovědět
11.3.2021 14:59
Avatar
Tomáš M.
Člen
Avatar
Tomáš M.:15.3.2021 8:04

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ší :-D.
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!

 
Nahoru Odpovědět
15.3.2021 8:04
Avatar
Tomáš M.
Člen
Avatar
Odpovídá na Tomáš M.
Tomáš M.:15.3.2021 8:05

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.

 
Nahoru Odpovědět
15.3.2021 8:05
Avatar
Petr Jílek
Člen
Avatar
Petr Jílek:15.3.2021 10:20

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"...

 
Nahoru Odpovědět
15.3.2021 10:20
Avatar
Petr Jílek
Člen
Avatar
Petr Jílek:15.3.2021 10:25

Jinak na 4. řádku RfcData data = (RfcData)objec­tData; bys měl už data po načtení ve VS vidět ne?

 
Nahoru Odpovědět
15.3.2021 10:25
Avatar
Tomáš M.
Člen
Avatar
Odpovídá na Petr Jílek
Tomáš M.:16.3.2021 12:17

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.CallFun­ction(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. :-(

 
Nahoru Odpovědět
16.3.2021 12:17
Avatar
Petr Jílek
Člen
Avatar
Petr Jílek:16.3.2021 12:32

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["RE­SULT_TABLE"]; když použiješ

var objectData= fn.Tables["RESULT_TABLE"];
 
Nahoru Odpovědět
16.3.2021 12:32
Avatar
Tomáš M.
Člen
Avatar
Odpovídá na Petr Jílek
Tomáš M.:16.3.2021 12:45

Vypadá to takto, viz. obrázek. No zkusím to přebouchat na jiný konektor.

 
Nahoru Odpovědět
16.3.2021 12:45
Avatar
Petr Jílek
Člen
Avatar
Petr Jílek:16.3.2021 13:42

jj, asi už jsme uhnuli od linq :) Každopádně kdyžtak založ nový téma SAP connector jestli chceš.

 
Nahoru Odpovědět
16.3.2021 13:42
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 22 zpráv z 22.