2010-02-03 36 views
12

Tôi có cột DATE mà tôi muốn làm tròn đến khoảng 10 phút tiếp theo thấp hơn trong truy vấn (xem ví dụ bên dưới).Vòng tròn đến 10 phút khoảng

Tôi đã thực hiện điều đó bằng cách cắt ngắn giây và sau đó trừ số cuối của phút.

WITH test_data AS (
     SELECT TO_DATE('2010-01-01 10:00:00', 'YYYY-MM-DD HH24:MI:SS') d FROM dual 
    UNION SELECT TO_DATE('2010-01-01 10:05:00', 'YYYY-MM-DD HH24:MI:SS') d FROM dual 
    UNION SELECT TO_DATE('2010-01-01 10:09:59', 'YYYY-MM-DD HH24:MI:SS') d FROM dual 
    UNION SELECT TO_DATE('2010-01-01 10:10:00', 'YYYY-MM-DD HH24:MI:SS') d FROM dual 
    UNION SELECT TO_DATE('2099-01-01 10:00:33', 'YYYY-MM-DD HH24:MI:SS') d FROM dual 
) 
-- #end of test-data 
SELECT 
    d, TRUNC(d, 'MI') - MOD(TO_CHAR(d, 'MI'), 10)/(24 * 60) 
FROM test_data 

Và đây là kết quả:

01.01.2010 10: 00:00     01.01.2010 10: 00:00
01.01.2010 10: 05 : 00     01.01.2010 10: 00:00
01.01.2010 10: 09:59     01.01.2010 10: 00:00
01.01.2010 10: 10:00     01.01.2010 10: 10:00
01.01.2099 10: 00 : 33     2099/01/01 10: 00:00

trình như mong đợi, nhưng là có một cách tốt hơn?

EDIT:

Tôi đã tò mò về hiệu suất, vì vậy tôi đã làm các xét nghiệm sau đây với 500.000 hàng và (không thực sự) ngày ngẫu nhiên. Tôi sẽ thêm các kết quả như ý kiến ​​cho các giải pháp được cung cấp.

DECLARE 
    t  TIMESTAMP := SYSTIMESTAMP; 
BEGIN 
    FOR i IN (
    WITH test_data AS (
     SELECT SYSDATE + ROWNUM/5000 d FROM dual 
     CONNECT BY ROWNUM <= 500000 
    ) 
    SELECT TRUNC(d, 'MI') - MOD(TO_CHAR(d, 'MI'), 10)/(24 * 60) 
    FROM test_data 
) 
    LOOP 
    NULL; 
    END LOOP; 
    dbms_output.put_line(SYSTIMESTAMP - t); 
END; 

Cách tiếp cận này mất 03.24 s.

+0

gì về 'CHỌN TRƯỜNG HỢP KHI TO_CHAR (date_col, 'MI') giữa 0 và 10 THEN TO_DATE (TO_CHAR (date_col, 'YYYY') || '-' || TO_CHAR (ngày_col, 'MM') || '-' || TO_CHAR (ngày_col, 'DD') || '' || TO_CHAR (ngày_col, 'HH') || ': 00' , 'YYYY-MM-DD HH: MI') END'? –

+0

@OMG Ngựa: Xin lỗi, nhưng trả về toàn bộ giờ khi '0 <= MI <= 10', nếu không' NULL'. –

+0

Không muốn lộn xộn với phần còn lại của WHEN –

Trả lời

10
select 
    trunc(sysdate, 'mi') 
    - numtodsinterval(mod(EXTRACT(minute FROM cast(sysdate as timestamp)), 10), 'minute') 
from dual; 

hoặc thậm chí

select 
    trunc(sysdate, 'mi') 
    - mod(EXTRACT(minute FROM cast(sysdate as timestamp)), 10)/(24 * 60) 
from dual; 
+0

@Tom: Chào mừng bạn đến với StackOverflow! Cả hai cách tiếp cận dường như hoạt động, đầu tiên là '04.18 s', thứ hai chỉ '03.33 s'! Hiệu suất của truy vấn thứ hai của bạn hầu như giống với truy vấn ban đầu của tôi, nhưng tránh 'TO_CHAR', vì vậy tôi sẽ chấp nhận câu trả lời của bạn. Cảm ơn! –

1

Bạn có thể lấy giá trị trả lại dưới dạng chuỗi và chuỗi con phía bên trái tối đa số phút cuối cùng và thay thế bằng 0. Tôi sẽ không chính xác nói rằng tốt hơn trừ khi bạn cung cấp một số loại số liệu.

+1

SELECT SYSDATE, TO_DATE (SUBSTR (TO_CHAR (SYSDATE, 'YYYYMMDD HH24MI'), 1, 12) || '0', 'YYYYMMDD HH24MI') TỪ DUAL Những gì anh ta đã nói trong Mã SQL và Ngoài ra những gì anh ta nói về hiệu suất, tôi không chắc chắn sẽ tốt hơn, nhưng bạn sẽ trông đơn giản hơn. – Craig

+0

Xem kiểm tra hiệu suất trong câu hỏi đã chỉnh sửa. Cách tiếp cận này lấy '04.32 s'. –

1

