Database
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Database

FORMAT () rất hay và tất cả, nhưng…

Quay lại khi SQL Server 2012 vẫn còn trong giai đoạn thử nghiệm, tôi đã viết blog về FORMAT() mới chức năng:SQL Server v.Next (Denali):CTP3 T-SQL Cải tiến:FORMAT ().

Vào thời điểm đó, tôi rất hào hứng với chức năng mới, đến nỗi tôi thậm chí không nghĩ đến việc thực hiện bất kỳ thử nghiệm hiệu suất nào. Tôi đã giải quyết vấn đề này trong một bài đăng blog gần đây hơn, nhưng chỉ trong bối cảnh rút ngắn thời gian so với ngày giờ:Cắt ngắn thời gian từ ngày giờ - tiếp theo.

Tuần trước, người bạn tốt của tôi Jason Horner (blog | @jasonhorner) đã troll tôi bằng những dòng tweet này:

Vấn đề của tôi với điều này chỉ là FORMAT() trông có vẻ thuận tiện, nhưng nó cực kỳ kém hiệu quả so với các cách tiếp cận khác (ồ và AS VARCHAR điều tồi tệ quá). Nếu bạn đang thực hiện điều này đôi chút và cho các kết quả nhỏ, tôi sẽ không lo lắng quá nhiều về nó; nhưng ở quy mô lớn, nó có thể khá đắt. Hãy để tôi minh họa bằng một ví dụ. Đầu tiên, hãy tạo một bảng nhỏ với 1000 ngày giả ngẫu nhiên:

SELECT TOP (1000) d = DATEADD(DAY, CHECKSUM(NEWID())%1000, o.create_date)
  INTO dbo.dtTest
  FROM sys.all_objects AS o
  ORDER BY NEWID();
GO
CREATE CLUSTERED INDEX d ON dbo.dtTest(d);

Bây giờ, hãy tính toán bộ nhớ cache với dữ liệu từ bảng này và minh họa ba trong số những cách phổ biến mà mọi người có xu hướng trình bày ngay lúc này:

SELECT d, 
  CONVERT(DATE, d), 
  CONVERT(CHAR(10), d, 120),
  FORMAT(d, 'yyyy-MM-dd')
FROM dbo.dtTest;

Bây giờ, hãy thực hiện các truy vấn riêng lẻ sử dụng các kỹ thuật khác nhau này. Chúng tôi sẽ chạy chúng 5 lần mỗi lần và chúng tôi sẽ chạy các biến thể sau:

  1. Chọn tất cả 1.000 hàng
  2. Chọn TOP (1) được sắp xếp theo khóa chỉ mục nhóm
  3. Gán cho một biến (buộc phải quét toàn bộ, nhưng ngăn kết xuất SSMS can thiệp vào hiệu suất)

Đây là tập lệnh:

-- select all 1,000 rows
GO
SELECT d FROM dbo.dtTest;
GO 5
SELECT d = CONVERT(DATE, d) FROM dbo.dtTest;
GO 5
SELECT d = CONVERT(CHAR(10), d, 120) FROM dbo.dtTest;
GO 5
SELECT d = FORMAT(d, 'yyyy-MM-dd') FROM dbo.dtTest;
GO 5
 
-- select top 1
GO
SELECT TOP (1) d FROM dbo.dtTest ORDER BY d;
GO 5
SELECT TOP (1) CONVERT(DATE, d) FROM dbo.dtTest ORDER BY d;
GO 5
SELECT TOP (1) CONVERT(CHAR(10), d, 120) FROM dbo.dtTest ORDER BY d;
GO 5
SELECT TOP (1) FORMAT(d, 'yyyy-MM-dd') FROM dbo.dtTest ORDER BY d;
GO 5
 
-- force scan but leave SSMS mostly out of it
GO
DECLARE @d DATE;
SELECT @d = d FROM dbo.dtTest;
GO 5
DECLARE @d DATE;
SELECT @d = CONVERT(DATE, d) FROM dbo.dtTest;
GO 5
DECLARE @d CHAR(10);
SELECT @d = CONVERT(CHAR(10), d, 120) FROM dbo.dtTest;
GO 5
DECLARE @d CHAR(10);
SELECT @d = FORMAT(d, 'yyyy-MM-dd') FROM dbo.dtTest;
GO 5

Bây giờ, chúng tôi có thể đo lường hiệu suất với truy vấn sau (hệ thống của tôi hoạt động khá êm; trên hệ thống của bạn, bạn có thể cần thực hiện lọc nâng cao hơn chỉ execution_count ):

SELECT 
  [t] = CONVERT(CHAR(255), t.[text]), 
  s.total_elapsed_time, 
  avg_elapsed_time = CONVERT(DECIMAL(12,2),s.total_elapsed_time / 5.0),
  s.total_worker_time, 
  avg_worker_time = CONVERT(DECIMAL(12,2),s.total_worker_time / 5.0),
  s.total_clr_time
