{"id":35259,"date":"2019-10-31T22:03:17","date_gmt":"2019-10-31T19:03:17","guid":{"rendered":"https:\/\/prohoster.info\/blog\/istoriya-odnogo-sql-rassledovaniya\/"},"modified":"2019-10-31T22:03:17","modified_gmt":"2019-10-31T19:03:17","slug":"istoriya-odnogo-sql-rassledovaniya","status":"publish","type":"post","link":"https:\/\/prohoster.info\/hr\/blog\/administrirovanie\/istoriya-odnogo-sql-rassledovaniya","title":{"rendered":"Povijest jedne SQL istrage","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>U prosincu pro\u0161le godine primio sam zanimljivo izvje\u0161\u0107e o gre\u0161ci od VWO tima za podr\u0161ku. Vrijeme u\u010ditavanja jednog od analiti\u010dkih izvje\u0161taja za velikog korporativnog klijenta \u010dinilo se nevjerojatno dugim. Kako je to bila moja odgovornost, odmah sam se fokusirao na rje\u0161avanje problema.<\/p>\n<p><\/p>\n<h2>Povijest<\/h2>\n<p><\/p>\n<p>Da bih pojasnio o \u010demu se radi, malo \u0107u re\u0107i o VWO-u. To je platforma koja omogu\u0107ava pokretanje razli\u010ditih ciljanih kampanja na va\u0161im web stranicama: provo\u0111enje A\/B eksperimenata, pra\u0107enje posjetitelja i konverzija, analizu prodajnog lijevka, prikazivanje toplinskih karata i reprodukciju snimaka posjeta.<\/p>\n<p><\/p>\n<p>No najva\u017enija funkcija platforme su izvje\u0161taji. Sve prethodno navedene funkcije su me\u0111usobno povezane. Za korporativne klijente, ogroman skup informacija bio bi potpuno beskoristan bez mo\u0107ne platforme koja ih prikazuje na na\u010din prikladan za analizu.<\/p>\n<p><\/p>\n<p>Kori\u0161tenjem platforme mo\u017eete postaviti bilo kakav upit na velikom skupu podataka. Evo jednostavnog primjera:<\/p>\n<p><\/p>\n<pre>Prika\u017ei sve klikove na stranici \"abc.com\"\nOD &lt;datuma d1&gt; DO &lt;datuma d2&gt;\nza osobe koje\nkoriste Chrome ILI\n(\u017eive u Europi I koriste iPhone)<\/pre>\n<p><\/p>\n<p>Obratite pa\u017enju na booleanske operatore. Oni su dostupni klijentima u su\u010delju za upite, omogu\u0107uju\u0107i izradu neograni\u010deno slo\u017eenih upita za dobivanje uzoraka.<\/p>\n<p><\/p>\n<h2>Spori upit<\/h2>\n<p><\/p>\n<p>Klijent o kojem se radi poku\u0161avao je u\u010diniti ne\u0161to \u0161to intuitivno treba brzo raditi:<\/p>\n<p><\/p>\n<pre>Prika\u017ei sve snimke sesija\nza korisnike koji su posjetili bilo koju stranicu\ns URL-om koji sadr\u017ei \"\\\/jobs\"<\/pre>\n<p><\/p>\n<p>Na ovoj web stranici bilo je ogromno koli\u010dine prometa, a pohranjivali smo vi\u0161e od milijun jedinstvenih URL adresa samo za nju. A \u017eeljeli su prona\u0107i prili\u010dno jednostavan uzorak URL-a, koji se odnosio na njihov poslovni model.<\/p>\n<p>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Preliminarna istraga<\/h2>\n<p><\/p>\n<p>Pogledajmo \u0161to se doga\u0111a u bazi podataka. Ispod je izvorni spori SQL upit:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">SELECT \n    count(*) \nFROM \n    acc_{account_id}.urls as recordings_urls, \n    acc_{account_id}.recording_data as recording_data, \n    acc_{account_id}.sessions as sessions \nWHERE \n    recording_data.usp_id = sessions.usp_id \n    AND sessions.referrer_id = recordings_urls.id \n    AND  (  urls &amp;&amp;  array(select id from acc_{account_id}.urls where url  ILIKE  '%enterprise_customer.com\\\/jobs%')::text[]   ) \n    AND r_time &gt; to_timestamp(1542585600) \n    AND r_time &lt; to_timestamp(1545177599) \n    AND recording_data.duration &gt;=5 \n    AND recording_data.num_of_pages &gt; 0 ;<\/code><\/pre>\n<p><\/p>\n<p>A evo vremenskih oznaka:<\/p>\n<p><\/p>\n<pre>Planirano vrijeme: 1.480 ms\nVrijeme izvr\u0161enja: 1431924.650 ms<\/pre>\n<p><\/p>\n<p>Upit je obuhvatio 150 tisu\u0107a redaka. Planer upita pokazao je nekoliko zanimljivih detalja, ali nikakvih o\u010diglednih uskih grla.<\/p>\n<p><\/p>\n<p>Pogledajmo upit dublje. Kao \u0161to se mo\u017ee vidjeti, on radi <code>JOIN<\/code> tri tablice:<\/p>\n<p><\/p>\n<ol>\n<li><strong>sessions<\/strong>: za prikaz informacija o sesiji: preglednik, korisni\u010dki agent, dr\u017eava i tako dalje.<\/li>\n<li><strong>recording_data<\/strong>: snimljeni URL-ovi, stranice, trajanje posjeta<\/li>\n<li><strong>urls<\/strong>: kako bismo izbjegli dupliciranje iznimno dugih URL-ova, pohranjujemo ih u zasebnu tablicu.<\/li>\n<\/ol>\n<p><\/p>\n<p>Tako\u0111er, obratite pozornost da su sve na\u0161e tablice ve\u0107 podijeljene prema <code>account_id<\/code>. Na taj na\u010din isklju\u010dena je situacija u kojoj bi zbog jednog izuzetno velikog ra\u010duna problemi nastali za ostale.<\/p>\n<p><\/p>\n<h2>U potrazi za tragovima<\/h2>\n<p><\/p>\n<p>Pri bli\u017eem pregledu vidimo da ne\u0161to nije u redu s konkretnim upitom. Treba obratiti pa\u017enju na ovu liniju:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">urls &amp;&amp; array(\n\tselect id from acc_{account_id}.urls \n\twhere url ILIKE '%enterprise_customer.com\/jobs%'\n)::text[]<\/code><\/pre>\n<p><\/p>\n<p>Prva pomisao bila je da mo\u017eda, zbog <code>ILIKE<\/code> na svim tim dugim URL-ovima (imamo vi\u0161e od 1,4 milijuna <strong>jedinstvenih\u00a0<\/strong>URL-ova, prikupljenih za ovaj ra\u010dun) performanse mogu opadati.<\/p>\n<p><\/p>\n<p>Ali, ne \u2014 nije do toga!<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">SELECT id FROM urls WHERE url ILIKE '%enterprise_customer.com\/jobs%';\n  id\n--------\n ...\n(198661 redaka)\n\nVrijeme: 5231.765 ms<\/code><\/pre>\n<p><\/p>\n<p>Sam upit za pretra\u017eivanje po obrascu traje samo 5 sekundi. Pretra\u017eivanje po obrascu na milijunu jedinstvenih URL-ova o\u010dito nije problem.<\/p>\n<p><\/p>\n<p>Sljede\u0107i osumnji\u010denik na popisu \u2014 nekoliko <code>JOIN<\/code>. Mo\u017eda je njihovo pretjerano kori\u0161tenje dovelo do usporavanja? Obi\u010dno <code>JOIN<\/code>\u2018d \u2014 naj o\u010digledniji kandidati za probleme s performansama, ali nisam verovao da je na\u0161 slu\u010daj tipi\u010dan.<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">analytics_db=# SELECT\n    count(*)\nFROM\n    acc_{account_id}.urls as recordings_urls,\n    acc_{account_id}.recording_data_0 as recording_data,\n    acc_{account_id}.sessions_0 as sessions\nWHERE\n    recording_data.usp_id = sessions.usp_id\n    AND sessions.referrer_id = recordings_urls.id\n    AND r_time &gt; to_timestamp(1542585600)\n    AND r_time =5\n    AND recording_data.num_of_pages &gt; 0 ;\n count\n-------\n  8086\n(1 redak)\n\nVrijeme: 147.851 ms<\/code><\/pre>\n<p><\/p>\n<p>I to tako\u0111er nije bio na\u0161 slu\u010daj. <code>JOIN<\/code>\u2018d su se pokazali prili\u010dno brzim.<\/p>\n<p><\/p>\n<h2>Su\u017eavanje kruga osumnji\u010denika<\/h2>\n<p><\/p>\n<p>Bio sam spreman po\u010deti mijenjati upit kako bismo postigli bilo kakva mogu\u0107a pobolj\u0161anja performansi. Tim smo razvili 2 glavne ideje:<\/p>\n<p><\/p>\n<ul>\n<li><strong>Koristiti EXISTS za podupit URL-ova<\/strong>: \u017deljeli smo jo\u0161 jednom provjeriti ima li problema s podupitom za URL-ove. Jedan od na\u010dina da se to postigne \u2014 jednostavno koristiti <code>EXISTS<\/code>. <code>EXISTS<\/code> <noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/current\/functions-subquery.html#FUNCTIONS-SUBQUERY-EXISTS\">mo\u017ee<\/a><\/noindex> zna\u010dajno pobolj\u0161anje performansi dolazi odmah nakon \u0161to se prona\u0111e jedini redak prema uvjetu.<\/li>\n<\/ul>\n<p><\/p>\n<pre><code class=\"plaintext\">SELECT\n\tcount(*) \nFROM \n    acc_{account_id}.urls as recordings_urls,\n    acc_{account_id}.recording_data as recording_data,\n    acc_{account_id}.sessions as sessions\nWHERE\n    recording_data.usp_id = sessions.usp_id\n    AND  (  1 = 1  )\n    AND sessions.referrer_id = recordings_urls.id\n    AND  (exists(select id from acc_{account_id}.urls where url  ILIKE '%enterprise_customer.com\/jobs%'))\n    AND r_time &gt; to_timestamp(1547585600)\n    AND r_time =5\n    AND recording_data.num_of_pages &gt; 0 ;\n count\n 32519\n(1 row)\nTime: 1636.637 ms<\/code><\/pre>\n<p><\/p>\n<p>Da, podupit kada je omotan u\u00a0<code>EXISTS<\/code>, \u010dini sve super brzim. Sljede\u0107e logi\u010dno pitanje je za\u0161to je upit s <code>JOIN<\/code>-ima i sam podupit brzi pojedina\u010dno, ali jako usporavaju zajedno?<\/p>\n<p><\/p>\n<ul>\n<li><strong>Premje\u0161tamo podupit u CTE <\/strong>: ako je upit brz sam po sebi, mo\u017eemo prvo izra\u010dunati brz rezultat, a zatim ga donijeti glavnom upitu.<\/li>\n<\/ul>\n<p><\/p>\n<pre><code class=\"plaintext\">WITH matching_urls AS (\n    select id::text from acc_{account_id}.urls where url  ILIKE  '%enterprise_customer.com\/jobs%'\n)\n\nSELECT \n    count(*) FROM acc_{account_id}.urls as recordings_urls, \n    acc_{account_id}.recording_data as recording_data, \n    acc_{account_id}.sessions as sessions,\n    matching_urls\nWHERE \n    recording_data.usp_id = sessions.usp_id \n    AND  (  1 = 1  )  \n    AND sessions.referrer_id = recordings_urls.id\n    AND (urls &amp;&amp; array(SELECT id from matching_urls)::text[])\n    AND r_time &gt; to_timestamp(1542585600) \n    AND r_time =5 \n    AND recording_data.num_of_pages &gt; 0;<\/code><\/pre>\n<p><\/p>\n<p>Ali je sve jo\u0161 uvijek bilo vrlo sporo.<\/p>\n<p><\/p>\n<h2>Pronalazimo krivca<\/h2>\n<p><\/p>\n<p>Cijelo to vrijeme pred o\u010dima mi je bila jedna sitnica od koje sam se stalno odmi\u010du\u0107i. No s obzirom na to da vi\u0161e ni\u0161ta nije preostalo, odlu\u010dio sam pogledati i to. Govorim o <code>&amp;&amp;<\/code> operatoru. Dok <code>EXISTS<\/code> samo pobolj\u0161ava performanse, <code>&amp;&amp;<\/code> je bio jedini preostali zajedni\u010dki faktor u svim verzijama sporog upita.<\/p>\n<p><\/p>\n<p>Gledaju\u0107i <noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/9.1\/functions-array.html\">dokumentaciju<\/a><\/noindex>, vidimo da <code>&amp;&amp;<\/code> se koristi kada je potrebno prona\u0107i zajedni\u010dke elemente izme\u0111u dva niza.<\/p>\n<p><\/p>\n<p>U izvornom upitu to je:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">AND  (  urls &amp;&amp;  array(select id from acc_{account_id}.urls where url  ILIKE  '%enterprise_customer.com\/jobs%')::text[]   )<\/code><\/pre>\n<p><\/p>\n<p>\u0160to zna\u010di da radimo pretra\u017eivanje prema obrascu na na\u0161im URL-ovima, a zatim nalazimo presjek sa svim URL-ovima s zajedni\u010dkim zapisima. To je malo zbunjuju\u0107e, jer \"urls\" ovdje ne referira na tablicu koja sadr\u017ei sve URL-ove, ve\u0107 na stupac \"urls\" unutar tablice <code>recording_data<\/code>.<\/p>\n<p><\/p>\n<p>S rastom sumnje u vezi <code>&amp;&amp;<\/code>, poku\u0161ao sam prona\u0107i potvrdu u planu upita generiranom <code>EXPLAIN ANALYZE<\/code> (ve\u0107 sam imao spremljeni plan, ali obi\u010dno mi je lak\u0161e eksperimentirati u SQL-u nego poku\u0161avati shvatiti neprozirnosti planera upita).<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">Filter: ((urls &amp;&amp; ($0)::text[]) AND (r_time &gt; '2018-12-17 12:17:23+00'::timestamp with time zone) AND (r_time = '5'::double precision) AND (num_of_pages &gt; 0))\n                           Redovi uklonjeni filtriranjem: 52710<\/code><\/pre>\n<p><\/p>\n<p>Bilo je nekoliko redaka filtara samo iz <code>&amp;&amp;<\/code>. \u0160to je zna\u010dilo da je ova operacija bila ne samo skupa, ve\u0107 je i izvr\u0161avana nekoliko puta.<\/p>\n<p><\/p>\n<p>Provjerio sam to izoliraju\u0107i uvjet<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">SELECT 1\nFROM \n    acc_{account_id}.urls as recordings_urls, \n    acc_{account_id}.recording_data_30 as recording_data_30, \n    acc_{account_id}.sessions_30 as sessions_30 \nWHERE \n\turls &amp;&amp;  array(select id from acc_{account_id}.urls where url  ILIKE  '%enterprise_customer.com\/jobs%')::text[]<\/code><\/pre>\n<p><\/p>\n<p>Ovaj upit se izvr\u0161avao sporo. Budu\u0107i da <code>JOIN<\/code>-i su brzi i podupiti su brzi, ostao je samo <code>&amp;&amp;<\/code> operator.<\/p>\n<p><\/p>\n<p>Samo ovo je klju\u010dna operacija. Uvijek moramo tra\u017eiti u cijeloj glavnoj tablici URL-ova kako bismo tra\u017eili prema obrascu, i uvijek trebamo prona\u0107i presjeke. Ne mo\u017eemo direktno pretra\u017eivati zapise URL-ova, jer su to samo ID-evi koji se odnose na <code>urls<\/code>.<\/p>\n<p><\/p>\n<h2>Na putu do rje\u0161enja<\/h2>\n<p><\/p>\n<p><code>&amp;&amp;<\/code> spor, jer su oba seta ogromna. Operacija \u0107e biti relativno brza ako zamijenim <code>urls<\/code> na <code>{ \"http:\/\/google.com\/\", \"http:\/\/wingify.com\/\" }<\/code>.<\/p>\n<p><\/p>\n<p>Po\u010deo sam tra\u017eiti na\u010din da u Postgresu napravim presjek skupova bez kori\u0161tenja <code>&amp;&amp;<\/code>, ali bez posebnog uspjeha.<\/p>\n<p><\/p>\n<p>Na kraju smo odlu\u010dili jednostavno rije\u0161iti problem izolirano: daj mi sve <code>urls<\/code> linije za koje URL odgovara obrascu. Bez dodatnih uslova, to \u0107e biti \u2014\u00a0<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">SELECT urls.url\nFROM \n\tacc_{account_id}.urls as urls,\n\t(SELECT unnest(recording_data.urls) AS id) AS unrolled_urls\nWHERE\n\turls.id = unrolled_urls.id AND\n\turls.url  ILIKE  '%jobs%'<\/code><\/pre>\n<p><\/p>\n<p>Umjesto\u00a0<code>JOIN<\/code> sintaksom samo sam koristio podupit i razvio <code>recording_data.urls<\/code> niz, kako bi se moglo direktno primijeniti uvjet u <code>GDJE<\/code>.<\/p>\n<p><\/p>\n<p>Najva\u017enija stvar ovdje je da <code>&amp;&amp;<\/code> se koristi za provjeru sadr\u017ei li odre\u0111eni zapis odgovaraju\u0107i URL. Malo su\u017eavaju\u0107i o\u010di, mo\u017ee se vidjeti kako se u ovoj operaciji premje\u0161ta kroz elemente niza (ili redove tablice) i zaustavi se kada se ispuni uvjet (odgovaranje). Ni\u0161ta ne podsje\u0107a? Aha, <code>EXISTS<\/code>.<\/p>\n<p><\/p>\n<p>Budu\u0107i da <code>recording_data.urls<\/code> mo\u017ee se pozivati izvan konteksta podupita, kada se to dogodi, mo\u017eemo se vratiti na\u0161em starom prijatelju <code>EXISTS<\/code> i obaviti mu podupit.<\/p>\n<p><\/p>\n<p>Kombiniraju\u0107i sve zajedno, dobivamo kona\u010dni optimizirani upit:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">ODABERITE \n    count(*) \nIZ \n    acc_{account_id}.urls kao recordings_urls, \n    acc_{account_id}.recording_data kao recording_data, \n    acc_{account_id}.sessions kao sessions \nGDE \n    recording_data.usp_id = sessions.usp_id \n    I \n    (  1 = 1  )  \n    I sessions.referrer_id = recordings_urls.id \n    I r_time &gt; to_timestamp(1542585600) \n    I r_time = 5 \n    I recording_data.num_of_pages &gt; 0\n    I POSTOJI(\n        ODABERITE urls.url\n        IZ \n            acc_{account_id}.urls kao urls,\n            (ODABERITE unnest(urls) KAO rec_url_id IZ acc_{account_id}.recording_data) \n            KAO unrolled_urls\n        GDE\n            urls.id = unrolled_urls.rec_url_id I\n            urls.url ILIKE '%enterprise_customer.com\/jobs%'\n    );\n<\/code><\/pre>\n<p><\/p>\n<p>I kona\u010dno vrijeme izvr\u0161avanja <code>Vrijeme: 1898.717 ms<\/code> Vrijeme je za proslavu?!?<\/p>\n<p><\/p>\n<p>Ne tako brzo! Prvo treba provjeriti ispravnost. Bio sam vrlo sumnji\u010dav prema <code>EXISTS<\/code> optimizaciji, jer ona mijenja logiku za ranije zavr\u0161avanje. Moramo biti sigurni da nismo dodali nejasnu gre\u0161ku u upit.<\/p>\n<p><\/p>\n<p>Jednostavna provjera bila je izvr\u0161avanje <code>count(*)<\/code> i na sporim i na brzim upitima za mnoge razli\u010dite skupove podataka. Zatim sam, za malo podskupova podataka, ru\u010dno provjerio ispravnost svih rezultata.<\/p>\n<p><\/p>\n<p>Sve provjere su dale stabilno pozitivne rezultate. Sve smo popravili!<\/p>\n<p><\/p>\n<h2>Izvu\u010dene Lekcije<\/h2>\n<p><\/p>\n<p>Mnoge lekcije se mogu izvu\u0107i iz ove pri\u010de:<\/p>\n<p><\/p>\n<ol>\n<li>Planovi upita ne govore cijelu pri\u010du, ali mogu dati naznake<\/li>\n<li>Glavni osumnji\u010deni nisu uvijek pravi zlo\u010dinci<\/li>\n<li>Spore upite se mogu podijeliti kako bi se izolirali uska mjesta<\/li>\n<li>Nisu sve optimizacije po prirodi reduktivne<\/li>\n<li>Kori\u0161tenje <code>EXIST<\/code>, gdje je to mogu\u0107e, mo\u017ee dovesti do dramati\u010dnog rasta performansi<\/li>\n<\/ol>\n<p><\/p>\n<h2>Izvje\u0161taj<\/h2>\n<p><\/p>\n<p>Pre\u0161li smo s vremena upita od ~24 minute na 2 sekunde \u2014 zna\u010dajan rast performansi! Iako je ovaj \u010dlanak dug, svi eksperimenti koje smo radili dogodili su se u jednom danu i procijenjeno je da su trajali od 1,5 do 2 sata za optimizacije i testiranje.<\/p>\n<p><\/p>\n<p>SQL je \u010dudesan jezik, ako se ne bojite njega, ve\u0107 poku\u0161avate razumjeti i koristiti ga. Imaju\u0107i dobro razumijevanje kako se SQL upiti izvr\u0161avaju, kako Baze podataka generiraju planove upita, kako rade indeksi i samo veli\u010dina podataka s kojima imate posla, mogli biste vrlo uspjeti u optimizaciji upita. No, jednako je va\u017eno nastaviti isprobavati razli\u010dite pristupe i polako raspraviti problem, nalaze\u0107i uska mjesta.<\/p>\n<p><\/p>\n<p>Najbolji dio postizanja sli\u010dnih rezultata je vidljivo pobolj\u0161anje brzine rada \u2013 kada se izvje\u0161taj koji prije nije mogao u\u010ditati sada u\u010ditava gotovo trenutno.<\/p>\n<p><\/p>\n<p><strong>Posebna zahvalnost\u00a0<\/strong>mojim kolegama\u00a0<em>u timu Aditya Mishre<\/em>,\u00a0<em>Aditya Gaur\u00a0<\/em>i\u00a0<em><noindex><a rel=\"nofollow\" href=\"https:\/\/twitter.com\/s0ftvar\">Varun Malhotra\u00a0<\/a><\/noindex><\/em>za brainstorming i\u00a0<em>Dinkaru Pandiru\u00a0<\/em>\u0161to je prona\u0161ao va\u017enu gre\u0161ku u na\u0161em kona\u010dnom zahtjevu prije nego \u0161to smo se s njim kona\u010dno oprostili!<\/p>\n<p>Izvor: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/455832\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u0412 \u0434\u0435\u043a\u0430\u0431\u0440\u0435 \u043f\u0440\u043e\u0448\u043b\u043e\u0433\u043e \u0433\u043e\u0434\u0430 \u044f \u043f\u043e\u043b\u0443\u0447\u0438\u043b \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0439 \u043e\u0442\u0447\u0435\u0442 \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0435 \u043e\u0442 \u043a\u043e\u043c\u0430\u043d\u0434\u044b\u00a0\u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 VWO. \u0412\u0440\u0435\u043c\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437 \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u043e\u0442\u0447\u0435\u0442\u043e\u0432 \u0434\u043b\u044f \u043a\u0440\u0443\u043f\u043d\u043e\u0433\u043e \u043a\u043e\u0440\u043f\u043e\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043a\u0430\u0437\u0430\u043b\u043e\u0441\u044c \u043d\u0435\u043f\u043e\u043c\u0435\u0440\u043d\u043e \u0431\u043e\u043b\u044c\u0448\u0438\u043c. \u0410 \u0442\u0430\u043a \u043a\u0430\u043a \u044d\u0442\u043e \u0441\u0444\u0435\u0440\u0430 \u043c\u043e\u0435\u0439 \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u0438, \u044f \u0442\u0443\u0442 \u0436\u0435 \u0441\u043e\u0441\u0440\u0435\u0434\u043e\u0442\u043e\u0447\u0438\u043b\u0441\u044f \u043d\u0430 \u0440\u0435\u0448\u0435\u043d\u0438\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b. \u041f\u0440\u0435\u0434\u044b\u0441\u0442\u043e\u0440\u0438\u044f \u0427\u0442\u043e\u0431\u044b \u0431\u044b\u043b\u043e \u043f\u043e\u043d\u044f\u0442\u043d\u043e \u043e \u0447\u0451\u043c \u0440\u0435\u0447\u044c, \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u0441\u043e\u0432\u0441\u0435\u043c \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043e VWO. \u042d\u0442\u043e \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430, [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-35259","post","type-post","status-publish","format-standard","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.5 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u0412 \u0434\u0435\u043a\u0430\u0431\u0440\u0435 \u043f\u0440\u043e\u0448\u043b\u043e\u0433\u043e \u0433\u043e\u0434\u0430 \u044f \u043f\u043e\u043b\u0443\u0447\u0438\u043b \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0439 \u043e\u0442\u0447\u0435\u0442 \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0435 \u043e\u0442 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 VWO. \u0412\u0440\u0435\u043c\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437 \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u043e\u0442\u0447\u0435\u0442\u043e\u0432 \u0434\u043b\u044f \u043a\u0440\u0443\u043f\u043d\u043e\u0433\u043e \u043a\u043e\u0440\u043f\u043e\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043a\u0430\u0437\u0430\u043b\u043e\u0441\u044c \u043d\u0435\u043f\u043e\u043c\u0435\u0440\u043d\u043e \u0431\u043e\u043b\u044c\u0448\u0438\u043c.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/hr\/blog\/administrirovanie\/istoriya-odnogo-sql-rassledovaniya\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.5\" \/>\n\t\t<meta property=\"og:locale\" content=\"hr_HR\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u0418\u0441\u0442\u043e\u0440\u0438\u044f \u043e\u0434\u043d\u043e\u0433\u043e SQL \u0440\u0430\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u044f | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u0412 \u0434\u0435\u043a\u0430\u0431\u0440\u0435 \u043f\u0440\u043e\u0448\u043b\u043e\u0433\u043e \u0433\u043e\u0434\u0430 \u044f \u043f\u043e\u043b\u0443\u0447\u0438\u043b \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0439 \u043e\u0442\u0447\u0435\u0442 \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0435 \u043e\u0442 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 VWO. \u0412\u0440\u0435\u043c\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437 \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u043e\u0442\u0447\u0435\u0442\u043e\u0432 \u0434\u043b\u044f \u043a\u0440\u0443\u043f\u043d\u043e\u0433\u043e \u043a\u043e\u0440\u043f\u043e\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043a\u0430\u0437\u0430\u043b\u043e\u0441\u044c \u043d\u0435\u043f\u043e\u043c\u0435\u0440\u043d\u043e \u0431\u043e\u043b\u044c\u0448\u0438\u043c.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/hr\/blog\/administrirovanie\/istoriya-odnogo-sql-rassledovaniya\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2019-10-31T19:03:17+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2019-10-31T19:03:17+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Pri\u010da o jednoj SQL istrazi | ProHoster","description":"U prosincu pro\u0161le godine primio sam zanimljiv izvje\u0161taj o gre\u0161ci od VWO tima za podr\u0161ku. Vrijeme u\u010ditavanja jednog od analiti\u010dkih izvje\u0161taja za velikog korporativnog klijenta \u010dinilo se nevjerojatno dugim.","canonical_url":"https:\/\/prohoster.info\/hr\/blog\/administrirovanie\/istoriya-odnogo-sql-rassledovaniya","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"hr_HR","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u0418\u0441\u0442\u043e\u0440\u0438\u044f \u043e\u0434\u043d\u043e\u0433\u043e SQL \u0440\u0430\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u044f | ProHoster","og:description":"\u0412 \u0434\u0435\u043a\u0430\u0431\u0440\u0435 \u043f\u0440\u043e\u0448\u043b\u043e\u0433\u043e \u0433\u043e\u0434\u0430 \u044f \u043f\u043e\u043b\u0443\u0447\u0438\u043b \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0439 \u043e\u0442\u0447\u0435\u0442 \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0435 \u043e\u0442 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 VWO. \u0412\u0440\u0435\u043c\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437 \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u043e\u0442\u0447\u0435\u0442\u043e\u0432 \u0434\u043b\u044f \u043a\u0440\u0443\u043f\u043d\u043e\u0433\u043e \u043a\u043e\u0440\u043f\u043e\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043a\u0430\u0437\u0430\u043b\u043e\u0441\u044c \u043d\u0435\u043f\u043e\u043c\u0435\u0440\u043d\u043e \u0431\u043e\u043b\u044c\u0448\u0438\u043c.","og:url":"https:\/\/prohoster.info\/hr\/blog\/administrirovanie\/istoriya-odnogo-sql-rassledovaniya","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2019-10-31T19:03:17+00:00","article:modified_time":"2019-10-31T19:03:17+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"35259","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":"2026-01-21 22:33:19","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-03-01 02:07:28","updated":"2026-01-21 22:33:19","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/posts\/35259","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/comments?post=35259"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/posts\/35259\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/media?parent=35259"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/categories?post=35259"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/tags?post=35259"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}