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

Mẫu bảng và các phương pháp khác để lấy các bộ số ngẫu nhiên

PostgreSQL’s TABLESAMPLE mang lại một số lợi thế hơn so với các cách truyền thống khác để nhận các bộ giá trị ngẫu nhiên.

TABLESAMPLE là một mệnh đề SQL SELECT và nó cung cấp hai phương pháp lấy mẫu là SYSTEMBERNOULLI . Với sự trợ giúp của TABLESAMPLE chúng ta có thể dễ dàng truy xuất các hàng ngẫu nhiên từ một bảng. Để đọc thêm về TABLESAMPLE, bạn có thể kiểm tra bài đăng trên blog trước đó .

Trong bài đăng trên blog này, chúng ta sẽ nói về các cách thay thế để lấy các hàng ngẫu nhiên. Cách mọi người chọn các hàng ngẫu nhiên trước TABLESAMPLE , ưu và nhược điểm của các phương pháp khác là gì và những gì chúng tôi thu được với TABLESAMPLE ?

Có những bài đăng trên blog tuyệt vời về cách chọn các hàng ngẫu nhiên, bạn có thể bắt đầu đọc các bài đăng trên blog sau để hiểu sâu về chủ đề này.

Suy nghĩ của tôi về việc lấy hàng ngẫu nhiên

Nhận hàng ngẫu nhiên từ bảng cơ sở dữ liệu

random_agg ()

Hãy so sánh các cách truyền thống để lấy các hàng ngẫu nhiên từ bảng với các cách mới do TABLESAMPLE cung cấp.

Trước TABLESAMPLE , có 3 phương pháp thường được sử dụng để chọn ngẫu nhiên các hàng từ một bảng.

1- Đặt hàng theo ngẫu nhiên ()

Vì mục đích thử nghiệm, chúng tôi cần tạo một bảng và đưa một số dữ liệu vào bên trong nó.

Hãy tạo bảng ts_test và chèn 1 triệu hàng vào đó:

CREATE TABLE ts_test (
  id SERIAL PRIMARY KEY,
  title TEXT
);

INSERT INTO ts_test (title)
SELECT
    'Record #' || i
FROM
    generate_series(1, 1000000) i;

Xem xét câu lệnh SQL sau để chọn 10 hàng ngẫu nhiên:

SELECT * FROM ts_test ORDER BY random() LIMIT 10;

Khiến PostgreSQL thực hiện quét toàn bộ bảng và đồng thời đặt hàng. Do đó, phương pháp này không được ưu tiên cho các bảng có số lượng hàng lớn vì lý do hiệu suất.

Hãy xem xét EXPLAIN ANALYZE đầu ra của truy vấn này ở trên:

random=# EXPLAIN ANALYZE SELECT * FROM ts_test ORDER BY random() LIMIT 10;
 QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------
 Limit (cost=33959.03..33959.05 rows=10 width=36) (actual time=1956.786..1956.807 rows=10 loops=1)
 -> Sort (cost=33959.03..35981.18 rows=808863 width=36) (actual time=1956.780..1956.789 rows=10 loops=1)
 Sort Key: (random())
 Sort Method: top-N heapsort Memory: 25kB
 -> Seq Scan on ts_test (cost=0.00..16479.79 rows=808863 width=36) (actual time=0.276..1001.109 rows=1000000 loops=1)
 Planning time: 1.434 ms
 Execution time: 1956.900 ms
(7 rows)

Dưới dạng EXPLAIN ANALYZE chỉ ra rằng, việc chọn 10 trong số 1 triệu hàng mất gần 2 giây. Truy vấn cũng sử dụng đầu ra của random() làm phím sắp xếp để sắp xếp kết quả. Sắp xếp có vẻ là công việc tốn thời gian nhất ở đây. Hãy thực hiện điều này với tình huống sử dụng TABLESAMPLE .

Trong PostgreSQL 9.5, để lấy số hàng chính xác một cách ngẫu nhiên, chúng ta có thể sử dụng phương pháp lấy mẫu SYSTEM_ROWS. Được cung cấp bởi tsm_system_rows mô-đun đóng góp, nó cho phép chúng tôi chỉ định bao nhiêu hàng sẽ được trả về bằng cách lấy mẫu. Thông thường, chỉ có thể chỉ định tỷ lệ phần trăm được yêu cầu với TABLESAMPLE SYSTEMBERNOULLI các phương pháp.

