{"id":52584,"date":"2019-11-12T00:00:00","date_gmt":"2019-11-11T21:00:00","guid":{"rendered":"https:\/\/prohoster.info\/blog\/blog_prohoster\/zamena-eav-na-jsonb-v-postgresql"},"modified":"2020-02-18T14:00:21","modified_gmt":"2020-02-18T11:00:21","slug":"zamena-eav-na-jsonb-v-postgresql","status":"publish","type":"post","link":"https:\/\/prohoster.info\/cs\/blog\/administrirovanie\/zamena-eav-na-jsonb-v-postgresql","title":{"rendered":"Nahrazen\u00ed EAV JSONB v PostgreSQL","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<blockquote><p>TL; DR: JSONB m\u016f\u017ee v\u00fdrazn\u011b zjednodu\u0161it n\u00e1vrh datab\u00e1zov\u00fdch sch\u00e9mat bez ohro\u017een\u00ed v\u00fdkonu dotaz\u016f.<\/p><\/blockquote>\n<p><\/p>\n<h3>\u00davod<\/h3>\n<p>\nUvedeme klasick\u00fd p\u0159\u00edklad, pravd\u011bpodobn\u011b jednoho z nejstar\u0161\u00edch pou\u017eit\u00ed v oblasti rela\u010dn\u00edch datab\u00e1z\u00ed (DB): m\u00e1me entitu a je t\u0159eba ulo\u017eit ur\u010dit\u00e9 vlastnosti (atributy) t\u00e9to entity. Ale ne v\u0161echny instance mohou m\u00edt stejnou sadu vlastnost\u00ed, nav\u00edc v budoucnu m\u016f\u017ee b\u00fdt pot\u0159eba p\u0159idat dal\u0161\u00ed vlastnosti.<\/p>\n<p>Nejjednodu\u0161\u0161\u00ed cesta, jak tento probl\u00e9m vy\u0159e\u0161it, je vytvo\u0159en\u00ed sloupce v tabulce DB pro ka\u017edou hodnotu vlastnosti a jednodu\u0161e vyplnit ty, kter\u00e9 jsou pot\u0159ebn\u00e9 pro konkr\u00e9tn\u00ed instanci entity. Skv\u011bl\u00e9! Probl\u00e9m vy\u0159e\u0161en\u2026 do chv\u00edle, kdy va\u0161e tabulka bude obsahovat miliony z\u00e1znam\u016f a vy budete pot\u0159ebovat p\u0159idat nov\u00fd z\u00e1znam.<\/p>\n<p>Pod\u00edv\u00e1me se na EAV vzor (<noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Entity%E2%80%93attribute%E2%80%93value_model\">Entity-Attribute-Value<\/a><\/noindex>), kter\u00fd se vyskytuje docela \u010dasto. Jedna tabulka obsahuje entity (z\u00e1znamy), druh\u00e1 tabulka obsahuje n\u00e1zvy vlastnost\u00ed (atribut\u016f) a t\u0159et\u00ed tabulka propojuje entity s jejich atributy a obsahuje hodnoty t\u011bchto atribut\u016f pro aktu\u00e1ln\u00ed entitu. To v\u00e1m umo\u017e\u0148uje m\u00edt r\u016fzn\u00e9 sady vlastnost\u00ed pro r\u016fzn\u00e9 objekty a tak\u00e9 p\u0159id\u00e1vat vlastnosti \"na m\u00edst\u011b\", ani\u017e byste m\u011bnili strukturu DB.<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><br \/>\nNicm\u00e9n\u011b, tuto pozn\u00e1mku bych nenapsal, kdyby nebylo nedostatk\u016f v p\u0159\u00edstupu vyu\u017e\u00edvaj\u00edc\u00edm EVA. Nap\u0159\u00edklad pro z\u00edsk\u00e1n\u00ed jedn\u00e9 nebo v\u00edce entit, kter\u00e9 maj\u00ed po 1 atributu, jsou pot\u0159eba 2 spojen\u00ed v dotazu: prvn\u00ed spojen\u00ed s tabulkou atribut\u016f, druh\u00e9 spojen\u00ed s tabulkou hodnot. Pokud entita m\u00e1 2 atributy, pot\u0159ebujete ji\u017e 4 spojen\u00ed! Krom\u011b toho jsou v\u0161echny atributy obvykle ulo\u017eeny jako \u0159et\u011bzce, co\u017e vede k p\u0159evodu typ\u016f jak pro v\u00fdsledek, tak pro podm\u00ednku WHERE. Pokud p\u00ed\u0161ete mnoho dotaz\u016f, je to pom\u011brn\u011b pl\u00fdtv\u00e1n\u00ed z hlediska vyu\u017eit\u00ed zdroj\u016f.<\/p>\n<p>I p\u0159es tyto o\u010dividn\u00e9 nev\u00fdhody se EAV ji\u017e dlouho pou\u017e\u00edv\u00e1 k \u0159e\u0161en\u00ed podobn\u00fdch probl\u00e9m\u016f. Bylo to nevyhnuteln\u00e9, a lep\u0161\u00ed alternativa zkr\u00e1tka neexistovala. <br \/>\nAle pak v PostgreSQL p\u0159i\u0161la nov\u00e1 \u201etechnologie\u201c\u2026<\/p>\n<p>Od verze PostgreSQL 9.4 byl p\u0159id\u00e1n datov\u00fd typ JSONB pro ukl\u00e1d\u00e1n\u00ed bin\u00e1rn\u00edch dat JSON. A\u010dkoli ukl\u00e1d\u00e1n\u00ed JSON v tomto form\u00e1tu obvykle zab\u00edr\u00e1 o n\u011bco v\u00edce m\u00edsta a \u010dasu ne\u017e prost\u00fd textov\u00fd JSON, prov\u00e1d\u011bn\u00ed operac\u00ed s n\u00edm je v\u00fdrazn\u011b rychlej\u0161\u00ed. JSONB tak\u00e9 podporuje indexov\u00e1n\u00ed, co\u017e \u010din\u00ed dotazy na n\u011bj je\u0161t\u011b rychlej\u0161\u00edmi.<\/p>\n<p>Datov\u00fd typ JSONB n\u00e1m umo\u017e\u0148uje nahradit objemn\u00fd vzor EAV p\u0159id\u00e1n\u00edm pouh\u00e9ho jednoho sloupce JSONB do na\u0161\u00ed tabulky entit, co\u017e v\u00fdrazn\u011b zjednodu\u0161uje n\u00e1vrh datab\u00e1ze. Mnoz\u00ed v\u0161ak tvrd\u00ed, \u017ee to by m\u011blo b\u00fdt spojeno se sn\u00ed\u017een\u00edm v\u00fdkonu\u2026 Z tohoto d\u016fvodu vznikl tento \u010dl\u00e1nek.<\/p>\n<h3>Nastaven\u00ed testovac\u00ed datab\u00e1ze<\/h3>\n<p>\nPro toto srovn\u00e1n\u00ed jsem vytvo\u0159il datab\u00e1zi na nov\u00e9 instalaci PostgreSQL 9.5 na 80dolarov\u00e9 sestav\u011b <noindex><a rel=\"nofollow\" href=\"https:\/\/www.digitalocean.com\/\">DigitalOcean<\/a><\/noindex> Ubuntu 14.04. Po nastaven\u00ed n\u011bkter\u00fdch parametr\u016f v postgresql.conf jsem spustil <noindex><a rel=\"nofollow\" href=\"https:\/\/gist.github.com\/coussej\/80c385332ce37df6687f\">tento<\/a><\/noindex> skript pomoc\u00ed psql. Pro prezentaci dat ve form\u00e1tu EAV byly vytvo\u0159eny n\u00e1sleduj\u00edc\u00ed tabulky:<\/p>\n<pre><code class=\"pgsql\">CREATE TABLE entity ( \n  id           SERIAL PRIMARY KEY, \n  name         TEXT, \n  description  TEXT\n);\nCREATE TABLE entity_attribute (\n  id          SERIAL PRIMARY KEY, \n  name        TEXT\n);\nCREATE TABLE entity_attribute_value (\n  id                  SERIAL PRIMARY KEY, \n  entity_id           INT    REFERENCES entity(id), \n  entity_attribute_id INT    REFERENCES entity_attribute(id), \n  value               TEXT\n);\n<\/code><\/pre>\n<p>\nN\u00ed\u017ee je uvedena tabulka, kde budou ulo\u017eena stejn\u00e1 data, ale s atributy ve sloupci typu JSONB \u2013 <i>&gt;, nebo je lze definovat prost\u0159ednictv\u00edm syst\u00e9mov\u00e9 vlastnosti<\/i>.<\/p>\n<pre><code class=\"pgsql\">CREATE TABLE entity_jsonb (\n  id          SERIAL PRIMARY KEY, \n  name        TEXT, \n  description TEXT,\n  properties  JSONB\n);\n<\/code><\/pre>\n<p>\nVypad\u00e1 to mnohem jednodu\u0161eji, \u017ee? Pot\u00e9 bylo do tabulek entit (<i>entity<\/i> &amp;&amp; <i>entity_jsonb<\/i>) vlo\u017eeno 10 milion\u016f z\u00e1znam\u016f a odpov\u00eddaj\u00edc\u00edm zp\u016fsobem byla vypln\u011bna stejn\u00e1 data v tabulk\u00e1ch, kde se pou\u017e\u00edv\u00e1 vzor EAV a p\u0159\u00edstup s JSONB sloupcem \u2013 <i>entity_jsonb.properties<\/i>. T\u00edmto zp\u016fsobem jsme z\u00edskali n\u011bkolik r\u016fzn\u00fdch typ\u016f dat mezi cel\u00fdm souborem vlastnost\u00ed. P\u0159\u00edklad dat:<\/p>\n<pre><code class=\"json\">{\n  id:          1\n  name:        \"Entity1\"\n  description: \"Testovac\u00ed entita \u010d. 1\"\n  properties:  {\n    color:        \"\u010derven\u00e1\"\n    length:       120\n    width:        3.1882420\n    hasSomething: true\n    country:      \"Belgie\"\n  } \n}<\/code><\/pre>\n<p>\nTak, nyn\u00ed m\u00e1me stejn\u00e1 data pro dv\u011b varianty. Poj\u010fme za\u010d\u00edt porovn\u00e1vat implementace v praxi!<\/p>\n<h3>Zjednodu\u0161en\u00ed n\u00e1vrhu<\/h3>\n<p>\nJak ji\u017e bylo zm\u00edn\u011bno, n\u00e1vrh DB byl v\u00fdrazn\u011b zjednodu\u0161en: jedna tabulka, d\u00edky pou\u017eit\u00ed sloupce JSONB pro vlastnosti, nam\u00edsto pou\u017eit\u00ed t\u0159\u00ed tabulek pro EAV. Jak se to v\u0161ak odr\u00e1\u017e\u00ed v dotazech? Aktualizace jednoho atributu entity vypad\u00e1 n\u00e1sledovn\u011b:<\/p>\n<pre><code class=\"pgsql\">-- EAV\nUPDATE entity_attribute_value \nSET value = 'modr\u00e1' \nWHERE entity_attribute_id = 1 \n  AND entity_id = 120;\n\n-- JSONB\nUPDATE entity_jsonb \nSET properties = jsonb_set(properties, '{\"color\"}', '\"modr\u00e1\"') \nWHERE id = 120;\n<\/code><\/pre>\n<p>\nJak vid\u00edme, posledn\u00ed dotaz nevypad\u00e1 jednodu\u0161e. Abychom aktualizovali hodnotu vlastnosti v objektu JSONB, mus\u00edme pou\u017e\u00edt funkci <noindex><a rel=\"nofollow\" href=\"http:\/\/www.postgresql.org\/docs\/9.5\/static\/functions-json.html\">jsonb_set()<\/a><\/noindex>, a mus\u00edme p\u0159edat na\u0161i novou hodnotu jako objekt JSONB. Nicm\u00e9n\u011b nemus\u00edme zn\u00e1t \u017e\u00e1dn\u00fd identifik\u00e1tor p\u0159edem. Pod\u00edvejme se na p\u0159\u00edklad s EAV, pot\u0159ebujeme zn\u00e1t jak entity_id, tak entity_attribute_id, abychom mohli prov\u00e9st aktualizaci. Pokud chcete aktualizovat vlastnost ve sloupci JSONB na z\u00e1klad\u011b n\u00e1zvu objektu, \u2013 to v\u0161e se d\u011bl\u00e1 jedn\u00edm jednoduch\u00fdm \u0159\u00e1dkem.<\/p>\n<p>Te\u010f si vyberme tu entitu, kterou jsme pr\u00e1v\u011b aktualizovali, podle podm\u00ednky jej\u00ed nov\u00e9 barvy:<\/p>\n<pre><code class=\"pgsql\">-- EAV\nSELECT e.name \nFROM entity e \n  INNER JOIN entity_attribute_value eav ON e.id = eav.entity_id\n  INNER JOIN entity_attribute ea ON eav.entity_attribute_id = ea.id\nWHERE ea.name = 'color' AND eav.value = 'modr\u00e1';\n\n-- JSONB\nSELECT name \nFROM entity_jsonb \nWHERE properties -&gt;&gt; 'color' = 'modr\u00e1';\n<\/code><\/pre>\n<p>\nMysl\u00edm, \u017ee se m\u016f\u017eeme shodnout na tom, \u017ee druh\u00e9 je krat\u0161\u00ed (bez join!), a tedy i \u010diteln\u011bj\u0161\u00ed. Tady vyhr\u00e1v\u00e1 JSONB! Pou\u017e\u00edv\u00e1me oper\u00e1tor JSON -&gt;&gt;, abychom z\u00edskali barvu jako textovou hodnotu z objektu JSONB. Existuje tak\u00e9 druh\u00fd zp\u016fsob, jak dos\u00e1hnout stejn\u00e9ho v\u00fdsledku v modelu JSONB pomoc\u00ed oper\u00e1toru @&gt;:<\/p>\n<pre><code class=\"pgsql\">-- JSONB \nSELECT name \nFROM entity_jsonb \nWHERE properties @&gt; '{\"color\": \"modr\u00e1\"}';\n<\/code><\/pre>\n<p>\nTo je trochu slo\u017eit\u011bj\u0161\u00ed: kontrolujeme, zda objekt JSON ve sloupci vlastnost\u00ed obsahuje objekt, kter\u00fd je napravo od oper\u00e1toru @&gt;. M\u00e9n\u011b \u010diteln\u00e9, ale v\u00fdkon\u011bj\u0161\u00ed (viz d\u00e1le). <\/p>\n<p>Zjednodu\u0161\u00edme pou\u017e\u00edv\u00e1n\u00ed JSONB je\u0161t\u011b v\u00edce, kdy\u017e pot\u0159ebujete vybrat n\u011bkolik vlastnost\u00ed sou\u010dasn\u011b. Zde se skute\u010dn\u011b hod\u00ed p\u0159\u00edstup JSONB: prost\u011b vyb\u00edr\u00e1me vlastnosti jako dal\u0161\u00ed sloupce v na\u0161em v\u00fdsledkov\u00e9m souboru bez nutnosti spojen\u00ed:<\/p>\n<pre><code class=\"pgsql\">-- JSONB \nSELECT name\n  , properties -&gt;&gt; 'color'\n  , properties -&gt;&gt; 'country'\nFROM entity_jsonb \nWHERE id = 120;\n<\/code><\/pre>\n<p>\nS EAV budete pot\u0159ebovat 2 spojen\u00ed pro ka\u017edou vlastnost, kterou chcete dotazovat. Podle m\u00e9ho n\u00e1zoru uveden\u00e9 dotazy ukazuj\u00ed velk\u00e9 zjednodu\u0161en\u00ed designu datab\u00e1ze. Dal\u0161\u00ed p\u0159\u00edklady, jak ps\u00e1t dotazy na JSONB, lze tak\u00e9 naj\u00edt v <noindex><a rel=\"nofollow\" href=\"http:\/\/schinckel.net\/2014\/05\/25\/querying-json-in-postgres\/\">tomto<\/a><\/noindex> p\u0159\u00edsp\u011bvku.<br \/>\nTe\u010f je \u010das promluvit o v\u00fdkonu.<\/p>\n<h3>V\u00fdkon<\/h3>\n<p>\nAbych porovnal v\u00fdkon, pou\u017eil jsem <noindex><a rel=\"nofollow\" href=\"http:\/\/www.postgresql.org\/docs\/9.1\/static\/sql-explain.html\">EXPLAIN ANALYZE<\/a><\/noindex> v dotazech, pro v\u00fdpo\u010det doby trv\u00e1n\u00ed. Ka\u017ed\u00fd dotaz byl proveden alespo\u0148 t\u0159ikr\u00e1t, proto\u017ee p\u0159i prvn\u00edm proveden\u00ed dotazu pl\u00e1nova\u010d dotaz\u016f pot\u0159ebuje v\u00edce \u010dasu. Nejprve jsem provedl dotazy bez jak\u00fdchkoli index\u016f. To o\u010dividn\u011b slou\u017eilo k v\u00fdhod\u011b JSONB, proto\u017ee spojen\u00ed pot\u0159ebn\u00e1 pro EAV nemohla vyu\u017e\u00edvat indexy (pole ciz\u00edho kl\u00ed\u010de nebyla indexov\u00e1na). Pot\u00e9 jsem vytvo\u0159il index pro 2 sloupce ciz\u00edch kl\u00ed\u010d\u016f v tabulce hodnot EAV, stejn\u011b jako index <noindex><a rel=\"nofollow\" href=\"http:\/\/www.postgresql.org\/docs\/9.1\/static\/textsearch-indexes.html\">GIN<\/a><\/noindex> pro sloupec JSONB.<\/p>\n<p>Aktualizace dat uk\u00e1zala n\u00e1sleduj\u00edc\u00ed v\u00fdsledky ohledn\u011b \u010dasu (v ms). Vezm\u011bte na v\u011bdom\u00ed, \u017ee m\u011b\u0159\u00edtko je logaritmick\u00e9:<\/p>\n<p><img decoding=\"async\" alt=\"Nahrazen\u00ed EAV JSONB v PostgreSQL\" src=\"\/wp-content\/uploads\/2019\/11\/8a12ccd7a46d04868b1cc5fcaf01a581.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nVid\u00edme, \u017ee JSONB je mnohem (&gt; 50000-x) rychlej\u0161\u00ed ne\u017e EAV, pokud nejsou pou\u017eity indexy, kv\u016fli v\u00fd\u0161e uveden\u00e9mu d\u016fvodu. Kdy\u017e indexujeme sloupce s prim\u00e1rn\u00edmi kl\u00ed\u010di, rozd\u00edl se t\u00e9m\u011b\u0159 vytr\u00e1c\u00ed, ale JSONB je st\u00e1le 1,3kr\u00e1t rychlej\u0161\u00ed ne\u017e EAV. V\u0161imn\u011bte si, \u017ee index ve sloupci JSONB zde nem\u00e1 \u017e\u00e1dn\u00fd vliv, proto\u017ee sloupec vlastnost\u00ed nepou\u017e\u00edv\u00e1me v hodnot\u00edc\u00edch krit\u00e9ri\u00edch. <\/p>\n<p>Pro v\u00fdb\u011br dat na z\u00e1klad\u011b hodnoty vlastnosti dost\u00e1v\u00e1me n\u00e1sleduj\u00edc\u00ed v\u00fdsledky (b\u011b\u017en\u00e9 m\u011b\u0159\u00edtko):<\/p>\n<p><img decoding=\"async\" alt=\"Nahrazen\u00ed EAV JSONB v PostgreSQL\" src=\"\/wp-content\/uploads\/2019\/11\/e9a1fd797ef60132e661da686f6214a3.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nM\u016f\u017eeme si v\u0161imnout, \u017ee JSONB op\u011bt pracuje rychleji ne\u017e EAV bez index\u016f, ale kdy\u017e EAV m\u00e1 indexy \u2013 p\u0159esto funguje rychleji ne\u017e JSONB. Pot\u00e9 jsem vid\u011bl, \u017ee \u010das pro dotazy JSONB byl stejn\u00fd, co\u017e m\u011b p\u0159ivedlo k tomu, \u017ee index GIN nefungoval. O\u010dit\u011b, kdy\u017e pou\u017e\u00edv\u00e1te index GIN pro sloupec s vypln\u011bn\u00fdmi vlastnostmi, funguje pouze p\u0159i pou\u017eit\u00ed oper\u00e1toru zahrnut\u00ed @&gt;. Pou\u017eil jsem to v nov\u00e9m testu, co\u017e m\u011blo obrovsk\u00fd vliv na \u010das: pouh\u00fdch 0,153 ms! To je 15000kr\u00e1t rychlej\u0161\u00ed ne\u017e EAV a 25000kr\u00e1t rychlej\u0161\u00ed ne\u017e oper\u00e1tor -&gt;. <\/p>\n<p>Mysl\u00edm, \u017ee to bylo dost rychl\u00e9!<\/p>\n<h3>Velikost tabulek DB<\/h3>\n<p>\nPoj\u010fme porovnat velikosti tabulek p\u0159i obou p\u0159\u00edstupech. V psql m\u016f\u017eeme uk\u00e1zat velikost v\u0161ech tabulek a index\u016f pomoc\u00ed p\u0159\u00edkazu <b>dti+<\/b><\/p>\n<p><img decoding=\"async\" alt=\"Nahrazen\u00ed EAV JSONB v PostgreSQL\" src=\"\/wp-content\/uploads\/2019\/11\/c6c1fe7901fe9ae2bd446cf4ae99b923.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nPro p\u0159\u00edstup EAV maj\u00ed velikosti tabulek kolem 3068 MB, a indexy a\u017e 3427 MB, co\u017e celkem d\u00e1v\u00e1 6,43 GB. P\u0159i pou\u017eit\u00ed p\u0159\u00edstupu s JSONB se pou\u017e\u00edv\u00e1 1817 MB pro tabulku a 318 MB pro indexy, co\u017e \u010din\u00ed 2,08 GB. To je t\u0159ikr\u00e1t m\u00e9n\u011b! Tento fakt m\u011b trochu p\u0159ekvapil, proto\u017ee ukl\u00e1d\u00e1me n\u00e1zvy vlastnost\u00ed v ka\u017ed\u00e9m objektu JSONB. <\/p>\n<p>Ale p\u0159esto \u010d\u00edsla mluv\u00ed sama za sebe: v EAV uchov\u00e1v\u00e1me 2 celo\u010d\u00edseln\u00e9 ciz\u00ed kl\u00ed\u010de na hodnotu atributu, \u010d\u00edm\u017e z\u00edsk\u00e1me 8 bajt\u016f dodate\u010dn\u00fdch dat. Krom\u011b toho jsou v EAV v\u0161echny hodnoty vlastnost\u00ed uchov\u00e1v\u00e1ny jako text, zat\u00edmco JSONB vyu\u017e\u00edv\u00e1 \u010d\u00edseln\u00e9 a logick\u00e9 hodnoty tam, kde je to mo\u017en\u00e9, co\u017e vede k men\u0161\u00edmu objemu.<\/p>\n<h3>Z\u00e1v\u011bry<\/h3>\n<p>\nCelkov\u011b si mysl\u00edm, \u017ee uchov\u00e1v\u00e1n\u00ed vlastnost\u00ed entit ve form\u00e1tu JSONB m\u016f\u017ee z\u00e1sadn\u011b zjednodu\u0161it n\u00e1vrh a \u00fadr\u017ebu va\u0161\u00ed datab\u00e1ze. Pokud prov\u00e1d\u00edte mnoho dotaz\u016f, pak v\u0161echno, co je ulo\u017eeno v jedn\u00e9 tabulce s entitou, skute\u010dn\u011b funguje efektivn\u011bji. A skute\u010dnost, \u017ee to zjednodu\u0161uje interakci mezi daty, je ji\u017e v\u00fdhoda, ale tak\u00e9 v\u00fdsledn\u00e1 datab\u00e1ze je 3kr\u00e1t men\u0161\u00ed.<\/p>\n<p>Tak\u00e9 podle proveden\u00fdch test\u016f lze doj\u00edt k z\u00e1v\u011bru, \u017ee ztr\u00e1ty v\u00fdkonu jsou velmi nepatrn\u00e9. V n\u011bkter\u00fdch p\u0159\u00edpadech dokonce JSONB funguje rychleji ne\u017e EAV, co\u017e ho d\u011bl\u00e1 je\u0161t\u011b lep\u0161\u00edm. Tento referen\u010dn\u00ed test v\u0161ak samoz\u0159ejm\u011b nezahrnuje v\u0161echny aspekty (nap\u0159\u00edklad entity s velmi velk\u00fdm mno\u017estv\u00edm vlastnost\u00ed, v\u00fdrazn\u00e9 zv\u00fd\u0161en\u00ed po\u010dtu vlastnost\u00ed existuj\u00edc\u00edch dat,\u2026), proto, pokud m\u00e1te n\u011bjak\u00e9 n\u00e1vrhy na jejich zlep\u0161en\u00ed, nev\u00e1hejte zanechat koment\u00e1\u0159!<br \/>\n<br \/>Zdroj: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/475178\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>TL; DR: JSONB \u043c\u043e\u0436\u0435\u0442 \u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u0442\u044c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0441\u0445\u0435\u043c\u044b \u0411\u0414 \u0431\u0435\u0437 \u0443\u0449\u0435\u0440\u0431\u0430 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u0445. \u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u041f\u0440\u0438\u0432\u0435\u0434\u0435\u043c \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043f\u0440\u0438\u043c\u0435\u0440, \u043d\u0430\u0432\u0435\u0440\u043d\u043e\u0435, \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437 \u0441\u0442\u0430\u0440\u0435\u0439\u0448\u0438\u0445 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0432 \u043c\u0438\u0440\u0435 \u0440\u0435\u043b\u044f\u0446\u0438\u043e\u043d\u043d\u044b\u0445 \u0411\u0414 (\u0431\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445): \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c, \u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0435 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 (\u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b) \u044d\u0442\u043e\u0439 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438. \u041d\u043e \u043d\u0435 \u0432\u0441\u0435 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u044b \u043c\u043e\u0433\u0443\u0442 \u0438\u043c\u0435\u044e\u0442 \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u044b\u0439 \u043d\u0430\u0431\u043e\u0440 \u0441\u0432\u043e\u0439\u0441\u0442\u0432, \u043a \u0442\u043e\u043c\u0443 \u0436\u0435 \u0432 [&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-52584","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=\"TL; DR: JSONB \u043c\u043e\u0436\u0435\u0442 \u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u0442\u044c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0441\u0445\u0435\u043c\u044b \u0411\u0414 \u0431\u0435\u0437 \u0443\u0449\u0435\u0440\u0431\u0430 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u0445. \u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u041f\u0440\u0438\u0432\u0435\u0434\u0435\u043c \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043f\u0440\u0438\u043c\u0435\u0440, \u043d\u0430\u0432\u0435\u0440\u043d\u043e\u0435, \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437 \u0441\u0442\u0430\u0440\u0435\u0439\u0448\u0438\u0445.\" \/>\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\/cs\/blog\/administrirovanie\/zamena-eav-na-jsonb-v-postgresql\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.3\" \/>\n\t\t<meta property=\"og:locale\" content=\"cs_CZ\" \/>\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\u0417\u0430\u043c\u0435\u043d\u0430 EAV \u043d\u0430 JSONB \u0432 PostgreSQL | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"TL; DR: JSONB \u043c\u043e\u0436\u0435\u0442 \u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u0442\u044c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0441\u0445\u0435\u043c\u044b \u0411\u0414 \u0431\u0435\u0437 \u0443\u0449\u0435\u0440\u0431\u0430 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u0445. \u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u041f\u0440\u0438\u0432\u0435\u0434\u0435\u043c \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043f\u0440\u0438\u043c\u0435\u0440, \u043d\u0430\u0432\u0435\u0440\u043d\u043e\u0435, \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437 \u0441\u0442\u0430\u0440\u0435\u0439\u0448\u0438\u0445.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/cs\/blog\/administrirovanie\/zamena-eav-na-jsonb-v-postgresql\" \/>\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-11-11T21:00:00+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-02-18T11:00:21+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\udd47Nahrazen\u00ed EAV JSONB v PostgreSQL | ProHoster","description":"TL; DR: JSONB m\u016f\u017ee v\u00fdrazn\u011b zjednodu\u0161it n\u00e1vrh sch\u00e9matu datab\u00e1ze bez ztr\u00e1ty v\u00fdkonu p\u0159i dotazech. \u00davod Uve\u010fme klasick\u00fd p\u0159\u00edklad, pravd\u011bpodobn\u011b jednoho z nejstar\u0161\u00edch.","canonical_url":"https:\/\/prohoster.info\/cs\/blog\/administrirovanie\/zamena-eav-na-jsonb-v-postgresql","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"cs_CZ","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\u0417\u0430\u043c\u0435\u043d\u0430 EAV \u043d\u0430 JSONB \u0432 PostgreSQL | ProHoster","og:description":"TL; DR: JSONB \u043c\u043e\u0436\u0435\u0442 \u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u0442\u044c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0441\u0445\u0435\u043c\u044b \u0411\u0414 \u0431\u0435\u0437 \u0443\u0449\u0435\u0440\u0431\u0430 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u0445. \u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u041f\u0440\u0438\u0432\u0435\u0434\u0435\u043c \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043f\u0440\u0438\u043c\u0435\u0440, \u043d\u0430\u0432\u0435\u0440\u043d\u043e\u0435, \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437 \u0441\u0442\u0430\u0440\u0435\u0439\u0448\u0438\u0445.","og:url":"https:\/\/prohoster.info\/cs\/blog\/administrirovanie\/zamena-eav-na-jsonb-v-postgresql","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-11-11T21:00:00+00:00","article:modified_time":"2020-02-18T11:00:21+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"52584","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-24 04:07:19","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 20:40:27","updated":"2026-01-24 04:07:19","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/posts\/52584","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/comments?post=52584"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/posts\/52584\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/media?parent=52584"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/categories?post=52584"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/cs\/wp-json\/wp\/v2\/tags?post=52584"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}