Bạn nhận được một ngoại lệ bằng cách sử dụng now()
bởi vì hàm không IMMUTABLE
(rõ ràng) và trích dẫn hướng dẫn sử dụng
:
Tôi thấy có hai cách để sử dụng chỉ mục từng phần (hiệu quả hơn nhiều):
1. Chỉ mục một phần với điều kiện sử dụng hằng số ngày:
CREATE INDEX queries_recent_idx ON queries_query (user_sid, created)
WHERE created > '2013-01-07 00:00'::timestamp;
Giả định created
thực sự được định nghĩa là timestamp
. Sẽ không hoạt động nếu cung cấp timestamp
hằng số cho một timestamptz
cột (timestamp with time zone
). Diễn viên từ timestamp
đến timestamptz
(hoặc ngược lại) phụ thuộc vào cài đặt múi giờ hiện tại và không thể thay đổi . Sử dụng một hằng số kiểu dữ liệu phù hợp. Hiểu kiến thức cơ bản về dấu thời gian có / không có múi giờ:
Thả và tạo lại chỉ số đó vào những giờ có lưu lượng truy cập thấp, có thể với một công việc cron hàng ngày hoặc hàng tuần (hoặc bất cứ điều gì đủ tốt cho bạn). Tạo chỉ mục khá nhanh, đặc biệt là một chỉ mục tương đối nhỏ. Giải pháp này cũng không cần thêm bất kỳ thứ gì vào bảng.
Giả sử không có quyền truy cập đồng thời vào bảng, việc tạo chỉ mục tự động có thể được thực hiện với một chức năng như sau:
CREATE OR REPLACE FUNCTION f_index_recreate()
RETURNS void
LANGUAGE plpgsql AS
$func$
BEGIN
DROP INDEX IF EXISTS queries_recent_idx;
EXECUTE format('
CREATE INDEX queries_recent_idx
ON queries_query (user_sid, created)
WHERE created > %L::timestamp'
, LOCALTIMESTAMP - interval '30 days'); -- timestamp constant
-- , now() - interval '30 days'); -- alternative for timestamptz
END
$func$;
Gọi:
SELECT f_index_recreate();
now()
(như bạn đã có) tương đương với CURRENT_TIMESTAMP
và trả về timestamptz
. Truyền tới timestamp
với now()::timestamp
hoặc sử dụng LOCALTIMESTAMP
thay vào đó.
db <> fiddle tại đây
sqlfiddle
cũ
Nếu bạn phải đối phó với truy cập đồng thời vào bảng, sử dụng DROP INDEX CONCURRENTLY
và CREATE INDEX CONCURRENTLY
. Nhưng bạn không thể gói các lệnh này thành một hàm vì theo tài liệu
:
Vì vậy, với hai giao dịch riêng biệt :
CREATE INDEX CONCURRENTLY queries_recent_idx2 ON queries_query (user_sid, created)
WHERE created > '2013-01-07 00:00'::timestamp; -- your new condition
Sau đó:
DROP INDEX CONCURRENTLY IF EXISTS queries_recent_idx;
Theo tùy chọn, đổi tên thành tên cũ:
ALTER INDEX queries_recent_idx2 RENAME TO queries_recent_idx;
2. Chỉ mục một phần với điều kiện trên thẻ "đã lưu trữ"
Thêm một archived
gắn thẻ vào bảng của bạn:
ALTER queries_query ADD COLUMN archived boolean NOT NULL DEFAULT FALSE;
UPDATE
cột trong khoảng thời gian bạn chọn để "gỡ bỏ" các hàng cũ hơn và tạo chỉ mục như:
CREATE INDEX some_index_name ON queries_query (user_sid, created)
WHERE NOT archived;
Thêm một điều kiện phù hợp vào các truy vấn của bạn (ngay cả khi nó có vẻ thừa) để cho phép nó sử dụng chỉ mục. Kiểm tra bằng EXPLAIN ANALYZE
liệu trình lập kế hoạch truy vấn có bắt kịp không - nó có thể sử dụng chỉ mục cho các truy vấn vào một ngày mới hơn. Nhưng nó sẽ không hiểu các điều kiện phức tạp hơn không khớp chính xác.
Bạn không phải thả và tạo lại chỉ mục, mà là UPDATE
trên bảng có thể đắt hơn giải trí lập chỉ mục và bảng lớn hơn một chút.
Tôi sẽ đi với đầu tiên tùy chọn (tái tạo chỉ mục). Trên thực tế, tôi đang sử dụng giải pháp này trong một số cơ sở dữ liệu. Lần thứ hai phải cập nhật tốn kém hơn.
Cả hai giải pháp đều giữ được tính hữu dụng của chúng theo thời gian, hiệu suất từ từ giảm xuống do nhiều hàng lỗi thời hơn được đưa vào chỉ mục.