Tối ưu câu lệnh SQL: Oracle so sánh thời gian

Ở bài viết Oracle so sánh thời gian ngày tháng – Oracle compare date time mình có chia sẻ về cách ta có thể so sánh thời gian trong Oracle. Nhưng trong quá trình triển khai thực thế, việc turning hệ thống thì thấy việc sử dụng các câu lệnh này sẽ có vấn đề về index trên cột này. Ta cùng phân tích và tối ưu câu lệnh SQL cho vấn đề này nhé.

Đối với câu lệnh PL/SQL, cụ thể là Oracle thì hàm to_char(…) gây mất index đối với cột dữ liệu kiểu DATETIME. Tương tự với việc sử dụng toán tử LIKE cho kiểu dữ liệu VARCHAR hoặc NVARCHAR. Nhưng đổi lại việc sử dụng hàm TRUNC lại không làm mất index kiểu dữ liệu DATETIME.
Để tối ưu câu lệnh phải so sánh thời gian ta tránh việc sử dụng hàm to_char(…) để tránh mất index column này. Mặc dù ta nhìn câu lệnh SQL khi turning seckhông được tường minh và dễ hiểu lắm. Đúng thôi, ta buộc phải đánh đổi, cuộc sống mà.

Cách tối ưu khi so sánh ngày tháng

– So sánh ngày:

Kiểu cũ: Ta tiến hành convert dữ liệu dạng ngày sang kiểu string và tiến hành so sánh:

ví dụ:
Cần lấy ngày tạo lớn hơn hoặc bằng ngày 06/04/2018

AND TO_CHAR(datecreate, ‘YYYYMMDD’) >= ‘20180406’;

Turning: Ta có thể viết lại hàm này một chút để nó sẽ chạy nhanh hơn, tránh gây ảnh hưởng hiệu năng hệ thống.
Như câu lệnh trên ta có thể thấy ta đã đều ép kiểu dữ liệu về String để so sánh rồi, chính việc luôn phải convert dữ liệu từ kiểu DATETIME về kiểu String nên việc này sẽ ảnh hưởng đến chỉ số CPU. Ta sẽ để việc so sánh này về so sánh bằng kiểu DATETIME, nghĩa là ta sẽ chuyển đối tượng so sánh về kiểu thời gian, cụ thể câu lệnh trên được viết lại thành:

AND TRUNC(datecreate) >= TO_DATE(‘20180406’,'YYYYMMDD');

Như vậy, hàm TO_DATE(‘20180406’,'YYYYMMDD') chỉ phải sử dụng 1 lần duy nhất để chuyển kiểu String về kiểu DATETIME để so sánh. Hàm TRUNC sẽ không gây mất index mà đơn giản để chính xác ta để nó về thời gian đầu tiên của ngày, nghĩa là 0h 00p.

Hoặc trong khoảng ngày cũng tương tự:
Cách 1:
AND TRUNC(datecreate) >= TO_DATE(‘20180406’,'YYYYMMDD')
AND TRUNC(datecreate) < TO_DATE(‘20180411’,'YYYYMMDD')

– So sánh ngày chi tiết tới thời gian:

Kiểu cũ: Ta tiến hành convert dữ liệu dạng ngày sang kiểu string và tiến hành so sánh:

ví dụ:

Cần lấy ngày tạo lớn hơn hoặc bằng 7h tối ngày 06/04/2018:

AND TO_CHAR(laa.datcre, ‘YYYYMMDD HH24:MI:SS’) >= ‘20180406 19:00:00’

Turning: Việc này thì ta cũng làm tương tự như trên nhưng cần lưu ý xử lý về vấn đề thời gian giờ – phút – giây. Việc chuyển định dạng thời gian này hơi phức tạp, mình sẽ nghiên cứu và bổ sung thêm.

Tối ưu câu lệnh SQL không chỉ tập trung vào các cú pháp và hướng xử lý như việc tối ưu trên mà ta còn cần phải chú ý tới dữ liệu ta sử dụng. Đối với những dữ liệu khi cần tìm kiếm nhiều ta cũng cần quan tâm đến việc đánh Index và bố trí phân vùng cho chúng được hợp lý và tốt hơn.

Oracle so sánh thời gian ngày tháng

Oracle so sánh thời gian ngày tháng – Oracle compare date time

Trong quá trình sử dụng PL/SQL, ta thường xuyên phải xử lý thông tin liên quan đến thời gian ngày tháng. Một số việc liên quan đến ngày tháng trong Oracle như: insert dữ liệu vào bảng, tạo các view, cập nhật dữ liệu và tạo các báo cáo.Trong bài này tôi xin giới thiệu với các bạn một số cách trên Oracle dùng để so sánh thời gian ngày tháng.

