Trong PostgreSQL, có hàm tổng hợp loại () đầu tiên an toàn không?


21

Câu hỏi đầy đủ viết lại

Tôi đang tìm một hàm tổng hợp First ().

Ở đây tôi tìm thấy một cái gì đó gần như hoạt động:

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

-- And then wrap an aggregate around it
CREATE AGGREGATE public.first (
        sfunc    = public.first_agg,
        basetype = anyelement,
        stype    = anyelement
);

Vấn đề là khi một cột varchar (n) đi qua hàm đầu tiên (), nó sẽ được chuyển đổi thành varchar đơn giản (không có kích thước). Cố gắng trả về truy vấn trong một hàm dưới dạng RETURNS SETOF bất kỳ, tôi nhận được lỗi sau:

LRI: cấu trúc truy vấn không khớp với loại kết quả chức năng Estado de SQL: 42804 Detalhe: Thay đổi ký tự loại trả về không khớp với ký tự loại dự kiến ​​khác nhau (40) trong cột 2. Contexto: Hàm PL / pgQuery vsr_table_at_time (bất kỳ thời gian, không có múi giờ ) dòng 31 tại QUAY LẠI

Trong cùng một trang wiki có một liên kết đến Phiên bản C của chức năng sẽ thay thế ở trên. Tôi không biết cách cài đặt nó, nhưng tôi tự hỏi liệu phiên bản này có thể giải quyết vấn đề của tôi không.

Trong khi đó, có cách nào để tôi có thể thay đổi chức năng trên để nó trả về cùng loại chính xác của cột đầu vào không?

Câu trả lời:


17

DISTINCT ON()

Cũng như một ghi chú bên lề, đây chính xác là những gì DISTINCT ON()không (không bị nhầm lẫn với DISTINCT)

SELECT DISTINCT ON ( expression [, ...] ) chỉ giữ hàng đầu tiên của mỗi bộ hàng trong đó các biểu thức đã cho ước tính bằng nhau . Các DISTINCT ONbiểu thức được diễn giải bằng các quy tắc tương tự như đối với ORDER BY(xem ở trên). Lưu ý rằng "hàng đầu tiên" của mỗi bộ là không thể đoán trước trừ khi ORDER BYđược sử dụng để đảm bảo rằng hàng mong muốn xuất hiện đầu tiên. Ví dụ

Vì vậy, nếu bạn đã viết,

SELECT myFirstAgg(z)
FROM foo
GROUP BY x,y;

Nó hiệu quả

SELECT DISTINCT ON(x,y) z
FROM foo;
-- ORDER BY z;

Trong đó nó mất đầu tiên z. Có hai sự khác biệt quan trọng,

  1. Bạn cũng có thể chọn các cột khác mà không mất phí tổng hợp thêm ..

    SELECT DISTINCT ON(x,y) z, k, r, t, v
    FROM foo;
    -- ORDER BY z, k, r, t, v;
  2. Bởi vì không có GROUP BYbạn không thể sử dụng tổng hợp (thực) với nó.

    CREATE TABLE foo AS
    SELECT * FROM ( VALUES
      (1,2,3),
      (1,2,4),
      (1,2,5)
    ) AS t(x,y,z);
    
    SELECT DISTINCT ON (x,y) z, sum(z)
    FROM foo;
    
    -- fails, as you should expect.
    SELECT DISTINCT ON (x,y) z, sum(z)
    FROM foo;
    
    -- would not otherwise fail.
    SELECT myFirstAgg(z), sum(z)
    FROM foo
    GROUP BY x,y;

Đừng quên ORDER BY

Ngoài ra, trong khi tôi không in đậm thì bây giờ tôi sẽ

Lưu ý rằng "hàng đầu tiên" của mỗi bộ là không thể dự đoán được trừ khi ORDER BY được sử dụng để đảm bảo rằng hàng mong muốn xuất hiện đầu tiên. Ví dụ

Luôn luôn sử dụng ORDER BYvớiDISTINCT ON

Sử dụng hàm tổng hợp được đặt hàng

Tôi tưởng tượng rất nhiều người đang tìm kiếm first_value, Các hàm tổng hợp được đặt hàng . Chỉ muốn ném nó ra ngoài kia. Nó sẽ trông như thế này, nếu chức năng tồn tại:

