Слабо-структурированные данные в СУБД
PostgreSQL (мастер-класс)
Александр Коротков, Иван Панченко
Postgres Professional
2015
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 1 / 72
1. Немного о массивах
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 2 / 72
Исходные данные: products
# d product
Table ”public.product”
Column | Type | Modifiers
---------------------+-----------------+-----------
product_id | character(10) | not null
product_title | text | not null
product_sales_rank | bigint | not null
product_group | text | not null
product_category | text |
product_subcategory | text |
similar_product_ids | character(10)[] | not null
Indexes:
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 3 / 72
Subscrip on – вытащить элемент по
заданному индексу
# SELECT product_id, similar_product_ids
FROM product LIMIT 5;
product_id | similar_product_ids
------------+----------------------------------------------------------
0001047655 | {0061007129,0061007358,0061007137,0061099341,0061007161}
0001053736 | {0440905605,0345336062,0140380531,0393320979,0395898714}
0001053744 | {}
0001054600 | {}
0001474103 | {0895403889,0001473123,0766177610,0001472933,0766187403}
(5 rows)
# SELECT product_id, similar_product_ids[2]
FROM product LIMIT 5;
product_id | similar_product_ids
------------+---------------------
0001047655 | 0061007358
0001053736 | 0345336062
0001053744 | NULL
0001054600 | NULL
0001474103 | 0001473123
(5 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 4 / 72
Subscrip on – вытащить slice
# SELECT product_id, similar_product_ids[1:2]
FROM product LIMIT 5;
product_id | similar_product_ids
------------+-------------------------
0001047655 | {0061007129,0061007358}
0001053736 | {0440905605,0345336062}
0001053744 | {}
0001054600 | {}
0001474103 | {0895403889,0001473123}
(5 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 5 / 72
Subscrip on – обновление
# UPDATE product
SET similar_product_ids[1] = ’0061007129’
WHERE product_id = ’0001047655’;
# UPDATE product
SET similar_product_ids[1:2] = ’{0440905605,0345336062}’
WHERE product_id = ’0001047655’;
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 6 / 72
Осторожно – произвольные границы!
# SELECT (’[-3:-2]={1,2}’::int[])[1];
int4
------
NULL
(1 row)
# SELECT (’[-3:-2]={1,2}’::int[])[-2];
int4
------
2
(1 row)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 7 / 72
Осторожно – многомерные массивы!
# SELECT (’{{1,2},{3,4}}’::int[])[1];
int4
------
NULL
(1 row)
# SELECT (’{{1,2},{3,4}}’::int[])[1][2];
int4
------
2
(1 row)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 8 / 72
array_lower и array_upper – верхняя и
нижняя границы массива
Откусываем у массива первый элемент.
# SELECT product_id,
similar_product_ids[
array_lower(similar_product_ids,1) + 1 :
array_upper(similar_product_ids,1)]
FROM product LIMIT 5;
product_id | similar_product_ids
------------+-----------------------------------------------
0001047655 | {0061007358,0061007137,0061099341,0061007161}
0001053736 | {0345336062,0140380531,0393320979,0395898714}
0001053744 | NULL
0001054600 | NULL
0001474103 | {0001473123,0766177610,0001472933,0766187403}
(5 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 9 / 72
Поиск по массиву: ANY
# SELECT product_id, product_title
FROM product WHERE ’0061007137’ = ANY(similar_product_ids);
product_id | product_title
------------+------------------------------------
0001047655 | Prodigal Daughter
0061007129 | Kane & Abel
0061007145 | The Prodigal Daughter
0061007161 | First Among Equals
0061007358 | Not a Penny More, Not a Penny Less
0061013315 | The Eleventh Commandment
0061013706 | Shall We Tell the President?
0061092045 | Honor Among Thieves
0061093653 | Twelve Red Herrings
0061099341 | As the Crow Flies
(10 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 10 / 72
Разворачивание массива в rowset: unnest
# SELECT unnest(similar_product_ids)
FROM product WHERE product_id = ’0061007137’;
unnest
------------
0061007358
0061099341
0061007145
0061007161
0061007129
(5 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 11 / 72
Сворачивание rowset в массив: array_agg
# SELECT array_agg(product_id)
FROM product
WHERE ’0061093653’ = ANY(similar_product_ids);
array_agg
-----------------------------------------------
{006018552X,0060185805,006100717X,0061032077}
(1 row)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 12 / 72
Задача #1
Для каждого продукта собрать массив тех,
которые ссылаются на него через
similar_product_ids.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 13 / 72
Задача #1: решение
SELECT
similar_id,
array_agg(product_id)
FROM (
SELECT
product_id,
unnest(similar_product_ids) AS similar_id
FROM
product) s
GROUP BY
similar_id;
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 14 / 72
Задача #2
Сделать выборку продуктов, для которых
все продукты, указанные в массиве
similar_product_ids имеют рейтинг не
меньше 100000.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 15 / 72
Задача #2: решение
SELECT
*
FROM
product p1
WHERE NOT EXISTS (
SELECT
1
FROM
unnest(p1.similar_product_ids) sid
JOIN
product p2
ON
p2.product_id = sid
WHERE
NOT (p2.product_sales_rank >= 100000));
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 16 / 72
Операторы &&, @>, <@ (1/2)
▶ a && b – у массивов a и b есть хотя бы один
общий элемент.
▶ a @> b – в массиве a есть все элементы массива b
и, быть может, другие.
▶ a <@ b – то же, что и b @> a.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 17 / 72
Операторы &&, @>, <@ (2/2)
# SELECT count(*) FROM product
WHERE similar_product_ids && ’{0061007137,0007149689}’;
count
-------
11
(1 row)
# SELECT count(*) FROM product
WHERE similar_product_ids @> ’{0061007137,0061007161}’;
count
-------
6
(1 row)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 18 / 72
Индексы по массивам (1/3)
▶ Встроенный GIN-индекс для массивов всех типов
(операторы &&, @>, <@, =).
▶ GiST-индекс для int[] в contrib/intarray.
▶ Индексы по конкретным элементам массива
(например, a[3]). Не очень интересно.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 19 / 72
Индексы по массивам (2/3)
# EXPLAIN ANALYZE SELECT * FROM product
WHERE similar_product_ids @> ’{0061007137,0061007161}’;
QUERY PLAN
---------------------------------------------------------------
Seq Scan on product (cost=0.00..9348.25 rows=1 width=161) (ac
Filter: (similar_product_ids @> ’{0061007137,0061007161}’::b
Rows Removed by Filter: 253614
Planning time: 0.120 ms
Execution time: 97.547 ms
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 20 / 72
Индексы по массивам (3/3)
# CREATE INDEX product_similar_product_ids_idx
ON product USING gin (similar_product_ids);
CREATE INDEX
Time: 18795,075 ms
# EXPLAIN ANALYZE SELECT * FROM product
WHERE similar_product_ids @> ’{0061007137,0061007161}’;
---------------------------------------------------------------
Bitmap Heap Scan on product (cost=28.00..32.01 rows=1 width=1
Recheck Cond: (similar_product_ids @> ’{0061007137,006100716
Heap Blocks: exact=5
-> Bitmap Index Scan on product_similar_product_ids_idx (c
Index Cond: (similar_product_ids @> ’{0061007137,00610
Planning time: 0.123 ms
Execution time: 0.089 ms
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 21 / 72
2. Hstore
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 22 / 72
Исходные данные: stocks (1/2)
Table ”public.stocks”
Column | Type | Modifiers
----------+---------+------------------------------------------
id | integer | not null default nextval(’stocks_id_seq’:
country | text | not null
industry | text | not null
sector | text | not null
company | text | not null
ticker | text | not null
volume | integer | not null
h | hstore | not null
Indexes:
”stocks_pkey” PRIMARY KEY, btree (id)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 23 / 72
Исходные данные: stocks (2/2)
# SELECT * FROM stocks LIMIT 1;
-[ RECORD 1 ]--------------------------------------------------
id | 1
country | USA
industry | Semiconductor Equipment & Materials
sector | Technology
company | Brooks Automation Inc.
ticker | BRKS
volume | 516739
h | ”Gap”=>”0.0151”, ”P/B”=>”1.05”, ”P/E”=>”6.07”, ”P/S”
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 24 / 72
Вытаскивание полей (1/2)
# SELECT id, h -> ’Price’, h -> ’Change’
FROM stocks LIMIT 10;
id | ?column? | ?column?
----+-------------------+----------
1 | 10.24 | 0.0291
2 | 26.94 | -0.023
3 | 92.56999999999999 | 0.0016
4 | 14.95 | 0.0047
5 | 26.65 | 0.0013
6 | 25.33 | -0.0261
7 | 62.7 | 0.0061
8 | 11.9 | -0.0017
9 | 24.44 | 0.0188
10 | 36.87 | 0.0104
(10 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 25 / 72
Вытаскивание полей (2/2)
# SELECT company FROM stocks
WHERE h -> ’Price’ > 1000.0;
ERROR: operator does not exist: text > numeric
# SELECT company FROM stocks
WHERE (h -> ’Price’)::numeric > 1000.0;
company
----------------------------
Seaboard Corp.
Berkshire Hathaway Inc.
Google Inc.
priceline.com Incorporated
(4 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 26 / 72
Превращаем hstore в набор пар
ключ-значение
# SELECT (each(h)).* FROM stocks WHERE id = 4;
key | value
-------------------------------+----------------------
Gap | 0
Price | 14.95
Change | 0.0047
50-Day Low | 0.0381
50-Day High | -0.0177
52-Week Low | 0.3304
Short Ratio | 0.14
......................................................
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 27 / 72
Задача #3
Собрать статистику по использованию
различных ключей в поле h таблицы stocks:
для каждого встречающегося ключа указать
его частоту.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 28 / 72
Задача #3: решение
# SELECT key,
count(*)::float8 /
(SELECT count(*) FROM stocks) freq
FROM (SELECT (each(h)).* FROM stocks) kv
GROUP BY key;
key | freq
-----------------------------------+-------------------
Shares Float | 0.725725281231498
Analyst Recom | 0.616489046773239
Insider Transactions | 0.554026050917703
Institutional Transactions | 0.705743043220841
P/B | 0.71166370633511
.......................................................
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 29 / 72
Оператор @>
a @> b тогда и только тогда, когда для каждой пары
ключ => значение из b, такая же пара присутствует и в
a.
Примеры.
key1 | key2 | result
-----------------+--------------+--------
a=>x, b=>y, c=>z | a=>x, b=>y | true
a=>x, b=>y | a=>x, b=>qqq | false
a=>x, b=>y | t=>x, b=>y | false
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 30 / 72
Операторы ?, ?&, ?|
▶ a ? b – проверяет наличие ключа b в массиве a.
▶ a ?& b – проверяет наличие всех ключей из
массива b в массиве a.
▶ a ?| b – проверяет наличие хотя бы одного из
ключей массива b в массиве a.
# SELECT count(*),
count(*) FILTER
(WHERE h ? ’Short Ratio’),
count(*) FILTER
(WHERE h ?| ’{Short Ratio, Change from Open}’),
count(*) FILTER
(WHERE h ?& ’{Short Ratio, Change from Open}’)
FROM stocks;
count | count | count | count
-------+-------+-------+-------
6756 | 5412 | 6743 | 5412
(1 row)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 31 / 72
Задача #4
Написать аналог оператора @> с помощью
функций each(hstore) и операторов ->, ?.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 32 / 72
Задача #4: решение
CREATE FUNCTION contains_sql(hstore, hstore) RETURNS bool
AS $$
SELECT NOT EXISTS (
SELECT 1
FROM each($2) kv
WHERE NOT (
$1 ? key AND
$1 -> key IS NOT DISTINCT FROM value)
)
$$ LANGUAGE sql;
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 33 / 72
Конструируем hstore
# SELECT hstore(’{key1, key2}’::text[],
’{value1, value2}’::text[]);
hstore
------------------------------------
”key1”=>”value1”, ”key2”=>”value2”
(1 row)
# SELECT hstore(array_agg(id::text),
array_agg(company))
FROM stocks WHERE id <= 4;
hstore
------------------------------------------------------
”1”=>”Brooks Automation Inc.”, ”2”=>”Broadcom Corp.”
(1 row)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 34 / 72
Задача #5
Посчитать для каждой отрасли (sector) из
таблицы stocks суммарный объём ценных
бумаг (volume), и выдать это в виде hstore
вида: отрасль => суммарный объём ценный
бумаг.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 35 / 72
Задача #5: решение
# SELECT hstore(
array_agg(sector),
array_agg(volume::text)) h
FROM (SELECT sector,
SUM(volume) AS volume
FROM stocks
GROUP BY sector) s;
-[ RECORD 1 ]--------------------------------------------------------
h | ”Services”=>”637878158”, ”Financial”=>”1389743864”,
| ”Utilities”=>”94268768”, ”Healthcare”=>”381247435”,
| ”Technology”=>”1225484169”, ”Conglomerates”=>”3547704”,
| ”Consumer Goods”=>”276207382”,
| ”Basic Materials”=>”625043686”,
| ”Industrial Goods”=>”200105263”
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 36 / 72
Индексы для hstore
▶ GIN/GiST индексы: индексируем ключи отдельно,
значения отдельно (операторы @>, ?, ?&, ?|).
▶ GIN индекс: индексируем пары hash(key) +
hash(value) (операторы @>, ?, ?&, ?|)
https://github.com/postgrespro/hstore_ops
▶ Индекс по отдельному ключу (h->’key’).
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 37 / 72
3. Jsonb
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 38 / 72
Исходные данные: company
# d company
Table ”public.company”
Column | Type | Modifiers
--------+-------+-----------
js | jsonb |
# SELECT js FROM company limit 5;
---------------------------------------------------------------
{”_id”: {”$oid”: ”52cdef7c4bab8bd675297d8a”}, ”name”: ”Wetpain
{”_id”: {”$oid”: ”52cdef7c4bab8bd675297d8b”}, ”name”: ”AdventN
{”_id”: {”$oid”: ”52cdef7c4bab8bd675297d8c”}, ”name”: ”Zoho”,
{”_id”: {”$oid”: ”52cdef7c4bab8bd675297d8d”}, ”ipo”: null, ”na
{”_id”: {”$oid”: ”52cdef7c4bab8bd675297d8e”}, ”ipo”: {”pub_day
(5 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 39 / 72
Jsonb pre y print
# SELECT jsonb_pretty(js) FROM company LIMIT 1;
-[ RECORD 1 ]+-------------------------------------------
jsonb_pretty | {
| ”_id”: {
| ”$oid”: ”52cdef7c4bab8bd675297d8a”
| },
| ”name”: ”Wetpaint”,
| ”image”: {
| ”available_sizes”: [
| [
| [
| 150,
| 75
.........................................................
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 40 / 72
Вытаскивание полей
# SELECT js -> ’name’,
js -> ’acquisition’ -> ’price_amount’,
js #> ’{acquisition,price_amount}’
FROM company
LIMIT 5;
?column? | ?column? | ?column?
-------------------------+-----------+-----------
”Wetpaint” | 30000000 | 30000000
”AdventNet” | NULL | NULL
”Zoho” | NULL | NULL
”Digg” | 500000 | 500000
”Facebook” | NULL | NULL
(5 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 41 / 72
Вытаскивание полей и типы (1/2)
-> вытаскивает тип jsonb
# SELECT js -> ’name’ FROM company
WHERE js -> ’name’ ILIKE ’%paint%’;
ERROR: operator does not exist: jsonb ~~* unknown
Приведение значения поля к строке
# SELECT (js -> ’name’)::text, js ->> ’name’
FROM company LIMIT 5;
text | ?column?
-------------+-----------
”Wetpaint” | Wetpaint
”AdventNet” | AdventNet
”Zoho” | Zoho
”Digg” | Digg
”Facebook” | Facebook
(5 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 42 / 72
Вытаскивание полей и типы (2/2)
# SELECT
js ->> ’name’,
js ->> ’number_of_employees’
FROM company
WHERE
js ->> ’name’ ILIKE ’%paint%’ AND
(js ->> ’number_of_employees’)::int >= 10;
?column? | ?column?
----------+----------
Wetpaint | 47
(1 row)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 43 / 72
Вытаскивание элементов массивов
# SELECT
js -> ’offices’ -> 0 ->> ’city’,
js #>> ’{offices,0,city}’
FROM company LIMIT 5;
?column? | ?column?
---------------+---------------
Seattle | Seattle
Pleasanton | Pleasanton
Pleasanton | Pleasanton
San Francisco | San Francisco
Menlo Park | Menlo Park
(5 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 44 / 72
Индексы по выражениям
# CREATE UNIQUE INDEX company_id_idx
ON company ((js -> ’_id’ ->> ’$oid’));
CREATE INDEX
# EXPLAIN ANALYZE SELECT js -> ’name’ FROM company
WHERE js -> ’_id’ ->> ’$oid’ = ’52cdef7c4bab8bd675297d8a’;
QUERY P
---------------------------------------------------------------
Index Scan using company_id_idx on company (cost=0.29..8.31 r
Index Cond: (((js -> ’_id’::text) ->> ’$oid’::text) = ’52cde
Planning time: 0.084 ms
Execution time: 0.156 ms
(4 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 45 / 72
Разворачивание JSON-массивов в rowset
# SELECT jsonb_array_elements(js -> ’competitions’)
FROM company
WHERE js -> ’_id’ ->> ’$oid’ = ’52cdef7c4bab8bd675297d8a’;
jsonb_array_elements
---------------------------------------------------------------------
{”competitor”: {”name”: ”Wikia”, ”permalink”: ”wikia”}}
{”competitor”: {”name”: ”JotSpot”, ”permalink”: ”jotspot”}}
{”competitor”: {”name”: ”Socialtext”, ”permalink”: ”socialtext”}}
{”competitor”: {”name”: ”Ning by Glam Media”, ”permalink”: ”ning”}}
{”competitor”: {”name”: ”Soceeo”, ”permalink”: ”soceeo”}}
{”competitor”: {”name”: ”Yola”, ”permalink”: ”yola”}}
{”competitor”: {”name”: ”SocialGO”, ”permalink”: ”socialgo”}}
{”competitor”: {”name”: ”IslamNor”, ”permalink”: ”islamnor”}}
(8 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 46 / 72
Разворачивание JSON-объекта
в key-value rowset
# SELECT (jsonb_each(js #> ’{offices, 0}’)).*
FROM company
WHERE js -> ’_id’ ->> ’$oid’ = ’52cdef7c4bab8bd675297d8a’;
key | value
--------------+--------------------
city | ”Seattle”
address1 | ”710 - 2nd Avenue”
address2 | ”Suite 1100”
latitude | 47.603122
zip_code | ”98104”
longitude | -122.333253
state_code | ”WA”
description | ””
country_code | ”USA”
(9 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 47 / 72
Оператор @>
▶ Для скалярных a и b, a @> b тогда и только тогда, когда a = b.
▶ Если a и b объекты, то для каждого ключа в b должен
существовать ключ в a, содержащий соответствующее значение.
▶ Если a и b массивы, то для каждого элемента в b должен
существовать содержащий его элемент в a.
Примеры.
arg1 | arg2 | result
---------------------+----------------------+--------
1 | 1 | true
1 | ”1” | false
{”a”: 1, ”b”: 2} | {”a”: 1} | true
{”a”: 1, ”b”: 2} | {”a”: 2} | false
[1, 2] | [1] | true
[{”a”: 1, ”b”: 2}] | [{”a”: 1}, {”b”: 2}] | true
[{”a”: 1}, {”b”: 2}] | [{”a”: 1, ”b”: 2}] | false
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 48 / 72
Установка plv8
$ git clone https://github.com/plv8/plv8.git
$ cd plv8
$ make USE_PGXS=1
$ sudo make USE_PGXS=1 install
$ make USE_PGXS=1 installcheck
$ psql DB -c ”CREATE EXTENSION plv8;”
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 49 / 72
Задача #6
Написать на plv8 функцию, которая для
компании выводит список стран, где у неё
есть офисы.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 50 / 72
Задача #6: решение
CREATE FUNCTION extract_countries(company jsonb)
RETURNS text[]
AS $$
var tmp = {}, result = [];
for(var i = 0; i < company.offices.length; i++)
tmp[company.offices[i].country_code] = true;
for(var i in tmp)
result.push(i)
return result;
$$ LANGUAGE plv8 IMMUTABLE STRICT;
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 51 / 72
Индекс по странам
CREATE INDEX company_countries_idx ON company
USING gin(extract_countries(js));
# EXPLAIN ANALYZE SELECT * FROM company
WHERE extract_countries(js) @> ’{RUS}’;
QUER
---------------------------------------------------------------
Bitmap Heap Scan on company (cost=8.73..348.48 rows=94 width=
(actual time=0.038..0.089 rows=50 loops=1)
Recheck Cond: (extract_countries(js) @> ’{RUS}’::text[])
Heap Blocks: exact=48
-> Bitmap Index Scan on company_countries_idx (cost=0.00..
Index Cond: (extract_countries(js) @> ’{RUS}’::text[])
Planning time: 0.057 ms
Execution time: 0.113 ms
(7 rows)
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 52 / 72
Задача #7
Написать на plv8 агрегат, который который
посчитает частоты различных размеров
картинок.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 53 / 72
Задача #7: решение (1/2)
CREATE FUNCTION image_size_agg_sfunc(state jsonb,
company jsonb) RETURNS jsonb
AS $$
if (!company.image) return state;
var sizes = company.image.available_sizes
for (var i = 0; i < sizes.length; i++)
{
var sizeKey = sizes[i][0][0] + ’x’ +
sizes[i][0][1];
if (sizeKey in state) state[sizeKey]++;
else state[sizeKey] = 1;
}
return state;
$$ LANGUAGE plv8 IMMUTABLE STRICT;
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 54 / 72
Задача #7: решение (2/2)
CREATE AGGREGATE image_size_agg (jsonb) (
SFUNC = image_size_agg_sfunc,
STYPE = jsonb,
INITCOND = ’{}’);
# SELECT image_size_agg(js) FROM company;
-[ RECORD 1 ]--+-----------------------------------------------
image_size_agg | {”1x1”: 15, ”32x8”: 3, ”150x7”: 1, ”150x8”: 5,
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 55 / 72
Установка jsquery
$ git clone https://github.com/postgrespro/jsquery.git
$ cd jsquery
$ make USE_PGXS=1
$ sudo make USE_PGXS=1 install
$ make USE_PGXS=1 installcheck
$ psql DB -c ”CREATE EXTENSION jsquery;”
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 56 / 72
Индексы для jsonb
▶ GIN jsonb_ops: индексируем ключи отдельно,
значения отдельно (операторы @>, ?, ?&, ?|).
▶ GIN jsonb_path_ops: индексируем hash(path +
value) (операторы @>).
▶ jsonb_path_value_ops: индексируем hash(path) +
hash(value) (операторы @>, jsonb @@ jsquery).
▶ jsonb_value_path_ops: индексируем hash(value) +
bloom(path) (операторы @>, jsonb @@ jsquery).
▶ Индексы по отдельным ключам (js -> ’key’).
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 57 / 72
Пример: найти комании с офисами в
России
-- Оператор @>
# SELECT js->’name’ FROM company
WHERE js @> ’{”offices”:[{”country_code”: ”RUS”}]}’;
-- Оператор jsquery
# SELECT js->’name’ FROM company
WHERE js @@ ’offices.#.country_code = ”RUS”’;
-- Разворачивание jsonb в rowset
# SELECT js->’name’ FROM company
WHERE EXISTS (
SELECT 1
FROM jsonb_array_elements(js->’offices’) el
WHERE el ->> ’country_code’ = ’RUS’);
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 58 / 72
Задача #8
Найти компании, конкуренты Wikia, у
которых есть офис в США.
Реализовать решение с помощью:
▶ оператора @>,
▶ jsquery,
▶ разворачивания jsonb в rowset.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 59 / 72
Задача #8: решение (1/3)
-- Оператор @>
# SELECT js->’name’ FROM company
WHERE
js @>
’{”competitions”:[{”competitor”:
{”name”: ”Wikia”}
}]}’ AND
js @>
’{”offices”:[{”country_code”: ”USA”}]}’;
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 60 / 72
Задача #8: решение (2/3)
-- jsquery
# SELECT js->’name’ FROM company
WHERE js @@
’competitions.#.competitor.name = ”Wikia” AND
offices.#.country_code = ”USA”’;
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 61 / 72
Задача #8: решение (3/3)
-- Разворачивание jsonb в rowset
SELECT js->’name’ FROM company
WHERE
EXISTS (
SELECT 1
FROM jsonb_array_elements(js->’competitions’) el
WHERE el -> ’competitor’ ->> ’name’ = ’Wikia’
) AND EXISTS (
SELECT 1
FROM jsonb_array_elements(js->’offices’) el
WHERE el ->> ’country_code’ = ’USA’
);
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 62 / 72
Задача #9
Найти компании, в которые финансовая
организация Trinity Ventures инвестировала
в 2007 или 2008 году.
Реализовать решение с помощью:
▶ оператора @>,
▶ jsquery,
▶ разворачивания jsonb в rowset.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 63 / 72
Задача #9: решение (1/3)
-- Оператор @>
# SELECT js->’name’ FROM company
WHERE
js @>
’{”funding_rounds”:[{”funded_year”: 2007,
”investments”:[{”financial_org”:
{”name”:”Trinity Ventures”}}
]}
]}’ OR
js @>
’{”funding_rounds”:[{”funded_year”: 2008,
”investments”:[{”financial_org”:
{”name”:”Trinity Ventures”}}
]}
]}’;
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 64 / 72
Задача #9: решение (2/3)
-- jsquery
# SELECT js->’name’ FROM company
WHERE
js @@
’funding_rounds.#(
funded_year IN (2007,2008) AND
investments.#.
financial_org.name = ”Trinity Ventures”)’;
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 65 / 72
Задача #9: решение (3/3)
-- Разворачивание jsonb в rowset
# SELECT js->’name’ FROM company
WHERE EXISTS (
SELECT 1
FROM jsonb_array_elements(js->’funding_rounds’) el
WHERE el -> ’funded_year’ IN (’2007’,’2008’) AND EXISTS
(SELECT 1
FROM jsonb_array_elements(el -> ’investments’) el2
WHERE el2 -> ’financial_org’ ->>
’name’ = ’Trinity Ventures’));
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 66 / 72
Задача #10
Найти компании, которые в 2008 году
привлекли более 100 000 000 USD
инвестиций.
Реализовать решение с помощью:
▶ jsquery,
▶ разворачивания jsonb в rowset.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 67 / 72
Задача #10: решение (1/2)
-- jsquery
# SELECT js -> ’name’
FROM company
WHERE js @@
’funding_rounds.# (funded_year = 2008 AND
raised_currency_code = ”USD” AND
raised_amount > 100000000)’;
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 68 / 72
Задача #10: решение (2/2)
-- Разворачивание jsonb в rowset
# SELECT js->’name’ FROM company
WHERE EXISTS (
SELECT 1
FROM jsonb_array_elements(js->’funding_rounds’) el
WHERE
el -> ’funded_year’ = ’2008’ AND
el ->> ’raised_currency_code’ = ’USD’ AND
el -> ’raised_amount’ > ’100000000’);
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 69 / 72
Задача #11
Найти компании, у которых суммарный
объём инвестиций был более 1 000 000 000
USD.
Реализовать решение с помощью
разворачивания jsonb в rowset.
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 70 / 72
Задача #11: решение
-- Разворачивание jsonb в rowset
SELECT js->’name’ FROM company
WHERE (
SELECT SUM((el ->> ’raised_amount’)::numeric)
FROM jsonb_array_elements(js->’funding_rounds’) el
WHERE
el ->> ’raised_currency_code’ = ’USD’) > 1000000000;
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 71 / 72
Спасибо за внимание!
Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 72 / 72

2015-12-05 Александр Коротков, Иван Панченко - Слабо-структурированные данные в СУБД глазами и руками разработчиков PostgreSQL

  • 1.
    Слабо-структурированные данные вСУБД PostgreSQL (мастер-класс) Александр Коротков, Иван Панченко Postgres Professional 2015 Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 1 / 72
  • 2.
    1. Немного омассивах Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 2 / 72
  • 3.
    Исходные данные: products #d product Table ”public.product” Column | Type | Modifiers ---------------------+-----------------+----------- product_id | character(10) | not null product_title | text | not null product_sales_rank | bigint | not null product_group | text | not null product_category | text | product_subcategory | text | similar_product_ids | character(10)[] | not null Indexes: Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 3 / 72
  • 4.
    Subscrip on –вытащить элемент по заданному индексу # SELECT product_id, similar_product_ids FROM product LIMIT 5; product_id | similar_product_ids ------------+---------------------------------------------------------- 0001047655 | {0061007129,0061007358,0061007137,0061099341,0061007161} 0001053736 | {0440905605,0345336062,0140380531,0393320979,0395898714} 0001053744 | {} 0001054600 | {} 0001474103 | {0895403889,0001473123,0766177610,0001472933,0766187403} (5 rows) # SELECT product_id, similar_product_ids[2] FROM product LIMIT 5; product_id | similar_product_ids ------------+--------------------- 0001047655 | 0061007358 0001053736 | 0345336062 0001053744 | NULL 0001054600 | NULL 0001474103 | 0001473123 (5 rows) Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 4 / 72
  • 5.
    Subscrip on –вытащить slice # SELECT product_id, similar_product_ids[1:2] FROM product LIMIT 5; product_id | similar_product_ids ------------+------------------------- 0001047655 | {0061007129,0061007358} 0001053736 | {0440905605,0345336062} 0001053744 | {} 0001054600 | {} 0001474103 | {0895403889,0001473123} (5 rows) Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 5 / 72
  • 6.
    Subscrip on –обновление # UPDATE product SET similar_product_ids[1] = ’0061007129’ WHERE product_id = ’0001047655’; # UPDATE product SET similar_product_ids[1:2] = ’{0440905605,0345336062}’ WHERE product_id = ’0001047655’; Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 6 / 72
  • 7.
    Осторожно – произвольныеграницы! # SELECT (’[-3:-2]={1,2}’::int[])[1]; int4 ------ NULL (1 row) # SELECT (’[-3:-2]={1,2}’::int[])[-2]; int4 ------ 2 (1 row) Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 7 / 72
  • 8.
    Осторожно – многомерныемассивы! # SELECT (’{{1,2},{3,4}}’::int[])[1]; int4 ------ NULL (1 row) # SELECT (’{{1,2},{3,4}}’::int[])[1][2]; int4 ------ 2 (1 row) Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 8 / 72
  • 9.
    array_lower и array_upper– верхняя и нижняя границы массива Откусываем у массива первый элемент. # SELECT product_id, similar_product_ids[ array_lower(similar_product_ids,1) + 1 : array_upper(similar_product_ids,1)] FROM product LIMIT 5; product_id | similar_product_ids ------------+----------------------------------------------- 0001047655 | {0061007358,0061007137,0061099341,0061007161} 0001053736 | {0345336062,0140380531,0393320979,0395898714} 0001053744 | NULL 0001054600 | NULL 0001474103 | {0001473123,0766177610,0001472933,0766187403} (5 rows) Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 9 / 72
  • 10.
    Поиск по массиву:ANY # SELECT product_id, product_title FROM product WHERE ’0061007137’ = ANY(similar_product_ids); product_id | product_title ------------+------------------------------------ 0001047655 | Prodigal Daughter 0061007129 | Kane & Abel 0061007145 | The Prodigal Daughter 0061007161 | First Among Equals 0061007358 | Not a Penny More, Not a Penny Less 0061013315 | The Eleventh Commandment 0061013706 | Shall We Tell the President? 0061092045 | Honor Among Thieves 0061093653 | Twelve Red Herrings 0061099341 | As the Crow Flies (10 rows) Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 10 / 72
  • 11.
    Разворачивание массива вrowset: unnest # SELECT unnest(similar_product_ids) FROM product WHERE product_id = ’0061007137’; unnest ------------ 0061007358 0061099341 0061007145 0061007161 0061007129 (5 rows) Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 11 / 72
  • 12.
    Сворачивание rowset вмассив: array_agg # SELECT array_agg(product_id) FROM product WHERE ’0061093653’ = ANY(similar_product_ids); array_agg ----------------------------------------------- {006018552X,0060185805,006100717X,0061032077} (1 row) Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 12 / 72
  • 13.
    Задача #1 Для каждогопродукта собрать массив тех, которые ссылаются на него через similar_product_ids. Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 13 / 72
  • 14.
    Задача #1: решение SELECT similar_id, array_agg(product_id) FROM( SELECT product_id, unnest(similar_product_ids) AS similar_id FROM product) s GROUP BY similar_id; Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 14 / 72
  • 15.
    Задача #2 Сделать выборкупродуктов, для которых все продукты, указанные в массиве similar_product_ids имеют рейтинг не меньше 100000. Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 15 / 72
  • 16.
    Задача #2: решение SELECT * FROM productp1 WHERE NOT EXISTS ( SELECT 1 FROM unnest(p1.similar_product_ids) sid JOIN product p2 ON p2.product_id = sid WHERE NOT (p2.product_sales_rank >= 100000)); Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 16 / 72
  • 17.
    Операторы &&, @>,<@ (1/2) ▶ a && b – у массивов a и b есть хотя бы один общий элемент. ▶ a @> b – в массиве a есть все элементы массива b и, быть может, другие. ▶ a <@ b – то же, что и b @> a. Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 17 / 72
  • 18.
    Операторы &&, @>,<@ (2/2) # SELECT count(*) FROM product WHERE similar_product_ids && ’{0061007137,0007149689}’; count ------- 11 (1 row) # SELECT count(*) FROM product WHERE similar_product_ids @> ’{0061007137,0061007161}’; count ------- 6 (1 row) Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 18 / 72
  • 19.
    Индексы по массивам(1/3) ▶ Встроенный GIN-индекс для массивов всех типов (операторы &&, @>, <@, =). ▶ GiST-индекс для int[] в contrib/intarray. ▶ Индексы по конкретным элементам массива (например, a[3]). Не очень интересно. Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 19 / 72
  • 20.
    Индексы по массивам(2/3) # EXPLAIN ANALYZE SELECT * FROM product WHERE similar_product_ids @> ’{0061007137,0061007161}’; QUERY PLAN --------------------------------------------------------------- Seq Scan on product (cost=0.00..9348.25 rows=1 width=161) (ac Filter: (similar_product_ids @> ’{0061007137,0061007161}’::b Rows Removed by Filter: 253614 Planning time: 0.120 ms Execution time: 97.547 ms Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 20 / 72
  • 21.
    Индексы по массивам(3/3) # CREATE INDEX product_similar_product_ids_idx ON product USING gin (similar_product_ids); CREATE INDEX Time: 18795,075 ms # EXPLAIN ANALYZE SELECT * FROM product WHERE similar_product_ids @> ’{0061007137,0061007161}’; --------------------------------------------------------------- Bitmap Heap Scan on product (cost=28.00..32.01 rows=1 width=1 Recheck Cond: (similar_product_ids @> ’{0061007137,006100716 Heap Blocks: exact=5 -> Bitmap Index Scan on product_similar_product_ids_idx (c Index Cond: (similar_product_ids @> ’{0061007137,00610 Planning time: 0.123 ms Execution time: 0.089 ms Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 21 / 72
  • 22.
    2. Hstore Александр Коротков,Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 22 / 72
  • 23.
    Исходные данные: stocks(1/2) Table ”public.stocks” Column | Type | Modifiers ----------+---------+------------------------------------------ id | integer | not null default nextval(’stocks_id_seq’: country | text | not null industry | text | not null sector | text | not null company | text | not null ticker | text | not null volume | integer | not null h | hstore | not null Indexes: ”stocks_pkey” PRIMARY KEY, btree (id) Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 23 / 72
  • 24.
    Исходные данные: stocks(2/2) # SELECT * FROM stocks LIMIT 1; -[ RECORD 1 ]-------------------------------------------------- id | 1 country | USA industry | Semiconductor Equipment & Materials sector | Technology company | Brooks Automation Inc. ticker | BRKS volume | 516739 h | ”Gap”=>”0.0151”, ”P/B”=>”1.05”, ”P/E”=>”6.07”, ”P/S” Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 24 / 72
  • 25.
    Вытаскивание полей (1/2) #SELECT id, h -> ’Price’, h -> ’Change’ FROM stocks LIMIT 10; id | ?column? | ?column? ----+-------------------+---------- 1 | 10.24 | 0.0291 2 | 26.94 | -0.023 3 | 92.56999999999999 | 0.0016 4 | 14.95 | 0.0047 5 | 26.65 | 0.0013 6 | 25.33 | -0.0261 7 | 62.7 | 0.0061 8 | 11.9 | -0.0017 9 | 24.44 | 0.0188 10 | 36.87 | 0.0104 (10 rows) Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 25 / 72
  • 26.
    Вытаскивание полей (2/2) #SELECT company FROM stocks WHERE h -> ’Price’ > 1000.0; ERROR: operator does not exist: text > numeric # SELECT company FROM stocks WHERE (h -> ’Price’)::numeric > 1000.0; company ---------------------------- Seaboard Corp. Berkshire Hathaway Inc. Google Inc. priceline.com Incorporated (4 rows) Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 26 / 72
  • 27.
    Превращаем hstore внабор пар ключ-значение # SELECT (each(h)).* FROM stocks WHERE id = 4; key | value -------------------------------+---------------------- Gap | 0 Price | 14.95 Change | 0.0047 50-Day Low | 0.0381 50-Day High | -0.0177 52-Week Low | 0.3304 Short Ratio | 0.14 ...................................................... Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 27 / 72
  • 28.
    Задача #3 Собрать статистикупо использованию различных ключей в поле h таблицы stocks: для каждого встречающегося ключа указать его частоту. Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 28 / 72
  • 29.
    Задача #3: решение #SELECT key, count(*)::float8 / (SELECT count(*) FROM stocks) freq FROM (SELECT (each(h)).* FROM stocks) kv GROUP BY key; key | freq -----------------------------------+------------------- Shares Float | 0.725725281231498 Analyst Recom | 0.616489046773239 Insider Transactions | 0.554026050917703 Institutional Transactions | 0.705743043220841 P/B | 0.71166370633511 ....................................................... Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 29 / 72
  • 30.
    Оператор @> a @>b тогда и только тогда, когда для каждой пары ключ => значение из b, такая же пара присутствует и в a. Примеры. key1 | key2 | result -----------------+--------------+-------- a=>x, b=>y, c=>z | a=>x, b=>y | true a=>x, b=>y | a=>x, b=>qqq | false a=>x, b=>y | t=>x, b=>y | false Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 30 / 72
  • 31.
    Операторы ?, ?&,?| ▶ a ? b – проверяет наличие ключа b в массиве a. ▶ a ?& b – проверяет наличие всех ключей из массива b в массиве a. ▶ a ?| b – проверяет наличие хотя бы одного из ключей массива b в массиве a. # SELECT count(*), count(*) FILTER (WHERE h ? ’Short Ratio’), count(*) FILTER (WHERE h ?| ’{Short Ratio, Change from Open}’), count(*) FILTER (WHERE h ?& ’{Short Ratio, Change from Open}’) FROM stocks; count | count | count | count -------+-------+-------+------- 6756 | 5412 | 6743 | 5412 (1 row) Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 31 / 72
  • 32.
    Задача #4 Написать аналогоператора @> с помощью функций each(hstore) и операторов ->, ?. Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 32 / 72
  • 33.
    Задача #4: решение CREATEFUNCTION contains_sql(hstore, hstore) RETURNS bool AS $$ SELECT NOT EXISTS ( SELECT 1 FROM each($2) kv WHERE NOT ( $1 ? key AND $1 -> key IS NOT DISTINCT FROM value) ) $$ LANGUAGE sql; Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 33 / 72
  • 34.
    Конструируем hstore # SELECThstore(’{key1, key2}’::text[], ’{value1, value2}’::text[]); hstore ------------------------------------ ”key1”=>”value1”, ”key2”=>”value2” (1 row) # SELECT hstore(array_agg(id::text), array_agg(company)) FROM stocks WHERE id <= 4; hstore ------------------------------------------------------ ”1”=>”Brooks Automation Inc.”, ”2”=>”Broadcom Corp.” (1 row) Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 34 / 72
  • 35.
    Задача #5 Посчитать длякаждой отрасли (sector) из таблицы stocks суммарный объём ценных бумаг (volume), и выдать это в виде hstore вида: отрасль => суммарный объём ценный бумаг. Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 35 / 72
  • 36.
    Задача #5: решение #SELECT hstore( array_agg(sector), array_agg(volume::text)) h FROM (SELECT sector, SUM(volume) AS volume FROM stocks GROUP BY sector) s; -[ RECORD 1 ]-------------------------------------------------------- h | ”Services”=>”637878158”, ”Financial”=>”1389743864”, | ”Utilities”=>”94268768”, ”Healthcare”=>”381247435”, | ”Technology”=>”1225484169”, ”Conglomerates”=>”3547704”, | ”Consumer Goods”=>”276207382”, | ”Basic Materials”=>”625043686”, | ”Industrial Goods”=>”200105263” Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 36 / 72
  • 37.
    Индексы для hstore ▶GIN/GiST индексы: индексируем ключи отдельно, значения отдельно (операторы @>, ?, ?&, ?|). ▶ GIN индекс: индексируем пары hash(key) + hash(value) (операторы @>, ?, ?&, ?|) https://github.com/postgrespro/hstore_ops ▶ Индекс по отдельному ключу (h->’key’). Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 37 / 72
  • 38.
    3. Jsonb Александр Коротков,Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 38 / 72
  • 39.
    Исходные данные: company #d company Table ”public.company” Column | Type | Modifiers --------+-------+----------- js | jsonb | # SELECT js FROM company limit 5; --------------------------------------------------------------- {”_id”: {”$oid”: ”52cdef7c4bab8bd675297d8a”}, ”name”: ”Wetpain {”_id”: {”$oid”: ”52cdef7c4bab8bd675297d8b”}, ”name”: ”AdventN {”_id”: {”$oid”: ”52cdef7c4bab8bd675297d8c”}, ”name”: ”Zoho”, {”_id”: {”$oid”: ”52cdef7c4bab8bd675297d8d”}, ”ipo”: null, ”na {”_id”: {”$oid”: ”52cdef7c4bab8bd675297d8e”}, ”ipo”: {”pub_day (5 rows) Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 39 / 72
  • 40.
    Jsonb pre yprint # SELECT jsonb_pretty(js) FROM company LIMIT 1; -[ RECORD 1 ]+------------------------------------------- jsonb_pretty | { | ”_id”: { | ”$oid”: ”52cdef7c4bab8bd675297d8a” | }, | ”name”: ”Wetpaint”, | ”image”: { | ”available_sizes”: [ | [ | [ | 150, | 75 ......................................................... Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 40 / 72
  • 41.
    Вытаскивание полей # SELECTjs -> ’name’, js -> ’acquisition’ -> ’price_amount’, js #> ’{acquisition,price_amount}’ FROM company LIMIT 5; ?column? | ?column? | ?column? -------------------------+-----------+----------- ”Wetpaint” | 30000000 | 30000000 ”AdventNet” | NULL | NULL ”Zoho” | NULL | NULL ”Digg” | 500000 | 500000 ”Facebook” | NULL | NULL (5 rows) Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 41 / 72
  • 42.
    Вытаскивание полей итипы (1/2) -> вытаскивает тип jsonb # SELECT js -> ’name’ FROM company WHERE js -> ’name’ ILIKE ’%paint%’; ERROR: operator does not exist: jsonb ~~* unknown Приведение значения поля к строке # SELECT (js -> ’name’)::text, js ->> ’name’ FROM company LIMIT 5; text | ?column? -------------+----------- ”Wetpaint” | Wetpaint ”AdventNet” | AdventNet ”Zoho” | Zoho ”Digg” | Digg ”Facebook” | Facebook (5 rows) Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 42 / 72
  • 43.
    Вытаскивание полей итипы (2/2) # SELECT js ->> ’name’, js ->> ’number_of_employees’ FROM company WHERE js ->> ’name’ ILIKE ’%paint%’ AND (js ->> ’number_of_employees’)::int >= 10; ?column? | ?column? ----------+---------- Wetpaint | 47 (1 row) Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 43 / 72
  • 44.
    Вытаскивание элементов массивов #SELECT js -> ’offices’ -> 0 ->> ’city’, js #>> ’{offices,0,city}’ FROM company LIMIT 5; ?column? | ?column? ---------------+--------------- Seattle | Seattle Pleasanton | Pleasanton Pleasanton | Pleasanton San Francisco | San Francisco Menlo Park | Menlo Park (5 rows) Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 44 / 72
  • 45.
    Индексы по выражениям #CREATE UNIQUE INDEX company_id_idx ON company ((js -> ’_id’ ->> ’$oid’)); CREATE INDEX # EXPLAIN ANALYZE SELECT js -> ’name’ FROM company WHERE js -> ’_id’ ->> ’$oid’ = ’52cdef7c4bab8bd675297d8a’; QUERY P --------------------------------------------------------------- Index Scan using company_id_idx on company (cost=0.29..8.31 r Index Cond: (((js -> ’_id’::text) ->> ’$oid’::text) = ’52cde Planning time: 0.084 ms Execution time: 0.156 ms (4 rows) Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 45 / 72
  • 46.
    Разворачивание JSON-массивов вrowset # SELECT jsonb_array_elements(js -> ’competitions’) FROM company WHERE js -> ’_id’ ->> ’$oid’ = ’52cdef7c4bab8bd675297d8a’; jsonb_array_elements --------------------------------------------------------------------- {”competitor”: {”name”: ”Wikia”, ”permalink”: ”wikia”}} {”competitor”: {”name”: ”JotSpot”, ”permalink”: ”jotspot”}} {”competitor”: {”name”: ”Socialtext”, ”permalink”: ”socialtext”}} {”competitor”: {”name”: ”Ning by Glam Media”, ”permalink”: ”ning”}} {”competitor”: {”name”: ”Soceeo”, ”permalink”: ”soceeo”}} {”competitor”: {”name”: ”Yola”, ”permalink”: ”yola”}} {”competitor”: {”name”: ”SocialGO”, ”permalink”: ”socialgo”}} {”competitor”: {”name”: ”IslamNor”, ”permalink”: ”islamnor”}} (8 rows) Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 46 / 72
  • 47.
    Разворачивание JSON-объекта в key-valuerowset # SELECT (jsonb_each(js #> ’{offices, 0}’)).* FROM company WHERE js -> ’_id’ ->> ’$oid’ = ’52cdef7c4bab8bd675297d8a’; key | value --------------+-------------------- city | ”Seattle” address1 | ”710 - 2nd Avenue” address2 | ”Suite 1100” latitude | 47.603122 zip_code | ”98104” longitude | -122.333253 state_code | ”WA” description | ”” country_code | ”USA” (9 rows) Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 47 / 72
  • 48.
    Оператор @> ▶ Дляскалярных a и b, a @> b тогда и только тогда, когда a = b. ▶ Если a и b объекты, то для каждого ключа в b должен существовать ключ в a, содержащий соответствующее значение. ▶ Если a и b массивы, то для каждого элемента в b должен существовать содержащий его элемент в a. Примеры. arg1 | arg2 | result ---------------------+----------------------+-------- 1 | 1 | true 1 | ”1” | false {”a”: 1, ”b”: 2} | {”a”: 1} | true {”a”: 1, ”b”: 2} | {”a”: 2} | false [1, 2] | [1] | true [{”a”: 1, ”b”: 2}] | [{”a”: 1}, {”b”: 2}] | true [{”a”: 1}, {”b”: 2}] | [{”a”: 1, ”b”: 2}] | false Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 48 / 72
  • 49.
    Установка plv8 $ gitclone https://github.com/plv8/plv8.git $ cd plv8 $ make USE_PGXS=1 $ sudo make USE_PGXS=1 install $ make USE_PGXS=1 installcheck $ psql DB -c ”CREATE EXTENSION plv8;” Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 49 / 72
  • 50.
    Задача #6 Написать наplv8 функцию, которая для компании выводит список стран, где у неё есть офисы. Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 50 / 72
  • 51.
    Задача #6: решение CREATEFUNCTION extract_countries(company jsonb) RETURNS text[] AS $$ var tmp = {}, result = []; for(var i = 0; i < company.offices.length; i++) tmp[company.offices[i].country_code] = true; for(var i in tmp) result.push(i) return result; $$ LANGUAGE plv8 IMMUTABLE STRICT; Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 51 / 72
  • 52.
    Индекс по странам CREATEINDEX company_countries_idx ON company USING gin(extract_countries(js)); # EXPLAIN ANALYZE SELECT * FROM company WHERE extract_countries(js) @> ’{RUS}’; QUER --------------------------------------------------------------- Bitmap Heap Scan on company (cost=8.73..348.48 rows=94 width= (actual time=0.038..0.089 rows=50 loops=1) Recheck Cond: (extract_countries(js) @> ’{RUS}’::text[]) Heap Blocks: exact=48 -> Bitmap Index Scan on company_countries_idx (cost=0.00.. Index Cond: (extract_countries(js) @> ’{RUS}’::text[]) Planning time: 0.057 ms Execution time: 0.113 ms (7 rows) Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 52 / 72
  • 53.
    Задача #7 Написать наplv8 агрегат, который который посчитает частоты различных размеров картинок. Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 53 / 72
  • 54.
    Задача #7: решение(1/2) CREATE FUNCTION image_size_agg_sfunc(state jsonb, company jsonb) RETURNS jsonb AS $$ if (!company.image) return state; var sizes = company.image.available_sizes for (var i = 0; i < sizes.length; i++) { var sizeKey = sizes[i][0][0] + ’x’ + sizes[i][0][1]; if (sizeKey in state) state[sizeKey]++; else state[sizeKey] = 1; } return state; $$ LANGUAGE plv8 IMMUTABLE STRICT; Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 54 / 72
  • 55.
    Задача #7: решение(2/2) CREATE AGGREGATE image_size_agg (jsonb) ( SFUNC = image_size_agg_sfunc, STYPE = jsonb, INITCOND = ’{}’); # SELECT image_size_agg(js) FROM company; -[ RECORD 1 ]--+----------------------------------------------- image_size_agg | {”1x1”: 15, ”32x8”: 3, ”150x7”: 1, ”150x8”: 5, Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 55 / 72
  • 56.
    Установка jsquery $ gitclone https://github.com/postgrespro/jsquery.git $ cd jsquery $ make USE_PGXS=1 $ sudo make USE_PGXS=1 install $ make USE_PGXS=1 installcheck $ psql DB -c ”CREATE EXTENSION jsquery;” Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 56 / 72
  • 57.
    Индексы для jsonb ▶GIN jsonb_ops: индексируем ключи отдельно, значения отдельно (операторы @>, ?, ?&, ?|). ▶ GIN jsonb_path_ops: индексируем hash(path + value) (операторы @>). ▶ jsonb_path_value_ops: индексируем hash(path) + hash(value) (операторы @>, jsonb @@ jsquery). ▶ jsonb_value_path_ops: индексируем hash(value) + bloom(path) (операторы @>, jsonb @@ jsquery). ▶ Индексы по отдельным ключам (js -> ’key’). Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 57 / 72
  • 58.
    Пример: найти команиис офисами в России -- Оператор @> # SELECT js->’name’ FROM company WHERE js @> ’{”offices”:[{”country_code”: ”RUS”}]}’; -- Оператор jsquery # SELECT js->’name’ FROM company WHERE js @@ ’offices.#.country_code = ”RUS”’; -- Разворачивание jsonb в rowset # SELECT js->’name’ FROM company WHERE EXISTS ( SELECT 1 FROM jsonb_array_elements(js->’offices’) el WHERE el ->> ’country_code’ = ’RUS’); Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 58 / 72
  • 59.
    Задача #8 Найти компании,конкуренты Wikia, у которых есть офис в США. Реализовать решение с помощью: ▶ оператора @>, ▶ jsquery, ▶ разворачивания jsonb в rowset. Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 59 / 72
  • 60.
    Задача #8: решение(1/3) -- Оператор @> # SELECT js->’name’ FROM company WHERE js @> ’{”competitions”:[{”competitor”: {”name”: ”Wikia”} }]}’ AND js @> ’{”offices”:[{”country_code”: ”USA”}]}’; Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 60 / 72
  • 61.
    Задача #8: решение(2/3) -- jsquery # SELECT js->’name’ FROM company WHERE js @@ ’competitions.#.competitor.name = ”Wikia” AND offices.#.country_code = ”USA”’; Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 61 / 72
  • 62.
    Задача #8: решение(3/3) -- Разворачивание jsonb в rowset SELECT js->’name’ FROM company WHERE EXISTS ( SELECT 1 FROM jsonb_array_elements(js->’competitions’) el WHERE el -> ’competitor’ ->> ’name’ = ’Wikia’ ) AND EXISTS ( SELECT 1 FROM jsonb_array_elements(js->’offices’) el WHERE el ->> ’country_code’ = ’USA’ ); Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 62 / 72
  • 63.
    Задача #9 Найти компании,в которые финансовая организация Trinity Ventures инвестировала в 2007 или 2008 году. Реализовать решение с помощью: ▶ оператора @>, ▶ jsquery, ▶ разворачивания jsonb в rowset. Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 63 / 72
  • 64.
    Задача #9: решение(1/3) -- Оператор @> # SELECT js->’name’ FROM company WHERE js @> ’{”funding_rounds”:[{”funded_year”: 2007, ”investments”:[{”financial_org”: {”name”:”Trinity Ventures”}} ]} ]}’ OR js @> ’{”funding_rounds”:[{”funded_year”: 2008, ”investments”:[{”financial_org”: {”name”:”Trinity Ventures”}} ]} ]}’; Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 64 / 72
  • 65.
    Задача #9: решение(2/3) -- jsquery # SELECT js->’name’ FROM company WHERE js @@ ’funding_rounds.#( funded_year IN (2007,2008) AND investments.#. financial_org.name = ”Trinity Ventures”)’; Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 65 / 72
  • 66.
    Задача #9: решение(3/3) -- Разворачивание jsonb в rowset # SELECT js->’name’ FROM company WHERE EXISTS ( SELECT 1 FROM jsonb_array_elements(js->’funding_rounds’) el WHERE el -> ’funded_year’ IN (’2007’,’2008’) AND EXISTS (SELECT 1 FROM jsonb_array_elements(el -> ’investments’) el2 WHERE el2 -> ’financial_org’ ->> ’name’ = ’Trinity Ventures’)); Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 66 / 72
  • 67.
    Задача #10 Найти компании,которые в 2008 году привлекли более 100 000 000 USD инвестиций. Реализовать решение с помощью: ▶ jsquery, ▶ разворачивания jsonb в rowset. Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 67 / 72
  • 68.
    Задача #10: решение(1/2) -- jsquery # SELECT js -> ’name’ FROM company WHERE js @@ ’funding_rounds.# (funded_year = 2008 AND raised_currency_code = ”USD” AND raised_amount > 100000000)’; Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 68 / 72
  • 69.
    Задача #10: решение(2/2) -- Разворачивание jsonb в rowset # SELECT js->’name’ FROM company WHERE EXISTS ( SELECT 1 FROM jsonb_array_elements(js->’funding_rounds’) el WHERE el -> ’funded_year’ = ’2008’ AND el ->> ’raised_currency_code’ = ’USD’ AND el -> ’raised_amount’ > ’100000000’); Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 69 / 72
  • 70.
    Задача #11 Найти компании,у которых суммарный объём инвестиций был более 1 000 000 000 USD. Реализовать решение с помощью разворачивания jsonb в rowset. Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 70 / 72
  • 71.
    Задача #11: решение --Разворачивание jsonb в rowset SELECT js->’name’ FROM company WHERE ( SELECT SUM((el ->> ’raised_amount’)::numeric) FROM jsonb_array_elements(js->’funding_rounds’) el WHERE el ->> ’raised_currency_code’ = ’USD’) > 1000000000; Александр Коротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 71 / 72
  • 72.
    Спасибо за внимание! АлександрКоротков, Иван Панченко Слабо-структурированные данные в СУБД PostgreSQL (мастер-класс) 72 / 72