Chọn các hàng có ít nhất một hàng trên mỗi bộ đáp ứng một điều kiện


7

Tôi có bảng sau:

create table test (
  company_id integer not null, 
  client_id integer not null, 
  client_status text,
  unique (company_id, client_id)
);

insert into test values
  (1, 1, 'y'),    -- company1

  (2, 2, null),   -- company2

  (3, 3, 'n'),    -- company3

  (4, 4, 'y'),    -- company4
  (4, 5, 'n'),

  (5, 6, null),   -- company5
  (5, 7, 'n')
;

Về cơ bản, có 5 công ty khác nhau, mỗi công ty có một hoặc nhiều khách hàng và mỗi khách hàng có trạng thái: 'y' hoặc 'n' (cũng có thể là null).

Điều tôi phải làm là chọn tất cả các cặp (company_id, client_id)cho tất cả các công ty có ít nhất một khách hàng có trạng thái không phải là 'n' ('y' hoặc null). Vì vậy, đối với dữ liệu mẫu ở trên, đầu ra phải là:

company_id;client_id
1;1
2;2
4;4
4;5
5;6
5;7

Tôi đã thử một cái gì đó với các chức năng của cửa sổ nhưng tôi không thể tìm ra cách so sánh số lượng TẤT CẢ khách hàng với số lượng khách hàng STATUS = 'n'.

select company_id,
count(*) over (partition by company_id) as all_clients_count
from test
-- where all_clients_count != ... ?

Tôi đã tìm ra cách để làm điều này, nhưng tôi không chắc liệu đó có phải là cách đúng hay không:

select sub.company_id, unnest(sub.client_ids)
from (
  select company_id, array_agg(client_id) as client_ids
  from test
  group by company_id
  having count(*) != count( (case when client_status = 'n' then 1 else null end) )
) sub

Yêu cầu của bạn không rõ ràng. Bạn có cần phải chọn các công ty và khách hàng hoặc chỉ các công ty?
Andrew Brennan

Bạn nói đúng, xin lỗi vì điều đó, tôi đã cập nhật câu hỏi (kết quả mong đợi). Khi có ít nhất một khách hàng trong công ty có trạng thái null hoặc trạng thái! = 'N' thì tôi muốn có được công ty này cùng với tất cả các khách hàng, ngay cả với status = 'n'. Nói cách khác, tôi muốn lọc tất cả các công ty (cùng với khách hàng) mà tất cả các khách hàng có trạng thái = 'n'.
dùng606521

Chi tiết thiếu quan trọng: Bạn không UNIQUEbị ràng buộc (company_id, client_id)trong định nghĩa bảng của bạn. Nhưng dữ liệu thử nghiệm của bạn làm cho nó có vẻ như (company_id, client_id)là, trên thực tế, là duy nhất. Là nó? Nếu không, bạn có cần * tất cả * hàng đủ điều kiện hoặc mỗi kết hợp company_idclient_idchỉ một lần không?
Erwin Brandstetter

Vâng, bạn đã đúng, (company_id, client_id) là duy nhất.
dùng606521

@ErwinBrandstetter xin lỗi, cuối cùng tôi đã sử dụng giải pháp (câu hỏi cuối cùng) của tôi vì bây giờ nó hoạt động khá tốt đối với tôi. Dù sao, tôi sẽ chấp nhận câu trả lời của bạn vì tôi khá chắc chắn rằng nó chính xác và đầy đủ nhất :).
dùng606521

Câu trả lời:


5

Về cơ bản bạn đang tìm kiếm biểu thức:

client_status IS DISTINCT FROM 'n'

Cột client_statusphải thực sự là kiểu dữ liệu boolean, không textcho phép biểu thức đơn giản hơn:

client_status IS NOT FALSE

Hướng dẫn có chi tiết trong chương Toán tử so sánh .


Giả sử bảng thực tế của bạn có UNIQUEhoặcPK có một ràng buộc , chúng tôi đến:

CREATE TABLE test (
  company_id    integer NOT NULL, 
  client_id     integer NOT NULL, 
  client_status boolean,
  PRIMARY KEY (company_id, client_id)
);

Truy vấn

Tất cả đều làm như vậy (những gì bạn yêu cầu), nhanh nhất phụ thuộc vào phân phối dữ liệu:

SELECT company_id, client_id
FROM   test t
WHERE  EXISTS (
   SELECT 1 FROM test
   WHERE  company_id = t.company_id
   AND    client_status IS NOT FALSE
   );

Hoặc là:

SELECT company_id, client_id
FROM   test t
JOIN  (
   SELECT company_id
   FROM   test t
   GROUP  BY 1
   HAVING bool_or(client_status IS NOT FALSE)
   ) c USING (company_id);

Hoặc là:

SELECT company_id, client_id
FROM   test t
JOIN  (
   SELECT DISTINCT company_id, client_status 
   FROM   test t
   ORDER  BY company_id, client_status DESC
   ) c USING (company_id)
WHERE  c.client_status IS NOT FALSE;

Sắp xếp các giá trị Boolean FALSE-> TRUE-> NULLtheo thứ tự tăng dần. Vì vậy, FALSEđến cuối cùng theo thứ tự giảm dần. Nếu có bất kỳ giá trị nào khác có sẵn, thì cái đó được chọn trước ...

PK được thêm vào được thực hiện với một chỉ mục hữu ích cho các truy vấn này. Nếu bạn muốn nhanh hơn, hãy thêm một chỉ mục một phần cho truy vấn 1:

CREATE INDEX test_special_idx ON test (company_id, client_id)
WHERE  client_status IS NOT FALSE;

Bạn cũng có thể sử dụng các chức năng của cửa sổ, nhưng điều đó sẽ chậm hơn. Ví dụ với first_value():

SELECT company_id, client_id
FROM  (
   SELECT company_id, client_id
        , first_value(client_status) OVER (PARTITION BY company_id
                                           ORDER BY client_status DESC) AS stat
   FROM   test t
   ) sub
WHERE stat IS NOT FALSE;

Đối với nhiều hàng trên mỗi company_id, một trong những kỹ thuật này có thể nhanh hơn, vẫn:


2

Tôi có thể đã hiểu lầm bạn nhưng tôi tưởng tượng một cái gì đó như:

 select * 
 from test x 
 where exists ( 
     select 1 
     from test y 
     where x.company_id = y.company_id 
       and coalesce(client_status, 'y') <> 'n'
 );

sẽ làm việc. hợp nhất được sử dụng để ánh xạ null thành 'y', nhưng bất cứ điều gì khác với 'n' nên làm

Sử dụng chức năng OLAP có thể giúp chúng tôi tiết kiệm "tham gia":

select company_id, client_id 
from (
    select x.*
         , count(nullif(coalesce(client_status,'y'),'n')) 
               over (partition by company_id) as cnt 
    from test x
) 
where cnt > 0;

Ở đây chúng tôi ánh xạ null -> 'y' và 'n' -> null. Vì Count (x) sẽ đếm các hàng trong đó x không phải là null, nên chúng tôi đếm các hàng trong đó client_status <> 'n'. Tôi đã sử dụng hàm OLAP để tránh GROUP BY, có nghĩa là chúng ta chỉ cần tham chiếu bảng một lần.


1

Tôi nghĩ rằng điều này có thể được đơn giản hóa một chút:

select company_id 
from test 
group by company_id 
having count(*) filter (where client_status!='n' or client_status is null) > 0;

0

Một truy vấn SQL tiêu chuẩn dưới đây sẽ hoạt động

select
  company_id,
  client_id
from test
where client_status!='n' or client_status is null;

Xin lỗi, lỗi của tôi, tôi đã không viết chính xác câu hỏi (kết quả mong đợi): khi có ít nhất một khách hàng trong công ty có trạng thái null hoặc trạng thái! = 'N' thì tôi muốn đưa công ty này đi cùng với tất cả các khách hàng, ngay cả với status = ' n '.
dùng606521
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.