SELECT a, b, first_value() WITHIN GROUP (ORDER BY z)    
FROM foo
GROUP BY a,b;

Nhưng, than ôi bạn có thể làm điều này.

SELECT a, b, percentile_disc(0) WITHIN GROUP (ORDER BY z)   
FROM foo
GROUP BY a,b;

1
Vấn đề với câu trả lời này là nó chỉ hoạt động nếu bạn muốn MỘT tổng hợp trong danh sách lựa chọn của bạn, điều này không được ngụ ý bởi câu hỏi. Ví dụ: nếu bạn muốn chọn từ một bảng và tìm một vài giá trị đầu tiên được sắp xếp, DISTINCT ONsẽ không hoạt động trong trường hợp này. Đây không phải là một hàm tổng hợp, bạn thực sự đang lọc dữ liệu và vì vậy bạn chỉ có thể thực hiện một lần.
DB140141

6

Yay, tôi đã tìm ra một cách dễ dàng với trường hợp của bạn bằng cách sử dụng một số tính năng trong PostgreQuery 9.4+

Hãy xem ví dụ này:

select  (array_agg(val ORDER BY i))[1] as first_value_orderby_i,
    (array_agg(val ORDER BY i DESC))[1] as last_value_orderby_i,
    (array_agg(val))[1] as last_value_all,
    (array_agg(val))[array_length(array_agg(val),1)] as last_value_all
   FROM (
        SELECT i, random() as val
        FROM generate_series(1,100) s(i)
        ORDER BY random()
    ) tmp_tbl

Tôi hy vọng nó sẽ giúp bạn trong trường hợp của bạn.


Vấn đề với giải pháp này là nó không hoạt động với DOMAINcác loại dữ liệu hoặc các ngoại lệ nhỏ khác. Nó cũng phức tạp và tốn thời gian hơn nhiều, xây dựng một mảng của toàn bộ tập dữ liệu. Giải pháp đơn giản là tạo một tập hợp tùy chỉnh, nhưng cho đến nay tôi vẫn chưa tìm thấy giải pháp lý tưởng ngay cả với điều đó. Các chức năng của cửa sổ cũng rất tệ, vì chúng không thể được sử dụng giống như cách bạn có thể sử dụng tổng hợp (với các câu lệnh LỌC hoặc trong CROSS THAM GIA CUỘC SỐNG)
AlexanderMP

5

Không phải là một câu trả lời trực tiếp cho câu hỏi của bạn nhưng bạn nên thử first_valuechức năng cửa sổ. Nó hoạt động như thế này:

CREATE TABLE test (
    id SERIAL NOT NULL PRIMARY KEY,
    cat TEXT,
    value VARCHAR(2)
    date TIMESTAMP WITH TIME ZONE

);

Sau đó, nếu bạn muốn mục đầu tiên trong mỗi cat(danh mục), bạn sẽ truy vấn như thế:

SELECT
    cat,
    first_value(date) OVER (PARTITION BY cat ORDER BY date)
FROM
    test;

hoặc là:

SELECT
    cat,
    first_value(date) OVER w
FROM
    test
WINDOW w AS (PARTITION BY cat ORDER BY date);

Xin lỗi, tôi không nghĩ rằng điều này áp dụng cho trường hợp sử dụng của tôi. First_value không phải là hàm tổng hợp, hiển thị tất cả các bản ghi của một giá trị chung nhất định (ví dụ con mèo của bạn) được đánh giá là đầu tiên theo một số thứ tự (ngày ví dụ của bạn). Nhu cầu của tôi là khác nhau. Tôi cần, trong cùng một lựa chọn, kết hợp một số cột bằng cách chọn giá trị không null đầu tiên. Đó là, nó sẽ xuất ra một bản ghi cho mỗi kết hợp giá trị trong GROUP BY.
Alexandre Neto

2
Ở trên có thể được thực hiện để làm việc bằng cách ném khác biệt vào hỗn hợp : select distinct x, first_value(y) over (partition by x), first_value(z) over (partition by x) from .... Có lẽ không hiệu quả nhưng đủ để tôi bắt đầu với việc tạo mẫu. Chắc chắn một cái gì đó để xem xét lại mặc dù!
Max Murphy
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.