Cách nhanh chóng để khám phá số hàng của một bảng trong PostgreSQL


107

Tôi cần biết số hàng trong bảng để tính phần trăm. Nếu tổng số lớn hơn một số hằng số được xác định trước, tôi sẽ sử dụng giá trị hằng số. Nếu không, tôi sẽ sử dụng số hàng thực tế.

Tôi có thể sử dụng SELECT count(*) FROM table. Nhưng nếu giá trị không đổi của tôi là 500.000 và tôi có 5.000.000.000 hàng trong bảng của mình, thì việc đếm tất cả các hàng sẽ lãng phí rất nhiều thời gian.

Có thể ngừng đếm ngay khi giá trị không đổi của tôi bị vượt qua không?

Tôi chỉ cần số hàng chính xác miễn là nó dưới giới hạn đã cho. Ngược lại, nếu số lượng vượt quá giới hạn, tôi sử dụng giá trị giới hạn để thay thế và muốn câu trả lời càng nhanh càng tốt.

Một cái gì đó như thế này:

SELECT text,count(*), percentual_calculus()  
FROM token  
GROUP BY text  
ORDER BY count DESC;

5
Bạn không thể chỉ cố gắng chọn n hàng đầu tiên trong đó n = hằng số + 1 ? Nếu nó trả về nhiều hơn hằng số của bạn, bạn biết bạn nên sử dụng hằng số của mình, và nếu nó không ổn thì bạn có tốt không?
gddc 30/10/11

Bạn có một bản sắc hoặc trường tăng tự động trong bảng
Sparky

1
@Sparky: Các PK được hỗ trợ theo trình tự không được đảm bảo liền kề, các hàng có thể bị xóa hoặc có thể có khoảng trống do các giao dịch bị hủy bỏ.
mu quá ngắn

Cập nhật của bạn dường như mâu thuẫn với câu hỏi ban đầu của bạn ... bạn có cần biết số hàng chính xác không, hay bạn chỉ cần biết con số chính xác nếu nó ở dưới ngưỡng?
Flimzy

1
@ RenatoDinhaniConceição: Bạn có thể giải thích chính xác vấn đề mà bạn đang cố gắng giải quyết không? Tôi nghĩ câu trả lời của tôi dưới đây giải quyết được những gì bạn nói ban đầu là vấn đề của bạn. Bản cập nhật làm cho nó trông giống như bạn muốn đếm (*) cũng như nhiều trường khác. Sẽ hữu ích nếu bạn có thể giải thích chính xác những gì bạn đang cố gắng làm. Cảm ơn.
Ritesh

Câu trả lời:


224

Đếm hàng trong các bảng lớn được biết là chậm trong PostgreSQL. Để có được một số chính xác, nó phải đếm đầy đủ các hàng do bản chất của MVCC . Có một cách để tăng tốc độ này lên một cách đáng kể nếu số lượng không không phải là chính xác như nó có vẻ là trong trường hợp của bạn.

Thay vì nhận được số lượng chính xác ( chậm với các bảng lớn):

SELECT count(*) AS exact_count FROM myschema.mytable;

Bạn nhận được một ước tính gần đúng như thế này ( cực kỳ nhanh ):

SELECT reltuples::bigint AS estimate FROM pg_class where relname='mytable';

Ước tính gần đến mức nào phụ thuộc vào việc bạn chạy ANALYZEđủ hay không. Nó thường rất gần.
Xem Câu hỏi thường gặp về Wiki PostgreSQL .
Hoặc trang wiki dành riêng cho hiệu suất đếm (*) .

Tốt hơn

Bài viết trên PostgreSQL Wiki đượcmột chút cẩu thả . Nó đã bỏ qua khả năng có thể có nhiều bảng cùng tên trong một cơ sở dữ liệu - trong các lược đồ khác nhau. Để giải thích cho điều đó:

SELECT c.reltuples::bigint AS estimate
FROM   pg_class c
JOIN   pg_namespace n ON n.oid = c.relnamespace
WHERE  c.relname = 'mytable'
AND    n.nspname = 'myschema'

Hoặc tốt hơn vẫn là

SELECT reltuples::bigint AS estimate
FROM   pg_class
WHERE  oid = 'myschema.mytable'::regclass;

Nhanh hơn, đơn giản hơn, an toàn hơn, thanh lịch hơn. Xem hướng dẫn về Các loại Định danh Đối tượng .

Sử dụng to_regclass('myschema.mytable') trong Postgres 9.4+ để tránh ngoại lệ cho tên bảng không hợp lệ:


TABLESAMPLE SYSTEM (n) trong Postgres 9.5+

SELECT 100 * count(*) AS estimate FROM mytable TABLESAMPLE SYSTEM (1);

Giống như @a_horse đã nhận xét , điều khoản mới được thêm vàoSELECT lệnh có thể hữu ích nếu số liệu thống kê trong pg_classkhông đủ hiện tại vì một số lý do. Ví dụ:

  • Không autovacuumchạy.
  • Ngay sau khi lớn INSERThoặcDELETE .
  • TEMPORARY bảng (không được bao phủ bởi autovacuum ).

Điều này chỉ xem xét lựa chọn ngẫu nhiên n % ( 1trong ví dụ) của các khối và đếm các hàng trong đó. Một mẫu lớn hơn làm tăng chi phí và giảm lỗi, lựa chọn của bạn. Độ chính xác phụ thuộc vào nhiều yếu tố hơn:

  • Phân phối kích thước hàng. Nếu một khối nhất định xảy ra chứa các hàng rộng hơn bình thường, số lượng sẽ thấp hơn bình thường, v.v.
  • Bộ giá trị chết hoặc FILLFACTORchiếm không gian trên mỗi khối. Nếu phân bổ không đồng đều trên bảng, ước tính có thể bị lệch.
  • Các lỗi làm tròn chung.

Trong hầu hết các trường hợp, ước tính từ pg_classsẽ nhanh hơn và chính xác hơn.

Câu trả lời cho câu hỏi thực tế

Trước tiên, tôi cần biết số hàng trong bảng đó, nếu tổng số lớn hơn một số hằng số được xác định trước,

Và liệu nó ...

... có thể tại thời điểm số đếm vượt qua giá trị không đổi của tôi, nó sẽ dừng việc đếm (và không đợi kết thúc quá trình đếm để thông báo số hàng lớn hơn).

Đúng. Bạn có thể sử dụng một truy vấn con vớiLIMIT :

SELECT count(*) FROM (SELECT 1 FROM token LIMIT 500000) t;

Postgres thực sự ngừng đếm vượt quá giới hạn đã cho, bạn sẽ có được số lượng chính xác và hiện tại cho tối đa n hàng (trong ví dụ là 500000), còn n thì ngược lại. Tuy nhiên, gần như không nhanh như ước tính pg_class.


8
Cuối cùng tôi đã cập nhật trang Postgres Wiki với truy vấn được cải thiện.
Erwin Brandstetter

5
Với 9,5 nhận được một nhanh chóng ước tính nên có thể sử dụng các tablesamplekhoản: ví dụselect count(*) * 100 as cnt from mytable tablesample system (1);
a_horse_with_no_name

1
@JeffWidman: Tất cả những ước tính này thể lớn hơn số lượng hàng thực tế vì nhiều lý do khác nhau. Ít nhất, việc xóa có thể đã xảy ra trong thời gian chờ đợi.
Erwin Brandstetter

2
@ErwinBrandstetter nhận ra rằng câu hỏi này đã cũ, nhưng nếu bạn gói truy vấn trong truy vấn con thì giới hạn này có còn hiệu quả không hay toàn bộ truy vấn con sẽ được thực thi sau đó bị giới hạn trong truy vấn bên ngoài. SELECT count(*) FROM (Select * from (SELECT 1 FROM token) query) LIMIT 500000) limited_query;(Tôi hỏi vì tôi đang cố gắng tính số lượng từ một truy vấn tùy ý có thể đã có mệnh đề giới hạn trong đó)
Nicholas Erdenberger.

1
@NicholasErdenberger: Điều đó phụ thuộc vào truy vấn con. Dù sao thì Postgres cũng cần phải xem xét nhiều hàng hơn giới hạn (như với ORDER BY somethingtrong khi nó không thể sử dụng chỉ mục hoặc với các hàm tổng hợp). Ngoài ra, chỉ một số hàng giới hạn từ truy vấn con được xử lý.
Erwin Brandstetter

12

Tôi đã làm điều này một lần trong một ứng dụng postgres bằng cách chạy:

EXPLAIN SELECT * FROM foo;

Sau đó, kiểm tra kết quả đầu ra với một regex hoặc logic tương tự. Đối với một SELECT * đơn giản, dòng đầu tiên của đầu ra sẽ giống như sau:

Seq Scan on uids  (cost=0.00..1.21 rows=8 width=75)

Bạn có thể sử dụng rows=(\d+)giá trị này làm ước tính sơ bộ về số hàng sẽ được trả lại, sau đó chỉ thực hiện giá trị thực tế SELECT COUNT(*)nếu ước tính nhỏ hơn 1,5 lần ngưỡng của bạn (hoặc bất kỳ con số nào bạn cho là hợp lý đối với ứng dụng của mình).

Tùy thuộc vào mức độ phức tạp của truy vấn của bạn, con số này có thể ngày càng ít chính xác hơn. Trên thực tế, trong ứng dụng của tôi, khi chúng tôi thêm các phép nối và các điều kiện phức tạp, nó trở nên không chính xác đến mức hoàn toàn vô giá trị, ngay cả khi biết chúng ta đã trả về bao nhiêu hàng trong lũy ​​thừa 100, vì vậy chúng tôi phải từ bỏ chiến lược đó.