Đầu tiên, chúng ta nên tạo tsm_system_rows phần mở rộng để sử dụng phương pháp này vì cả TABLESAMPLE SYSTEMTABLESAMPLE BERNOULLI phương pháp không đảm bảo rằng tỷ lệ phần trăm được cung cấp sẽ dẫn đến số hàng dự kiến. Vui lòng kiểm tra TABLESAMPLE p trước đó ost để nhớ lại lý do tại sao họ làm việc như vậy.

Hãy bắt đầu bằng cách tạo tsm_system_rows phần mở rộng:

random=# CREATE EXTENSION tsm_system_rows;
CREATE EXTENSION

Bây giờ chúng ta hãy so sánh “ORDER BY random()EXPLAIN ANALYZE xuất với EXPLAIN ANALYZE đầu ra của tsm_system_rows truy vấn trả về 10 hàng ngẫu nhiên trong 1 bảng hàng 1 triệu.

random=# EXPLAIN ANALYZE SELECT * FROM ts_test TABLESAMPLE SYSTEM_ROWS(10);
 QUERY PLAN
-------------------------------------------------------------------------------------------------------
 Sample Scan on ts_test (cost=0.00..4.10 rows=10 width=18) (actual time=0.069..0.083 rows=10 loops=1)
 Sampling: system_rows ('10'::bigint)
 Planning time: 0.646 ms
 Execution time: 0.159 ms
(4 rows)

Toàn bộ truy vấn mất 0,159 mili giây. Phương pháp lấy mẫu này so sánh cực nhanh với “ORDER BY random() ”Mất 1956,9 mili giây.

2- So sánh với ngẫu nhiên ()

SQL sau cho phép chúng tôi truy xuất các hàng ngẫu nhiên với xác suất 10%

SELECT * FROM ts_test WHERE random() <= 0.1;

Phương pháp này nhanh hơn so với sắp xếp theo thứ tự ngẫu nhiên vì nó không sắp xếp các hàng đã chọn. Nó sẽ trả về phần trăm gần đúng các hàng từ bảng giống như BERNOULLI hoặc SYSTEM TABLESAMPLE các phương pháp.

Hãy kiểm tra EXPLAIN ANALYZE đầu ra của random() truy vấn ở trên:

random=# EXPLAIN ANALYZE SELECT * FROM ts_test WHERE random() <= 0.1;
 QUERY PLAN
------------------------------------------------------------------------------------------------------------------
 Seq Scan on ts_test (cost=0.00..21369.00 rows=333333 width=18) (actual time=0.089..280.483 rows=100014 loops=1)
 Filter: (random() <= '0.1'::double precision)
 Rows Removed by Filter: 899986
 Planning time: 0.704 ms
 Execution time: 367.527 ms
(5 rows)

Truy vấn mất 367,5 mili giây. Tốt hơn nhiều so với ORDER BY random() . Nhưng khó kiểm soát số lượng hàng chính xác hơn. Hãy so sánh “random()EXPLAIN ANALYZE xuất với EXPLAIN ANALYZE đầu ra của TABLESAMPLE BERNOULLI truy vấn trả về khoảng 10% số hàng ngẫu nhiên trong bảng 1 triệu hàng.

random=# EXPLAIN ANALYZE SELECT * FROM ts_test TABLESAMPLE BERNOULLI (10);
 QUERY PLAN
--------------------------------------------------------------------------------------------------------------------
 Sample Scan on ts_test  (cost=0.00..7369.00 rows=100000 width=18) (actual time=0.015..147.002 rows=100155 loops=1)
   Sampling: bernoulli ('10'::real)
 Planning time: 0.076 ms
 Execution time: 239.289 ms
(4 rows)

Chúng tôi đã đưa 10 làm tham số cho BERNOULLI bởi vì chúng tôi cần 10% tổng số hồ sơ.

Ở đây chúng ta có thể thấy rằng BERNOULLI mất 239,289 mili giây để thực thi. Hai phương pháp này khá giống nhau về cách chúng hoạt động, BERNOULLI nhanh hơn một chút vì lựa chọn ngẫu nhiên được thực hiện ở cấp thấp hơn. Một lợi thế của việc sử dụng BERNOULLI so với WHERE random() <= 0.1REPEATABLE mệnh đề mà chúng tôi đã mô tả trong TABLESAMPLE trước đăng bài.

