Sử dụng Condition của SQL

Tram Ho

Khi mới lập trình RoR mình đã bị phụ thuộc rất nhiều vào ngôn ngữ Ruby và framework Rails, dẫn đến việc khi search key trên google mình luôn thêm 1 từ khóa là “ruby” hoặc “rails”. Khi đó với mình mysql thực sự chỉ là 1 “kho lưu trữ dữ liệu” chứ không phải “hệ quản trị cơ sở dữ liệu”.

Khi gặp 1 bài toán khoai về lấy dữ liệu thì mình sẽ luôn search google với từ khóa “rails” để xem có kết quả gì liên quan đến model không, và nếu không có hướng giải quyết thì: “Ui dồi ôi, thế này thì nàm nàm xao được!”.

Đấy là 1 sai lầm rất lớn của mình, vì mình đã hoàn toàn bỏ qua những tính năng hay ho của SQL mà chạy theo những thứ khá cứng nhắc của ngôn ngữ lập trình. Bài viết sau đây mình sẽ trình bày 1 tip mình vừa học lỏm được của 1 anh lead, mình cũng không rõ là nó có tốt không hay nó có mới mẻ với mọi người không, nhưng cho đến thời điểm hiện tại thì nó giúp mình rất nhiều và hy vọng là nó có thể giúp mọi ngưới trong 1 trường hợp nào đấy (trong trường hợp rules dự án cho phép viết kiểu này nhé).

Vậy tip ở đây là gì?

Bạn có bao giờ gặp trường hợp như này hay tương tự chưa?

Bạn có 1 bảng nhưng lại có 2 trường kiểu date để lưu 2 loại ngày khác nhau(nếu 1 record có date_a thì không có date_b và ngược lại) và spec lại phải sort cả 2 bảng đấy cùng lúc theo ASC hoặc DESC như ví dụ sau:

Ta có bảng User có 2 trường date_a và date_b:

iddate_adate_b
126-08-2020nil
2nil04-06-2020
303-04-2020nil
4nil23-09-2020

Và spec bắt chúng ta không phải sort như này:

query:

kết quả:

iddate_adate_b
303-04-2020nil
126-08-2020nil
2nil04-06-2020
4nil23-09-2020

như chúng ta thấy 04-06 nhỏ hơn 26-08 nhưng lại ở dưới vì đơn giản cột date_a của nó bị nil. Và câu query trên mang ý nghĩa là order date_a trước nếu 2 hay nhiều bản ghi có giá trị date_a bằng nhau thì lúc này date_b: :asc mới thực sự có giá trị (nó sẽ sort các đứa có date_a bằng nhau dựa vào date_b)

Nhưng ví dụ khách hàng lại bảo “Không, tôi không thích thế, Tôi muốn nó phải sort cả 2 cùng lúc như này cơ :”

iddate_adate_b
303-04-2020nil
2nil04-06-2020
126-08-2020nil
4nil23-09-2020

“ơ thế khó vl”, nếu search google với từ khóa “rails” thôi thì chắc cũng hẹo hoặc nếu có cách đi nữa thì chắc cũng sẽ lằng nhằng phết. Thì đây là lúc sử dụng SQL chứ còn gì nữa ? Mà mình tin phần lớn mọi người cũng sẽ nghĩ đến SQL ngay thôi 😀

Vậy vấn đề là viết như nào để sort được như trên ?

Mình đã sử dụng đến các hàm điều kiện của SQL (IF ELSE, CASE WHEN), cái mà lúc đó mình nghĩ “Ơ SQL có IF ELSE à?”:

Ngoại ra mình có thêm 1 cột flag để sử dụng điều kiệu IF (nếu flag = true thì sử dụng date_a, flag = false thì sẻ dung date_b để sort). Vây bây giờ chúng ta có bảng như sau:

iddate_adate_bflag
126-08-2020niltrue
2nil04-06-2020false
303-04-2020niltrue
4nil23-09-2020false

và thứ viết 1 scope như sau:

Câu lệnh trên sẽ ra kết quả ưng ý khách hàng. Nhưng bạn nên sử dụng Arel và đổi thành như này nhé.

kết quả:

iddate_adate_bflag
303-04-2020niltrue
2nil04-06-2020false
126-08-2020niltrue
4nil23-09-2020false

chúng ta có câu query như sau:

Và còn 1 ví dụ rất hay ho nữa của IF ELSE nữa nhé:

Chúng ta có 2 bảng sau:
User:

idname
1Thể
2Thảo

Payment:

user_idpricepaid
1100true
150fasle
230true
270false

Và chúng ta phải hiển thị dạng bảng trên view như sau:

TênTổng tiềnTiền đã thanh toán
Thể150100
Thảo10030

Bình thường chúng ta sẽ dùng render collection từng row user đúng chứ, và từng user chúng ta lại viết 1 hàm để tình tổng tiền, và 1 hàm tính tổng tiền đã thanh toán để gọi ngoài view.
Và kết quả là chắc chắn sẽ bị 2n+1 query (1 query tổng tiền, 1 query tổng tiền đã thanh toán của từng user).

Một số bạn “chiếu cũ” từng trải sẽ nghĩ ra ngay là viết 1 scope khi để trên controller để giải quyết n+1 query cột “Tổng tiền” như sau:

Và gọi lại trên controller:

và ngoài view chúng ta chỉ cần gọi

là đã giải quyết được cột “tổng tiền” không bị query lại rồi.

Thế còn tiền “Tiền đã thanh toán” thì sao ?, khoai phết vì nó còn phải có trường paid = true nữa. Chẳng nhẽ lại phải viết scope khác để thêm where paid = true, rồi join lại với User à ? Thế dở hơi quá! Mà có được không nhỉ ?

Chúng ta sẽ sử dụng IF ELSE luôn trong scope vừa nãy:

và ngoài view gọi

Vậy là giải quyết được tất cả n + 1 query

Tài liệu tham khảo

https://www.mysqltutorial.org/mysql-if-statement/

Chia sẻ bài viết ngay

Nguồn bài viết : Viblo