Получить указанные c значения из столбца jsonb - PullRequest
2 голосов
/ 02 февраля 2020

У меня есть таблица Customer

  • customerId (int)
  • customerName (строка)
  • customerOrders (jsonB)

customerOrders имеет следующую структуру:

{ 
 "nodeValue":[
   { 
      "key": "key1",
      "value": "value1"
   },
   { 
      "key": "key2",
      "value": "value2"
   },
   { 
      "key": "key3",
      "value": "value3"
   },
   { 
      "key": "key4",
      "value": "value4"
   },
   { 
      "key": "key5",
      "value": "value5"
   }
 ]
}

Я пытаюсь получить значение nodeValue строк key = 'key3' AND key = 'key4'.

Например: вернуть значение 'key3' и 'key4', WHERE key = 'key3' AND key = 'key4'.

Я пытаюсь сделать что-то вроде:

SELECT value, value 
from public.customers 
where nodeValue.key3 = 'key3' 
  AND nodeValue.key4 = 'key4'

Ответы [ 4 ]

2 голосов
/ 26 марта 2020

получить значение nodeValue строк, где key = 'key3' AND key = 'key4'.

Лучше всего использовать jsonb оператор "содержит" @>:

SELECT customerid, customerOrders->'nodeValue'
FROM   customer
WHERE  customerOrders->'nodeValue' @> '[{"key": "key3"}]'
AND    customerOrders->'nodeValue' @> '[{"key": "key4"}]';

Чтобы сделать это быстрый для больших таблиц, поддерживают его с индексом. В идеале индекс jsonb_path_ops:

CREATE INDEX customer_nodeValue_idx ON customer
USING gin ((customerOrders->'nodeValue') jsonb_path_ops);  -- parentheses required

См .:

Более поздняя версия вашего вопроса немного сложнее:

возвращает значение для 'key3' и 'key4'

SELECT c.customerid, o.values
FROM   customer c
CROSS  JOIN LATERAL (
   SELECT ARRAY(
      SELECT o.ord->>'value'
      FROM   jsonb_array_elements(c.customerOrders->'nodeValue') o(ord)
      WHERE (o.ord->>'key' = 'key3' OR
             o.ord->>'key' = 'key4')
      )
   ) o(values)
WHERE  c.customerOrders->'nodeValue' @> '[{"key": "key3"}]'
AND    c.customerOrders->'nodeValue' @> '[{"key": "key4"}]';

Первый фильтр квалифицирует строки, как указано выше (с быстрым поиском по индексу). Затем откройте документ JSON и постройте ответ в подзапросе LATERAL. Должно быть самым простым, чистым и быстрым. О технике см .:

Я добавил customerid к результат обоих запросов для идентификации строк. Это необязательно.

db <> скрипка здесь (демонстрирует оба)

Дизайн

Вы можете упростить свою раздутую JSON макет примерно такой:

'{
    "key1": "value1",
    "key2": "value2",
    "key3": "value3",
    "key4": "value4",
    "key5": "value5"
}'

Или хотя бы:

'[
    {
        "key1": "value1"
    },
    {
        "key2": "value2"
    },
    {
        "key3": "value3"
    },
    {
        "key4": "value4"
    },
    {
        "key5": "value5"
    }
]'

Сделал бы все значительно проще, меньше и быстрее.

2 голосов
/ 02 февраля 2020
with orders as 
(
   select jsonb_array_elements(customerOrders->'nodeValue') as orders
   from customers
) 
select orders->'value' as val 
from orders 
where 
    orders->>'key' = 'key3' 
or  orders->>'key' = 'key4';

результат:

значение1

значение2

Обратите внимание на использование оператора - >> для получения значения в виде текста

дб-скрипка

1 голос
/ 24 марта 2020

Сначала вам нужно создать тип (или таблицу)

CREATE TYPE key_value_type AS (
    key text,
    value text
);

Затем вы можете использовать jsonb_populate_recordset function

SELECT
    r_key1.value AS key1,
    r_key2.value AS key2
FROM
    customers c
    JOIN LATERAL jsonb_populate_recordset(NULL::key_value_type, customerOrders -> 'nodeValue') r_key1 
      ON r_key1.key = 'key1'
    JOIN LATERAL jsonb_populate_recordset(NULL::key_value_type, customerOrders -> 'nodeValue') r_key2 
      ON r_key2.key = 'key2'

В качестве альтернативы с функцией jsonb_to_recordset (без создания типа)

SELECT
    r_key1.value AS key1,
    r_key2.value AS key2
FROM
    customers c
    JOIN LATERAL jsonb_to_recordset(customerOrders -> 'nodeValue') AS r_key1 (key text,
        value text) ON r_key1.key = 'key1'
    JOIN LATERAL jsonb_to_recordset(customerOrders -> 'nodeValue') AS r_key2 (key text,
        value text) ON r_key2.key = 'key2'

Вот результат для обеих альтернатив;

| key1   | key2   |
| ------ | ------ |
| value1 | value2 |
1 голос
/ 02 февраля 2020

Я подозреваю, что вы действительно хотите

SELECT
  (SELECT orders->>'value'
    FROM json_array_elements(customerOrders->'nodeValue') AS orders
    WHERE orders->>'key' = 'key3'
  ) AS value_key3,
  (SELECT orders->>'value'
    FROM json_array_elements(customerOrders->'nodeValue') AS orders
    WHERE orders->>'key' = 'key4'
  ) AS value_key4
FROM public.customers;

, но на самом деле ваша структура данных не подходит для этой цели. Вместо массива с парами ключ-значение используйте объект с ключами и значениями в качестве свойств. При этом вы можете легко получить результат с помощью

SELECT
  customerOrders->'nodeValue'->>'key3' AS value_key3,
  customerOrders->'nodeValue'->>'key4' AS value_key4
FROM public.customers;
...