3- Cột bổ sung có giá trị ngẫu nhiên

Phương pháp này đề xuất thêm một cột mới với các giá trị được chỉ định ngẫu nhiên, thêm chỉ mục vào đó, thực hiện sắp xếp theo cột đó và tùy chọn cập nhật các giá trị của chúng theo định kỳ để phân phối ngẫu nhiên.

Chiến lược này cho phép chủ yếu lấy mẫu ngẫu nhiên có thể lặp lại. Nó hoạt động nhanh hơn nhiều so với phương pháp đầu tiên nhưng cần nỗ lực để thiết lập lần đầu tiên và dẫn đến chi phí hiệu suất trong các hoạt động chèn, cập nhật và xóa.

Hãy áp dụng phương pháp này trên ts_test của chúng tôi bảng.

Đầu tiên, chúng tôi sẽ thêm một cột mới có tên là randomcolumn với loại chính xác kép vì random() của PostgreSQL hàm trả về một số với độ chính xác gấp đôi.

random=# ALTER TABLE ts_test ADD COLUMN randomcolumn DOUBLE PRECISION;
ALTER TABLE

Sau đó, chúng tôi sẽ cập nhật cột mới bằng cách sử dụng random() chức năng.

random=# \timing
Timing is on.
random=# BEGIN;
BEGIN
Time: 2.071 ms
random=# UPDATE ts_test SET randomcolumn = RANDOM();
UPDATE 1000000
Time: 8483.741 ms
random=# COMMIT;
COMMIT
Time: 2.615 ms

Phương pháp này có chi phí ban đầu để tạo một cột mới và điền vào cột mới đó các giá trị ngẫu nhiên (0,0-1,0), nhưng đó là chi phí một lần. Trong ví dụ này, đối với 1 triệu hàng, mất gần 8,5 giây.

Hãy thử quan sát xem kết quả của chúng ta có thể tái tạo được không bằng cách truy vấn 100 hàng với phương pháp mới của chúng tôi:

random=# SELECT * FROM ts_test ORDER BY randomcolumn LIMIT 100;
 -------+---------------+----------------------
 13522  | Record #13522  | 6.4261257648468e-08
 671584 | Record #671584 | 6.4261257648468e-07
 714012 | Record #714012 | 1.95764005184174e-06
 162016 | Record #162016 | 3.44449654221535e-06
 106867 | Record #106867 | 3.66196036338806e-06
 865669 | Record #865669 | 3.96883115172386e-06
 927    | Record #927    | 4.65428456664085e-06
 526017 | Record #526017 | 4.65987250208855e-06
 98338  | Record #98338  | 4.91179525852203e-06
 769625 | Record #769625 | 4.91319224238396e-06
 ...
 462484 | Record #462484 | 9.83504578471184e-05
 (100 rows)

Khi chúng tôi thực hiện truy vấn ở trên, chúng tôi hầu như nhận được cùng một tập hợp kết quả nhưng điều này không được đảm bảo vì chúng tôi đã sử dụng random() hàm để điền randomcolumn và trong trường hợp này, nhiều cột có thể có cùng giá trị. Để đảm bảo rằng chúng tôi nhận được cùng một kết quả cho mỗi lần nó chạy, chúng tôi nên cải thiện truy vấn của mình bằng cách thêm cột ID vào ORDER BY theo cách này, chúng tôi đảm bảo rằng ORDER BY mệnh đề chỉ định một tập hợp các hàng duy nhất, vì cột id có chỉ mục khóa chính trên đó.

Bây giờ, hãy chạy truy vấn cải tiến bên dưới:

random=# SELECT * FROM ts_test ORDER BY randomcolumn, id LIMIT 100;
 id | title | randomcolumn
