Tản mạn về NULL

Vũ Huy Tâm

1. NULL là một giá trị đặc biệt có mặt trong tất cả các loại dữ liệu, từ kiểu số, ngày, chuỗi, đến bit… Nó đại diện cho giá trị “không biết”, hoặc “không tồn tại”. NULL không trùng với số 0 của kiểu số và cũng không trùng với chuỗi trống (”) của kiểu chuỗi. Ví dụ bạn có bảng dữ liệu sinh viên có chứa cột số lần thi lại; nếu một bản ghi chứa NULL ở cột này có nghĩa là ta không biết số lần thi lại của sinh viên đó là bao nhiêu; điều này khác với khi nó chứa 0 nghĩa là ta đã biết sinh viên đó chưa phải thi lại môn nào.
Vì NULL nghĩa là “không biết” nên mọi thao tác với NULL đều dẫn đến NULL. Đây là điều bạn cần ghi nhớ để tránh phiền phức khi liên quan đến xử lý NULL.

2. Nếu bạn cộng một số hoặc chuỗi với NULL, kết quả sẽ là NULL:

DECLARE @i INT, @c VARCHAR(10) --mặc định các biến này đều là NULL
SELECT 1+@i
SELECT 'a'+@c
SELECT DATEADD(d,@i,GETDATE())

Một tình huống nhiều khi gây rắc rối là khi bạn tạo một câu SQL động và thực hiện nó, nhưng không hay biết là chuỗi SQL động đó là NULL vì một thành phần khi tạo lập nó là NULL:

DECLARE @c VARCHAR(10), @SQL VARCHAR(100)
SET @c = NULL
SET @SQL = 'SELECT * FROM dbo.Tbl1 WHERE c='+@c
--SELECT @SQL
EXEC (@SQL)

Ở ví dụ trên, vì biến @c là NULL nên @SQL cũng là NULL, cho nên không có kết quả nào trả về, và ta ngẩn người ra vì không nhận được kết quả cũng không nhận được báo lỗi. EXEC(NULL) luôn thành công vì nó không phải làm gì cả.

3. Khi so sánh với NULL, ta cũng nhận được kết quả NULL thay vì giá trị boolean “Đúng” hoặc “Sai”. Vì thế khi truy vấn bảng mà trong mệnh đề WHERE có dùng cột chứa NULL, các bản ghi NULL luôn là ngoại lệ. Để lấy được các bản ghi này bạn cần dùng “IS NULL”. Còn để lấy được các bản ghi không NULL, bạn dùng “IS NOT NULL”. Ví dụ:

CREATE TABLE dbo.KhoHoaQua(MaSo INT, LoaiQua NVARCHAR(20), SoThung INT) --bảng Kho Hoa Quả, gồm Loại quả và số thùng.
INSERT INTO dbo.KhoHoaQua VALUES(1, N'Táo', 8)
INSERT INTO dbo.KhoHoaQua VALUES(2, N'Nho', NULL)
INSERT INTO dbo.KhoHoaQua VALUES(3, N'Cam', 15)
 
SELECT * FROM dbo.KhoHoaQua WHERE SoThung < 10 --chỉ có táo
SELECT * FROM dbo.KhoHoaQua WHERE SoThung >= 10 --chỉ có cam
SELECT * FROM dbo.KhoHoaQua WHERE SoThung IS NULL --giờ mới thấy nho
SELECT * FROM dbo.KhoHoaQua WHERE SoThung IS NOT NULL --chỉ lấy các bản ghi với SoThung không NULL

Để chuyển đổi NULL về một giá trị chỉ định, bạn dùng hàm ISNULL(). Ví dụ dưới qui NULL về 0 cho cột SoThung:

SELECT * FROM dbo.KhoHoaQua WHERE ISNULL(SoThung,0) < 10 --giờ có cả táo và nho

Một tình huống rắc rối khác với NULL là khi bạn dùng “NOT IN”, tình huống này được nêu chi tiết trong bài Lưu Ý Khi Viết Câu Lệnh Dùng “NOT IN”
4. Khi cột được khai báo là khóa chính, nó phải là NOT NULL, vì nếu chứa NULL nó không còn khả năng làm định danh cho bản ghi nữa.
Khi cột chứa NULL được tạo index, tất cả các bản ghi chứa NULL được lưu chung vào một node index, chúng được coi như có cùng giá trị. Vì thế nếu index là unique index, chỉ tối đa một bản ghi được chứa NULL.
Nếu một cột làm khóa ngoại và tham chiếu đến một cột ở bảng khác, cột khóa ngoại đó có thể chứa NULL mà không bị coi là vi phạm ràng buộc. Ví dụ bạn có bảng BanHang lưu các giao dịch bán hàng từ kho hoa quả trên; bảng BanHang chứa cột MaSo là mã số của loại hoa quả được bán và là khóa ngoại tham chiếu đến bảng KhoHoaQua; cột MaSo khi đó chỉ có thể chứa các giá trị 1,2,3 hoặc NULL.

5. Vì NULL có mặt trong mọi kiểu dữ liệu, bạn có thể CAST NULL đến bất kỳ kiểu dữ liệu nào bạn cần (CAST là hàm chuyển đổi kiểu dữ liệu):

SELECT *, CAST(NULL AS DECIMAL(8,2)) AS Gia
INTO #t
FROM dbo.KhoHoaQua

