{"id":74953,"date":"2020-03-22T08:42:22","date_gmt":"2020-03-22T05:42:22","guid":{"rendered":"https:\/\/prohoster.info\/blog\/administrirovanie\/dba-gramotno-organizovyvaem-sinhronizaczii-i-importy"},"modified":"2020-03-22T08:42:22","modified_gmt":"2020-03-22T05:42:22","slug":"dba-gramotno-organizovyvaem-sinhronizaczii-i-importy","status":"publish","type":"post","link":"https:\/\/prohoster.info\/ro\/blog\/administrirovanie\/dba-gramotno-organizovyvaem-sinhronizaczii-i-importy","title":{"rendered":"DBA: organiz\u0103m eficient sincroniz\u0103rile \u0219i importurile","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>\u00cen procesarea complex\u0103 a unor seturi mari de date (diferite <noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/ETL\">procese ETL<\/a><\/noindex>: importuri, conversii \u0219i sincroniz\u0103ri cu o surs\u0103 extern\u0103) apare adesea necesitatea <b>de a \"memora\" temporar \u0219i de a procesa rapid<\/b> ceva voluminos.<\/p>\n<p>O sarcin\u0103 tipic\u0103 de acest tip sun\u0103 de obicei cam a\u0219a: <i>\"Iat\u0103 aici <noindex><a rel=\"nofollow\" href=\"https:\/\/sbis.ru\/accounting\">fiscalitatea a exportat din client-banca<\/a><\/noindex> pl\u0103\u021bile recente primite, trebuie s\u0103 le \u00eenc\u0103rc\u0103m rapid pe site \u0219i s\u0103 le leg\u0103m de conturi\"<\/i><\/p>\n<p>\u00cens\u0103 c\u00e2nd volumul acestui \u201eceva\u201d \u00eencepe s\u0103 fie m\u0103surat \u00een sute de megabytes, iar serviciul trebuie s\u0103 continue s\u0103 func\u021bioneze cu baza de date \u00een regimul 24\/7, apar multe efecte secundare care v\u0103 vor complica via\u021ba.<br \/>\n<img decoding=\"async\" alt=\"DBA: organiz\u0103m eficient sincroniz\u0103rile \u0219i importurile\" src=\"\/wp-content\/uploads\/2020\/03\/f74afb2cd6f5f8de26a0932166933c95.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nPentru a face fa\u021b\u0103 acestora \u00een PostgreSQL (\u0219i nu doar \u00een el), po\u021bi folosi anumite op\u021biuni de optimizare, care vor permite procesarea mai rapid\u0103 \u0219i cu un consum mai mic de resurse.<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>1. Unde s\u0103 \u00eenc\u0103rc\u0103m?<\/h2>\n<p>\nMai \u00eent\u00e2i s\u0103 ne stabilim unde putem \u00eenc\u0103rca datele pe care dorim s\u0103 le \"proces\u0103m\".<\/p>\n<h3>1.1. Tabele temporare (TEMPORARY TABLE)<\/h3>\n<p>\n\u00cen principiu, pentru PostgreSQL, tabelele temporare sunt la fel ca \u0219i oricare alte tabele. Prin urmare, supersti\u021biile de tipul <i><b>\" acolo totul este stocat doar \u00een memorie, care poate s\u0103 se termine\"<\/b><\/i>sunt false. Dar exist\u0103 \u0219i c\u00e2teva diferen\u021be esen\u021biale.<\/p>\n<h4>Spa\u021biu de nume propriu pentru fiecare conexiune la Baz\u0103 de date<\/h4>\n<p>\nDac\u0103 dou\u0103 conexiuni \u00eencearc\u0103 simultan s\u0103 execute <code>CREATE TABLE x<\/code>, atunci cineva va ob\u021bine cu siguran\u021b\u0103 <b>eroarea de unicitate<\/b> a obiectelor din Baza de date.<\/p>\n<p>Dar dac\u0103 ambele \u00eencearc\u0103 s\u0103 execute <code>CREA\u021aI <b>TEMPORAR<\/b> TABELA x<\/code>, atunci ambele o vor face corect \u0219i fiecare va primi <b>exemplarul s\u0103u<\/b> al tabelei. \u0218i nu va exista nimic comun \u00eentre ele.<\/p>\n<h4>\"Auto-distrugerea\" la deconectare<\/h4>\n<p>\nLa \u00eenchiderea conexiunii, toate tabelele temporare sunt \u0219terse automat, a\u0219a c\u0103 nu are rost s\u0103 efectuezi \"manual\" <code>DROP TABLE x<\/code> cuiva, \u00een afar\u0103 de...<\/p>\n<p>Dac\u0103 lucrezi prin <b>pgbouncer \u00een modul de tranzac\u021bie<\/b>, baza tot continu\u0103 s\u0103 considere c\u0103 aceast\u0103 conexiune este \u00eenc\u0103 activ\u0103, iar \u00een ea aceast\u0103 tabel\u0103 temporar\u0103 exist\u0103 \u00een continuare.<\/p>\n<p>A\u0219adar, \u00eencercarea de a o crea din nou, dintr-o alt\u0103 conexiune la pgbouncer, va duce la o eroare. Dar acest lucru poate fi evitat, folosind <code>CREA\u021aI O MAS\u0102 TEMPORAR\u0102 <b>DAC\u0102 NU EXIST\u0102<\/b> x<\/code>.<\/p>\n<p>Adev\u0103rat, mai bine s\u0103 nu faci asta, deoarece po\u021bi \u201edescoperi brusc\u201d acolo datele r\u0103mase de la \u201eproprietarul anterior\u201d. \u00cen schimb, este mult mai bine s\u0103 cite\u0219ti manualul \u0219i s\u0103 vezi c\u0103, atunci c\u00e2nd creezi o tabel\u0103, exist\u0103 posibilitatea de a ad\u0103uga <code>LA CLOSARE <b>\u0218TERGE<\/b><\/code> \u2014 adic\u0103, la finalizarea tranzac\u021biei, tabela va fi \u0219tears\u0103 automat.<\/p>\n<h4>Non-replicare<\/h4>\n<p>\nDin cauza apartenen\u021bei doar la o anumit\u0103 conexiune, tabelele temporare nu sunt replicate. Totu\u0219i <b>aceasta scute\u0219te de necesitatea unei dubl\u0103ri a datelor<\/b> \u00een heap + WAL, a\u0219a c\u0103 INSERT\/UPDATE\/DELETE \u00een ea este semnificativ mai rapid.<\/p>\n<p>Dar, deoarece tabela temporar\u0103 este totu\u0219i \u201eaproape obi\u0219nuit\u0103\u201d, nu o po\u021bi crea nici pe replica. Cel pu\u021bin, deocamdat\u0103, de\u0219i un patch corespunz\u0103tor circul\u0103 de mult.<\/p>\n<h3>1.2. Tabelele ne-jurnalizate (UNLOGGED TABLE)<\/h3>\n<p>\nDar ce s\u0103 faci, de exemplu, dac\u0103 ai un proces ETL voluminos care nu poate fi realizat \u00eentr-o singur\u0103 tranzac\u021bie, iar tu ai <b>pgbouncer \u00een modul de tranzac\u021bie<\/b>?..<\/p>\n<p>Sau fluxul de date este at\u00e2t de mare \u00eenc\u00e2t <b>capacitatea unei singure conexiuni<\/b> cu baza de date (pute\u021bi citi, un singur proces pe CPU)?..<\/p>\n<p>Sau o parte din opera\u021biuni se desf\u0103\u0219oar\u0103 <b>asynchronously<\/b> \u00een conexiuni diferite?..<\/p>\n<p>Aici singura variant\u0103 este - <b>a crea temporar o tabel\u0103 non-temporar\u0103<\/b>. Un joc de cuvinte, da. Adic\u0103:<\/p>\n<ul>\n<li>ai creat \u201etabelele tale\u201d cu nume c\u00e2t mai aleatorii, pentru a nu te intersecta cu nimeni<\/li>\n<li><b>Extract<\/b>: ai \u00eenc\u0103rcat datele dintr-o surs\u0103 extern\u0103 \u00een ele<\/li>\n<li><b>Transform<\/b>: ai transformat, completat c\u00e2mpurile cheie de leg\u0103tur\u0103<\/li>\n<li><b>Load<\/b>: ai transferat datele gata \u00een tabelele de destina\u021bie<\/li>\n<li>ai \u0219ters \u201etabelele tale\u201d<\/li>\n<\/ul>\n<p>\n\u0218i acum - o linguri\u021b\u0103 de catran. Practic, <b>toat\u0103 scrierea \u00een PostgreSQL se face de dou\u0103 ori<\/b> \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/postgrespro\/blog\/461523\/\">mai \u00eent\u00e2i \u00een WAL<\/a><\/noindex>, apoi \u00een corpurile tabelelor\/indec\u0219ilor. Toate acestea sunt realizate pentru a sus\u021bine ACID \u0219i o vizibilitate corect\u0103 a datelor \u00eentre <code>COMMIT<\/code>\u2018inserate \u0219i <code>ROLLBACK<\/code>\u2018inserate \u00een tranzac\u021bii.<\/p>\n<p>Dar noi nu avem nevoie de asta! Tot procesul nostru <b>a trecut fie complet cu succes, fie nu.<\/b>. Nu conteaz\u0103 c\u00e2te tranzac\u021bii intermediare are - nu ne intereseaz\u0103 \u201ea continua procesul din mijloc\u201d, mai ales c\u00e2nd nu este clar unde a fost.<\/p>\n<p>Pentru asta, dezvoltatorii PostgreSQL au implementat \u00eenc\u0103 din versiunea 9.1 a\u0219a ceva precum <noindex><a rel=\"nofollow\" href=\"https:\/\/postgrespro.ru\/docs\/postgresql\/12\/sql-createtable#SQL-CREATETABLE-UNLOGGED\">tabelele ne-jurnalizate (UNLOGGED)<\/a><\/noindex>:<\/p>\n<blockquote><p>Cu aceast\u0103 specifica\u021bie, tabela este creat\u0103 ca ne-jurnalizat\u0103. Datele scrise \u00een tabelele ne-jurnalizate nu trec prin jurnalul de pre-\u00eenregistrare (vezi Capitolul 29), rezult\u00e2nd c\u0103 astfel de tabele <b>func\u021bioneaz\u0103 mult mai repede dec\u00e2t cele obi\u0219nuite<\/b>. Cu toate acestea, ele nu sunt protejate \u00eempotriva c\u0103derilor; \u00een cazul unei c\u0103deri sau al unei opriri de urgen\u021b\u0103 a serverului, tabelul ne-\u00eenregistrat <b>este t\u0103iat automat<\/b>. \u00cen plus, con\u021binutul tabelului ne-\u00eenregistrat <b>nu este replicat<\/b> pe serverele secundare. Orice indici crea\u021bi pentru tabelul ne-\u00eenregistrat devin automat ne-\u00eenregistra\u021bi.<\/p><\/blockquote>\n<p>Pe scurt, <b>va fi mult mai rapid<\/b>, dar dac\u0103 serverul de baze de date \u201ecade\u201d \u2014 va fi nepl\u0103cut. Dar c\u00e2t de des se \u00eent\u00e2mpl\u0103 asta \u0219i poate procesul dvs. ETL s\u0103 \u00eel finalizeze corect \u201edin mijloc\u201d dup\u0103 \u201erevitalizarea\u201d bazei de date?..<\/p>\n<p>Dac\u0103 nu, iar cazul de mai sus este similar cu al vostru \u2014 folosi\u021bi <code>UNLOGGED<\/code>, dar niciodat\u0103 <b>nu activa\u021bi acest atribut pe tabelele reale<\/b>, ale c\u0103ror date v\u0103 sunt dragi.<\/p>\n<h3>1.3. PE FINAL { \u0218TERGE LINIILE | CAD }<\/h3>\n<p>\nAceast\u0103 construc\u021bie permite la crearea tabelului s\u0103 defineasc\u0103 un comportament automat la finalizarea tranzac\u021biei.<\/p>\n<p>Despre <code>LA CLOSARE <b>\u0218TERGE<\/b><\/code> am men\u021bionat mai sus, el genereaz\u0103 <code>DROP TABLE<\/code>, dar \u00een cazul <code>LA CLOSARE <b>\u0218TERGE R\u00c2NDURI<\/b><\/code> situa\u021bia este mai interesant\u0103 \u2014 aici se genereaz\u0103 <code>TRUNCATE TABLE<\/code>.<\/p>\n<p>Din moment ce \u00eentreaga infrastructur\u0103 de stocare a metadescrierii tabelului temporar este exact la fel ca cea a unui tabel obi\u0219nuit, atunci <b>crearea \u0219i \u0219tergerea constant\u0103 a tabelelor temporare duce la o \u201eumflare\u201d puternic\u0103 a tabelelor de sistem<\/b> pg_class, pg_attribute, pg_attrdef, pg_depend,\u2026<\/p>\n<p>Acum imagina\u021bi-v\u0103 c\u0103 ave\u021bi un worker pe o conexiune direct\u0103 cu baza de date, care deschide o nou\u0103 tranzac\u021bie \u00een fiecare secund\u0103, creeaz\u0103, umple, proceseaz\u0103 \u0219i \u0219terge un tabel temporar\u2026 Vor acumula gunoi \u00een tabelele de sistem, ceea ce va cauza \u00eent\u00e2rzieri suplimentare la fiecare opera\u021bie.<\/p>\n<p>\u00cen general, nu face\u021bi a\u0219a! \u00cen acest caz, este mult mai eficient <code>CREATE TEMPORARY TABLE x ... PE FINAL \u0218TERGE LINIILOR<\/code> s\u0103 fie scos din ciclul tranzac\u021biilor \u2014 atunci la \u00eenceputul fiec\u0103rei noi tranzac\u021bii tabelele vor <b>exista deja<\/b> (economisind apelul <code>CREA\u021aI<\/code>), dar <b>va fi gol<\/b>, datorit\u0103 <code>TRUNCATE<\/code> (apelul s\u0103u l-am economisit \u0219i noi) la finalizarea tranzac\u021biei anterioare.<\/p>\n<h3>1.4. ASEMENEA... INCLUDERE ...<\/h3>\n<p>\nAm men\u021bionat la \u00eenceput c\u0103 unul dintre cazurile tipice de utilizare pentru tabelele temporare \u2014 este diferite tipuri de importuri \u2014 \u0219i dezvoltatorul copiaz\u0103 obosit lista de c\u00e2mpuri ale tabelei \u021bint\u0103 \u00een declara\u021bia tabelului s\u0103u temporar\u2026<\/p>\n<p>Dar lenea este motorul progresului! A\u0219adar, <b>crearea unui nou tabel \u201edup\u0103 exemplu\u201d<\/b> se poate face mult mai simplu:<\/p>\n<pre><code class=\"sql\">CREATE TEMPORARY TABLE import_table(\n  LIKE target_table\n);<\/code><\/pre>\n<p>\nDeoarece este posibil s\u0103 se genereze o cantitate foarte mare de date \u00een aceast\u0103 tabel\u0103, c\u0103ut\u0103rile vor deveni destul de lente. Dar exist\u0103 o solu\u021bie tradi\u021bional\u0103 pentru aceasta - indec\u0219ii! \u0218i, da, <b>tabloul temporar poate avea de asemenea indec\u0219i<\/b>.<\/p>\n<p>Deoarece, de multe ori, indec\u0219ii necesari coincid cu indec\u0219ii tabelei \u021bint\u0103, po\u021bi pur \u0219i simplu s\u0103 scrii <code>CAUTA target_table <b>INCLUD\u00c2 INDEXURILE<\/b><\/code>.<\/p>\n<p>Dac\u0103 ai nevoie \u0219i de <code>DEFAULT<\/code>-valori (de exemplu, pentru completarea valorilor cheii primare), po\u021bi utiliza <code>CAUTA target_table <b>INCLUD\u0102RILE IMPLICIT\u0102<\/b><\/code>. Sau pur \u0219i simplu - <code>CAUTA target_table <b>INCLUSIV TOATE<\/b><\/code> \u2014 va copia valorile implicite, indec\u0219ii, constr\u00e2ngerile,\u2026<\/p>\n<p>Dar aici trebuie s\u0103 \u00een\u021belegi c\u0103, dac\u0103 ai creat <b>tabela de import direct cu indec\u0219i, atunci datele vor fi \u00eenc\u0103rcate mai lent<\/b>, dec\u00e2t dac\u0103 le \u00eencarci mai \u00eent\u00e2i pe toate \u0219i apoi aplici indec\u0219ii - uit\u0103-te ca exemplu la cum face <noindex><a rel=\"nofollow\" href=\"https:\/\/postgrespro.ru\/docs\/postgresql\/12\/app-pgdump\">pg_dump<\/a><\/noindex>.<\/p>\n<p>\u00cen general, <noindex><a rel=\"nofollow\" href=\"https:\/\/postgrespro.ru\/docs\/postgresql\/12\/sql-createtable\">RTFM<\/a><\/noindex>!<\/p>\n<h2>2. Cum s\u0103 scrii?<\/h2>\n<p>\nVoi spune simplu - folose\u0219te <code><noindex><a rel=\"nofollow\" href=\"https:\/\/postgrespro.ru\/docs\/postgresql\/12\/sql-copy\">COPIE<\/a><\/noindex><\/code>-flux \u00een loc de \u201epachet\u201d <code>INSERT<\/code>, <noindex><a rel=\"nofollow\" href=\"https:\/\/www.citusdata.com\/blog\/2017\/11\/08\/faster-bulk-loading-in-postgresql-with-copy\/\">accelereaz\u0103 de mai multe ori<\/a><\/noindex>. Po\u021bi chiar s\u0103 o faci direct dintr-un fi\u0219ier preg\u0103tit anterior.<\/p>\n<h2>3. Cum s\u0103 procesezi?<\/h2>\n<p>\nA\u0219adar, s\u0103 presupunem c\u0103 introducerea noastr\u0103 arat\u0103 cam a\u0219a:<\/p>\n<ul>\n<li>ai \u00een baza de date o tabel\u0103 cu datele clien\u021bilor de <b>1M \u00eenregistr\u0103ri<\/b><\/li>\n<li>\u00een fiecare zi clientul \u00ee\u021bi trimite un nou <b>\u00eentreg \u201eprofil\u201d<\/b><\/li>\n<li>din experien\u021b\u0103 \u0219tii c\u0103 de la o dat\u0103 la alta <b>se schimb\u0103 nu mai mult de 10K \u00eenregistr\u0103ri<\/b><\/li>\n<\/ul>\n<p>\nUn exemplu clasic al unei astfel de situa\u021bii este <noindex><a rel=\"nofollow\" href=\"https:\/\/www.gnivc.ru\/technical_support\/classifiers_reference\/kladr\/\">baza KLDAR<\/a><\/noindex> \u2014 exist\u0103 multe adrese, dar \u00een fiecare export s\u0103pt\u0103m\u00e2nal de modific\u0103ri (schimb\u0103ri de nume de localit\u0103\u021bi, fuziuni de str\u0103zi, apari\u021bia de noi case) sunt foarte pu\u021bine chiar \u0219i la scar\u0103 na\u021bional\u0103.<\/p>\n<h3>3.1. Algoritmul de sincronizare complet\u0103<\/h3>\n<p>\nPentru simplificare, s\u0103 presupunem c\u0103 nu trebuie nici m\u0103car s\u0103 restructurezi datele - trebuie doar s\u0103 aduci tabela \u00een forma dorit\u0103, adic\u0103:<\/p>\n<ul>\n<li><b>s\u0103 \u0219tergi<\/b> tot ce nu mai exist\u0103<\/li>\n<li><b>s\u0103 actualizezi<\/b> tot ce existase deja \u0219i trebuie actualizat<\/li>\n<li><b>s\u0103 inserezi<\/b> tot ce nu mai fusese \u00eenc\u0103<\/li>\n<\/ul>\n<p>\nDe ce trebuie s\u0103 efectuezi opera\u021biile \u00een aceast\u0103 ordine? Pentru c\u0103 astfel dimensiunea tabelei va cre\u0219te minim (<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/tensor\/blog\/491366\/\">aminte\u0219te-\u021bi de MVCC!<\/a><\/noindex>).<\/p>\n<h4>DELETE FROM dst<\/h4>\n<p>\nNu, desigur, po\u021bi face totul doar cu dou\u0103 opera\u021bii:<\/p>\n<ul>\n<li><b>s\u0103 \u0219tergi<\/b> (<code>DELETE<\/code>) \u00een general tot<\/li>\n<li><b>s\u0103 inserezi<\/b> totul din noul profil<\/li>\n<\/ul>\n<p>\nDar astfel, datorit\u0103 MVCC, <b>dimensiunea tabelei va cre\u0219te exact de dou\u0103 ori<\/b>! A ob\u021bine +1M \u00eenregistr\u0103ri \u00een tabel din cauza actualiz\u0103rii a 10K - e o redundan\u021b\u0103 cam mare\u2026<\/p>\n<h4>TRUNCATE dst<\/h4>\n<p>\nUn dezvoltator mai experimentat \u0219tie c\u0103 \u00eentreaga tabel\u0103 poate fi \u0219tears\u0103 destul de ieftin:<\/p>\n<ul>\n<li><b>a cur\u0103\u021ba<\/b> (<code>TRUNCATE<\/code>) \u00eentreaga tabel\u0103<\/li>\n<li><b>s\u0103 inserezi<\/b> totul din noul profil<\/li>\n<\/ul>\n<p>\nMetoda este eficient\u0103, <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/tensor\/blog\/481866\/\">uneori este complet aplicabil\u0103<\/a><\/noindex>, dar avem o problem\u0103\u2026 Vom \u00eenc\u0103rca 1M de \u00eenregistr\u0103ri timp de mult timp, a\u0219a c\u0103 nu ne putem permite s\u0103 l\u0103s\u0103m tabelul gol \u00een tot acest timp (a\u0219a cum se va \u00eent\u00e2mpla f\u0103r\u0103 a fi \u00eenv\u0103luit \u00eentr-o singur\u0103 tranzac\u021bie).<\/p>\n<p>Asta \u00eenseamn\u0103 c\u0103:<\/p>\n<ul>\n<li>\u00eencepem <b>o tranzac\u021bie de lung\u0103 durat\u0103<\/b><\/li>\n<li><code>TRUNCATE<\/code> impune <b>AccessExclusive<\/b>-blocare<\/li>\n<li>facem o inser\u021bie \u00eendelungat\u0103, iar ceilal\u021bi \u00een acest timp <b>nu pot nici m\u0103car <code>SELECT<\/code><\/b><\/li>\n<\/ul>\n<p>\nCeva nu merge bine\u2026<\/p>\n<h4>ALTER TABLE\u2026 RENAME\u2026 \/ DROP TABLE ...<\/h4>\n<p>\nCa alternativ\u0103 \u2013 putem \u00eenc\u0103rca totul \u00eentr-un tabel nou \u0219i apoi pur \u0219i simplu s\u0103-l redenumim \u00een locul celui vechi. C\u00e2teva detalii nepl\u0103cute:<\/p>\n<ul>\n<li>asta la fel <b>AccessExclusive<\/b>, de\u0219i semnificativ mai pu\u021bin \u00een timp<\/li>\n<li>se reseteaz\u0103 toate planurile de interogare\/statistica acestui tabel, <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/tensor\/blog\/479656\/\">trebuie s\u0103 rul\u0103m ANALYZE<\/a><\/noindex><\/li>\n<li><b>se stric\u0103 toate cheile externe<\/b> (FK) pe tabel<\/li>\n<\/ul>\n<p>\nA fost un patch WIP de la Simon Riggs, care a propus s\u0103 facem <code>ALTER<\/code>-opera\u021biune pentru \u00eenlocuirea corpului tabelului la nivel de fi\u0219ier, f\u0103r\u0103 a atinge statisticile \u0219i FK, dar nu a adunat cvorumul.<\/p>\n<h4>DELETE, UPDATE, INSERT<\/h4>\n<p>\nDeci, ne oprim pe varianta non-blocant\u0103 din cele trei opera\u021bii. Aproape trei\u2026 Cum putem face acest lucru cel mai eficient?<\/p>\n<pre><code class=\"sql\">-- facem totul \u00een cadrul tranzac\u021biei, pentru ca nimeni s\u0103 nu vad\u0103 \"st\u0103rile intermediare\"\nBEGIN;\n\n-- cre\u0103m o tabel\u0103 temporar\u0103 cu datele importate\nCREATE TEMPORARY TABLE tmp(\n  LIKE dst INCLUDING INDEXES -- pe baza modelului, \u00eempreun\u0103 cu indec\u0219ii\n) ON COMMIT DROP; -- \u00een afara tranzac\u021biei nu mai avem nevoie de ea\n\n-- rapid, rapid umplem noul model prin COPY\nCOPY tmp FROM STDIN;\n-- ...\n-- .\n\n-- \u0219tergem lipsurile\nDELETE FROM\n  dst D\nUSING\n  dst X\nLEFT JOIN\n  tmp Y\n    USING(pk1, pk2) -- c\u00e2mpurile cheii primare\nWHERE\n  (D.pk1, D.pk2) = (X.pk1, X.pk2) AND\n  Y IS NOT DISTINCT FROM NULL; -- \"antijoin\"\n\n-- actualiz\u0103m restul\nUPDATE\n  dst D\nSET\n  (f1, f2, f3) = (T.f1, T.f2, T.f3)\nFROM\n  tmp T\nWHERE\n  (D.pk1, D.pk2) = (T.pk1, T.pk2) AND\n  (D.f1, D.f2, D.f3) IS DISTINCT FROM (T.f1, T.f2, T.f3); -- nu are sens s\u0103 actualiz\u0103m coresponden\u021bele\n\n-- inser\u0103m lipsurile\nINSERT INTO\n  dst\nSELECT\n  T.*\nFROM\n  tmp T\nLEFT JOIN\n  dst D\n    USING(pk1, pk2)\nWHERE\n  D IS NOT DISTINCT FROM NULL;\n\nCOMMIT;\n<\/code><\/pre>\n<p><\/p>\n<h3>3.2. Prelucrarea post-import<\/h3>\n<p>\n\u00cen acela\u0219i KLADeR, toate \u00eenregistr\u0103rile modificate trebuie s\u0103 fie supuse unei prelucr\u0103ri suplimentare \u2013 normalizate, extrase cuvinte cheie, aduse la structuri necesare. Dar cum s\u0103 afli \u2013 <b>ce anume s-a modificat<\/b>, f\u0103r\u0103 a complica codul de sincronizare, ideal, f\u0103r\u0103 a-l atinge deloc?<\/p>\n<p>Dac\u0103 accesul la scriere \u00een momentul sincroniz\u0103rii este rezervat doar procesului t\u0103u, po\u021bi folosi un trigger care s\u0103 colecteze toate modific\u0103rile pentru noi:<\/p>\n<pre><code class=\"sql\">-- tabele \u021bint\u0103\nCREATE TABLE kladr(...);\nCREATE TABLE kladr_house(...);\n\n-- tabele cu istoricul modific\u0103rilor\nCREATE TABLE kladr$log(\n  ro kladr, -- aici se afl\u0103 versiunile complete ale \u00eenregistr\u0103rilor vechi\/noi\n  rn kladr\n);\n\nCREATE TABLE kladr_house$log(\n  ro kladr_house,\n  rn kladr_house\n);\n\n-- func\u021bie comun\u0103 pentru logarea modific\u0103rilor\nCREATE OR REPLACE FUNCTION diff$log() RETURNS trigger AS $$\nDECLARE\n  dst varchar = TG_TABLE_NAME || '$log';\n  stmt text = '';\nBEGIN\n  -- verific\u0103m necesitatea log\u0103rii la actualizarea \u00eenregistr\u0103rii\n  IF TG_OP = 'UPDATE' THEN\n    IF NEW IS NOT DISTINCT FROM OLD THEN\n      RETURN NEW;\n    END IF;\n  END IF;\n  -- cre\u0103m o \u00eenregistrare \u00een log\n  stmt = 'INSERT INTO ' || dst::text || '(ro,rn)VALUES(';\n  CASE TG_OP\n    WHEN 'INSERT' THEN\n      EXECUTE stmt || 'NULL,$1)' USING NEW;\n    WHEN 'UPDATE' THEN\n      EXECUTE stmt || '$1,$2)' USING OLD, NEW;\n    WHEN 'DELETE' THEN\n      EXECUTE stmt || '$1,NULL)' USING OLD;\n  END CASE;\n  RETURN NEW;\nEND;\n$$ LANGUAGE plpgsql;\n<\/code><\/pre>\n<p>\nAcum putem aplica (sau activa prin) triggerii \u00eenainte de sincronizare <code>ALTER TABLE ... ENABLE TRIGGER ...<\/code>):<\/p>\n<pre><code class=\"sql\">CREATE TRIGGER log\n  AFTER INSERT OR UPDATE OR DELETE\n  ON kladr\n    FOR EACH ROW\n      EXECUTE PROCEDURE diff$log();\n\nCREATE TRIGGER log\n  AFTER INSERT OR UPDATE OR DELETE\n  ON kladr_house\n    FOR EACH ROW\n      EXECUTE PROCEDURE diff$log();\n<\/code><\/pre>\n<p>\nApoi extragem cu u\u0219urin\u021b\u0103 toate modific\u0103rile necesare din tabelele de log \u0219i le proces\u0103m prin manipulatoare suplimentare.<\/p>\n<h3>3.3. Importarea seturilor de date asociate<\/h3>\n<p>\nAnterior, am discutat despre cazuri \u00een care structurile de date ale sursei \u0219i destina\u021biei coincid. Dar ce trebuie s\u0103 facem dac\u0103 exportul dintr-un sistem extern are un format diferit de structura noastr\u0103 de stocare?<\/p>\n<p>S\u0103 lu\u0103m ca exemplu stocarea clien\u021bilor \u0219i a facturilor aferente, un caz tipic de 'mul\u021bi-la-unu':<\/p>\n<pre><code class=\"sql\">CREATE TABLE client(\n  client_id\n    serial\n      PRIMARY KEY\n, inn\n    varchar\n      UNIQUE\n, name\n    varchar\n);\n\nCREATE TABLE invoice(\n  invoice_id\n    serial\n      PRIMARY KEY\n, client_id\n    integer\n      REFERENCES client(client_id)\n, number\n    varchar\n, dt\n    date\n, sum\n    numeric(32,2)\n);<\/code><\/pre>\n<p>\nIat\u0103 c\u0103 exportul din sursa extern\u0103 ne vine sub forma 'totul \u00eentr-unul':<\/p>\n<pre><code class=\"sql\">CREATE TEMPORARY TABLE invoice_import(\n  client_inn\n    varchar\n, client_name\n    varchar\n, invoice_number\n    varchar\n, invoice_dt\n    date\n, invoice_sum\n    numeric(32,2)\n);<\/code><\/pre>\n<p>\nEste clar c\u0103 datele clien\u021bilor pot fi duplicate \u00een aceast\u0103 variant\u0103, iar \u00eenregistrarea principal\u0103 este 'factura':<\/p>\n<pre><code class=\"plaintext\">0123456789;Vasile;A-01;2020-03-16;1000.00\n9876543210;Petre;A-02;2020-03-16;666.00\n0123456789;Vasile;B-03;2020-03-16;9999.00\n<\/code><\/pre>\n<p>\nPentru model, vom insera datele noastre de test, dar \u021binem minte \u2014 <code>COPIE<\/code> mai eficient!<\/p>\n<pre><code class=\"sql\">INSERT INTO invoice_import\nVALUES\n  ('0123456789', 'Vasile', 'A-01', '2020-03-16', 1000.00)\n, ('9876543210', 'Petre', 'A-02', '2020-03-16', 666.00)\n, ('0123456789', 'Vasile', 'B-03', '2020-03-16', 9999.00);<\/code><\/pre>\n<p>\nMai \u00eent\u00e2i, vom identifica 'categoriile' la care se refer\u0103 'faptele' noastre. \u00cen cazul nostru, facturile fac referire la clien\u021bi:<\/p>\n<pre><code class=\"sql\">CREATE TEMPORARY TABLE client_import AS\nSELECT DISTINCT ON(client_inn)\n-- se poate folosi doar SELECT DISTINCT, dac\u0103 datele sunt \u00eentr-adev\u0103r neconflictuale\n  client_inn inn\n, client_name \"name\"\nFROM\n  invoice_import;<\/code><\/pre>\n<p>\nPentru a corela corect facturile cu ID-urile clien\u021bilor, trebuie mai \u00eent\u00e2i s\u0103 aflam sau s\u0103 gener\u0103m aceste identificatoare. S\u0103 ad\u0103ug\u0103m c\u00e2mpuri pentru ele:<\/p>\n<pre><code class=\"sql\">ALTER TABLE invoice_import ADD COLUMN client_id integer;\nALTER TABLE client_import ADD COLUMN client_id integer;<\/code><\/pre>\n<p>\nVom folosi metoda descris\u0103 mai sus pentru sincronizarea tabelelor cu o mic\u0103 ajustare - nu vom actualiza \u0219i nu vom \u0219terge nimic \u00een tabela \u021bint\u0103, deoarece importul clien\u021bilor este \u00abappend-only\u00bb:<\/p>\n<pre><code class=\"sql\">-- stabilim \u00een tabela de import ID-urile deja existente\nUPDATE\n  client_import T\nSET\n  client_id = D.client_id\nFROM\n  client D\nWHERE\n  T.inn = D.inn; -- cheie unic\u0103\n\n-- inser\u0103m \u00eenregistr\u0103rile lips\u0103 \u0219i stabilim ID-urile acestora\nWITH ins AS (\n  INSERT INTO client(\n    inn\n  , name\n  )\n  SELECT\n    inn\n  , name\n  FROM\n    client_import\n  WHERE\n    client_id IS NULL -- dac\u0103 ID-ul nu s-a stabilit\n  RETURNING *\n)\nUPDATE\n  client_import T\nSET\n  client_id = D.client_id\nFROM\n  ins D\nWHERE\n  T.inn = D.inn; -- cheie unic\u0103\n\n-- stabilim ID-urile clien\u021bilor pentru \u00eenregistr\u0103rile facturilor\nUPDATE\n  invoice_import T\nSET\n  client_id = D.client_id\nFROM\n  client_import D\nWHERE\n  T.client_inn = D.inn; -- cheie aplica\u021bional\u0103\n<\/code><\/pre>\n<p>\nDe fapt, totul este \u00een <code>invoice_import<\/code> acum avem c\u00e2mpul de leg\u0103tur\u0103 umplut <code>client_id<\/code>, cu care vom insera factura.<br \/>\n<br \/>Sursa: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/tensor\/blog\/492464\/\">habr.com<\/a> <\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u041f\u0440\u0438 \u0441\u043b\u043e\u0436\u043d\u043e\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u043d\u0430\u0431\u043e\u0440\u043e\u0432 \u0434\u0430\u043d\u043d\u044b\u0445 (\u0440\u0430\u0437\u043d\u044b\u0435 ETL-\u043f\u0440\u043e\u0446\u0435\u0441\u0441\u044b: \u0438\u043c\u043f\u043e\u0440\u0442\u044b, \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0430\u0446\u0438\u0438 \u0438 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u0438 \u0441 \u0432\u043d\u0435\u0448\u043d\u0438\u043c \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u043e\u043c) \u0447\u0430\u0441\u0442\u043e \u0432\u043e\u0437\u043d\u0438\u043a\u0430\u0435\u0442 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u044c \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u00ab\u0437\u0430\u043f\u043e\u043c\u043d\u0438\u0442\u044c\u00bb, \u0438 \u0441\u0440\u0430\u0437\u0443 \u0431\u044b\u0441\u0442\u0440\u043e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0447\u0442\u043e-\u0442\u043e \u043e\u0431\u044a\u0435\u043c\u043d\u043e\u0435. \u0422\u0438\u043f\u043e\u0432\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 \u043f\u043e\u0434\u043e\u0431\u043d\u043e\u0433\u043e \u0440\u043e\u0434\u0430 \u0437\u0432\u0443\u0447\u0438\u0442 \u043e\u0431\u044b\u0447\u043d\u043e \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0442\u0430\u043a: \u00ab\u0412\u043e\u0442 \u0442\u0443\u0442 \u0431\u0443\u0445\u0433\u0430\u043b\u0442\u0435\u0440\u0438\u044f \u0432\u044b\u0433\u0440\u0443\u0437\u0438\u043b\u0430 \u0438\u0437 \u043a\u043b\u0438\u0435\u043d\u0442-\u0431\u0430\u043d\u043a\u0430 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0435 \u043f\u043e\u0441\u0442\u0443\u043f\u0438\u0432\u0448\u0438\u0435 \u043e\u043f\u043b\u0430\u0442\u044b, \u043d\u0430\u0434\u043e \u0438\u0445 \u0431\u044b\u0441\u0442\u0440\u0435\u043d\u044c\u043a\u043e \u0432\u043a\u0430\u0447\u0430\u0442\u044c \u043d\u0430 \u0441\u0430\u0439\u0442 \u0438 \u043f\u0440\u0438\u0432\u044f\u0437\u0430\u0442\u044c \u043a \u0441\u0447\u0435\u0442\u0430\u043c\u00bb \u041d\u043e \u043a\u043e\u0433\u0434\u0430 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":74954,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-74953","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.2.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u041f\u0440\u0438 \u0441\u043b\u043e\u0436\u043d\u043e\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u043d\u0430\u0431\u043e\u0440\u043e\u0432 \u0434\u0430\u043d\u043d\u044b\u0445 (\u0440\u0430\u0437\u043d\u044b\u0435 ETL-\u043f\u0440\u043e\u0446\u0435\u0441\u0441\u044b: \u0438\u043c\u043f\u043e\u0440\u0442\u044b, \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0430\u0446\u0438\u0438 \u0438 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u0438 \u0441 \u0432\u043d\u0435\u0448\u043d\u0438\u043c \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u043e\u043c) \u0447\u0430\u0441\u0442\u043e.\" \/>\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\/ro\/blog\/administrirovanie\/dba-gramotno-organizovyvaem-sinhronizaczii-i-importy\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.2.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"ro_RO\" \/>\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\udd47DBA: \u0433\u0440\u0430\u043c\u043e\u0442\u043d\u043e \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u0435\u043c \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u0438\u043c\u043f\u043e\u0440\u0442\u044b | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u041f\u0440\u0438 \u0441\u043b\u043e\u0436\u043d\u043e\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u043d\u0430\u0431\u043e\u0440\u043e\u0432 \u0434\u0430\u043d\u043d\u044b\u0445 (\u0440\u0430\u0437\u043d\u044b\u0435 ETL-\u043f\u0440\u043e\u0446\u0435\u0441\u0441\u044b: \u0438\u043c\u043f\u043e\u0440\u0442\u044b, \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0430\u0446\u0438\u0438 \u0438 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u0438 \u0441 \u0432\u043d\u0435\u0448\u043d\u0438\u043c \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u043e\u043c) \u0447\u0430\u0441\u0442\u043e.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/ro\/blog\/administrirovanie\/dba-gramotno-organizovyvaem-sinhronizaczii-i-importy\" \/>\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=\"2020-03-22T05:42:22+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-03-22T05:42:22+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\udd47DBA: organiz\u0103m eficient sincroniz\u0103rile \u0219i importurile | ProHoster","description":"\u00cen cazul proces\u0103rii complexe a unor seturi mari de date (diverse procese ETL: importuri, conversii \u0219i sincroniz\u0103ri cu o surs\u0103 extern\u0103) adesea.","canonical_url":"https:\/\/prohoster.info\/ro\/blog\/administrirovanie\/dba-gramotno-organizovyvaem-sinhronizaczii-i-importy","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"ro_RO","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\udd47DBA: \u0433\u0440\u0430\u043c\u043e\u0442\u043d\u043e \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u0435\u043c \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u0438\u043c\u043f\u043e\u0440\u0442\u044b | ProHoster","og:description":"\u041f\u0440\u0438 \u0441\u043b\u043e\u0436\u043d\u043e\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u043d\u0430\u0431\u043e\u0440\u043e\u0432 \u0434\u0430\u043d\u043d\u044b\u0445 (\u0440\u0430\u0437\u043d\u044b\u0435 ETL-\u043f\u0440\u043e\u0446\u0435\u0441\u0441\u044b: \u0438\u043c\u043f\u043e\u0440\u0442\u044b, \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0430\u0446\u0438\u0438 \u0438 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u0438 \u0441 \u0432\u043d\u0435\u0448\u043d\u0438\u043c \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u043e\u043c) \u0447\u0430\u0441\u0442\u043e.","og:url":"https:\/\/prohoster.info\/ro\/blog\/administrirovanie\/dba-gramotno-organizovyvaem-sinhronizaczii-i-importy","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":"2020-03-22T05:42:22+00:00","article:modified_time":"2020-03-22T05:42:22+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"74953","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":null,"breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 18:04:26","updated":"2022-09-30 13:25:20","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/posts\/74953","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/comments?post=74953"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/posts\/74953\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/media\/74954"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/media?parent=74953"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/categories?post=74953"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/tags?post=74953"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}