--------+----------------+----------------------
 13522  | Record #13522  | 6.4261257648468e-08
 671584 | Record #671584 | 6.4261257648468e-07
 714012 | Record #714012 | 1.95764005184174e-06
 162016 | Record #162016 | 3.44449654221535e-06
 106867 | Record #106867 | 3.66196036338806e-06
 865669 | Record #865669 | 3.96883115172386e-06
 927    | Record #927    | 4.65428456664085e-06
 526017 | Record #526017 | 4.65987250208855e-06
 98338  | Record #98338  | 4.91179525852203e-06
 769625 | Record #769625 | 4.91319224238396e-06 
 ...
 462484 | Record #462484 | 9.83504578471184e-05
 (100 rows)

Bây giờ chúng tôi chắc chắn rằng chúng tôi có thể nhận được mẫu ngẫu nhiên có thể lặp lại bằng cách sử dụng phương pháp này.

Đã đến lúc xem EXPLAIN ANALYZE kết quả của truy vấn cuối cùng của chúng tôi:

random=# EXPLAIN ANALYZE SELECT * FROM ts_test ORDER BY randomcolumn, id LIMIT 100;
 QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------
 Limit (cost=55571.28..55571.53 rows=100 width=26) (actual time=1951.811..1952.039 rows=100 loops=1)
 -> Sort (cost=55571.28..58071.28 rows=1000000 width=26) (actual time=1951.804..1951.891 rows=100 loops=1)
 Sort Key: randomcolumn, id
 Sort Method: top-N heapsort Memory: 32kB
 -> Seq Scan on ts_test (cost=0.00..17352.00 rows=1000000 width=26) (actual time=0.058..995.011 rows=1000000 loops=1)
 Planning time: 0.481 ms
 Execution time: 1952.215 ms
(7 rows)

Để so sánh phương pháp này với TABLESAMPLE phương pháp, hãy chọn SYSTEM phương thức với REPEATABLE tùy chọn, vì phương pháp này cung cấp cho chúng tôi kết quả có thể lặp lại.

TABLESAMPLE SYSTEM về cơ bản phương pháp thực hiện lấy mẫu mức khối / trang; nó đọc và trả về các trang ngẫu nhiên từ đĩa. Do đó, nó cung cấp tính ngẫu nhiên ở cấp độ trang thay vì cấp độ bộ. Đó là lý do tại sao rất khó để kiểm soát số lượng hàng đã truy xuất một cách chính xác. Nếu không có trang nào được chọn, chúng tôi có thể không nhận được bất kỳ kết quả nào. Việc lấy mẫu ở cấp độ trang cũng dễ có hiệu ứng phân cụm.

TABLESAMPLE SYNTAX cũng giống như đối với BERNOULLISYSTEM , chúng tôi sẽ chỉ định tỷ lệ phần trăm giống như chúng tôi đã làm trong BERNOULLI phương pháp.

Nhắc nhở nhanh: TỔNG HỢP

SELECT select_expression
FROM table_name
TABLESAMPLE sampling_method ( argument [, ...] ) [ REPEATABLE ( seed ) ]
...

Để truy xuất khoảng 100 hàng, chúng tôi cần yêu cầu 100 * 100/1 000 000 =0,01% số hàng của bảng. Vì vậy, tỷ lệ phần trăm của chúng tôi sẽ là 0,01.

Trước khi bắt đầu truy vấn, hãy nhớ cách REPEATABLE làm. Về cơ bản, chúng tôi sẽ chọn một thông số gốc và chúng tôi sẽ nhận được kết quả giống nhau cho mỗi lần chúng tôi sử dụng cùng một thông số với truy vấn trước đó. Nếu chúng tôi cung cấp một hạt giống khác, truy vấn sẽ tạo ra một tập kết quả hoàn toàn khác.

Hãy thử xem kết quả với truy vấn.

random=# Select * from ts_test tablesample system (0.01) repeatable (60);
 id | title | randomcolumn
--------+----------------+---------------------
 659598 | Record #659598 | 0.964113113470376
 659599 | Record #659599 | 0.531714483164251
 659600 | Record #659600 | 0.477636905387044
 659601 | Record #659601 | 0.861940925940871
 659602 | Record #659602 | 0.545977566856891
 659603 | Record #659603 | 0.326795583125204
 659604 | Record #659604 | 0.469275736715645
 659605 | Record #659605 | 0.734953186474741
 659606 | Record #659606 | 0.41613544523716 
 ...
 659732 | Record #659732 | 0.893704727292061
 659733 | Record #659733 | 0.847225237637758
 (136 rows)

