{"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\/nl\/blog\/administrirovanie\/istoriya-odnogo-sql-rassledovaniya","title":{"rendered":"Het verhaal van een SQL-onderzoek","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>In december vorig jaar ontving ik een interessante foutmelding van het VWO-ondersteuningsteam. De laadtijd van een van de analytische rapporten voor een grote zakelijke klant leek ongewoon lang. Aangezien dit binnen mijn verantwoordelijkheid valt, concentreerde ik me onmiddellijk op het oplossen van het probleem.<\/p>\n<p><\/p>\n<h2>Achtergrond<\/h2>\n<p><\/p>\n<p>Om duidelijk te maken waar het om gaat, zal ik kort iets vertellen over VWO. Dit is een platform waarmee je verschillende gerichte campagnes op je websites kunt uitvoeren: A\/B-experimenten uitvoeren, bezoekers en conversies volgen, verkoopfunnels analyseren, heatmaps weergeven en sessierecords afspelen.<\/p>\n<p><\/p>\n<p>Maar het belangrijkste van het platform is rapportage. Al deze functies zijn met elkaar verbonden. Voor zakelijke klanten zou een enorme hoeveelheid informatie eenvoudigweg nutteloos zijn zonder een krachtig platform dat deze presenteert voor analyse.<\/p>\n<p><\/p>\n<p>Met het platform kun je willekeurige verzoeken doen op een grote dataset. Hier is een eenvoudig voorbeeld:<\/p>\n<p><\/p>\n<pre>Toon alle klikken op de pagina \"abc.com\"\nVAN &lt;datum d1&gt; TOT &lt;datum d2&gt;\nvoor mensen die\nChrome gebruikten OF\n(in Europa waren EN iPhone gebruikten)<\/pre>\n<p><\/p>\n<p>Let op de logische operators. Deze zijn beschikbaar voor klanten in de verzoekinterface, zodat ze zo complex mogelijke verzoeken kunnen doen om selecties te verkrijgen.<\/p>\n<p><\/p>\n<h2>Langzame aanvraag<\/h2>\n<p><\/p>\n<p>De betrokken klant probeerde iets te doen dat intu\u00eftief snel zou moeten werken:<\/p>\n<p><\/p>\n<pre>Laat alle sessierecords zien\nvoor gebruikers die een pagina hebben bezocht\nmet een URL die \"\\\/jobs\" bevat<\/pre>\n<p><\/p>\n<p>Deze site had een enorme hoeveelheid verkeer, en we bewaarden meer dan een miljoen unieke URL-adressen alleen al voor deze site. En ze wilden een vrij eenvoudig URL-sjabloon vinden dat betrekking had op hun bedrijfsmodel.<\/p>\n<p>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Voorlopig onderzoek<\/h2>\n<p><\/p>\n<p>Laten we eens kijken naar wat er in de database gebeurt. Hieronder staat de oorspronkelijke langzame SQL-query:<\/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 =5 \n    AND recording_data.num_of_pages &gt; 0 ;<\/code><\/pre>\n<p><\/p>\n<p>Hier zijn de tijden:<\/p>\n<p><\/p>\n<pre>Geplande tijd: 1.480 ms\nUitvoertijd: 1431924.650 ms<\/pre>\n<p><\/p>\n<p>De query doorzocht 150.000 rijen. De queryplanner toonde een paar interessante details, maar geen duidelijke knelpunten.<\/p>\n<p><\/p>\n<p>Laten we de query verder bestuderen. Zoals te zien is, maakt hij <code>JOIN<\/code> drie tabellen aan:<\/p>\n<p><\/p>\n<ol>\n<li><strong>sessions<\/strong>: om sessie-informatie weer te geven: browser, user agent, land, enzovoort.<\/li>\n<li><strong>recording_data<\/strong>: geregistreerde URL's, pagina's, duur van bezoeken<\/li>\n<li><strong>urls<\/strong>: om duplicatie van extreem lange URL's te voorkomen, bewaren we ze in een aparte tabel.<\/li>\n<\/ol>\n<p><\/p>\n<p>Let ook op dat al onze tabellen al zijn gescheiden op <code>account_id<\/code>. Dit voorkomt dat, door \u00e9\u00e9n bijzonder groot account, andere accounts problemen ondervinden.<\/p>\n<p><\/p>\n<h2>Op zoek naar aanwijzingen<\/h2>\n<p><\/p>\n<p>Bij nader inzien zien we dat er iets niet klopt met deze specifieke query. We moeten deze regel nader bekijken:<\/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>De eerste gedachte was dat het mogelijk aan <code>ILIKE<\/code> ligt, aangezien al deze lange URL's (we hebben meer dan 1,4 miljoen <strong>unieke\u00a0<\/strong>URL's verzameld voor dit account) de prestaties zouden kunnen be\u00efnvloeden.<\/p>\n<p><\/p>\n<p>Maar nee \u2014 dat is het niet!<\/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 rijen)\n\nTijd: 5231.765 ms<\/code><\/pre>\n<p><\/p>\n<p>De zoekopdracht op basis van het patroon duurt slechts 5 seconden. Zoeken op een miljoen unieke URL's blijkt duidelijk geen probleem te zijn.<\/p>\n<p><\/p>\n<p>De volgende verdachte op de lijst \u2014 meerdere <code>JOIN<\/code>. Misschien heeft hun overmatig gebruik geleid tot vertraging? Gewoonlijk <code>JOIN<\/code>'s zijn de meest voor de hand liggende kandidaten voor prestatieproblemen, maar ik geloofde niet dat ons geval typisch was.<\/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 rij)\n\nTijd: 147.851 ms<\/code><\/pre>\n<p><\/p>\n<p>En ook dat was niet ons geval. <code>JOIN<\/code>'s bleken verrassend snel te zijn.<\/p>\n<p><\/p>\n<h2>De kring van verdachten verkleinen<\/h2>\n<p><\/p>\n<p>Ik was bereid om de query te wijzigen om eventuele prestatieverbeteringen te bereiken. Mijn team en ik ontwikkelden 2 belangrijke idee\u00ebn:<\/p>\n<p><\/p>\n<ul>\n<li><strong>Gebruik EXISTS voor de subquery voor URL's<\/strong>: We wilden nogmaals controleren of er problemen waren met de subquery voor de URL's. Een manier om dit te bereiken is simpelweg te gebruiken <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> verbeterde prestaties aanzienlijk, aangezien het onmiddellijk eindigt wanneer het de enige rij volgens de voorwaarde vindt.<\/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 rij)\nTijd: 1636.637 ms<\/code><\/pre>\n<p><\/p>\n<p>Ja. De subquery, wanneer verpakt in\u00a0<code>EXISTS<\/code>, maakt alles super snel. De volgende logische vraag is waarom de query met <code>JOIN<\/code>-en en de subquery zelf snel zijn als ze afzonderlijk worden uitgevoerd, maar samen vreselijk traag zijn?<\/p>\n<p><\/p>\n<ul>\n<li><strong>We verplaatsen de subquery naar CTE <\/strong>: als de query op zichzelf snel is, kunnen we gewoon eerst het snelle resultaat berekenen en het vervolgens aan de hoofdquery geven.<\/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>Maar dit was nog steeds erg traag.<\/p>\n<p><\/p>\n<h2>We vinden de schuldige.<\/h2>\n<p><\/p>\n<p>De hele tijd flitste er \u00e9\u00e9n klein ding voor mijn ogen, waar ik constant voor wegkeek. Maar omdat er verder niets meer overbleef, besloot ik het ook maar te bekijken. Ik heb het over <code>&amp;&amp;<\/code> de operator. Tot nu toe <code>EXISTS<\/code> verbeterde de prestaties, <code>&amp;&amp;<\/code> was de enige resterende gemene deler in alle versies van de trage query.<\/p>\n<p><\/p>\n<p>Kijkend naar <noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/9.1\/functions-array.html\">documentatie<\/a><\/noindex>, zien we dat <code>&amp;&amp;<\/code> wordt gebruikt wanneer we gemeenschappelijke elementen tussen twee arrays moeten vinden.<\/p>\n<p><\/p>\n<p>In de originele query is dit:<\/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>Wat betekent dat we een patroon zoeken in onze urls, en vervolgens de intersectie vinden met alle urls met gemeenschappelijke records. Dit is een beetje verwarrend, aangezien \"urls\" hier niet verwijst naar de tabel die alle URL's bevat, maar naar de kolom \"urls\" in de tabel <code>recording_data<\/code>.<\/p>\n<p><\/p>\n<p>Met groeiende vermoedens over <code>&amp;&amp;<\/code>, probeerde ik ze te bevestigen in het queryplan dat gegenereerd werd. <code>EXPLAIN ANALYZE<\/code> (ik had al een opgeslagen plan, maar ik vind het meestal gemakkelijker om in SQL te experimenteren dan om de ondoorzichtigheid van queryplanners te begrijpen).<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">Filter: ((urls &amp;&amp; ($0)::text[]) EN (r_time &gt; '2018-12-17 12:17:23+00'::timestamp met tijdszone) EN (r_time = '5'::double precision) EN (num_of_pages &gt; 0))\n                           Rijen verwijderd door filter: 52710<\/code><\/pre>\n<p><\/p>\n<p>Er waren verschillende filterregels alleen van <code>&amp;&amp;<\/code>. Wat betekende dat deze operatie niet alleen kostbaar was, maar ook meerdere keren werd uitgevoerd.<\/p>\n<p><\/p>\n<p>Ik controleerde dit door de voorwaarde te isoleren<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">SELECT 1\nFROM \n    acc_{account_id}.urls als recordings_urls, \n    acc_{account_id}.recording_data_30 als recording_data_30, \n    acc_{account_id}.sessions_30 als sessions_30 \nWAAR \n\turls &amp;&amp;  array(select id from acc_{account_id}.urls waar url  ILIKE  '%enterprise_customer.com\/jobs%')::text[]<\/code><\/pre>\n<p><\/p>\n<p>Deze query werd langzaam uitgevoerd. Aangezien <code>JOIN<\/code>-en subqueries snel zijn, bleef alleen de <code>&amp;&amp;<\/code> operator over.<\/p>\n<p><\/p>\n<p>Maar dit is wel de sleuteloperatie. We moeten altijd in de principale URL-tabel zoeken om op het patroon te zoeken, en we moeten altijd intersecties vinden. We kunnen niet direct in de URL-records zoeken, omdat dit gewoon ID's zijn die verwijzen naar <code>urls<\/code>.<\/p>\n<p><\/p>\n<h2>Op weg naar de oplossing<\/h2>\n<p><\/p>\n<p><code>&amp;&amp;<\/code> langzaam is, omdat beide sets enorm zijn. De operatie zal relatief snel zijn als ik vervang <code>urls<\/code> en een werkende opdracht krijgen. <code>{ \"http:\/\/google.com\/\", \"http:\/\/wingify.com\/\" }<\/code>.<\/p>\n<p><\/p>\n<p>Ik begon te zoeken naar een manier om in Postgres verzamelingen te kruisen zonder gebruik te maken van <code>&amp;&amp;<\/code>, maar zonder veel succes.<\/p>\n<p><\/p>\n<p>Uiteindelijk besloten we gewoon het probleem ge\u00efsoleerd op te lossen: geef me alle <code>urls<\/code> lijnen waarvoor de URL overeenkomt met het sjabloon. Zonder extra voorwaarden zal dit zijn \u2014\u00a0<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">SELECT urls.url\nFROM \n\tacc_{account_id}.urls als urls,\n\t(SELECT unnest(recording_data.urls) AS id) ALS unrolled_urls\nWAAR\n\turls.id = unrolled_urls.id EN\n\turls.url  ILIKE  '%jobs%'<\/code><\/pre>\n<p><\/p>\n<p>In plaats van\u00a0<code>JOIN<\/code> de syntaxis gebruikte ik gewoon een subquery en rolde <code>recording_data.urls<\/code> de array uit, zodat de voorwaarde rechtstreeks kan worden toegepast op <code>WAAR<\/code>.<\/p>\n<p><\/p>\n<p>Het belangrijkste hieraan is dat <code>&amp;&amp;<\/code> wordt gebruikt om te controleren of deze opname de overeenkomstige URL bevat. Als je goed kijkt, zie je in deze operatie de iteratie over de array-elementen (of tabelrijen) en de stop bij het voldoen aan de voorwaarde (overeenkomst). Herinnert dit je aan iets? Ja, <code>EXISTS<\/code>.<\/p>\n<p><\/p>\n<p>Omdat je op <code>recording_data.urls<\/code> kan verwijzen vanuit de context van de subquery, wanneer dit gebeurt, kunnen we terugkeren naar onze oude vriend <code>EXISTS<\/code> en deze met de subquery omhullen.<\/p>\n<p><\/p>\n<p>Door alles samen te voegen, krijgen we de uiteindelijke geoptimaliseerde query:<\/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  (  1 = 1  )  \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    AND EXISTS(\n        SELECT urls.url\n        FROM \n            acc_{account_id}.urls as urls,\n            (SELECT unnest(urls) AS rec_url_id FROM acc_{account_id}.recording_data) \n            AS unrolled_urls\n        WHERE\n            urls.id = unrolled_urls.rec_url_id AND\n            urls.url ILIKE '%enterprise_customer.com\/jobs%'\n    );\n<\/code><\/pre>\n<p><\/p>\n<p>En de uiteindelijke uitvoeringstijd <code>Tijd: 1898.717 ms<\/code> Tijd om te vieren?!?<\/p>\n<p><\/p>\n<p>Niet zo snel! Eerst moet de correctheid worden gecontroleerd. Ik was uiterst wantrouwig tegenover <code>EXISTS<\/code> optimalisatie, omdat dit de logica verandert naar een eerder einde. We moeten er zeker van zijn dat we geen onopgemerkte fout aan de query hebben toegevoegd.<\/p>\n<p><\/p>\n<p>Een eenvoudige controle bestond uit het uitvoeren van <code>count(*)<\/code> zowel op langzame als op snelle queries voor een groot aantal verschillende datasets. Vervolgens heb ik handmatig de correctheid van alle resultaten gecontroleerd voor een kleine subset van de gegevens.<\/p>\n<p><\/p>\n<p>Alle controles gaven consistent positieve resultaten. We hebben alles gerepareerd!<\/p>\n<p><\/p>\n<h2>Geleerde lessen<\/h2>\n<p><\/p>\n<p>Er kunnen veel lessen uit dit verhaal worden getrokken:<\/p>\n<p><\/p>\n<ol>\n<li>Query-plannen vertellen niet het volledige verhaal, maar kunnen aanwijzingen geven<\/li>\n<li>De belangrijkste verdachten zijn niet altijd de echte boosdoeners<\/li>\n<li>Trage queries kunnen worden opgesplitst om knelpunten te isoleren<\/li>\n<li>Niet alle optimalisaties zijn van nature reducerend<\/li>\n<li>Gebruik van <code>EXIST<\/code>, waar mogelijk, kan leiden tot een aanzienlijke prestatieverbetering<\/li>\n<\/ol>\n<p><\/p>\n<h2>Uitslag<\/h2>\n<p><\/p>\n<p>We zijn van een querytijd van ~24 minuten naar 2 seconden gegaan \u2014 een zeer serieuze prestatieverbetering! Hoewel dit artikel groot is geworden, zijn alle experimenten die we deden op \u00e9\u00e9n dag uitgevoerd en kostten naar schatting 1,5 tot 2 uur voor optimalisatie en testen.<\/p>\n<p><\/p>\n<p>SQL is een wonderlijke taal, als je er niet bang voor bent, maar probeert deze te begrijpen en te gebruiken. Met een goed begrip van hoe SQL-queries worden uitgevoerd, hoe DB query-plannen genereert, hoe indexen werken en gewoon de grootte van de gegevens waarmee je te maken hebt, kun je echt uitblinken in query-optimalisatie. Even belangrijk is echter om te blijven experimenteren met verschillende benaderingen en het probleem langzaam te splitsen om knelpunten te vinden.<\/p>\n<p><\/p>\n<p>Het mooiste aan het behalen van dergelijke resultaten is de merkbare verbetering van de snelheid - een rapport dat voorheen niet eens geladen kon worden, laadt nu bijna onmiddellijk.<\/p>\n<p><\/p>\n<p><strong>Speciale waardering\u00a0<\/strong>aan mijn collega's\u00a0<em>in het team Aditya Mishra<\/em>,\u00a0<em>Aditya Gaur\u00a0<\/em>en\u00a0<em><noindex><a rel=\"nofollow\" href=\"https:\/\/twitter.com\/s0ftvar\">Varun Malhotra\u00a0<\/a><\/noindex><\/em>voor de brainstorm en\u00a0<em>Dinkar Pandir\u00a0<\/em>voor het vinden van een belangrijke fout in onze laatste aanvraag, voordat we uiteindelijk afscheid van hem namen!<\/p>\n<p>Bron: <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\/nl\/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=\"nl_NL\" \/>\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\/nl\/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\udd47Het verhaal van een SQL-onderzoek | ProHoster","description":"In december vorig jaar ontving ik een interessante foutmelding van het VWO-ondersteuningsteam. De laadtijd van \u00e9\u00e9n van de analytische rapporten voor een grote zakelijke klant leek onredelijk lang.","canonical_url":"https:\/\/prohoster.info\/nl\/blog\/administrirovanie\/istoriya-odnogo-sql-rassledovaniya","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"nl_NL","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\/nl\/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\/nl\/wp-json\/wp\/v2\/posts\/35259","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/comments?post=35259"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/posts\/35259\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/media?parent=35259"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/categories?post=35259"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/tags?post=35259"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}