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: Điều kiện về biến trong mệnh đề Where

Hôm nay làm việc thì có chị trong team đố về việc viết 1 thủ tục Oracle như sau:
Có một bảng dữ liệu gồm 4 cột Họ Tên, Số điện thoại, Số CMT và Email. Viết thủ tục đếm xem có bao nhiêu người thỏa mãn 3 tiêu chí mà người dùng nhập vào là Số điện thoại, Số CMT và Email (người dùng có thể nhập hoặc không nhập bất cứ thông tin ở tiêu chí nào cả).

Ban đầu tiếp cận cũng thấy dễ nhưng dần dần cũng khoai. Hướng triển khai ban đầu sử dụng cách check điều kiện có nhập hay không trước sau đó tiến hành nối thành một query cuối cùng.

Phần check điều kiện null:

condition:='';
if P_SDT is not null then
condition:= condition || ' AND SDT='||P_SDT;
end if;
if P_EMAIL is not null then
condition:= condition || ' AND EMAIL='||P_EMAIL;
end if;
if P_CMT is not null then
condition:= condition || ' AND CMT='||P_CMT;
end if;

Phần nối câu lệnh để run:

select count(*) into w_count
from NGUOI_DUNG from 1=1 || condition;

Câu lệnh này không thể thực thi được, Oracle không hiểu được để xử lý khi nối chuỗi vào câu lệnh SQL.

hoặc áp dụng chính câu lệnh kiểm tra trong mệnh đề Where:

select count(*) into w_count
from NGUOI_DUNG from 1=1
AND (P_CMT is not null AND CMT=P_CMT)
AND (P_EMAIL is not null AND EMAIL=P_EMAIL)
AND (P_SDT is not null AND SDT=P_SDT)
;

Câu lệnh này không thực thi được do nó luôn thực thi cả phần ngoặc trong AND (Ban đầu mình ngây thơ là trong cả cái AND to, cái CMT=P_CMT sẽ được thực thi khi cái P_CMT is not null đúng) 🙁

Xử nhiều cách khác nhau nhưng đều không được, tra Google thì không tìm được giải pháp khả dĩ do chưa có được từ khóa phù hợp.
Cuối cùng cũng tìm ra một phương pháp khả dĩ và sử dụng được như sau:

select count(*) into w_count
from NGUOI_DUNG from 1=1
and (
SDT=(CASE
WHEN P_SDT is not null THEN P_SDT
ELSE SDT
END
)
)
and (
EMAIL=(CASE
WHEN P_EMAIL is not null THEN P_EMAIL
ELSE EMAIL
END
)
)
and (
CMT=(CASE
WHEN P_CMT is not null THEN P_CMT
ELSE CMT
END
)
)
;

Phần này quan trong ở điểm nếu P_CMT is not null đúng thì CMT=P_CMT còn ngược lại thì mệnh đề CASE WHEN trả về chính CMT để CMT=CMT, 2 cột dữ liệu trung tên thì như điều kiện 1=1 nên nó luôn đúng.

Từ phần này có thể đơn giản hóa bằng câu lệnh NVL, CMT=NVL(P_CMT,CMT,P_CMT). Nhưng viết thế này người nào không rõ về NVL sẽ không hiểu lắm. Cách trên tuy dài nhưng dễ hiểu hơn.

Chạy câu lệnh Oracle không bị failse khi mất kết nối

Vừa rồi mình gặp trường hợp đó là thấy 1 job hàng đêm chạy bị không thành công.
Anh lead team nói rằng do bị đứt đường truyền hoặc timeout của hệ thống. Mình cũng không biết nữa, đành chạy lại bằng tay và ngờ vực vấn đề này.
Một lần khác cũng xảy ra việc tương tự nhưng mình chạy cũng bị failse. Theo mình hiểu thì tự 1 lệnh mình chạy ở client, khi mình đã run thì ở phía server nó sẽ thực thi và sẽ phải hoàn thành do lệnh đã gửi đi rồi. Nhưng thực tế là đúng việc thỉnh thoảng mình run cũng bị timeout và việc chạy bị failse.

Khi mình chạy lại job bị failse đó thì cũng có lần ok có lần not ok. Anh ấy có gợi ý sử dụng VNC để chạy. Mình chỉ nghĩ rằng nếu sử dụng SQL Develop để chạy thì có thể timeout, nếu mình tiến hành SSH vào thì như là lên server đó rồi khác gì VNC chứ?

Nhưng đúng là thực tế không phải vậy, việc SSH thì đó là 1 phiên làm việc gọi là session, nếu đã là 1 session thì cũng là 1 phiên và hoàn toàn có thể bị timeout, cũng như là mình chạy bằng SQL Develop vậy.

Việc rõ hơn này là do 1 chị trong team có giải thích, việc sử dụng VNC cũng như là teamview vào server vậy, VNC được kết nối như phần cứng. Do vậy sẽ không bị timeout, khi chị việc backup hoặc restore thì đều phải dùng VNC vì sợ đường truyền có thể bị đứt hoặc timeout session do các công việc đó sử dụng nhiều thời gian.