FROM sys.dm_exec_query_stats AS s 
CROSS APPLY sys.dm_exec_sql_text(s.[sql_handle]) AS t
WHERE s.execution_count = 5
  AND t.[text] LIKE N'%dbo.dtTest%'
ORDER BY s.last_execution_time;

Kết quả trong trường hợp của tôi khá nhất quán:

Truy vấn (bị cắt ngắn) Thời lượng (micro giây)
total_elapsed avg_elapsed total_clr
CHỌN 1.000 hàng SELECT d FROM dbo.dtTest ORDER BY d; 1,170 234.00 0
SELECT d = CONVERT(DATE, d) FROM dbo.dtTest ORDER BY d; 2,437 487.40 0
SELECT d = CONVERT(CHAR(10), d, 120) FROM dbo.dtTest ORD ... 151,521 30,304.20 0
SELECT d = FORMAT(d, 'yyyy-MM-dd') FROM dbo.dtTest ORDER ... 240,152 48,030.40 107,258
SELECT TOP (1) SELECT TOP (1) d FROM dbo.dtTest ORDER BY d; 251 50.20 0
SELECT TOP (1) CONVERT(DATE, d) FROM dbo.dtTest ORDER BY ... 440 88.00 0
SELECT TOP (1) CONVERT(CHAR(10), d, 120) FROM dbo.dtTest ... 301 60.20 0
SELECT TOP (1) FORMAT(d, 'yyyy-MM-dd') FROM dbo.dtTest O ... 1,094 218.80 589
Assign variable DECLARE @d DATE; SELECT @d = d FROM dbo.dtTest; 639 127.80 0
DECLARE @d DATE; SELECT @d = CONVERT(DATE, d) FROM dbo.d ... 644 128.80 0
DECLARE @d CHAR(10); SELECT @d = CONVERT(CHAR(10), d, 12 ... 1,972 394.40 0
DECLARE @d CHAR(10); SELECT @d = FORMAT(d, 'yyyy-MM-dd') ... 118,062 23,612.40 98,556

 

And to visualize the avg_elapsed_time đầu ra (bấm để phóng to):

FORMAT () rõ ràng là kẻ thua cuộc:kết quả avg_elapsed_time (micro giây)

Chúng ta có thể học được gì từ những kết quả này (một lần nữa):

  1. Đầu tiên và quan trọng nhất, FORMAT() đắt tiền .
  2. FORMAT() thừa nhận có thể cung cấp tính linh hoạt hơn và đưa ra các phương pháp trực quan hơn, phù hợp với các phương pháp trong các ngôn ngữ khác như C #. Tuy nhiên, ngoài chi phí của nó, và while CONVERT() số kiểu khó hiểu và ít đầy đủ hơn, bạn có thể phải sử dụng phương pháp cũ hơn, vì FORMAT() chỉ hợp lệ trong SQL Server 2012 và mới hơn.
  3. Ngay cả CONVERT() ở chế độ chờ phương pháp có thể rất tốn kém (mặc dù chỉ nghiêm trọng như vậy trong trường hợp SSMS phải hiển thị kết quả - rõ ràng nó xử lý các chuỗi khác với giá trị ngày).
  4. Chỉ cần kéo trực tiếp giá trị ngày giờ ra khỏi cơ sở dữ liệu luôn hiệu quả nhất. Bạn nên tính toán thêm thời gian cần thiết để ứng dụng của bạn định dạng ngày tháng như mong muốn ở tầng bản trình bày - rất có thể bạn sẽ không muốn SQL Server liên quan đến định dạng đẹp đẽ chút nào (và trên thực tế, nhiều người sẽ tranh luận rằng đây là nơi logic đó luôn thuộc về).

Ở đây chúng ta chỉ đang nói chuyện về micro giây, nhưng chúng ta cũng chỉ nói về 1.000 hàng. Chia tỷ lệ theo kích thước bảng thực tế của bạn và tác động của việc chọn phương pháp định dạng sai có thể rất nghiêm trọng.

Nếu bạn muốn thử thử nghiệm này trên máy của riêng mình, tôi đã tải lên một tập lệnh mẫu:FormatIsNiceAndAllBut.sql_.zip


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Cách tạo bảng với nhiều phím ngoại và không bị nhầm lẫn

  2. Mang đám mây của riêng bạn (BYOC) so với Lưu trữ chuyên dụng tại ScaleGrid

  3. Làm thế nào để làm việc với sự kế thừa trong Entity Framework Core

  4. Phân trang bằng OFFSET / FETCH:Một cách tốt hơn

  5. Hạn chế một máy chủ được liên kết với một đăng nhập cục bộ duy nhất (Ví dụ T-SQL)