Chúng tôi nhận được 136 hàng, vì bạn có thể coi con số này phụ thuộc vào số lượng hàng được lưu trữ trong một trang.

Bây giờ, hãy thử chạy cùng một truy vấn với cùng một số hạt giống:

random=# Select * from ts_test tablesample system (0.01) repeatable (60);
 id | title | randomcolumn
--------+----------------+---------------------
 659598 | Record #659598 | 0.964113113470376
 659599 | Record #659599 | 0.531714483164251
 659600 | Record #659600 | 0.477636905387044
 659601 | Record #659601 | 0.861940925940871
 659602 | Record #659602 | 0.545977566856891
 659603 | Record #659603 | 0.326795583125204
 659604 | Record #659604 | 0.469275736715645
 659605 | Record #659605 | 0.734953186474741
 659606 | Record #659606 | 0.41613544523716 
 ...
 659732 | Record #659732 | 0.893704727292061
 659733 | Record #659733 | 0.847225237637758
 (136 rows)

Chúng tôi nhận được cùng một tập hợp kết quả nhờ vào REPEATABLE lựa chọn. Bây giờ chúng ta có thể so sánh TABLESAMPLE SYSTEM phương pháp với phương pháp cột ngẫu nhiên bằng cách xem EXPLAIN ANALYZE đầu ra.

random=# EXPLAIN ANALYZE SELECT * FROM ts_test TABLESAMPLE SYSTEM (0.01) REPEATABLE (60);
 QUERY PLAN
---------------------------------------------------------------------------------------------------------
 Sample Scan on ts_test (cost=0.00..5.00 rows=100 width=26) (actual time=0.091..0.230 rows=136 loops=1)
 Sampling: system ('0.01'::real) REPEATABLE ('60'::double precision)
 Planning time: 0.323 ms
 Execution time: 0.398 ms
(4 rows)

SYSTEM phương pháp sử dụng quét mẫu và nó cực kỳ nhanh chóng. Điểm lùi duy nhất của phương pháp này là chúng tôi không thể đảm bảo rằng tỷ lệ phần trăm được cung cấp sẽ dẫn đến số hàng dự kiến.

Kết luận

Trong bài đăng trên blog này, chúng tôi đã so sánh TABLESAMPLE tiêu chuẩn (SYSTEM , BERNOULLI ) và tsm_system_rows mô-đun với các phương pháp ngẫu nhiên cũ hơn.

Tại đây, bạn có thể xem lại các kết quả một cách nhanh chóng:

  • ORDER BY random() chậm vì sắp xếp
  • random() <= 0.1 tương tự như BERNOULLI phương pháp
  • Việc thêm cột có giá trị ngẫu nhiên cần công việc ban đầu và có thể dẫn đến các vấn đề về hiệu suất
  • SYSTEM nhanh nhưng khó kiểm soát số lượng hàng
  • tsm_system_rows nhanh và có thể kiểm soát số lượng hàng

Kết quả là tsm_system_rows đánh bại bất kỳ phương pháp nào khác để chỉ chọn một vài hàng ngẫu nhiên.

Nhưng người chiến thắng thực sự chắc chắn là TABLESAMPLE chinh no. Nhờ khả năng mở rộng, các tiện ích mở rộng tùy chỉnh (tức là tsm_system_rows , tsm_system_time ) có thể được phát triển bằng cách sử dụng TABLESAMPLE các hàm phương thức.

Ghi chú của nhà phát triển: Bạn có thể tìm thêm thông tin về cách viết phương pháp lấy mẫu tùy chỉnh trong chương Phương pháp lấy mẫu bảng A của tài liệu PostgreSQL.

Lưu ý cho tương lai: Chúng ta sẽ thảo luận về Dự án AXLE và tiện ích mở rộng tsm_system_time trong TABLESAMPLE tiếp theo của chúng tôi bài đăng trên blog.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. PG ::InvalidParameterValue:ERROR:giá trị không hợp lệ cho tham số client_min_messages:hoảng sợ

  2. Ràng buộc bảng chéo trong PostgreSQL

  3. Chuyển tùy ý nhiều hàng thành cột trong PostgreSQL

  4. Làm cách nào để kết hợp cả ngày với trường ngày giờ?

  5. Cách hoạt động của hàm Power () trong PostgreSQL