Không nhất thiết bất kỳ tốt hơn, nhưng phương pháp khác:

WITH test_data AS (
     SELECT TO_DATE('2010-01-01 10:00:00', 'YYYY-MM-DD HH24:MI:SS') d FROM dual 
    UNION SELECT TO_DATE('2010-01-01 10:05:00', 'YYYY-MM-DD HH24:MI:SS') d FROM dual 
    UNION SELECT TO_DATE('2010-01-01 10:09:59', 'YYYY-MM-DD HH24:MI:SS') d FROM dual 
    UNION SELECT TO_DATE('2010-01-01 10:10:00', 'YYYY-MM-DD HH24:MI:SS') d FROM dual 
    UNION SELECT TO_DATE('2099-01-01 10:00:33', 'YYYY-MM-DD HH24:MI:SS') d FROM dual 
) 
-- #end of test-data 
SELECT 
    d, TRUNC(d) + FLOOR((d-TRUNC(d))*24*6)/(24*6) 
FROM test_data 
+0

Xem kiểm tra hiệu suất trong câu hỏi đã chỉnh sửa. Cách tiếp cận này lấy '04.16 s'. –

+0

Được chấp nhận vì nó ngắn và khá dễ đọc. Cảm ơn! Sẽ gắn bó với giải pháp của tôi mặc dù, vì phải mất ít thời gian hơn. –

+0

@Tony Andrews: Đã chấp nhận câu trả lời của Tom thay vì câu trả lời của bạn, vì tôi thấy nó trực quan hơn và hiệu suất tốt hơn. Hy vọng bạn không quan tâm ... –

3

Tôi thường ghét làm ngày -> nhân vật -> Ngày chuyển đổi khi nó không cần thiết. Tôi muốn sử dụng số.

select trunc((sysdate - trunc(sysdate))*60*24,-1)/(60*24)+trunc(sysdate) from dual;  

Thao tác này sẽ lấy phút từ ngày hiện tại, cắt bớt khoảng thời gian 10 phút, sau đó thêm chúng trở lại để đặt lại ngày. Tất nhiên, bạn có thể thay thế sysdate với bất kỳ ngày nào bạn muốn. Nó tin tưởng chuyển đổi tiềm ẩn nhiều hơn tôi muốn nhưng ít nhất nó sẽ làm việc cho bất kỳ định dạng ngày NLS nào.

+0

Xem bài kiểm tra hiệu suất trong câu hỏi đã chỉnh sửa. Cách tiếp cận này lấy '04.39 s'. –

1

phương pháp khác,

select my_date - mod((my_date-trunc(my_date))*24*60, 10)/24/60 
from (
    select sysdate my_date from dual 
); 

Một thay thế có thể được nhanh hơn vì nó loại bỏ các cuộc gọi đến trunc.

select my_date - mod((my_date-to_date('1970', 'yyyy'))*24*60, 10)/24/60 
from (
    select sysdate my_date from dual 
); 
+0

+1: Một cách tiếp cận thú vị khác hoạt động. Lấy '04,60 s' trong bài kiểm tra của tôi, tôi đoán' mod' làm cho nó chậm hơn so với cách tiếp cận của tôi. –

+0

Tôi đã đăng một giải pháp thay thế, thao tác này sẽ xóa cuộc gọi để cắt. Nó sẽ rất thú vị để xem nếu có bất kỳ sự khác biệt trong thử nghiệm của bạn. –

+0

@ar: Rất tiếc, không được thông báo về cập nhật của bạn. Truy vấn được cập nhật của bạn hoạt động tốt hơn: '04.22 s' –

0

Để trở về khoảng thời gian 10 phút trên hôm sau, tôi sử dụng các truy vấn sau đây. Tôi hy vọng nó sẽ có ích bởi vì tôi không thể chỉ đơn giản là làm một

trunc(sysdate, 'mi') + mod(EXTRACT(minute FROM cast(sysdate as timestamp)), 10)/(24 * 60)

Tôi đã làm điều này và nó làm việc cho tôi.

select 
 
case when mod(EXTRACT(minute FROM cast(sysdate as timestamp)),5) between 1 and 4 
 
     then trunc(sysdate,'mi')+((5*TRUNC(EXTRACT(minute FROM cast(sysdate as timestamp))/5, 
 
0)+5)-EXTRACT(minute FROM cast(sysdate as timestamp)))/1440 
 
     else trunc(sysdate,'mi') 
 
end 
 
from dual

này được dựa trên this đường bưu điện.

0

Tôi nghĩ rằng để giải quyết điều này có một cách dễ dàng hơn và nhanh hơn để làm tròn đến 10 giây tiếp theo, 1 phút, 10 phút vv. Cố gắng vận dụng dấu thời gian của bạn như là một chuỗi sử dụng SUBSTR() như thế này:

SELECT 
SUBSTR(datetime(d),1,18)||'0' AS Slot10sec, 
SUBSTR(datetime(d),1,17)||'00' AS Slot1min, 
SUBSTR(datetime(d),1,15)||'0:00' AS Slot10min, 
SUBSTR(datetime(d),1,14)||'00:00' AS Slot1h, 
MY_VALUE 
FROM MY_TABLE; 
Các vấn đề liên quan