2013-08-22 19 views
30

Tôi có đoạn mã sau vào chức năng sql của tôi:sử dụng không hợp lệ phụ thực hành Insert trong vòng một hàm

if @max_chi > -999 
begin 
    INSERT INTO CH_TABLE(X1, X2, VALUE) 
    VALUES(cur_out.sessionnumber, maxpos, max_chi) 

    commit 
end 

Sau đây là SQL Server 2008 Query và nó mang lại cho tôi một lỗi:

Invalid use of a side-effecting operator 'INSERT' within a function.

Tại sao tôi không được phép thực hiện việc này? Tôi có thể làm gì để sữa nó?

+1

Rõ ràng là giữ cho [bảng tạm thời] (http://stackoverflow.com/questions/9844854/is-it-possible-to-have-temp-tables-in-a-function), mặc dù [bạn có thể sử dụng TABLE biến] (http://stackoverflow.com/a/9844898/1028230) để thực hiện điều tương tự. Đi con số. – ruffin

Trả lời

59

Bạn không thể sử dụng hàm để chèn dữ liệu vào bảng cơ sở. Chức năng trả lại dữ liệu. Đây được liệt kê như the very first limitation in the documentation:

User-defined functions cannot be used to perform actions that modify the database state.

"Sửa đổi trạng thái cơ sở dữ liệu" bao gồm thay đổi bất kỳ dữ liệu trong cơ sở dữ liệu (mặc dù một biến bảng là một ngoại lệ rõ ràng OP sẽ không quan tâm đến 3 năm trước - biến bảng này chỉ sống trong suốt thời gian gọi hàm và không ảnh hưởng đến các bảng bên dưới theo bất kỳ cách nào).

Bạn nên sử dụng quy trình được lưu trữ chứ không phải chức năng.

+3

Có, bạn có thể INSERT bên trong một hàm, bằng cách bắt chước lệnh INSERT bằng cách sử dụng một biến. Xem câu trả lời của tôi dưới đây. – Fandango68

+0

Có, câu trả lời đúng là dưới đây http://stackoverflow.com/a/40307859/2656881 – menkow

+0

@mvv Đó có thể là một cách để giả mạo nó nhưng tự hỏi tại sao điều này cần phải được thực hiện trong một chức năng ở nơi đầu tiên, làm thế nào thoải mái bạn nghĩ rằng người dùng trung bình đang mở ra tất cả các tác động bảo mật của xp_cmdshell, và tại sao bạn không thể chỉ sử dụng một thủ tục? Bạn có thực sự muốn một hàm vô hướng có thể gọi xp_cmdshell cho mỗi hàng trong một bảng hàng 2 tỷ không? Tôi chắc chắn không. Một hàm trong T-SQL trả về dữ liệu, nó không có nghĩa là làm việc giống như các hàm truyền thống trong các ngôn ngữ khác. –

5

Không thể sử dụng các chức năng để sửa đổi thông tin bảng cơ sở, sử dụng quy trình được lưu trữ.

4

Tôi đã tìm thấy một cách để chèn hoặc cập nhật, do đó bạn chỉ cần thay thế mã bên trong biến số @sql.

CREATE FUNCTION [dbo].[_tmp_func](@orderID NVARCHAR(50)) 
RETURNS INT 
AS 
BEGIN 
DECLARE @sql varchar(4000), @cmd varchar(4000) 
SELECT @sql = 'INSERT INTO _ord (ord_Code) VALUES (''' + @orderID + ''') ' 
SELECT @cmd = 'sqlcmd -S ' + @@servername + 
       ' -d ' + db_name() + ' -Q "' + @sql + '"' 
EXEC master..xp_cmdshell @cmd, 'no_output' 
RETURN 1 
END 
+2

"Tìm thấy một cách". Về cơ bản, bạn đang gọi sqlcmd.exe từ bên trong sql (mà thậm chí không tồn tại trên Linux hoặc Azure). Đây là một jerry-rig, một sửa chữa nhanh chóng, một kludge, một loại McGyver điều ngu ngốc. Làm ơn, đừng! Phá vỡ một vấn đề không phải là một giải pháp! –

+0

JCKödel, tôi hoàn toàn đồng ý với bạn, nó không phải là một giải pháp, nó chỉ là một cách để làm điều đó. Giải pháp đúng - bạn nên sử dụng thủ tục lưu sẵn, KHÔNG CHỨC NĂNG! Aaron Bertrand giải thích nó rất tốt (xem câu trả lời tốt nhất cho câu hỏi hiện tại ở trên). Và tôi khuyên bạn nên tránh sử dụng giải pháp này, chỉ để kiểm tra điều gì đó ... –

1

Có ngoại lệ (Tôi đang sử dụng SQL 2014) khi bạn chỉ sử dụng Chèn/Cập nhật/Xóa trên Bảng khai báo. Các câu lệnh Insert/Update/Delete này không thể chứa câu lệnh OUTPUT. Các hạn chế khác là bạn không được phép thực hiện một MERGE, thậm chí vào một bảng khai báo. Tôi đã chia nhỏ các câu lệnh Merge của mình, điều đó không có tác dụng, vào các câu lệnh Insert/Update/Delete đã hoạt động.

Lý do tôi không chuyển đổi nó thành quy trình được lưu trữ là hàm bảng nhanh hơn (thậm chí không có MERGE) so với quy trình được lưu trữ. Điều này là mặc dù các thủ tục lưu trữ cho phép tôi sử dụng Temp-Tables có số liệu thống kê. Tôi cần chức năng bảng rất nhanh, vì nó được gọi là 20-K lần/ngày. Hàm bảng này không bao giờ cập nhật cơ sở dữ liệu.

Tôi cũng nhận thấy rằng các hàm SQL NewId() và RAND() không được phép trong một hàm.