Как использовать ARRAY содержит оператор с ЛЮБОЙ - PullRequest
0 голосов
/ 04 декабря 2018

У меня есть таблица, в которой один столбец является массивом:

CREATE TABLE inherited_tags (
   id serial,
   tags text[]
);

Примеры значений:

INSERT INTO inherited_tags (tags) VALUES 
    (ARRAY['A','B','C']),  -- id: 1
    (ARRAY['D','E']),      -- id: 2
    (ARRAY['A','B']),      -- id: 3
    (ARRAY['C','D']),      -- id: 4
    (ARRAY['D','F']),      -- id: 5
    (ARRAY['A']);          -- id: 6

Я хочу найти строки, в которых теги столбец содержит некоторыеподмножество слов внутри массива.Например, для ввода:

ARRAY[ARRAY['A','C'], ARRAY['F'], ARRAY['E']]::text[][]

Я хочу найти все строки, которые содержат ('A' и 'C') ИЛИ ('F') ИЛИ ('E').Так, например, выше, я должен получить строки с идентификаторами: 1, 2, 5.

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

SELECT * FROM inherited_tags WHERE
   tags @> ANY(ARRAY[ARRAY['A','C'], ARRAY['F'], ARRAY['E']]::text[][])

, но я получаю ошибку:

ERROR:  operator does not exist: text[] @> text
LINE 1: SELECT * FROM inherited_tags where tags <@ ANY(ARRAY[ARRAY['...

Postgres 9.6

Решение plpgsql приемлемо, но предпочтительнее SQL.

DB-FIDDLE: https://www.db -fiddle.com / f / cKCr7Sfab6u8rqaCHhJvPk / 0

Ответы [ 3 ]

0 голосов
/ 04 декабря 2018

Проблема заключается в том, что типы данных text[] и text[][] внутренне являются одинаковыми.Массив имеет базовый тип и размеры, и оператор ANY всегда извлекает базовый тип для сравнения, который всегда будет text, а не text[].Не помогает то, что многомерные массивы требуют, чтобы каждый подэлемент имел ту же длину, что и каждый другой.Вы можете иметь ARRAY[ARRAY['A','C'],ARRAY['B','N']], но не ARRAY[ARRAY[2,3],ARRAY[1]].

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

CREATE OR REPLACE FUNCTION check_tag_matches(
    IN leftside text[],
    IN rightside text)
  RETURNS BOOLEAN AS
$BODY$
 DECLARE rightarr text[];
BEGIN
  SELECT CAST(rightside as text[]) INTO rightarr;
  RETURN SELECT leftside @> rightarr;
END;
$BODY$
LANGUAGE plpgsql STABLE;

CREATE OPERATOR public.>>(
  PROCEDURE = check_tag_matches,
  LEFTARG = text[],
  RIGHTARG = text,
  COMMUTATOR = >>);

Затем при тестировании:

test=# SELECT * FROM inherited_tags WHERE
   tags >> ANY(ARRAY[ARRAY['A','M'], ARRAY['F','E'], ARRAY['E','R']]::text[][]);
ERROR:  malformed array literal: "A"
DETAIL:  Array value must start with "{" or dimension information.
CONTEXT:  SQL statement "SELECT CAST(rightside as text[])"
PL/pgSQL function check_tag_matches(text[],text) line 4 at SQL statement

Кажется, что при попытке использовать многомерный массив, такой как ARRAY[ARRAY['A','M'], ARRAY['F','E'], ARRAY['E','R']]::text[][] в ANY(), он не повторяетсяболее ARRAY['A','M'], затем ARRAY['F','E'], затем ARRAY['E','R'], но более 'A','M','F','E','E','R'.То же самое происходит, когда с unnest.

test=# SELECT unnest(ARRAY[ARRAY['A','M'], ARRAY['F','E'], ARRAY['E','R']]::text[][]);
 unnest 
--------
 A
 M
 F
 E
 E
 R
(6 rows)

Остальные опции должны определить функцию, которая будет читать array_length(rightside,1) и array_length(rightside,2), и использовать вложенные циклы для проверки всего этого, или вы можете отправитьнесколько запросов, чтобы получить унаследованные теги для каждого тега или каким-либо образом реструктурировать ваши данные.И вы даже не можете получить доступ к элементу ARRAY['A','M'], используя rightside[1] для его итерации, вы вынуждены перейти на самый глубокий уровень.

0 голосов
/ 04 декабря 2018

вы можете попробовать

 SELECT * FROM table WHERE 
    tags @> ARRAY['A','C']::varchar[]
       OR
    tags @> ARRAY['E']::varchar[]
       OR
    tags @> ARRAY['F']::varchar[]
0 голосов
/ 04 декабря 2018

Я не думаю, что вы можете сделать это с одним условием из-за требования "содержит A и C".

SELECT * 
FROM inherited_tags 
WHERE tags @> ARRAY['A','C']
   OR tags && array['F', 'E'];

tags @> ARRAY['A','C'] выбирает те, где tags содержит все элементов из ARRAY['A','C'], а tags && array['F', 'E'] выбирает те строки, которые содержат хотя бы один из тегов из array['F', 'E']

Обновленная база данных Fiddle: https://www.db -fiddle.com/f/rXsjqEN3ry67uxJtEs3GM9/0

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