2008-11-12 22 views
12

Đây là thứ xuất hiện thường xuyên đến mức tôi gần như không nghĩ về nó nữa nhưng tôi gần như chắc chắn rằng tôi không làm điều này một cách tốt nhất.Cách tốt nhất để chọn hàng có dấu thời gian gần nhất phù hợp với tiêu chí

Câu hỏi đặt ra: Giả sử bạn có bảng sau

CREATE TABLE TEST_TABLE 
(
    ID   INTEGER, 
    TEST_VALUE NUMBER, 
    UPDATED  DATE, 
    FOREIGN_KEY INTEGER 
); 

cách tốt nhất để chọn test_value gắn liền với hàng cập nhật gần đây nhất mà FOREIGN_KEY = 10 là gì?

EDIT: Hãy làm điều này thú vị hơn vì các câu trả lời dưới đây chỉ đơn giản là đi theo phương pháp sắp xếp của tôi và sau đó chọn hàng trên cùng. Không tệ nhưng đối với lợi nhuận lớn, đơn đặt hàng sẽ giết hiệu suất. Vì vậy, điểm thưởng: làm thế nào để làm điều đó một cách có thể mở rộng (tức là không có thứ tự không cần thiết bởi).

+0

Thứ tự bởi sẽ không giết hiệu suất nếu có chỉ mục tại chỗ có thể được tận dụng –

Trả lời

13

chức năng phân tích là bạn bè

SQL> select * from test_table; 

     ID TEST_VALUE UPDATED FOREIGN_KEY 
---------- ---------- --------- ----------- 
     1   10 12-NOV-08   10 
     2   20 11-NOV-08   10 

SQL> ed 
Wrote file afiedt.buf 

    1* select * from test_table 
SQL> ed 
Wrote file afiedt.buf 

    1 select max(test_value) keep (dense_rank last order by updated) 
    2 from test_table 
    3* where foreign_key = 10 
SQL>/

MAX(TEST_VALUE)KEEP(DENSE_RANKLASTORDERBYUPDATED) 
------------------------------------------------- 
               10 

Bạn cũng có thể mở rộng mà để có được những thông tin cho toàn bộ hàng

SQL> ed 
Wrote file afiedt.buf 

    1 select max(id) keep (dense_rank last order by updated) id, 
    2   max(test_value) keep (dense_rank last order by updated) test_value 
, 
    3   max(updated) keep (dense_rank last order by updated) updated 
    4 from test_table 
    5* where foreign_key = 10 
SQL>/

     ID TEST_VALUE UPDATED 
---------- ---------- --------- 
     1   10 12-NOV-08 

Và cách tiếp cận phân tích thường khá mờ nhạt hiệu quả.

Tôi cũng nên chỉ ra rằng các chức năng phân tích là tương đối mới, vì vậy nếu bạn đang ở trên một cái gì đó sớm hơn 9.0.1, điều này có thể không hoạt động. Đó không phải là một dân số lớn nữa, nhưng luôn có một vài người bị mắc kẹt trên các phiên bản cũ.

+0

đó là một số truy vấn điên người bạn của tôi, công việc tốt –

+0

Sẽ không truy vấn của tôi rownum dưới đây thực hiện tốt hơn? Phân tích đồng ý là một giải pháp chung chung tốt hơn. –

+0

Tôi không chắc cách phân tích hoạt động thực sự nhưng CS cơ bản sẽ cho bạn biết thời gian chạy tối ưu cho nhiệm vụ này truy vấn phải là O (n) trong đó n là số hàng khớp với vị trí. Với một đơn đặt hàng của nó sẽ là O (n^2) –

1

Các thể kém như vậy Tôi hiện đi về làm một cái gì đó như thế này là

SELECT TEST_VALUE 
FROM TEST_TABLE 
WHERE ID = (
    SELECT ID 
    FROM (
    SELECT ID 
    FROM TEST_TABLE 
    WHERE FOREIGN_KEY = 10 
    ORDER BY UPDATED DESC 
) 
    WHERE ROWNUM = 1 
) 

nhưng xin StackOverflow thiên tài, dạy tôi một số thủ thuật

3

Hoặc sử dụng một phụ truy vấn

WHERE updated = (SELECT MAX(updated) ...) 

hoặc chọn bản ghi TOP 1 với

ORDER BY updated DESC 

Trong Oracle cú pháp này sẽ là:

SELECT 
    * 
