Sau khi ngủ quên nó, tôi có một ý tưởng hoàn toàn mới, đơn giản hơn, nhanh hơn:
CREATE OR REPLACE FUNCTION f_combos(_arr anyarray)
RETURNS TABLE (combo anyarray) LANGUAGE plpgsql AS
$BODY$
BEGIN
IF array_upper(_arr, 1) IS NULL THEN
combo := _arr; RETURN NEXT; RETURN;
END IF;
CASE array_upper(_arr, 1)
-- WHEN 0 THEN -- does not exist
WHEN 1 THEN
RETURN QUERY VALUES ('{}'), (_arr);
WHEN 2 THEN
RETURN QUERY VALUES ('{}'), (_arr[1:1]), (_arr), (_arr[2:2]);
ELSE
RETURN QUERY
WITH x AS (
SELECT f.combo FROM f_combos(_arr[1:array_upper(_arr, 1)-1]) f
)
SELECT x.combo FROM x
UNION ALL
SELECT x.combo || _arr[array_upper(_arr, 1)] FROM x;
END CASE;
END
$BODY$;
Gọi:
SELECT * FROM f_combos('{1,2,3,4,5,6,7,8,9}'::int[]) ORDER BY 1;
512 hàng, tổng thời gian chạy:2,899 mili giây
Giải thích
- Xử lý các trường hợp đặc biệt bằng
NULL
và mảng trống. - Xây dựng các kết hợp cho một mảng ban đầu gồm hai.
- Bất kỳ mảng nào dài hơn cũng được chia thành:
- các kết hợp cho cùng một mảng có độ dài n-1
- cộng với tất cả những thứ được kết hợp với phần tử n .. đệ quy .
Thực sự đơn giản, khi bạn đã có nó.
- Hoạt động cho mảng số nguyên 1 chiều bắt đầu bằng chỉ số dưới 1 (xem bên dưới).
- Nhanh gấp 2-3 lần giải pháp cũ, mở rộng quy mô tốt hơn.
- Hoạt động cho bất kỳ lại kiểu phần tử (sử dụng kiểu đa hình).
- Bao gồm mảng trống trong kết quả được hiển thị trong câu hỏi (và như @Craig đã chỉ ra cho tôi trong phần nhận xét).
- Ngắn hơn, thanh lịch hơn.
Điều này giả định chỉ số con của mảng bắt đầu từ 1 (Mặc định). Nếu bạn không chắc chắn về các giá trị của mình, hãy gọi hàm như sau để chuẩn hóa:
SELECT * FROM f_combos(_arr[array_lower(_arr, 1):array_upper(_arr, 1)]);
Không chắc liệu có cách nào thanh lịch hơn để chuẩn hóa các chỉ số con của mảng hay không. Tôi đã đăng câu hỏi về điều đó:
Chuẩn hóa các chỉ số con của mảng cho mảng 1 chiều để chúng bắt đầu bằng 1
Giải pháp cũ (chậm hơn)
CREATE OR REPLACE FUNCTION f_combos2(_arr int[], _a int[] = '{}', _z int[] = '{}')
RETURNS SETOF int[] LANGUAGE plpgsql AS
$BODY$
DECLARE
i int;
j int;
_up int;
BEGIN
IF array_length(_arr,1) > 0 THEN
_up := array_upper(_arr, 1);
FOR i IN array_lower(_arr, 1) .. _up LOOP
FOR j IN i .. _up LOOP
CASE j-i
WHEN 0,1 THEN
RETURN NEXT _a || _arr[i:j] || _z;
WHEN 2 THEN
RETURN NEXT _a || _arr[i:i] || _arr[j:j] || _z;
RETURN NEXT _a || _arr[i:j] || _z;
ELSE
RETURN NEXT _a || _arr[i:i] || _arr[j:j] || _z;
RETURN QUERY SELECT *
FROM f_combos2(_arr[i+1:j-1], _a || _arr[i], _arr[j] || _z);
END CASE;
END LOOP;
END LOOP;
ELSE
RETURN NEXT _arr;
END IF;
END;
$BODY$;
Gọi:
SELECT * FROM f_combos2('{7,15,48}'::int[]) ORDER BY 1;
Hoạt động cho mảng số nguyên 1 chiều. Điều này có thể được tối ưu hóa hơn nữa, nhưng điều đó chắc chắn không cần thiết cho phạm vi của câu hỏi này.
ORDER BY
để áp đặt thứ tự hiển thị trong câu hỏi.
Cung cấp cho NULL hoặc mảng trống, dưới dạng NULL
được đề cập trong các bình luận.
Đã thử nghiệm với PostgreSQL 9.1, nhưng sẽ hoạt động với bất kỳ phiên bản hiện đại nào. array_lower()
và array_upper()
đã xuất hiện ít nhất kể từ PostgreSQL 7.4. Chỉ có mặc định tham số là mới trong phiên bản 8.4. Có thể dễ dàng thay thế.
Hiệu suất là tốt.
SELECT DISTINCT * FROM f_combos('{1,2,3,4,5,6,7,8,9}'::int[]) ORDER BY 1;
511 hàng, tổng thời gian chạy:7,729 mili giây
Giải thích
Nó được xây dựng dựa trên hình thức đơn giản này điều đó chỉ tạo ra tất cả các kết hợp của các phần tử lân cận:
CREATE FUNCTION f_combos(_arr int[])
RETURNS SETOF int[] LANGUAGE plpgsql AS
$BODY$
DECLARE
i int;
j int;
_up int;
BEGIN
_up := array_upper(_arr, 1);
FOR i in array_lower(_arr, 1) .. _up LOOP
FOR j in i .. _up LOOP
RETURN NEXT _arr[i:j];
END LOOP;
END LOOP;
END;
$BODY$;
Nhưng điều này sẽ không thành công đối với các mảng con có nhiều hơn hai phần tử. Vì vậy:
-
Đối với bất kỳ mảng con nào có 3 phần tử, một mảng chỉ có hai phần tử bên ngoài được thêm vào. đây là một phím tắt cho trường hợp đặc biệt này để cải thiện hiệu suất và không hoàn toàn cần thiết .
-
Đối với bất kỳ mảng con nào có nhiều hơn 3 phần tử, tôi lấy hai phần tử bên ngoài và điền vào tất cả sự kết hợp của các phần tử bên trong được tạo bởi cùng một hàm đệ quy .