如何使用ARRAY包含ANY运算符

时间:2018-12-04 09:00:04

标签: sql arrays postgresql postgresql-9.6

我有一张表,其中一列是数组:

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

我想找到带有 tags 列的行,这些行包含一些数组内的单词子集。例如,输入:

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

我想找到所有包含('A'和'C')OR('F')OR('E')的行。因此,例如在上面的示例中,我应该获得ID为: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字段:https://www.db-fiddle.com/f/cKCr7Sfab6u8rqaCHhJvPk/0

3 个答案:

答案 0 :(得分:1)

由于“包含A C”的要求,我认为您无法在单个条件下做到这一点。

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

tags @> ARRAY['A','C']tags中选择ARRAY['A','C']包含 all 元素的那些元素,而tags && array['F', 'E']从其中选择至少包含其中一个标签的那些行array['F', 'E']

更新的数据库小提琴:https://www.db-fiddle.com/f/rXsjqEN3ry67uxJtEs3GM9/0

答案 1 :(得分:1)

问题来自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]元素,因此必须对其进行最深入的研究。

答案 2 :(得分:1)

你可以尝试

Select 
    visitorid, 
    VisitId, 
    visittime, 
    MAX(a.hitnumber) action_hit, 
    b.Pagetitle, 
    b.EventCategory
from 
    table a
left join (
   Select
      visitorid, 
      visitId, 
      visittime,
      hitnumber, 
      Pagetitle, 
      EventCategory
   FROM 
      table ) b ON a.visitorid = b.visitorid AND a.visitId = b.visitId 
   AND a.visittime = b.visittime
GROUP BY 1,2,3,5,6
HAVING MAX(a.hitnumber) = b.hitnumber