2013-08-22 8 views
30

Mam następujący kod w mojej funkcji SQL:Nieprawidłowe użycie side-dokonywaniu operator Insert wewnątrz funkcji

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

    commit 
end 

Poniżej znajduje się SQL Server 2008 Query i wyskakuje mi błąd:

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

Dlaczego nie mogę tego zrobić? Co mogę zrobić, aby to naprawić?

+1

Najwyraźniej posiada dla [tabele tymczasowe zbyt] (http://stackoverflow.com/questions/9844854/is-it-possible-to-have-temp-tables-in-a-function), choć [można użyć Zmienne TABLE] (http://stackoverflow.com/a/9844898/1028230) w celu osiągnięcia tego samego. Domyśl. – ruffin

Odpowiedz

59

Nie można użyć funkcji wstawiania danych do tabeli podstawowej. Funkcje return danych. To jest wymieniony jako the very first limitation in the documentation:

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

„Modyfikuj stan bazy danych” obejmuje zmiany jakichkolwiek danych w bazie danych (choć zmienna stół jest oczywistym wyjątkiem PO nie będzie obchodziło około 3 lata temu - zmienna ta tabela żyje tylko przez czas trwania wywołania funkcji iw żaden sposób nie wpływa na znajdujące się pod nim tabele).

Powinieneś używać procedury składowanej, a nie funkcji.

+3

Tak, możesz INSERT wewnątrz funkcji, przez zminimalizowanie polecenia INSERT przy użyciu zmiennej. Zobacz moją odpowiedź poniżej. – Fandango68

+0

Tak, prawdziwa odpowiedź znajduje się poniżej http://stackoverflow.com/a/40307859/2656881 – menkow

+0

@mvv To może być sposób na sfałszowanie go, ale zadaj sobie pytanie, dlaczego to musi być zrobione w funkcji, po pierwsze, jak wygodne myślisz, że przeciętny użytkownik otwiera wszystkie implikacje związane z bezpieczeństwem xp_cmdshell i dlaczego nie możesz po prostu użyć procedury? Czy naprawdę potrzebujesz funkcji skalarnej, która mogłaby wywoływać xp_cmdshell dla każdego wiersza w tabeli o wielkości 2 miliardów wierszy? Z pewnością nie. Funkcja w T-SQL zwraca dane, nie ma działać tak jak tradycyjne funkcje w innych językach. –

5

Funkcje nie mogą być używane do modyfikowania informacji tabeli podstawowej, użyj procedury przechowywanej.

4

Znalazłem sposób na wstawienie lub aktualizację, więc wystarczy tylko wymienić kod wewnątrz zmiennej @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

"Znaleziono jeden sposób". Zasadniczo wywołujesz sqlcmd.exe z wewnątrz sql (który nawet nie istnieje na Linuksie lub Azure). To jest jerry-rig, quick fix, kludge, głupia rzecz McGyvera. Proszę, nie rób tego! Ominięcie problemu nie jest rozwiązaniem! –

+0

JCKödel, całkowicie się z tobą zgadzam, to nie jest rozwiązanie, to tylko jeden sposób, aby to zrobić. Prawidłowe rozwiązanie - powinieneś używać procedury składowanej, NIE FUNKCJI! Aaron Bertrand wyjaśnił to bardzo dobrze (patrz najlepsza odpowiedź na obecne pytanie powyżej). I sugerowałbym unikanie użycia tego rozwiązania, tylko po to, aby przetestować coś ... –

1

Istnieje wyjątek (używam SQL 2014), gdy używasz tylko wstawiania/aktualizacji/usuwania na zadeklarowanych tabelach. Te instrukcje Insert/Update/Delete nie mogą zawierać instrukcji OUTPUT. Drugim ograniczeniem jest to, że nie wolno ci robić MERGE, nawet w Deklaracji-Tabeli. Zerwałem moje instrukcje łączenia, które nie działały, na instrukcje Insert/Update/Delete, które działały.

Powodem, dla którego nie przekonwertowałem tego do procedury przechowywanej, jest to, że funkcja table była szybsza (nawet bez MERGE) niż procedura przechowywana. Dzieje się tak pomimo procedury przechowywanej, która pozwala mi używać tabel temperatury z danymi statystycznymi. Potrzebowałem funkcji stołowej, aby była bardzo szybka, ponieważ nazywa się to 20-K razy/dzień. Ta funkcja tabel nigdy nie aktualizuje bazy danych.

Zauważyłem również, że funkcje SQL NewId() i RAND() nie są dozwolone w funkcji.