PostgreSQL - Как использовать jsonb_array_length в предложении where

У нас есть таблица PostgreSQL со столбцом jsonb. Некоторые из узлов в json могут быть представлены как массив или объект на входе. Я пытаюсь написать запрос, который даст мне длину массива, если узел представляет собой массив, а размер массива больше 1

select
    count(*) as policycount, policynumber 
from
    policymaster
where 
        jsonb_typeof((payload-> 'node1'::text) -> 'node2'::text) = 'array'
-- and     jsonb_array_length((payload-> 'node1'::text) -> 'node2'::text) > 1
group by policynumber 
order by 1 desc     

Если я попытаюсь добавить

and jsonb_array_length((payload-> 'node1' -> 'node2') > 1

тогда я получаю

SQL Error [42601]: ERROR: syntax error at or near "group"
Position: 310

Если я попытаюсь

 and     jsonb_array_length((payload-> 'node1'::text) -> 'node2'::text) > 1

я получил

SQL Error [22023]: ERROR: cannot get array length of a non-array

Поскольку это смесь объекта и массива, проверка массива в

where 
    jsonb_typeof((payload-> 'node1'::text) -> 'node2'::text) = 'array'

тоже вроде не помогает

Как я могу получить только те записи, где node2 является массивом, а размер этого массива больше 1?

Подвопрос

Когда я выполнил запрос @jjanes как есть

select
    count(*) as policycount, policynumber
from
    policymaster
where
        case when jsonb_typeof((payload-> 'node1'::text) -> 'node2'::text) = 'array'
        then   jsonb_array_length((payload-> 'node1'::text) -> 'node2'::text) > 1 
        else false end
group by policynumber
order by 1 desc     ;

Результаты были пустыми.

Когда я изменил входные параметры для обоих вызовов функций ниже, это дало ожидаемые результаты.

select
    count(*) as policycount, policynumber
from
    policymaster
where
        case when jsonb_typeof(payload-> 'node1' -> 'node2') = 'array'
        then   jsonb_array_length(payload-> 'node1' -> 'node2') > 1 
        else false end
group by policynumber
order by 1 desc     ;

Я только начал работать над PostgreSQL, поэтому не совсем понимаю функции json/jsonb.

Из того, что я понял, часть ::text любого объекта jsonb преобразует его из jsonb в текст, но не уверен, как именно ведет себя эта часть.

(payload-> 'node1'::text) -> 'node2'::text) 

Можете ли вы объяснить эту часть. Может быть, тогда это поможет мне понять, почему запрос с ::text для обоих этих узлов не работает в операторе case, но работает при использовании по отдельности по-разному.

Спасибо

«Из того, что я понял, ::текстовая часть любого объекта jsonb» - у вас нет приведения ::text к значениям jsonb, они есть у литералов имени ключа. payload-> 'node1'::text то же, что и payload-> ('node1'::text) - оба ненужны, просто напишите payload-> 'node1'.

Bergi 01.11.2022 19:23

Два запроса, которые вы даете в своем обновлении, дают мне идентичные результаты. (как надо). Вы не переводите jsonb в текст, вы переводите строку «node1» в текст. В этом нет необходимости, но и проблем быть не должно. (Некоторые клиенты при передаче связанного параметра хотят заранее знать тип этого параметра, поэтому в таких случаях может потребоваться приведение).

jjanes 01.11.2022 23:47
Структурированный массив Numpy
Структурированный массив Numpy
Однако в реальных проектах я чаще всего имею дело со списками, состоящими из нескольких типов данных. Как мы можем использовать массивы numpy, чтобы...
T - 1Bits: Генерация последовательного массива
T - 1Bits: Генерация последовательного массива
По мере того, как мы пишем все больше кода, мы привыкаем к определенным способам действий. То тут, то там мы находим код, который заставляет нас...
Что такое деструктуризация массива в JavaScript?
Что такое деструктуризация массива в JavaScript?
Деструктуризация позволяет распаковывать значения из массивов и добавлять их в отдельные переменные.
1
2
66
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Наверное, это может помочь

with temp_cte as (
 select * from policymaster 
 where  jsonb_typeof(payload-> 'node1' -> 'node2')='array'
)

select
    policynumber, count(*) as policycount 
from
    temp_cte
where 
   jsonb_array_length(payload-> 'node1' -> 'node2') > 1
group by policynumber 
order by 1 desc  

Вы не должны приводить массив как текст внутри функции jsonb_array_length.

Спасибо. Пробовал вышеописанное, но все равно получаю ту же ошибку SQL Error [22023]: ERROR: cannot get array length of a non-array. Любое другое предложение?

adbdkb 01.11.2022 11:29

Странный. Я фильтрую эти записи в файле temp_cte. Итак, во втором запросе у нас есть только записи, где node2 — это массив.

Mitko Keckaroski 01.11.2022 12:35

Да, я тоже так думал. Но когда я его выполняю, я получаю эту ошибку

adbdkb 01.11.2022 12:48
Ответ принят как подходящий

Синтаксическая ошибка возникает только потому, что ваши скобки не сбалансированы. Если вы исправите это, вы получите другую ошибку, которую вы получали.

Нет никакой гарантии, что простое использование AND приведет к короткому замыканию так, как вы хотите. Вы можете использовать CASE, чтобы заставить 2-й не выполняться, если 1-й не даст желаемого результата, как описано в документах:

select
    count(*) as policycount, policynumber
from
    policymaster
where
        case when jsonb_typeof((payload-> 'node1'::text) -> 'node2'::text) = 'array'
        then   jsonb_array_length((payload-> 'node1'::text) -> 'node2'::text) > 1 
        else false end
group by policynumber
order by 1 desc     ;

Причина, по которой CTE не работает в последних версиях, заключается в том, что планировщик просто включает CTE в остальную часть запроса и таким образом выдает тот же план. Вы можете предотвратить это с помощью with temp_cte as MATERIALIZED (

Спасибо. Он работает с некоторой модификацией и есть вопрос по этой части. Я обновил фактический вопрос с помощью подвопроса вместо того, чтобы пытаться уместить информацию в комментариях.

adbdkb 01.11.2022 17:18

Другие вопросы по теме