Cơ sở dữ liệu Microsoft SQL Server lưu trữ thông tin ngày và giờ ở nhiều định dạng khác nhau. Phổ biến nhất trong số đó là DateTime , DateTime2 và Ngày . Vì nó xảy ra với tất cả các loại dữ liệu, các vấn đề có thể diễn ra theo thời gian. Trong bài viết này, chúng tôi sẽ tập trung vào việc gỡ rối một số vấn đề phổ biến nhất mà bạn có thể gặp phải khi làm việc với kiểu dữ liệu ngày và giờ trong SQL.
Các vấn đề liên quan đến các định dạng ngày khác nhau theo khu vực
Định dạng ngày tháng khác nhau trên toàn cầu. Ví dụ, người Anh viết ngày tháng dưới dạng dd-mm-yyyy, trong khi người Mỹ viết ngày tháng ở định dạng mm-dd-yyyy. Theo cách này, cùng một ngày, ngày 31 tháng 12 năm 2020, được viết là 31-12-2020 ở định dạng ngày của Anh và là 12-31-2020 ở định dạng của Mỹ.
Các vấn đề về khả năng sử dụng có thể phát sinh nếu bạn không chỉ định ngày ở định dạng tương ứng với cài đặt ngôn ngữ của phiên bản SQL Server của bạn.
Tập lệnh sau chuyển đổi chuỗi văn bản có thông tin ngày thành định dạng DATETIME. Cài đặt ngôn ngữ được đặt thành BRITISH. Chuỗi văn bản chứa 31-12-2020 04:25:30 .
Nếu bạn truyền chuỗi này sang định dạng DATETIME, 31 sẽ được coi là ngày, trong khi 12 sẽ là tháng theo mặc định. Do đó, chuyển đổi sẽ thành công như được hiển thị từ đầu ra:
SET LANGUAGE BRITISH;
DECLARE @date VARCHAR(50) = '31-12-2020 04:25:30';
SELECT CAST(@date AS DATETIME);
Tuy nhiên, nếu bạn cố gắng chuyển đổi chuỗi chứa ngày 31-12-2020 sang định dạng DATETIME bằng US_ENGLISH cài đặt ngôn ngữ, bạn sẽ nhận được một lỗi như hình dưới đây:
SET LANGUAGE US_ENGLISH;
DECLARE @date VARCHAR(50) = '31-12-2020 04:25:30';
SELECT CAST(@date AS DATETIME);
Lỗi xảy ra do cài đặt ngôn ngữ US_ENGLISH xác định 31 dưới dạng một tháng thay vì một ngày. Là tháng giá trị không được lớn hơn 12, chúng tôi đang nhận được lỗi giá trị nằm ngoài phạm vi .
Nếu bạn chỉ định ngày là 12-31-2020 và sau đó chuyển đổi chuỗi ngày thành DATETIME bằng cách sử dụng cài đặt US_ENGLISH, bạn sẽ thấy chuyển đổi thành công:
SET LANGUAGE US_ENGLISH;
DECLARE @date VARCHAR(50) = '12-31-2020 04:25:30';
SELECT CAST(@date AS DATETIME);
Tương tự, chuyển đổi 12-31-2020 chuỗi ngày trong cài đặt ngôn ngữ BRITISH cũng gây ra lỗi - 31 được coi là một tháng, điều đó không thể xảy ra.
SET LANGUAGE BRITISH;
DECLARE @date VARCHAR(50) = '12-31-2020 04:25:30';
SELECT CAST(@date AS DATETIME);
Để chuyển đổi chính xác ngày của bạn bất kể cài đặt ngôn ngữ, bạn có thể sử dụng tiêu chuẩn ISO 8601 cho định dạng ngày. Để tuân thủ tiêu chuẩn này, hãy chỉ định ngày là yyyy-mm-ddThh:mm:ss .
Ví dụ:chuỗi ngày 2020-12-31T04:25:30 được chuyển đổi thành công sang kiểu dữ liệu DATETIME trong cài đặt ngôn ngữ BRITISH:
SET LANGUAGE BRITISH;
DECLARE @date VARCHAR(50) = '2020-12-31T04:25:30';
SELECT CAST(@date AS DATETIME);
Tập lệnh sau hiển thị cùng một chuỗi được chuyển đổi thành DATETIME với cài đặt US_ENGLISH:
SET LANGUAGE US_ENGLISH;
DECLARE @date VARCHAR(50) = '2020-12-31T04:25:30';
SELECT CAST(@date AS DATETIME);
Cân nhắc về Múi giờ
Bạn có thể muốn phát triển một số ứng dụng cơ sở dữ liệu SQL Server cho đối tượng toàn cầu. Vì vậy, bạn có thể cần thêm thông tin múi giờ cho các loại dữ liệu ngày và giờ.
Trong SQL Server, kiểu dữ liệu DATETIMEOFFSET lưu trữ thông tin ngày và giờ cùng với độ lệch múi giờ. Độ lệch múi giờ được chỉ định là UTC +/- số giờ.
Ví dụ:tập lệnh sau sử dụng phương thức SYSDATETIMEOFFSET () để lấy ngày, giờ và thông tin bù đắp của hệ thống đang chạy phiên bản SQL Server của bạn. Các giá trị do hàm SYSDATETIMEOFFSET () trả về được lưu trữ trong biến kiểu DATETIMEOFFSET @dateoffset. Giá trị của biến @dateoffset được in bằng câu lệnh SELECT:
DECLARE @dateoffset DATETIMEOFFSET = SYSDATETIMEOFFSET();
SELECT @dateoffset
Kết quả đầu ra bên dưới cho thấy ngày và giờ hiện tại và giá trị bù đắp. Trong trường hợp này, nó là +02:00.
Bạn cũng có thể chỉ nhận được giá trị bù đắp từ biến DATETIMEOFFSET. Để làm như vậy, bạn cần chuyển biến kiểu DATETIMEOFFSET làm giá trị tham số thứ hai cho hàm DATENAME (). Tham số đầu tiên của phương thức DATENAME () phải là tzoffset .
Tập lệnh sau trả về phần bù thời gian của ngày hệ thống hiện tại:
DECLARE @dateoffset DATETIMEOFFSET = SYSDATETIMEOFFSET();
SELECT DATENAME(tzoffset, @dateoffset)
Để tạo biến DATETIMEOFFSET tùy chỉnh, hãy chỉ định giá trị cho các phần bù ngày, giờ và thời gian. Ví dụ:trong tập lệnh sau, giá trị cho ngày là 2015-02-22 , giá trị cho phần thời gian là 23:59:59:999 và giá trị chênh lệch thời gian là +05:00 .
DECLARE @dateoffset DATETIMEOFFSET = '2015-02-22 23:59:59:999 +05:00';
SELECT @dateoffset
Cuối cùng, bạn cũng có thể cập nhật thông tin chênh lệch thời gian bằng cách sử dụng SWITCHOFFSET () chức năng.
Bạn cần vượt qua DATETIMEOFFSET nhập biến làm giá trị tham số đầu tiên và chuyển khoảng cách thời gian mới làm giá trị tham số thứ hai cho SWITCHOFFSET chức năng.
Tập lệnh sau cập nhật giá trị bù thời gian cho biến DATETIMEOFFSET từ +05:00 đến +09:00.
DECLARE @dateoffset DATETIMEOFFSET = '2015-02-22 23:59:59:999 +05:00';
SELECT SWITCHOFFSET(@dateoffset, '+09:00');
Chọn Bản ghi Sử dụng GIỮA Toán tử với DateTime
GIỮA toán tử trong máy chủ SQL lọc các bản ghi giữa phạm vi giá trị được chuyển cho nó.
Bạn có thể sử dụng toán tử GIỮA để trả về các bản ghi giữa hai ngày. Tuy nhiên, bạn nên hết sức cẩn thận khi sử dụng nó để lọc các bản ghi có ngày tháng.
Ví dụ:tập lệnh sau tạo Hostel giả cơ sở dữ liệu và thêm một Sinh viên bàn tới nó.
CREATE DATABASE Hostel
USE Hostel
CREATE TABLE Student
(
Id INT PRIMARY KEY IDENTITY(1,1),
Name VARCHAR (50) NOT NULL,
Gender VARCHAR (50),
BirthDate DateTime
)
Tập lệnh tiếp theo thêm một số bản ghi giả vào Học sinh bàn. Ngày sinh cột Sinh viên bảng lưu trữ ngày tháng. Từ đoạn mã này, bạn có thể thấy rằng hai học sinh Sara và Nik có cùng ngày sinh. Tuy nhiên, thời điểm sinh thì khác:
INSERT INTO Student
VALUES ('Jack', 'Male', '2017-06-30 16:30:35'),
('Sara', 'Female', '2015-02-22 00:00:00'),
('Elisa', 'Female', '2020-03-16 22:24:39'),
('Nik', 'Male', '2015-02-22 09:45:55'),
('Jos', 'Male', '2015-03-25 11:55:20')
Bạn sẽ nghĩ rằng toán tử GIỮA có thể được sử dụng để truy xuất hồ sơ của tất cả các học sinh sinh vào ngày 22 tháng 2 năm 2015.
SELECT * FROM Student
WHERE BirthDate BETWEEN '2015-02-22' AND '2015-02-22'
Nhưng nếu bạn thực thi tập lệnh trên, bạn sẽ thấy rằng chỉ có một bản ghi được trả về, bất chấp thời gian phần cũng được bao gồm.
Lý do là toán tử BETWEEN xử lý theo mặc định giá trị DATETIME của 2015-02-22 as 2015-02-22 00:00:00 . Do đó, toán tử GIỮA trong truy vấn trên đã tìm kiếm các bản ghi với Ngày sinh giá trị từ 2015-02-22 00:00:00 và 2015-02-22 00:00:00 .
Để giải quyết vấn đề này, chúng ta phải chỉ định phần thời gian khi sử dụng toán tử GIỮA với kiểu dữ liệu DATETIME.
Tập lệnh sau sẽ trả về tất cả các bản ghi trong khoảng thời gian từ 2015-02-22 00:00:00 và 2015-02-22 23:59:59:999 . Phần thời gian cho giới hạn ngày trên là 23:59:999.
SELECT * FROM Student
WHERE BirthDate BETWEEN '2015-02-22' AND '2015-02-22 23:59:59:999';
Trong đầu ra, chúng tôi nhận được hai bản ghi cho Ngày sinh - ngày 22 tháng 2 năm 2015 .
Các vấn đề liên quan đến phạm vi ngày
Kiểu dữ liệu DATETIME chỉ hỗ trợ các năm từ 1753 đến 9999. Do đó, nếu bạn cố gắng lưu trữ ngày có giá trị năm lớn hơn 9999 hoặc nhỏ hơn 1753, bạn sẽ gặp lỗi.
Tập lệnh sau cố gắng chuyển đổi 1392-12-31 chuỗi ngày tháng. 1392 nhỏ hơn 1753. Do đó, chúng tôi có lỗi giá trị nằm ngoài phạm vi.
DECLARE @date VARCHAR(50) = '1392-12-31 04:25:30';
SELECT CAST(@date AS DATETIME);
Để lưu trữ các giá trị năm nhỏ hơn 1753 , bạn có thể sử dụng DATETIME2 loại dữ liệu. Nó lưu trữ các giá trị năm từ 0000 đến 9999.
Tập lệnh sau chuyển đổi thành công chuỗi ngày 1392-12-31 thành kiểu dữ liệu DATETIME2:
DECLARE @date VARCHAR(50) = '1392-12-31 04:25:30';
SELECT CAST(@date AS DATETIME2);
Sử dụng TRY_COVERT cho Chuyển đổi DateTime
Hàm CONVERT trong SQL Server chuyển đổi dữ liệu từ kiểu này sang kiểu khác. Bạn có thể sử dụng nó để chuyển đổi các định dạng dữ liệu kiểu ngày tháng sang các định dạng khác và ngược lại. Tuy nhiên, nếu chuyển đổi không thành công, hàm CONVERT sẽ xuất hiện lỗi.
Ví dụ:chúng tôi đang chuyển đổi chuỗi 2015-31-31 sang định dạng DATETIME:
DECLARE @date VARCHAR(50) = '2015-31-13';
SELECT CONVERT(DATETIME, @date ,105) as DOB_CONV
Nếu bạn muốn trả về giá trị NULL khi chuyển đổi không thành công thay vì thông báo lỗi, hãy sử dụng TRY_CONVERT hàm số. Phương pháp này sẽ không để ứng dụng gặp sự cố - nó chỉ trả về giá trị NULL.
DECLARE @date VARCHAR(50) = '2015-31-13';
SELECT TRY_CONVERT(DATETIME, @date ,105) as DOB_CONV
Kết luận
Làm việc với SQL Server, bạn có thể phải đối mặt với nhiều vấn đề làm hỏng trải nghiệm của bạn và làm phức tạp các nhiệm vụ. Mặt khác, biết những rắc rối phổ biến nhất là phương pháp hiệu quả nhất để ngăn chặn chúng xảy ra. Đó là lý do tại sao chúng tôi dành riêng bài viết này để gỡ rối những bất tiện có thể xảy ra trong quá trình làm việc của bạn với thông tin về ngày và giờ.
Cũng lưu ý rằng các công cụ hiện đại để làm việc với cơ sở dữ liệu SQL Server có thể làm cho cuộc sống của các chuyên gia DB đơn giản hơn nhiều. Đặc biệt, dbForge Studio cho SQL Server cung cấp tính năng Visual Data Editor để áp dụng khi xử lý ngày tháng. Bạn có thể sử dụng nó để xem và chỉnh sửa ngày tháng theo cách thân thiện với người dùng nhất.