{"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\/da\/blog\/administrirovanie\/istoriya-odnogo-sql-rassledovaniya","title":{"rendered":"Historien om en SQL-unders\u00f8gelse","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>I december sidste \u00e5r modtog jeg en interessant fejlrapport fra VWO-supportteamet. Indl\u00e6sningstiden for en af de analytiske rapporter til en stor virksomhedskunde syntes urimeligt lang. Da dette er mit ansvarsomr\u00e5de, fokuserede jeg straks p\u00e5 at l\u00f8se problemet.<\/p>\n<p><\/p>\n<h2>Background<\/h2>\n<p><\/p>\n<p>For at g\u00f8re det klart, hvad der menes, vil jeg fort\u00e6lle lidt om VWO. Dette er en platform, der g\u00f8r det muligt at k\u00f8re forskellige m\u00e5lrettede kampagner p\u00e5 ens hjemmesider: udf\u00f8re A\/B tests, spore bes\u00f8gende og konverteringer, lave salgstragtanalyser, vise varme kort og afspille bes\u00f8gsoptagelser.<\/p>\n<p><\/p>\n<p>Men det vigtigste ved platformen er udarbejdelsen af rapporter. Alle de n\u00e6vnte funktioner h\u00e6nger sammen. For virksomhedskunder ville et massivt informationss\u00e6t v\u00e6re helt ubrugeligt uden en kraftfuld platform, der pr\u00e6senterer dem til analyse.<\/p>\n<p><\/p>\n<p>Ved at bruge platformen kan man lave en vilk\u00e5rlig foresp\u00f8rgsel p\u00e5 et stort datas\u00e6t. Her er et simpelt eksempel:<\/p>\n<p><\/p>\n<pre>Vis alle klik p\u00e5 siden \"abc.com\"\nFRA &lt;dato d1&gt; TIL &lt;dato d2&gt;\nfor personer, der\nbrugte Chrome ELLER\n(var i Europa OG brugte iPhone)<\/pre>\n<p><\/p>\n<p>Bem\u00e6rk de booleske operatorer. De er tilg\u00e6ngelige for kunderne i foresp\u00f8rgselsgr\u00e6nsefladen, s\u00e5 man kan lave s\u00e5 komplekse foresp\u00f8rgsler, man \u00f8nsker, for at f\u00e5 udtr\u00e6k.<\/p>\n<p><\/p>\n<h2>Langsom foresp\u00f8rgsel<\/h2>\n<p><\/p>\n<p>Kunden, som vi taler om, fors\u00f8gte at g\u00f8re noget, der intuitivt burde fungere hurtigt:<\/p>\n<p><\/p>\n<pre>Vis alle sessionsoptagelser\nfor brugere der bes\u00f8gte enhver side\nmed en URL, der indeholder \"\/jobs\"<\/pre>\n<p><\/p>\n<p>P\u00e5 denne hjemmeside var der enormt mange bes\u00f8gende, og vi gemte over en million unikke URL-adresser kun for den. De \u00f8nskede at finde en ret simpel URL-skabelon, der var relateret til deres forretningsmodel.<\/p>\n<p>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Forel\u00f8big unders\u00f8gelse<\/h2>\n<p><\/p>\n<p>Lad os se p\u00e5, hvad der sker i databasen. Her er den originale langsomme SQL-foresp\u00f8rgsel:<\/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>Og her er tiderne:<\/p>\n<p><\/p>\n<pre>Forventet tid: 1.480 ms\nUdf\u00f8relsestid: 1431924.650 ms<\/pre>\n<p><\/p>\n<p>Foresp\u00f8rgslen omfattede 150.000 r\u00e6kker. Sp\u00f8rgselsplanl\u00e6ggeren viste et par interessante detaljer, men der var ingen \u00e5benlyse flaskehalse.<\/p>\n<p><\/p>\n<p>Lad os unders\u00f8ge foresp\u00f8rgslen n\u00e6rmere. Som det fremg\u00e5r, g\u00f8r den <code>JOIN<\/code> tre tabeller:<\/p>\n<p><\/p>\n<ol>\n<li><strong>sessions<\/strong>: til at vise sessionsinformation: browser, brugeragent, land og s\u00e5 videre.<\/li>\n<li><strong>recording_data<\/strong>: optagede URLs, sider, bes\u00f8gsvarighed<\/li>\n<li><strong>urls<\/strong>: for at undg\u00e5 duplikering af ekstremt lange URLs, gemmer vi dem i en separat tabel.<\/li>\n<\/ol>\n<p><\/p>\n<p>Bem\u00e6rk ogs\u00e5, at alle vores tabeller allerede er opdelt efter <code>account_id<\/code>. P\u00e5 den m\u00e5de undg\u00e5s situationen, hvor problemer opst\u00e5r for de andre p\u00e5 grund af en s\u00e6rlig stor konto.<\/p>\n<p><\/p>\n<h2>I jagten p\u00e5 spor<\/h2>\n<p><\/p>\n<p>Ved n\u00e6rmere eftersyn ser vi, at der er noget galt med denne specifikke foresp\u00f8rgsel. Det er v\u00e6rd at se n\u00e6rmere p\u00e5 denne linje:<\/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>Den f\u00f8rste tanke var, at m\u00e5ske p\u00e5 grund af <code>ILIKE<\/code> p\u00e5 alle disse lange URLs (vi har over 1,4 millioner <strong>unikke\u00a0<\/strong>URL-adresser indsamlet til denne konto) kunne ydeevnen v\u00e6re p\u00e5virket.<\/p>\n<p><\/p>\n<p>Men nej \u2014 det var ikke tilf\u00e6ldet!<\/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 r\u00e6kker)\n\nTid: 5231.765 ms<\/code><\/pre>\n<p><\/p>\n<p>Selve foresp\u00f8rgslen til at s\u00f8ge efter m\u00f8nsteret tager kun 5 sekunder. S\u00f8gning efter m\u00f8nsteret i en million unikke URLs er klart ikke et problem.<\/p>\n<p><\/p>\n<p>N\u00e6ste mist\u00e6nkte p\u00e5 listen \u2014 flere <code>JOIN<\/code>. M\u00e5ske deres overdrevne brug f\u00f8rte til neds\u00e6ttelse? Normalt <code>JOIN<\/code>'er \u2014 de mest \u00e5benlyse kandidater til ydeevneproblemer, men jeg troede ikke, at vores tilf\u00e6lde var typisk.<\/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 r\u00e6kke)\n\nTid: 147.851 ms<\/code><\/pre>\n<p><\/p>\n<p>Og det var heller ikke vores tilf\u00e6lde. <code>JOIN<\/code>'er viste sig at v\u00e6re meget hurtige.<\/p>\n<p><\/p>\n<h2>Sn\u00e6vrer mist\u00e6nkte ind<\/h2>\n<p><\/p>\n<p>Jeg var klar til at begynde at \u00e6ndre foresp\u00f8rgslen for at opn\u00e5 forbedringer i ydeevnen. Vi i teamet udviklede 2 hovedideer:<\/p>\n<p><\/p>\n<ul>\n<li><strong>Brug af EXISTS til URL-underforesp\u00f8rgsel<\/strong>: Vi \u00f8nskede at dobbelttjekke, om der var problemer med underforesp\u00f8rgslen for URLs. En m\u00e5de at g\u00f8re dette p\u00e5 er simpelthen at bruge <code>EXISTS<\/code>. <code>EXISTS<\/code> <noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/current\/functions-subquery.html#FUNCTIONS-SUBQUERY-EXISTS\">kan<\/a><\/noindex> kraftigt forbedre ydeevnen da det stopper med det samme, s\u00e5 snart det finder den eneste r\u00e6kke i henhold til betingelsen.<\/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>Ja, selve underforesp\u00f8rgslen, n\u00e5r den er indkapslet i\u00a0<code>EXISTS<\/code>, g\u00f8r alt super hurtigt. Det n\u00e6ste logiske sp\u00f8rgsm\u00e5l er, hvorfor foresp\u00f8rgslen med <code>JOIN<\/code>-er og selve underforesp\u00f8rgslen er hurtige hver for sig, men i f\u00e6llesskab bremser de forf\u00e6rdeligt?<\/p>\n<p><\/p>\n<ul>\n<li><strong>Vi flytter underforesp\u00f8rgslen til CTE <\/strong>: hvis foresp\u00f8rgslen er hurtig i sig selv, kan vi blot beregne det hurtige resultat f\u00f8rst og derefter give det til hovedforesp\u00f8rgslen<\/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>Men selv det var stadig meget langsomt.<\/p>\n<p><\/p>\n<h2>Vi finder synderen<\/h2>\n<p><\/p>\n<p>I hele denne tid har en lille detalje sv\u00f8mmet forbi mine \u00f8jne, som jeg konstant har fors\u00f8gt at kaste v\u00e6k. Men da der ikke var meget tilbage, besluttede jeg at kigge n\u00e6rmere p\u00e5 den. Jeg taler om <code>&amp;&amp;<\/code> operatoren. Indtil <code>EXISTS<\/code> kunne jeg blot forbedre ydeevnen, <code>&amp;&amp;<\/code> var den eneste tilbagev\u00e6rende f\u00e6lles faktor i alle versioner af den langsomme foresp\u00f8rgsel.<\/p>\n<p><\/p>\n<p>Ser vi p\u00e5 <noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/9.1\/functions-array.html\">dokumentationen<\/a><\/noindex>, ser vi, at <code>&amp;&amp;<\/code> benyttes, n\u00e5r der skal findes f\u00e6lles elementer mellem to arrays.<\/p>\n<p><\/p>\n<p>I den originale foresp\u00f8rgsel er det:<\/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>Hvilket betyder, at vi laver et m\u00f8nsters\u00f8gning p\u00e5 vores url'er, og derefter finder vi sk\u00e6ringspunktet med alle url'er med f\u00e6lles poster. Dette er en smule forvirrende, da 'urls' her ikke henviser til tabellen, der indeholder alle URL'er, men til kolonnen 'urls' i tabellen <code>recording_data<\/code>.<\/p>\n<p><\/p>\n<p>Med stigende mistanker om <code>&amp;&amp;<\/code>, fors\u00f8gte jeg at finde bekr\u00e6ftelse p\u00e5 dem i foresp\u00f8rgselsplanen, der blev genereret af <code>EXPLAIN ANALYZE<\/code> (jeg har allerede haft en gemt plan, men jeg plejer at finde det lettere at eksperimentere i SQL, end at pr\u00f8ve at forst\u00e5 de uklare anmodningsplanl\u00e6ggere).<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">Filter: ((urls &amp;&amp; ($0)::text[]) OG (r_time &gt; '2018-12-17 12:17:23+00'::timestamp with time zone) OG (r_time = '5'::double precision) OG (num_of_pages &gt; 0))\n                           R\u00e6kker fjernet af filter: 52710<\/code><\/pre>\n<p><\/p>\n<p>Der var flere filterlinjer kun fra <code>&amp;&amp;<\/code>. Hvilket bet\u00f8d, at denne operation ikke kun var kostbar, men ogs\u00e5 blev udf\u00f8rt flere gange.<\/p>\n<p><\/p>\n<p>Jeg kontrollerede dette ved at isolere betingelsen<\/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>Denne foresp\u00f8rgsel k\u00f8rte langsomt. Da <code>JOIN<\/code>-er er hurtige og underforesp\u00f8rgsler er hurtige, var der kun <code>&amp;&amp;<\/code> operat\u00f8ren tilbage.<\/p>\n<p><\/p>\n<p>Her er kun den n\u00f8gleoperation. Vi skal altid s\u00f8ge i hele den prim\u00e6re tabel af URL'er for at s\u00f8ge efter m\u00f8nstre, og vi skal altid finde sk\u00e6ringspunkter. Vi kan ikke s\u00f8ge direkte i url-posterne, fordi de bare er ID'er, der refererer til <code>urls<\/code>.<\/p>\n<p><\/p>\n<h2>P\u00e5 vej mod l\u00f8sningen<\/h2>\n<p><\/p>\n<p><code>&amp;&amp;<\/code> langsom, fordi begge s\u00e6t er enorme. Operationen vil v\u00e6re relativt hurtig, hvis jeg erstatter <code>urls<\/code> og f\u00e5 en fungerende kommando. <code>{ \"http:\/\/google.com\/\", \"http:\/\/wingify.com\/\" }<\/code>.<\/p>\n<p><\/p>\n<p>Jeg begyndte at lede efter en m\u00e5de at lave m\u00e6ngdeintersektioner i Postgres uden at bruge <code>&amp;&amp;<\/code>, men uden s\u00e6rlig succes.<\/p>\n<p><\/p>\n<p>I sidste ende besluttede vi at l\u00f8se problemet isoleret: giv mig alle <code>urls<\/code> strenge, hvor url'en matcher skabelonen. Uden yderligere betingelser, vil dette v\u00e6re \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>I stedet for\u00a0<code>JOIN<\/code> syntaksen brugte jeg blot en underforesp\u00f8rgsel og rullede ud <code>recording_data.urls<\/code> arrayet, s\u00e5 betingelsen kunne anvendes direkte i <code>HVOR<\/code>.<\/p>\n<p><\/p>\n<p>Det vigtigste her er, at <code>&amp;&amp;<\/code> bruges til at kontrollere, om den p\u00e5g\u00e6ldende post indeholder den relevante URL. Hvis man ser lidt n\u00e6rmere p\u00e5 denne operation, kan man se bev\u00e6gelsen gennem elementerne i arrayet (eller tabellens r\u00e6kker) og stoppet, n\u00e5r betingelsen (matchen) er opfyldt. Minder det dig om noget? Aha, <code>EXISTS<\/code>.<\/p>\n<p><\/p>\n<p>Da <code>recording_data.urls<\/code> kan refereres til udefra konteksten af underforesp\u00f8rgslen, n\u00e5r det sker, kan vi vende tilbage til vores gamle ven <code>EXISTS<\/code> og indkapsle det i underforesp\u00f8rgslen.<\/p>\n<p><\/p>\n<p>N\u00e5r vi samler alt, f\u00e5r vi den endelige optimerede foresp\u00f8rgsel:<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">V\u00c6LG \n    COUNT(*) \nFRA \n    acc_{account_id}.urls som recordings_urls, \n    acc_{account_id}.recording_data som recording_data, \n    acc_{account_id}.sessions som sessions \nHVOR \n    recording_data.usp_id = sessions.usp_id \n    OG  (  1 = 1  )  \n    OG sessions.referrer_id = recordings_urls.id \n    OG r_time &gt; to_timestamp(1542585600) \n    OG r_time = 5 \n    OG recording_data.num_of_pages &gt; 0\n    OG EXISTS(\n        V\u00c6LG urls.url\n        FRA \n            acc_{account_id}.urls som urls,\n            (V\u00c6LG unnest(urls) SOM rec_url_id FRA acc_{account_id}.recording_data) \n            SOM unrolled_urls\n        HVOR\n            urls.id = unrolled_urls.rec_url_id OG\n            urls.url  ILIKE  '%enterprise_customer.com\/jobs%'\n    );\n<\/code><\/pre>\n<p><\/p>\n<p>Og den endelige udf\u00f8relsestid <code>Tid: 1898.717 ms<\/code> Er det tid til at fejre?!?<\/p>\n<p><\/p>\n<p>Ikke s\u00e5 hurtigt! F\u00f8rst skal vi tjekke korrektheden. Jeg var meget skeptisk over for <code>EXISTS<\/code> optimering, da det \u00e6ndrer logikken til en tidligere afslutning. Vi skal sikre os, at vi ikke har tilf\u00f8jet en skjult fejl i foresp\u00f8rgslen.<\/p>\n<p><\/p>\n<p>En simpel kontrol indebar udf\u00f8relsen af <code>count(*)<\/code> b\u00e5de p\u00e5 langsomme og hurtige foresp\u00f8rgsler for et stort antal forskellige datas\u00e6t. Derefter, for en lille undergruppe af data, kontrollerede jeg alle resultater manuelt.<\/p>\n<p><\/p>\n<p>Alle kontroller gav stabilt positive resultater. Vi har rettet alt!<\/p>\n<p><\/p>\n<h2>Udtrukne L\u00e6rerige Punkter<\/h2>\n<p><\/p>\n<p>Fra denne historie kan der udtr\u00e6kkes mange lektioner:<\/p>\n<p><\/p>\n<ol>\n<li>Planer for foresp\u00f8rgsler fort\u00e6ller ikke hele historien, men kan give indikationer<\/li>\n<li>De vigtigste mist\u00e6nkte er ikke altid de virkelige syndere<\/li>\n<li>Langsomme foresp\u00f8rgsler kan opdeles for at isolere flaskehalse<\/li>\n<li>Ikke alle optimeringer er i natur reduktive<\/li>\n<li>Brugen af <code>EXIST<\/code>, hvor det er muligt, kan f\u00f8re til en dramatisk stigning i ydeevne<\/li>\n<\/ol>\n<p><\/p>\n<h2>Output<\/h2>\n<p><\/p>\n<p>Vi gik fra en foresp\u00f8rgselstid p\u00e5 ~24 minutter til 2 sekunder - en meget betydelig stigning i ydeevne! Selvom denne artikel blev lang, skete alle de eksperimenter, vi foretog, p\u00e5 \u00e9n dag og ansl\u00e5s at have taget fra 1,5 til 2 timer for optimeringer og testning.<\/p>\n<p><\/p>\n<p>SQL er et fantastisk sprog, hvis man ikke er bange for det, men fors\u00f8ger at forst\u00e5 og bruge det. Med en god forst\u00e5else af, hvordan SQL-foresp\u00f8rgsler udf\u00f8res, hvordan databasen genererer foresp\u00f8rgselsplaner, hvordan indekser fungerer, og blot st\u00f8rrelsen af de data, man har med at g\u00f8re, vil man kunne g\u00f8re store fremskridt i foresp\u00f8rgselsoptimering. Det er lige s\u00e5 vigtigt dog at forts\u00e6tte med at afpr\u00f8ve forskellige tilgange og langsomt nedbryde problemet ved at finde flaskehalse.<\/p>\n<p><\/p>\n<p>Den bedste del ved at opn\u00e5 s\u00e5danne resultater er den m\u00e6rkbare forbedring i hastigheden \u2014 n\u00e5r en rapport, der f\u00f8r ikke engang kunne indl\u00e6ses, nu n\u00e6sten indl\u00e6ses \u00f8jeblikkeligt.<\/p>\n<p><\/p>\n<p><strong>S\u00e6rlig tak\u00a0<\/strong>til mine kolleger\u00a0<em>i teamet Aditya Mishra<\/em>,\u00a0<em>Aditya Gaur\u00a0<\/em>og\u00a0<em><noindex><a rel=\"nofollow\" href=\"https:\/\/twitter.com\/s0ftvar\">Varun Malhotra\u00a0<\/a><\/noindex><\/em>for id\u00e9udvekslingen og\u00a0<em>Dinkar Pandir\u00a0<\/em>for at finde den vigtige fejl i vores endelige foresp\u00f8rgsel, f\u00f8r vi endelig sagde farvel til den!<\/p>\n<p>Kilde: <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.3 - 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\/da\/blog\/administrirovanie\/istoriya-odnogo-sql-rassledovaniya\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.3\" \/>\n\t\t<meta property=\"og:locale\" content=\"da_DK\" \/>\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\/da\/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\udd47Historien om en SQL-unders\u00f8gelse | ProHoster","description":"I december sidste \u00e5r modtog jeg en interessant fejlrapport fra VWO supportteamet. Indl\u00e6sningstiden for en af analysens rapporter til en stor virksomhedskunde virkede uforholdsm\u00e6ssigt lang.","canonical_url":"https:\/\/prohoster.info\/da\/blog\/administrirovanie\/istoriya-odnogo-sql-rassledovaniya","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"da_DK","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\/da\/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\/da\/wp-json\/wp\/v2\/posts\/35259","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/comments?post=35259"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/posts\/35259\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/media?parent=35259"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/categories?post=35259"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/tags?post=35259"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}