PostgreSQL
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> PostgreSQL

Nhận giá trị từ hàng đầu tiên và hàng cuối cùng cho mỗi nhóm

Có nhiều cách khác nhau đơn giản hơn và nhanh hơn.

2x DISTINCT ON

SELECT *
FROM  (
   SELECT DISTINCT ON (name)
          name, week AS first_week, value AS first_val
   FROM   tbl
   ORDER  BY name, week
   ) f
JOIN (
   SELECT DISTINCT ON (name)
          name, week AS last_week, value AS last_val
   FROM   tbl
   ORDER  BY name, week DESC
   ) l USING (name);

Hoặc ngắn hơn:

SELECT *
FROM  (SELECT DISTINCT ON (1) name, week AS first_week, value AS first_val FROM tbl ORDER BY 1,2) f
JOIN  (SELECT DISTINCT ON (1) name, week AS last_week , value AS last_val  FROM tbl ORDER BY 1,2 DESC) l USING (name);

Đơn giản và dễ hiểu. Cũng nhanh nhất trong các bài kiểm tra cũ của tôi. Giải thích chi tiết cho DISTINCT ON :

  • Chọn hàng đầu tiên trong mỗi GROUP BY nhóm?

Chức năng cửa sổ 2x, 1x DISTINCT ON

SELECT DISTINCT ON (name)
       name, week AS first_week, value AS first_val
     , first_value(week)  OVER w AS last_week
     , first_value(value) OVER w AS last_value
FROM   tbl t
WINDOW w AS (PARTITION BY name ORDER BY week DESC)
ORDER  BY name, week;

WINDOW rõ ràng mệnh đề chỉ rút ngắn mã, không ảnh hưởng đến hiệu suất.

first_value() thuộc loại hỗn hợp

Các hàm tổng hợp min() hoặc max() không chấp nhận các loại kết hợp làm đầu vào. Bạn sẽ phải tạo các hàm tổng hợp tùy chỉnh (không khó lắm).
Nhưng các hàm cửa sổ first_value()last_value() làm . Dựa trên đó, chúng tôi có thể đưa ra các giải pháp đơn giản:

Truy vấn đơn giản

SELECT DISTINCT ON (name)
       name, week AS first_week, value AS first_value
     ,(first_value((week, value)) OVER (PARTITION BY name ORDER BY week DESC))::text AS l
FROM   tbl t
ORDER  BY name, week;

Đầu ra có tất cả dữ liệu, nhưng các giá trị của tuần trước được đưa vào một bản ghi ẩn danh (tùy chọn truyền sang text ). Bạn có thể cần các giá trị đã phân tách.

Kết quả bị phân rã với việc sử dụng loại bảng một cách có cơ hội

Để làm được điều đó, chúng tôi cần một loại composite nổi tiếng. Định nghĩa bảng được điều chỉnh sẽ cho phép sử dụng trực tiếp loại bảng một cách có cơ hội:

CREATE TABLE tbl (week int, value int, name text);  -- optimized column order

week và giá trị value đến trước, vì vậy bây giờ chúng ta có thể sắp xếp theo chính loại bảng:

SELECT (l).name, first_week, first_val
     , (l).week AS last_week, (l).value AS last_val
FROM  (
   SELECT DISTINCT ON (name)
          week AS first_week, value AS first_val
        , first_value(t) OVER (PARTITION BY name ORDER BY week DESC) AS l
   FROM   tbl t
   ORDER  BY name, week
   ) sub;

Kết quả được phân tách từ loại hàng do người dùng xác định

Điều đó có lẽ không thể xảy ra trong hầu hết các trường hợp. Đăng ký kiểu kết hợp với CREATE TYPE (vĩnh viễn) hoặc với CREATE TEMP TABLE (trong suốt thời gian của phiên):

CREATE TEMP TABLE nv(last_week int, last_val int);  -- register composite type
SELECT name, first_week, first_val, (l).last_week, (l).last_val
FROM (
   SELECT DISTINCT ON (name)
          name, week AS first_week, value AS first_val
        , first_value((week, value)::nv) OVER (PARTITION BY name ORDER BY week DESC) AS l
   FROM   tbl t
   ORDER  BY name, week
   ) sub;

Các hàm tổng hợp tùy chỉnh first() &last()

Tạo các hàm và tổng hợp một lần cho mỗi cơ sở dữ liệu:

CREATE OR REPLACE FUNCTION public.first_agg (anyelement, anyelement)
  RETURNS anyelement
  LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE AS
'SELECT $1;'

CREATE AGGREGATE public.first(anyelement) (
  SFUNC = public.first_agg
, STYPE = anyelement
, PARALLEL = safe
);


CREATE OR REPLACE FUNCTION public.last_agg (anyelement, anyelement)
  RETURNS anyelement
  LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE AS
'SELECT $2';

CREATE AGGREGATE public.last(anyelement) (
  SFUNC = public.last_agg
, STYPE = anyelement
, PARALLEL = safe
);

Sau đó:

SELECT name
     , first(week) AS first_week, first(value) AS first_val
     , last(week)  AS last_week , last(value)  AS last_val
FROM  (SELECT * FROM tbl ORDER BY name, week) t
GROUP  BY name;

Có lẽ là giải pháp thanh lịch nhất. Nhanh hơn với mô-đun bổ sung first_last_agg cung cấp triển khai C.
So sánh các hướng dẫn trong Postgres Wiki.

Có liên quan:

  • Tính toán mức tăng trưởng người theo dõi theo thời gian cho mỗi người có ảnh hưởng

db <> fiddle here (hiển thị tất cả)
Old sqlfiddle

Mỗi truy vấn này về cơ bản nhanh hơn đáng kể so với câu trả lời hiện được chấp nhận trong một bài kiểm tra nhanh trên bảng có 50 nghìn hàng với EXPLAIN ANALYZE .

Có nhiều cách hơn. Tùy thuộc vào phân phối dữ liệu, các kiểu truy vấn khác nhau có thể nhanh hơn (nhiều). Xem:

  • Tối ưu hóa truy vấn GROUP BY để truy xuất hàng mới nhất cho mỗi người dùng


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Sử dụng bản sao lôgic PostgreSQL để duy trì máy chủ kiểm tra đọc / ghi luôn cập nhật

  2. Giám sát cơ sở dữ liệu PostgreSQL:Mẹo để giám sát những gì

  3. Postgresql GROUP_CONCAT tương đương?

  4. PostgreSQL multi INSERT ... RETURNING với nhiều cột

  5. PostgreSQL:Sửa đổi CHỦ SỞ HỮU trên tất cả các bảng đồng thời trong PostgreSQL