Đối với Oracle, có kiểu dữ liệu liên quan đến ngày tháng (DateTime, Date, Timestamp) và hàm xử lý ngày tháng trong oracle (to_date(), trunc()…). Trong Oracle so sánh thời gian ngày tháng có hai cách đó là so sánh trực tiếp và gian tiếp.

So sánh thời gian ngày tháng trực tiếp

Trong Oracle, kiểu dữ liệu về thời gian ngày tháng cơ bản như dạng kiểu số nên ta có thể thao tác tính toán và so sánh như dữ liệu kiểu số (number) như bình thường. Việc so sánh này do các toán tử như lớn hơn, nhỏ hơn, bằng… giữa các dữ liệu.

Sử dụng các toán tử này có ưu điểm là không bị mất Index trên trường dữ liệu muốn so sánh nên việc so sánh thường rất là nhanh chóng và tối ưu.

Ngược lại vì kiểu ngày tháng như kiểu số nên rất chính xác nhưng kiểu ngày tháng rất phụ thuộc vào kiểu dữ liệu. Kiểu dữ liệu ngày tháng ta định nghĩa như Date, DateTime, Timestamp thường phân bậc năm – tháng – ngày – giờ – phút – giây… nên đôi lúc rất dễ nhầm lẫn khi ta so sánh mà không nắm chắc kiểu dữ liệu. Ví dụ như lấy dữ liệu của ngày hôm nay mà kiểu dữ liệu lưu là DateTime thì phải lấy dữ liệu lớn hơn hôm qua và nhỏ hơn hôm nay mới đủ. Để khắc phục điều này ta phải dùng đến hàm trunc() để làm tròn các thông tin ngày tháng để tiện so sánh và chính xác. Ta có thể tham khảo hàm trunc() với nhiều tham số như: trunc(), trunc(sysdate,’month’), trunc(sysdate, ‘year’)…

So sánh thời gian ngày tháng gián tiếp

Như đã nói ở trên, việc so sánh thời gian ngày tháng trực tiếp đôi lúc gặp một số lỗi nhất định do sơ suất. Để khắc phục điều này và làm tăng tính trực quan khi nhìn câu lệnh SQL ta có thể để Oracle so sánh thời gian một cách gián tiếp. Cũng giống như việc làm tròn dữ liệu, ta có thể chuyển đổi dữ liệu ngày tháng về cùng một định dạng khác rồi dùng chúng như một cách gián tiếp để so sánh. Ở đây tôi thường sử dụng việc chuyển đổi dữ liệu về kiểu NVARCHAR để tiến hành xử lý. Việc chuyển đổi này có nhiều điểm lợi ích ở trên nhưng có 1 nhược điểm quan trọng đó là khi sử dụng hàm to_char() để chuyển đổi ta đã vô tình làm mất Index của dữ liệu. Điều này đối với các bảng dữ liệu có lượng record lớn sẽ làm mất Index sẽ làm làm chậm hệ thống, nhưng với những lệnh SQL đơn giản thì vẫn rất thuận tiện và cần thiết. Sau đây là một số hàm so sánh bằng cách gián tiếp này:

– So sánh ngày:

Ta tiến hành convert dữ liệu dạng ngày sang kiểu string và tiến hành so sánh:

ví dụ:
Cần lấy ngày tạo lớn hơn hoặc bằng ngày 06/04/2018

AND TO_CHAR(datecreate, ‘YYYYMMDD’) >= ‘20180406’

Hoặc trong một khoảng ngày:

Cách 1:

AND TO_CHAR(datecreate, ‘YYYYMMDD’) >= ‘20180406’
AND TO_CHAR(datecreate, ‘YYYYMMDD’) < ‘20180411’

Cách 2:

AND TO_CHAR(datecreate, ‘YYYYMMDD’) between ‘20180406’ and ‘20180410’

– So sánh ngày chi tiết tới thời gian:

Ta tiến hành convert dữ liệu dạng ngày sang kiểu string và tiến hành so sánh:

ví dụ:

Cần lấy ngày tạo lớn hơn hoặc bằng 7h tối ngày 06/04/2018:

AND TO_CHAR(laa.datcre, ‘YYYYMMDD HH24:MI:SS’) >= ‘20180406 19:00:00’

Hoặc trong một khoảng thời gian:

Cách 1:

and TO_CHAR(laa.datcre, ‘YYYYMMDD HH24:MI:SS’) >= ‘20180406 19:00:00’
and TO_CHAR(laa.datcre, ‘YYYYMMDD HH24:MI:SS’) < ‘20180407 00:00:01’

Cách 2:

and TO_CHAR(laa.datcre, ‘YYYYMMDD HH24:MI:SS’) between ‘20180406 19:00:00’ and ‘20180407 00:00:00’

Có thể bạn quan tâm: