SELECT rows HAVING 结果等于来自 DISTINCT

问题描述

我想返回与所有 key 都存在的 parts 匹配的结果。鉴于:

create table things (
  id int not null,key int not null,part character varying(1),details character varying(64),CONSTRAINT things_pkey PRIMARY KEY (id)
  );

还有这个:

id 部分 详情
1 1 1a 详情
2 1 b 1b 详情
3 1 c 1c 详情
4 2 2a 详情
5 2 b 2b 详情
6 2 c 2c 详情
7 3 3a 细节
8 3 c 3c 详情
9 4 b 4b 详情
10 5 b 5b 详情
11 6 b 6b 详情
12 6 c 6c 详情
13 7 7a 详情
14 8 8a 详情

我可以做到这一点:

id 部分 详情
1 1 1a 详情
2 1 b 1b 详情
3 1 c 1c 详情
4 2 2a 详情
5 2 b 2b 详情
6 2 c 2c 详情

使用此查询

select * 
from things t
where t.key in (
  select x.key
  from things x
  group by x.key
  having count(distinct part) = 3
);

但我真的想匹配不同的部分,而不仅仅是它的数量,例如having distinct part = ['a','b','c']。我可以在查询中执行此操作还是仅在应用程序代码中执行此操作?

http://sqlfiddle.com/#!17/38b399/6

编辑

本质上,我所追求的是一大块行,其中存在 part 的所有 thing。一件事有八个部分。它们将被处理并删除该表中的记录。永远重复。

这是来自 pgAdmin 的 CREATE 脚本(降低了噪音):

CREATE TABLE public.things (
    id uuid PRIMARY KEY,key character varying(255) COLLATE pg_catalog."default" NOT NULL,part character varying(3) COLLATE pg_catalog."default" NOT NULL,details character varying(1024) COLLATE pg_catalog."default",timezone character varying(128) COLLATE pg_catalog."default",client_id uuid,CONSTRAINT things_client_id_fkey FOREIGN KEY (client_id)
        REFERENCES public.clients (id)

);

CREATE INDEX things_client_id_index ON public.things (client_id);
CREATE UNIQUE INDEX unique_things ON public.things (key,part,client_id);

解决方法

基本上这可以被转换为 的情况。

检查每个键的不同部分计数的查询必须处理表的所有行。此外,除此之外,不同 计数很昂贵。聚合和比较数组的成本甚至更高。

如果大多数行符合条件,那不会有太大区别,因为无论如何都会处理整个表。对于一个小的选择,任何这样的方法都表现得非常糟糕。相比之下,可以使用索引的替代查询技术将大放异彩。

理想情况下,您有一个单独的键表,每个相关键占一行。然后使用这样的东西:

SELECT *
FROM   keys k
WHERE  EXISTS (SELECT FROM things WHERE key = k.key AND part = 'a')
AND    EXISTS (SELECT FROM things WHERE key = k.key AND part = 'b')
AND    EXISTS (SELECT FROM things WHERE key = k.key AND part = 'c');

things 需要 (part,key) 上的多列索引以使其快速。

即使您没有 keys 表:

SELECT t1.key
FROM   things t1
JOIN   things t2 USING (key)
JOIN   things t3 USING (key)
WHERE  t1.part = 'a'
AND    t2.part = 'b'
AND    t3.part = 'c';

dbfiddle here

最佳查询取决于您对过滤器和结果格式的精确要求,以及精确架构定义。

相关:

,

我认为 count(distinct) 可以满足您的需求。如果你想明确指定部分,你可以使用:

where t.key in (
  select x.key
  from things x
  group by x.key
  having array_agg(distinct part order by part)::text[] = array['a','b','c']
);