2012-11-05 27 views
5

Tôi có bảng này:trung bình qua khó khăn để xác định phân vùng

create table t (value int, dt date); 

value |  dt  
-------+------------ 
    10 | 2012-10-30 
    15 | 2012-10-29 
    null | 2012-10-28 
    null | 2012-10-27 
    7 | 2012-10-26 

Và tôi muốn kết quả này:

value |  dt  
-------+------------ 
    10 | 2012-10-30 
    5 | 2012-10-29 
    5 | 2012-10-28 
    5 | 2012-10-27 
    7 | 2012-10-26 

Tôi muốn các giá trị null, cũng như một giá trị trước đó không null, được thay thế bằng giá trị trung bình của giá trị không null trước đó khi bảng được sắp xếp theo ngày giảm dần. Trong ví dụ này, giá trị 15 là giá trị không null trước đó của hai giá trị null tiếp theo. Vì vậy, 15/3 = 5.

SQL Fiddle

+0

+1 Câu hỏi rất hay. Và nó có tất cả mọi thứ nó cần - tốt, tôi suy ra PostgreSQL 9.2 từ fiddle. –

Trả lời

4

Tôi tìm thấy một giải pháp đáng ngạc nhiên đơn giản:

SELECT max(value) OVER (PARTITION BY grp) 
    /count(*) OVER (PARTITION BY grp) AS value 
     ,dt 
FROM (
    SELECT *, count(value) OVER (ORDER BY dt DESC) AS grp 
    FROM t 
    ) a; 

-> sqlfiddle

Kể từ count() bỏ qua NULL giá trị, bạn có thể sử dụng một số chạy (mặc định trong chức năng cửa sổ) để nhóm các giá trị một cách nhanh chóng (->grp).

Mỗi nhóm có chính xác giá trị không null, vì vậy chúng tôi có thể sử dụng min/max/sum để nhận được cùng một kết quả trong một chức năng cửa sổ khác. Chia cho số thành viên (count(*) lần này, để đếm NULL giá trị!) Trong số grp và chúng tôi đã hoàn tất.

+0

Đẹp, nhưng có vẻ như đặc trưng của PostgreSQL. – jsalvata

+0

@jsalvata: "Nhưng"? Bạn có nhận thấy thẻ [PostgreSQL] không? Ngoài ra, đây là SQL chuẩn. [-> ** sqlfiddle cho máy chủ SQL ** với truy vấn giống hệt nhau] (http://www.sqlfiddle.com/#!6/fb11e/1). –

+1

Không, tôi đã không làm vậy. Crappy mySQL không hỗ trợ nó. Vâng, nó là tiêu chuẩn. – jsalvata

1

Như một câu đố, đây là một giải pháp ... trong thực tế nó có thể thực hiện khủng khiếp tuỳ theo tính chất của dữ liệu. Xem các chỉ số của bạn, trong mọi trường hợp:

create database tmp; 
create table t (value float, dt date); -- if you use int, you need to care about rounding 
insert into t values (10, '2012-10-30'), (15, '2012-10-29'), (null, '2012-10-28'), (null, '2012-10-27'), (7, '2012-10-26'); 

select t1.dt, t1.value, t2.dt, t2.value, count(*) cnt 
from t t1, t t2, t t3 
where 
    t2.dt >= t1.dt and t2.value is not null 
    and not exists (
     select * 
     from t 
     where t.dt < t2.dt and t.dt >= t1.dt and t.value is not null 
    ) 
    and t3.dt <= t2.dt 
    and not exists (
     select * 
     from t where t.dt >= t3.dt and t.dt < t2.dt and t.value is not null 
    ) 
group by t1.dt; 

+------------+-------+------------+-------+-----+ 
| dt   | value | dt   | value | cnt | 
+------------+-------+------------+-------+-----+ 
| 2012-10-26 |  7 | 2012-10-26 |  7 | 1 | 
| 2012-10-27 | NULL | 2012-10-29 | 15 | 3 | 
| 2012-10-28 | NULL | 2012-10-29 | 15 | 3 | 
| 2012-10-29 | 15 | 2012-10-29 | 15 | 3 | 
| 2012-10-30 | 10 | 2012-10-30 | 10 | 1 | 
+------------+-------+------------+-------+-----+ 
5 rows in set (0.00 sec) 

select dt, value/cnt 
from (
    select t1.dt , t2.value, count(*) cnt 
    from t t1, t t2, t t3 
    where 
     t2.dt >= t1.dt and t2.value is not null 
     and not exists (
      select * 
      from t 
      where t.dt < t2.dt and t.dt >= t1.dt and t.value is not null 
     ) 
    and t3.dt <= t2.dt 
    and not exists (
     select * 
     from t 
     where t.dt >= t3.dt and t.dt < t2.dt and t.value is not null 
    ) 
    group by t1.dt 
) x; 

+------------+-----------+ 
| dt   | value/cnt | 
+------------+-----------+ 
| 2012-10-26 |   7 | 
| 2012-10-27 |   5 | 
| 2012-10-28 |   5 | 
| 2012-10-29 |   5 | 
| 2012-10-30 |  10 | 
+------------+-----------+ 
5 rows in set (0.00 sec) 

Giải thích:

  • t1 là bảng gốc
  • t2 là hàng trong bảng với ngày lớn hơn ít nhất là với một giá trị không null
  • t3 là tất cả các hàng ở giữa, vì vậy chúng tôi có thể nhóm theo các hàng khác và đếm

Xin lỗi tôi không thể rõ ràng hơn. Đó là khó hiểu đối với tôi quá :-)

+0

Nếu quá phức tạp để giải thích, rất có thể, nó quá phức tạp. :) –

+0

Thật vậy. Có vẻ gần như dễ hiểu sau khi chỉnh sửa của Clodoaldo. – jsalvata