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

CTE và Nghịch lý sinh nhật

Một truy vấn thú vị đã được Will Leinweber chỉnh sửa từ Postgres Open:

-- this returns a different result each time it is ran
with recursive s as (
  select random()
union
  select random() from s
) select count(*) from s;

Tôi thích ví dụ này:một kết quả đáng ngạc nhiên, có thể được giải thích bằng (và thực sự giúp giải thích) hành vi CTE.

Sự thật bất ngờ được biểu thị bằng từ "nghịch lý" và trên thực tế, đây là một biểu hiện (một "ví dụ", trong biệt ngữ của các lập trình viên) của cái được gọi là Nghịch lý sinh nhật .

Công thức đơn giản nhất của nó có lẽ là thế này:nếu bạn chọn ngẫu nhiên 23 người, xác suất để hai người trong số họ có cùng ngày sinh là lớn hơn 50%.

Kết quả thật bất ngờ, bởi vì có 366 sinh nhật khác nhau, và con số 23 dường như rất nhỏ so với 366.

Tuy nhiên, nó là chính xác, vì nó có thể được hiển thị bằng một tính toán trực tiếp. Trong PostgreSQL, chúng ta có thể chạy một CTE đệ quy khác:

WITH RECURSIVE r(i,acc) AS (
  SELECT 1, 1 :: double precision
UNION
  SELECT i + 1,
    acc * (((366 - i) :: double precision) / 366)
  FROM r WHERE acc > 0.5
) SELECT count(1) FROM r;

kết quả là 23.

CTE đệ quy dừng khi bước đệ quy không thêm bất kỳ hàng mới nào. Trong truy vấn cuối cùng, acc đại diện cho xác suất mà i đầu tiên sinh nhật là khác nhau, vì vậy đệ quy dừng khi con số đó không quá 50%.

Trong truy vấn được đề cập ở phần đầu, chúng tôi sẽ gọi tắt là "truy vấn ngẫu nhiên", CTE đệ quy kết thúc khi random() không thêm một hàng mới. Nghĩa là, khi giá trị được tính toán ngẫu nhiên đã được tính toán trong một lần lặp trước đó; đó là bởi vì CTE đệ quy đang sử dụng UNION thay vì UNION ALL .

Đây thực sự là nghịch lý Sinh nhật, với 366 được thay thế bằng số giá trị khác biệt lớn nhất có thể có random() có thể sản xuất. Con số đó chính xác là bao nhiêu?

random() hàm trả về một giá trị chính xác kép, định nghĩa chính xác của nó phụ thuộc vào hệ thống. Tuy nhiên, không phải tất cả các giá trị chính xác kép đều có thể được tạo ra; hàm C bên dưới có thể tạo ra 2 ^ 31 kết quả khác nhau, bất kể kích thước bit của giá trị chính xác kép. Điều này đủ tốt trong thực tế, đồng thời đảm bảo khả năng tương thích với tất cả các kiến ​​trúc và triển khai thư viện khác nhau.

Vì vậy, chúng tôi có thể thay thế 366 bằng 2 ^ 31 trong truy vấn của mình và thay vì 23, chúng tôi nhận được câu trả lời là 54563.

Nó có gần đến đầu ra thực tế của truy vấn ngẫu nhiên không? Hãy để chúng tôi chạy nó một vài lần, thu thập kết quả và tính giá trị trung bình:

gianni=# create table t(n int);
CREATE TABLE

gianni=# with recursive s as (
  select random()
union
  select random() from s
) insert into t select count(1) from s;
INSERT 0 1

/* repeat the last query 49 times */

gianni=# select count(1), avg(n) from t;

 count | avg
-------+--------------------
    50 | 54712.060000000000
 (1 row)

Mức trung bình của các kết quả thực tế khá gần với ngưỡng kỳ vọng là 54563; sự khác biệt nhỏ hơn 0,3%, khá chính thống , chúng tôi có thể nói!


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Định dạng đầu ra thay thế cho psql

  2. làm thế nào để thực thi một tập lệnh .sql trên heroku?

  3. Ngăn chặn kích hoạt đệ quy trong PostgreSQL

  4. Chức năng cửa sổ hoặc Biểu thức bảng thông thường:đếm các hàng trước đó trong phạm vi

  5. Ruby 'pg' gem liên kết đến bản sao sai của libpq.5.dylib (trên OSX)