FROM 
(
    SELECT * FROM test_table 
    ORDER BY updated DESC 
) 
WHERE 
    ROWNUM = 1 
+0

Nhiều hơn một bản ghi với FOREIGN_KEY khác nhau có thể được cập nhật cùng một lúc ... –

+0

Tôi không phải từ dân gian Oracle , như bạn có thể đoán được bằng sự lựa chọn cú pháp của tôi. :-) Nhưng khái niệm chung về việc chọn bản ghi TOP 1 sẽ vượt qua biên giới cú pháp. – Tomalak

+0

vâng, đó dường như là những gì mọi người ngụ ý, nhưng đối với việc trả lời lớn, nhu cầu đặt hàng sẽ giết chết preformance –

-1
select test_value 
from 
(
    select test_value 
    from test_table 
    where foreign_key=10 
    order by updated desc 
) 
where rownum = 1 

Oracle là đủ thông minh để nhận ra nó chỉ cần một hàng duy nhất từ ​​chọn bên trong và nó sẽ làm điều này một cách hiệu quả.

-1

sẽ không làm việc này:

SELECT TOP 1 ID 
FROM test_table 
WHERE FOREIGN_KEY = 10 
ORDER BY UPDATED DESC 

không cần một subquery ...

+0

Không có mệnh đề TOP nào trong Oracle ... – Tomalak

+0

cũng bạn vẫn cần một truy vấn phụ để chọn test_value –

+0

Oh. Không nhận thức được điều đó. My Apologies :) Đoán tôi quá quen với MS SQL – TJMonk15

2

Thứ nhất, bạn sẽ luôn cần phải xem xét tất cả các hàng có khóa ngoài đó và tìm một hàng có giá trị CẬP NHẬT cao nhất ... có nghĩa là MAX hoặc ORDER BY. Hiệu quả của việc so sánh một phần là do trình tối ưu hóa, do đó sẽ phụ thuộc vào phiên bản Oracle của bạn. Cấu trúc dữ liệu của bạn có thể có tác động lớn hơn đến hiệu suất thực tế. Một chỉ mục trên FOREIGN_KEY, UPDATED DESC, TEST_VALUE có lẽ sẽ cung cấp giải pháp có khả năng mở rộng nhất để truy vấn vì thông thường Oracle có thể đưa ra câu trả lời chỉ truy cập vào một khối lá đơn.Có thể có một tác động bất lợi về chèn khi các bản ghi mới phải được chèn vào cấu trúc đó.

+0

có nhưng Max chỉ là O (n) trong khi thứ tự bằng cao hơn –

0

Hiệu suất sẽ phụ thuộc vào những gì được lập chỉ mục. Đây là một phương pháp.

WITH 
ten AS 
(
    SELECT * 
    FROM TEST_TABLE 
    WHERE FOREIGH_KEY = 10 
) 
SELECT TEST_VALUE 
FROM ten 
WHERE UPDATED = 
(
    SELECT MAX(DATE) 
    FROM ten 
) 
1
SELECT TEST_VALUE 
    FROM TEST_TABLE 
WHERE UPDATED  = (SELECT MAX(UPDATED) 
          FROM TEST_TABLE 
         WHERE FOREIGN_KEY = 10) 
    AND FOREIGN-KEY = 10 
    AND ROWNUM  = 1 -- Just in case records have the same UPDATED date 

Thay rằng mất kỷ lục đầu tiên bạn có thể phá vỡ một tie với ID hightest hoặc có thể tối thiểu/TEST_VALUE lớn nhất.

Chỉ mục của FOREIGN_KEY, CẬP NHẬT sẽ giúp thực hiện truy vấn.

1

Cho đến khi tôi đọc câu trả lời của Justin Cave, tôi đã sử dụng mẫu sau để lấy các bản ghi gần đây nhất.

WITH test_table_ranked AS (
    SELECT 
     test_table.*, 
     ROW_NUMBER() OVER (
      PARTITION BY foreign_key ORDER BY updated DESC 
     ) AS most_recent 
    FROM 
     test_table 
) 
SELECT * 
FROM test_table_ranked 
WHERE most_recent = 1 
-- AND foreign_key = 10 

Truy vấn này tìm các cập nhật mới nhất cho mỗi khóa ngoại trong bảng. Mặc dù câu trả lời của Justin là nhanh hơn khi khóa được biết, truy vấn này cũng hoạt động trong SQL Server.

Các vấn đề liên quan