Từ việc này mình thấy mình cũng chưa nắm được chắc chắn phần này lắm, vẫn còn hơi mông lung. 😀

Monitoring hệ thống dành cho DBA

Không phải là em ỷ lại đâu, nhưng quả thực đối với newbie thì ngay việc biết được cần phải monitor những cái gì của một Oracle database cũng là vấn đề. Em thử đưa ra một số cái nhé:
– Monitor phần dung lượng lưu trữ trên các storage, tablespace, cảnh báo khi tablespace đạt đến % lưu trữ nào đó
– Monitor memory của Oracle server (các thành phần chi tiết), cảnh báo khi memory xuống thấp hơn giá trị nào đó
– Monitor các vấn đề khác liên quan đến performance như usage CPU, IOPS
– Monitor status của Oracle instance như có ping được tới Oracle server không, Oracle instance có start hay shutdown, có kết nối được tới Oracle instance theo cổng nào đó không…
– Monitor các process đang chạy trên Oracle server, % CPU mà các process đó chiếm dụng…
– Monitor các session kết nối tới Oracle server, cảnh báo khi số lượng session đạt tới giới hạn nào đó. User nào đang kết nối tới Oralce instance, kết nối bằng chương trình gì? Cảnh báo khi có failure authentication của user nào đó…
Theo em những yêu cầu monitor trên đều là cơ bản, nhưng do kiến thức của em chỉ có vậy, nên mới dừng ở mức đó thôi. hix. Hy vọng các bác có kinh nghiệm đưa ra những gợi ý sâu hơn về những khía cạnh khác cần phải monitor, và gợi ý về giải pháp monitor software của các hãng thứ 3 giúp em (và những ai chưa biết) nhé. Em xin cảm ơn

Lưu trữ và backup sự thay đổi của các package của hệ thống ORACLE (hoặc procedure, function…)

Lưu trữ và backup sự thay đổi của các package của hệ thống ORACLE (hoặc procedure, function…)

Đứng vai trò là một Delevlop Database, trong Database công ty sẽ có rất nhiều các pakage, các package này cũng không chỉ có mỗi một mình sửa mà cả team cùng sửa. Cũng giống như code, việc này hay xảy ra conflic khi build gây lỗi cả hệ thống.

Hoặc đơn giản hơn, trước khi sửa ta luôn phải lưu package lại trước khi sửa. Vì nhiều lúc không phải lúc nào cũng có hệ thống test để có thể làm việc được.

Để tự động hóa việc lưu trữ cũng như lưu được lịch sử sửa đổi package, mình sẽ thực hiện 1 tiến trình như sau:
– Bước 1: hàng ngày hoặc nửa ngày 1 lần, tiến hành extract từng package ra 1 file sql hoặc text để tiến hành lưu trữ
– Bước 2: Hoặc là lưu trữ toàn bộ lại theo từng lần. Hoặc tiến hành đưa vào source quản lý mã nguồn.
Từ đây ta có thể dễ dàng tra cứu lại hoặc tiến hành khôi phục lại (rollback) code sql mà ta có thể đã làm lỗi gì đó.

Đây là 1 procedure mà mình đã tạo:
– Lấy toàn bộ các package có tên bắt đầu P_VN để tiến hành backup
– Lấy mã nguồn từng package lưu vào file có tên là tên package và lưu trữ vào thư mục: EXTRACT_2018 (tên biến trong oracle maps vào ổ cứng, mình đã có 1 bài viết về cái này, bạn có thể xem tại: …)

CREATE OR REPLACE PROCEDURE pro_flat_export
IS
l_clob CLOB;
l_name VARCHAR2(300);
rc sys_refcursor;
l_buffer VARCHAR2 (32767);
l_amount BINARY_INTEGER := 32767;
l_pos INTEGER := 1;
wquery varchar2(3000);
col1 varchar2(500);
cursor c_getdata is
SELECT OBJECT_NAME FROM ALL_OBJECTS WHERE OBJECT_TYPE IN (‘PACKAGE’) and OBJECT_NAME like ‘P_VN%’;
BEGIN
for i_getdata in c_getdata loop
col1:=i_getdata.OBJECT_NAME;
select dbms_metadata.get_ddl(‘PACKAGE’,col1,USER) into l_clob from dual;
DBMS_XSLPROCESSOR.clob2file (l_clob, ‘EXTRACT_2018′, col1||’.sql’);
end loop;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (SQLERRM);
END;

Ta có thể chạy định kỳ thủ tục này hoặc cần thì chạy:
begin
pro_flat_export();
end;

Ý tưởng để quản lý mã nguồn giống như quản lý mã nguồn cho code lập trình, ta sử dụng SVN hoặc GIT.
Ta tạo GIT bình thường, khi chạy tạo ra các file mới này ta đẩy vào thư mục GIT để thay thế code cũ. Sau đó tiến hành commit lên thôi, vì đây ta lưu ra txt hoặc sql nên có xem lại được theo version.