Tại sao TVP phải S READN SÀNG và tại sao các tham số của các loại khác không thể S READN SÀNG


19

Theo các tham số blog này cho một chức năng hoặc một thủ tục được lưu trữ về cơ bản là giá trị truyền qua nếu chúng không phải là OUTPUTtham số và về cơ bản được coi là phiên bản an toàn hơn của tham chiếu qua nếu chúng là OUTPUTtham số.

Lúc đầu, tôi nghĩ mục tiêu buộc TVP phải được tuyên bố READONLYlà báo hiệu rõ ràng cho các nhà phát triển rằng TVP không thể được sử dụng như một OUTPUTtham số, nhưng phải có nhiều hơn nữa vì chúng ta không thể tuyên bố không phải TVP như READONLY. Ví dụ như các lỗi sau:

create procedure [dbo].[test]
@a int readonly
as
    select @a

Msg 346, Cấp 15, Trạng thái 1, Kiểm tra thủ tục
Tham số "@a" không thể được khai báo S READN SÀNG vì nó không phải là tham số có giá trị bảng.

  1. số liệu thống kê không được lưu trữ trên TVP, lý do đằng sau việc ngăn chặn các hoạt động DML là gì?
  2. Có liên quan đến việc không muốn TVP là OUTPUTtham số vì một số lý do?

Câu trả lời:


19

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.

  1. 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 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 OUTPUTcá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.

  2. 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 NVARCHARVARBINARYdữ 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 SqlStringhoặc SqlBinarytươ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 SqlCharshoặc SqlBytestương ứng. Các loại SqlCharsSqlBytescho 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ự.

  3. 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à OUTPUThay 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).

Ergo: các READONLYtừ khóa là cần thiết để ngăn chặn các hoạt động DML trên TVPs vì chúng là biến Bảng rằng đang thực sự thông qua "bằng cách tham khảo", và do đó bất kỳ sửa đổi cho họ sẽ được phản ánh ngay lập tức, ngay cả khi Stored Procedure gặp một lỗi, và không có là cách khác để ngăn chặn điều đó.

Ngoài ra, các tham số của các kiểu dữ liệu khác không thể sử dụng READONLYvì chúng đã là bản sao của những gì được truyền vào và vì vậy nó sẽ không bảo vệ bất cứ thứ gì chưa được bảo vệ. Điều đó và cách mà các tham số của các kiểu dữ liệu khác hoạt động được dự định là đọc-ghi, do đó, có lẽ sẽ còn nhiều việc hơn để thay đổi API đó thành một khái niệm chỉ đọc.


Giải thích rất chi tiết. Cảm ơn. Vì vậy, không có cách nào để sửa đổi một biến bảng đã qua (một TYPEbiến TVP của người dùng hoặc a DECLARE 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ếu RETURNSgiá trị của chức năng của tôi là một bảng có cùng DDL như loại TVP?
mpag

@mpag Cảm ơn. TVP một biến bảng, không có sự khác biệt. Bạn không vượt qua trong loại, bạn chuyển vào một biến bảng được tạo từ một loại hoặc từ một khai báo lược đồ rõ ràng. Ngoài ra, bạn không thể SETbiến một bảng, ít nhất là tôi không biết. Và ngay cả khi bạn có thể: a) bạn không thể truy cập vào tập kết quả thông qua =toán tử và b) TVP vẫn được đánh dấu là READONLY, vì vậy cài đặt nó sẽ vi phạm điều đó. Chỉ cần đổ nội dung vào một bảng tạm thời hoặc một biến bảng khác mà bạn tạo trong Proc.
Solomon Rutzky

Cảm ơn một lần nữa. Về cơ bản, tôi đã quyết định sử dụng cách tiếp cận bảng tạm thời.
mpag

5

Câu trả lời Wiki cộng đồng được tạo ra từ một nhận xét về câu hỏi của Martin Smith

Có một mục Kết nối hoạt động (được gửi bởi Erland Sommarskog) cho việc này:

Hạn chế thư giãn rằng các tham số bảng phải được đọc chỉ khi các SP gọi nhau

Phản hồi duy nhất của Microsoft cho đến nay cho biết (nhấn mạnh thêm):

Cảm ơn các phản hồi về điều này. Chúng tôi đã nhận được phản hồi tương tự từ một số lượng lớn khách hàng. Việc cho phép các tham số có giá trị bảng được đọc / ghi liên quan đến khá nhiều công việc ở phía SQL Engine cũng như các giao thức máy khách. Do các hạn chế về thời gian / tài nguyên cũng như các ưu tiên khác, chúng tôi sẽ không thể đảm nhận công việc này như một phần của phiên bản SQL Server 2008. Tuy nhiên, chúng tôi đã điều tra vấn đề này và có vấn đề này trong radar của chúng tôi để giải quyết như là một phần của phiên bản SQL Server tiếp theo. Chúng tôi đánh giá cao và hoan nghênh phản hồi ở đây.

Srini Acharya
Trình quản lý chương trình cao cấp
SQL Server Công cụ quan hệ

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.