Nhưng nếu truy vấn của bạn đủ đơn giản để Pg có thể dự đoán trong phạm vi sai số hợp lý nào đó, nó sẽ trả về bao nhiêu hàng, thì nó có thể phù hợp với bạn.


2

Tham khảo lấy từ Blog này.

Bạn có thể sử dụng bên dưới để truy vấn để tìm số hàng.

Sử dụng pg_class:

 SELECT reltuples::bigint AS EstimatedCount
    FROM   pg_class
    WHERE  oid = 'public.TableName'::regclass;

Sử dụng pg_stat_user_tables:

SELECT 
    schemaname
    ,relname
    ,n_live_tup AS EstimatedCount 
FROM pg_stat_user_tables 
ORDER BY n_live_tup DESC;

Chỉ cần lưu ý nhanh rằng bạn cần PHÂN TÍCH CHÂN TÍCH bảng của bạn để phương pháp này hoạt động.
William Abma

1

Trong Oracle, bạn có thể sử dụng rownumđể giới hạn số hàng được trả về. Tôi đoán cấu trúc tương tự cũng tồn tại trong các SQL khác. Vì vậy, với ví dụ bạn đã đưa ra, bạn có thể giới hạn số hàng được trả về 500001 và áp dụng count(*)sau đó:

SELECT (case when cnt > 500000 then 500000 else cnt end) myCnt
FROM (SELECT count(*) cnt FROM table WHERE rownum<=500001)

1
Bảng SELECT count (*) cnt FROM sẽ luôn trả về một hàng duy nhất. Không chắc về cách LIMIT sẽ thêm bất kỳ lợi ích nào ở đó.
Chris Bednarski,

@ChrisBednarski: Tôi đã xác minh phiên bản oracle của câu trả lời của mình trên Oracle db. Nó hoạt động tuyệt vời và giải quyết những gì tôi nghĩ là vấn đề của OP (0,05 giây count(*)với rownum, 1 giây mà không sử dụng rownum). Vâng, SELECT count(*) cnt FROM tablesẽ luôn luôn trở lại 1 hàng, nhưng với điều kiện LIMIT, nó sẽ trở lại "500.001" khi kích thước bảng là trên 500000 và <size> khi bảng của kích thước <= 500000.
Ritesh

2
Truy vấn PostgreSQL của bạn hoàn toàn vô nghĩa. Sai về mặt cú pháp và logic. Vui lòng sửa hoặc loại bỏ nó.
Erwin Brandstetter

@ErwinBrandstetter: Đã xóa, không nhận ra PostgreSQL quá khác biệt.
Ritesh

@allrite: không nghi ngờ gì nữa, truy vấn Oracle của bạn hoạt động tốt. LIMIT hoạt động khác nhau. Ở mức cơ bản, nó giới hạn số hàng được trả về cho máy khách chứ không phải số hàng được cơ sở dữ liệu truy vấn.
Chris Bednarski

0

Cột văn bản rộng bao nhiêu?

Với GROUP BY, bạn không thể làm gì nhiều để tránh bị quét dữ liệu (ít nhất là quét chỉ mục).

Tôi khuyên bạn nên:

  1. Nếu có thể, hãy thay đổi lược đồ để xóa dữ liệu văn bản trùng lặp. Bằng cách này, số lượng sẽ xảy ra trên một trường khóa nước ngoài hẹp trong bảng 'nhiều'.

  2. Ngoài ra, tạo một cột được tạo với HASH của văn bản, sau đó NHÓM THEO cột băm. Một lần nữa, điều này là để giảm khối lượng công việc (quét qua một chỉ mục cột hẹp)

Biên tập:

Câu hỏi ban đầu của bạn không hoàn toàn phù hợp với chỉnh sửa của bạn. Tôi không chắc liệu bạn có biết rằng COUNT, khi được sử dụng với GROUP BY, sẽ trả về số lượng mục trên mỗi nhóm chứ không phải số mục trong toàn bộ bảng.


0

Bạn có thể nhận được số lượng bằng truy vấn dưới đây (không có * hoặc bất kỳ tên cột nào).

select from table_name;

2
Điều này dường như không nhanh hơn count(*).
nắng

-3

Đối với SQL Server (2005 trở lên), một phương pháp nhanh chóng và đáng tin cậy là:

SELECT SUM (row_count)
FROM sys.dm_db_partition_stats
WHERE object_id=OBJECT_ID('MyTableName')   
AND (index_id=0 or index_id=1);

Thông tin chi tiết về sys.dm_db_partition_stats được giải thích trong MSDN

Truy vấn thêm các hàng từ tất cả các phần của bảng (có thể) được phân vùng.

index_id = 0 là một bảng không có thứ tự (Heap) và index_id = 1 là một bảng có thứ tự (chỉ mục được phân cụm)

Các phương pháp nhanh hơn (nhưng không đáng tin cậy) được trình bày chi tiết tại đây.

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.