Lời giải thích dường như gắn liền với sự kết hợp của: a) một chi tiết từ blog được liên kết không được đề cập trong câu hỏi này, b) tính thực dụng của TVP phù hợp với cách các tham số luôn được truyền vào và ra, c) và bản chất của các biến bảng.
Chi tiết bị thiếu trong bài đăng trên blog được liên kết chính xác là cách các biến được truyền vào và ra khỏi Thủ tục và Hàm được lưu trữ (liên quan đến cụm từ trong Câu hỏi về "phiên bản an toàn hơn của tham chiếu qua nếu chúng là tham số OUTPUT") :
TSQL sử dụng một ngữ nghĩa sao chép / sao chép để truyền tham số cho các thủ tục và hàm được lưu trữ ....
... khi Proc được lưu trữ kết thúc thực thi (không gặp lỗi), một bản sao được thực hiện để cập nhật tham số được truyền với bất kỳ thay đổi nào được thực hiện cho nó trong Proc được lưu trữ.
Lợi ích thực sự của phương pháp này là trong trường hợp lỗi. Nếu xảy ra lỗi ở giữa quá trình thực thi của thủ tục được lưu trữ, mọi thay đổi được thực hiện đối với tham số sẽ không lan truyền trở lại cho người gọi.
Nếu từ khóa OUTPUT không xuất hiện, không có bản sao nào được thực hiện.
Điểm mấu chốt:
Các tham số cho các procs được lưu trữ không bao giờ phản ánh việc thực thi một phần của Proc được lưu trữ nếu nó gặp lỗi.
Phần 1 của câu đố này là các tham số luôn được truyền "theo giá trị". Và, chỉ khi tham số được đánh dấu là OUTPUT
và Thủ tục lưu trữ hoàn thành thành công thì giá trị hiện tại mới thực sự được gửi trở lại. Nếu OUTPUT
các giá trị được truyền thực sự "bằng tham chiếu", thì con trỏ tới vị trí trong bộ nhớ của biến đó sẽ là thứ được truyền chứ không phải giá trị. Và nếu bạn truyền vào con trỏ (tức là địa chỉ bộ nhớ) thì mọi thay đổi được thực hiện sẽ được phản ánh ngay lập tức, ngay cả khi dòng tiếp theo của Quy trình được lưu trữ gây ra lỗi và nó hủy bỏ việc thực thi.
Để tổng hợp Phần 1: các giá trị biến luôn được sao chép; chúng không được tham chiếu bởi địa chỉ bộ nhớ của chúng.
Với Phần 1, chính sách luôn sao chép các giá trị biến có thể dẫn đến các vấn đề về tài nguyên khi biến được truyền vào là khá lớn. Tôi đã không kiểm tra để xem có bao loại blob được xử lý ( VARCHAR(MAX)
, NVARCHAR(MAX)
, VARBINARY(MAX)
, XML
, và những người không nên được sử dụng nữa: TEXT
, NTEXT
, và IMAGE
), nhưng nó là an toàn để nói rằng bất kỳ bảng dữ liệu được thông qua tại có thể là khá lớn. Sẽ rất hợp lý khi những người phát triển tính năng TVP mong muốn một khả năng "tham chiếu qua" thực sự để ngăn tính năng mới tuyệt vời của họ phá hủy một số hệ thống lành mạnh (nghĩa là muốn một cách tiếp cận có thể mở rộng hơn). Như bạn có thể thấy trong tài liệu đó là những gì họ đã làm:
Transact-SQL chuyển các tham số có giá trị bảng thành các thường trình bằng cách tham chiếu để tránh tạo bản sao của dữ liệu đầu vào.
Ngoài ra, mối quan tâm quản lý bộ nhớ này không phải là một khái niệm mới vì nó có thể được tìm thấy trong API SQLCLR được giới thiệu trong SQL Server 2005 (TVP được giới thiệu trong SQL Server 2008). Khi truyền NVARCHAR
và VARBINARY
dữ liệu vào mã SQLCLR (tức là các tham số đầu vào trên các phương thức .NET trong Hội đồng SQLCLR), bạn có tùy chọn đi theo cách tiếp cận "theo giá trị" bằng cách sử dụng một trong hai SqlString
hoặc SqlBinary
tương ứng, hoặc bạn có thể đi với "theo tham chiếu" "Cách tiếp cận bằng cách sử dụng một trong hai SqlChars
hoặc SqlBytes
tương ứng. Các loại SqlChars
và SqlBytes
cho phép truyền toàn bộ dữ liệu vào .NET CLR sao cho bạn có thể kéo các khối nhỏ của các giá trị lớn thay vì sao chép toàn bộ giá trị 200 MB (tối đa 2 GB, phải).
Tóm lại, Phần 2: TVP, về bản chất, sẽ có xu hướng tiêu thụ nhiều bộ nhớ (và do đó làm giảm hiệu suất) nếu nằm trong mô hình "luôn sao chép giá trị". Do đó TVP thực hiện "vượt qua tham chiếu" thực sự.
Phần cuối cùng là lý do tại sao Phần 2 quan trọng: tại sao việc chuyển qua TVP thực sự "bằng cách tham chiếu" thay vì tạo một bản sao của nó thay đổi bất cứ điều gì. Và điều đó được trả lời bởi mục tiêu thiết kế là cơ sở cho Phần 1: Các thủ tục được lưu trữ không hoàn thành thành công không nên thay đổi, theo bất kỳ cách nào, bất kỳ tham số đầu vào nào, cho dù chúng có được đánh dấu là OUTPUT
hay không. Việc cho phép các hoạt động DML sẽ có ảnh hưởng ngay lập tức đến giá trị của TVP vì nó tồn tại trong ngữ cảnh cuộc gọi (vì chuyển qua tham chiếu có nghĩa là bạn đang thay đổi điều được truyền vào chứ không phải bản sao của nội dung được truyền vào).
Bây giờ, một người nào đó, ở đâu đó, có lẽ đang nói chuyện với màn hình của họ, "Chà, chỉ cần xây dựng một cơ sở tự động để khôi phục mọi thay đổi được thực hiện cho các tham số TVP nếu bất kỳ điều gì được đưa vào Thủ tục lưu trữ. Duh. Vấn đề đã được giải quyết." Không quá nhanh. Đây là nơi mà bản chất của Biến bảng xuất hiện: các thay đổi được thực hiện đối với Biến bảng không bị ràng buộc bởi Giao dịch! Vì vậy, không có cách nào để đẩy các thay đổi trở lại. Và trên thực tế, đây là một mẹo được sử dụng để lưu thông tin được tạo trong một giao dịch nếu cần phải quay lại :-).
Để tổng hợp Phần 3: Biến bảng không cho phép thay đổi "hoàn tác" đối với chúng trong trường hợp có lỗi khiến Thủ tục lưu trữ bị hủy bỏ. Và điều này vi phạm mục tiêu thiết kế là có các tham số không bao giờ phản ánh việc thực hiện một phần (Phần 1).
TYPE
biến TVP của người dùng hoặc aDECLARE x as TABLE (...)
) với một thủ tục được lưu trữ? Tôi có thể làm điều đó, mặc dù có dung lượng bộ nhớ lớn hơn, với chức năng thay vìset @tvp = myfunction(@tvp)
nếuRETURNS
giá trị của chức năng của tôi là một bảng có cùng DDL như loại TVP?