Bảng #t ở trên sẽ có thêm cột Gia có kiểu DECIMAL(8,2).

6. Khi dùng các hàm tính toán aggregate (như COUNT, SUM…), SQL loại bỏ các bản ghi NULL ra trước khi thực hiện do đó tránh được rắc rối khi dính với NULL:

SELECT COUNT(*), COUNT(SoThung), SUM(SoThung),AVG(SoThung)
FROM dbo.KhoHoaQua

Ở ví dụ trên, COUNT(*) vẫn đếm tất cả các bản ghi trong bảng, nhưng COUNT(SoThung) chỉ đếm các bản ghi khi SoThung khác NULL. Hai hàm sau đó là SUM và AVG cũng chỉ tính toán trên các bản ghi khi SoThung khác NULL.




Tags: , ,

9 Comments
Posted on 26/8/2013 | Categories: SQL Server Programming

Các bài viết tương tự

Comments
  • Minh Khôi (11/02/2014 10:52 pm)

    Hi All,

    Nhờ các bạn chỉ dùm có cách nào để xử lý các trường NULL của kiểu dữ liệu DATETIME cho nó tháng khoảng trắng được không.

    VD mình có 1 bảng trong đó có cột Ngay_Thang, khi insert dữ liệu cột này mình để trống, nên khi SELECT bảng thì nó sẽ thể hiện là NULL.

    Mình muốn nó không thể hiện NULL mà sẽ để trống trường này thì làm như thế nào?

    • Red Devilic (12/02/2014 10:16 pm)

      1 – Sử dụng Default
      2 – Bạn có thể format lại dữ liệu khi SELECT bằng cách sử dụng CASE hoặc ISNULL

      • Demank (04/07/2015 4:19 am)

        Our LED lights cetrunrly come include 40 or 80 LED lamps and could be tailored for use with any energy supply. The primary limitation to the adoption of LED lighting as a lighting normal is the current excessive cost of led bulbs. Although the fee retains going down, LED mild bulbs are nonetheless expensive. A single LED gentle bulb, changing a 25 watt incandescent, will price round $20. Although LED’s are costly, the price is recouped over time and in vitality cost savings. Consider that it is significantly cheaper to take care of led lights, the perfect value comes from industrial use where upkeep and substitute prices are expensive. Site visitors lights and outdoor signs, for instance, are being converted to LED’s in lots of countries. Smaller arrays, similar to those in flashlights, headlamps and small activity lights are nice for specialty and outside use. LED based mostly automotive headlights are present being used in excessive end luxury cars.

  • emga (12/02/2014 4:14 am)

    Bác nào cho em hỏi để null hay là để default sẽ tối ưu hơn ạ?

    VD : dữ liệu kiểu int , nếu không nhập vao để là null hay để defaul là 0 sẽ tốt hơn ?

    • nguoihanoi (13/02/2014 1:35 am)

      Tốt ưu hơn về mặt nào ?

      Về mặt lưu trữ thì tùy vào loại dữ liệu, với int như ví dụ của bác thì null hay 0 đều như nhau.

      Về mặt ứng dụng thì tùy người, có người cho dùng rằng 0 dễ kiểm soát hơn, có người lại cho rằng dùng null chính xác hơn. Tui thì thiên về null vì theo tui csdl cần phải chính xác, và null cũng đại diện cho 1 trường hợp. Ví dụ bác có trường Kết Quả, có giá trị thắng, hòa, và thua tương ứng với 1,0,-1. Vậy trong trường hợp chưa biết kết quả thì trường đó nên có giá trị gì ? Null là thích hợp nhất, chứ không thể là 0 được.

      Tuy nhiên dùng Null phải rất cẩn thận. Bác đọc kỹ bài viết của tác giả chắc sẽ rõ hơn.

      • Banhxe0 (08/07/2015 6:04 am)

        Em không hiểu lắm, tại sao lại là Null trong khi mình có thể có giá trị (1-thua,2-thắng,3-hòa,0-chưa xác định).
        Cái ví dụ này khá giống việc bỏ qua trường ngày sinh,nó default thì sẽ nhảy về 1900-01-01 trong khi đáng lẽ ra phải là Null(chưa xác định).
        Nên việc valid dữ liệu nhập vào để trên app trước.

  • Red Devilic (12/02/2014 10:14 pm)

    Nhớ ko nhầm thì riêng kiểu int, nếu để null thì mặc định sẽ nhận giá trị 0.

    Nguyên tắc như sau, nếu bạn để NULL và thiết lập Default thì nó sẽ nhận giá trị được quy định bởi Default

    • Vũ Huy Tâm (14/02/2014 11:33 am)

      NULL luôn là NULL và không bao giờ bằng 0. Nếu bạn query WHERE INTCol IS NULL và WHERE INTCol = 0 thì kết quả luôn khác nhau (INTCol là trường kiểu int)

      • Red Devilic (19/02/2014 2:41 am)

        Vậy chắc em nhầm, không rõ trước đây em đã đọc ở đâu nhưng chưa kiểm tra lại.

Leave a Reply

Hướng dẫn: Để nhập mã T-SQL bạn dùng thẻ <pre lang="tsql"> và </pre>.
Ví dụ: <pre lang="tsql">SELECT * FROM MyTable</pre>