Массив все NULL в PostgreSQL - PullRequest
       21

Массив все NULL в PostgreSQL

5 голосов
/ 28 июля 2011

Есть ли выражение, которое возвращает TRUE, если все элементы массива PostgreSQL равны NULL?

Если бы это было значение, отличное от NULL, я, конечно, мог бы использовать что-то вроде:

SELECT 4 = ALL (ARRAY[4,5]::integer[]);

Однако я хочу выполнить операцию ALL с помощью теста IS NULL, а не теста = 4. Я не думаю, что есть синтаксис ALL для этого, и семантика вокруг NULL составлена ​​из массивов, которые я сам не мог придумать для формы, которая достигает этого. Отсюда мой вопрос переполнения стека. ; -)

Я знаю, что мог бы написать функцию в pl / sql или pl / pgsql, которая делает это, но я хотел бы посмотреть, есть ли прямое выражение, прежде чем прибегать к этому.

Ответы [ 5 ]

8 голосов
/ 07 марта 2014
<b>1 = ALL(arr) IS NULL AND 2 = ALL(arr) IS NULL</b>

1 и 2 могут быть любым двумя различными числами.

Альтернативы и производительность

Существует несколько способов.Я собрал тестовый пример, чтобы увидеть, какой из них самый быстрый:

SELECT arr
     , 1 = ALL(arr) IS NULL AND 2 = ALL(arr) IS NULL      AS chk_simpl
     , TRUE = ALL (SELECT unnest(arr) IS NULL)            AS chk_michael
     , (SELECT bool_and(e IS NULL) FROM unnest(arr) e)    AS chk_bool_and
     , NOT EXISTS (SELECT unnest(arr) EXCEPT SELECT null) AS chk_exist
FROM  (
   VALUES
     ('{[1,2,NULL,3}'::int[])
    ,('{1,1,1}')
    ,('{2,2,2}')
    ,('{NULL,NULL,NULL]}'::int[])
   ) t1(arr);

Второй ответ из , принятого в настоящее время ответа @ michael .Столбцы расположены в порядке выполнения выражения.Самый быстрый первый.Мое предложенное выражение на самом деле во много раз быстрее , чем остальные.Отсюда мой ответ.

SQL Fiddle с демонстрацией и тестом производительности.

Как это работает?

Выражение 1 = ALL(arr) дает

TRUE .. если все элементы 1
FALSE .. если любой элемент <> 1 (любой элемент IS NOT NULL)
NULL.. если хотя бы один элемент IS NULL и ни один элемент не является <> 1

Итак, если мы знаем один элемент, который не может отобразиться, как -1, мы можем упроститьдо:

-1 = ALL(arr) IS NULL

Если может отображаться любое число , проверьте наличие двух разных номеров.Результат может быть NULL для обоих, если массив не содержит ничего, кроме NULL.Вуаля.

8 голосов
/ 28 июля 2011

Я думаю, что получил кратчайший ответ, сохраняя при этом конструкцию 4 = ALL (ARRAY[4,5]::integer[]);:

Живой тест: https://www.db -fiddle.com / f / 6DuB1N4FdcvZdxKiHczu5y / 1

select
y, true = ALL (select unnest(z) is null)
from x
2 голосов
/ 28 июля 2011

Еще один подход, чтобы сделать код короче, использовать КАЖДУЮ агрегатную функцию

create table x
(
y serial,
z int[]
);

insert into x(z) values(array[null,null,null]::int[])
insert into x(z) values(array[null,7,null]::int[])
insert into x(z) values(array[null,3,4]::int[])
insert into x(z) values(array[null,null,null,null]::int[])


with a as
(
    select y, unnest(z) as b
    from x
)
select y, every(b is null)
from a 
group by y
order by y

Выход:

 y | every
---+-------
 1 | t
 2 | f
 3 | f
 4 | t
(4 rows)

Другой подход, генерирующий значения NULL для сравнения:

select  y, 
    z = 
    (select array_agg(null::int) 
     from generate_series(1, array_upper(z, 1) )) as IsAllNulls
from    x

В основе логики кода выше, это возвращает истину:

SELECT ARRAY[NULL,NULL]::int[] = ARRAY[NULL,NULL]::int[]

Другой подход, используйте array_fill

select  y, z = array_fill(null::int, array[ array_upper(z, 1) ] )
from    x

Caveat, конструкция массива и array_fill не являются симметричными, проверьте их:

select array[5]

-- array[5] here has different meaning from array[5] above
select array_fill(null::int, array[5]) 
2 голосов
/ 28 июля 2011

Я не совсем этим горжусь, но:

=> select not exists (
    select 1
    from (select all unnest(ARRAY[NULL, NULL, NULL]) is null as x) as dt
    where x = 'f'
);
 ?column? 
----------
 t
(1 row)

=> select not exists (
    select 1
    from (select all unnest(ARRAY[NULL, 11, NULL]) is null as x) as dt
    where x = 'f'
);
 ?column? 
----------
 f
(1 row)

Да, существует множество подзапросов, но, возможно, вы можете заставить его работать или упростить его до того, что будет работать.

1 голос
/ 02 апреля 2015

Просто ради разнообразия опций я использовал для этого:

select array_remove(ARRAY[null::int, null, null], null) = '{}'

Этот метод также возвращает true, если в массиве вообще нет значений, что полезно при сохранении нулевого значения вместо пустого массива или массива всех нулей, например, при запуске при обновлении:

NEW.arrvalue := CASE WHEN array_remove(NEW.arrvalue, null) <> '{}' THEN NEW.arrvalue END;
...