wywołanie funkcji z innej bazy danych działającej na bazie w której jest wykonywana

0

Witam ponownie ;)
Przychodzę z nowymi problemami :P

Chcę zrobić funkcję, która będzie zapisana w jeden bazie danych a wywoływana będzie w innych. Funkcja będzie zwracać wyniki z tabel z bazy w której będzie wywoływana.
W tym celu w parametrach funkcji dodaję dodatkowy parametr @data_base_id int, który będzie identyfikował bazę danych w której wykonywana jest funkcja.
Następnie w funkcji robię coś takiego:

	declare @data_base_name nvarchar(50)
	SELECT @data_base_name = DB_NAME(@data_base_id)

A problem jest taki, że nie wiem jak zrobić zapytanie, które odwoływać się będzie do tej bazy danych (w której jest wywoływana funkcja).

Próbowałem coś w tym stylu (różne wersje):

SELECT xxx FROM @data_base_name.CDN.yyy

lub z dodaniem nawiasów kwadratowych tj.

SELECT xxx FROM [@data_base_name].CDN.yyy

Ale ciągle gdzieś jest błąd.

Mam więc pytanie jak to powinno być zrobione?
Z góry dziękuję za pomoc!

2
execute ('SELECT xxx FROM ' + @data_base_name +'.CDN.yyy')
0
katelx napisał(a):
execute ('SELECT xxx FROM ' + @data_base_name +'.CDN.yyy')

Dzięki. Niestety ciągle jest coś nie tak :-/.
Może ktoś rzucić okiem gdzie może być błąd?

Kopiuję tylko początek funkcji (podobno błąd jest w 11 linijce)

create function cdn.GetSaldo (@nr_doc nvarchar(30), @data_base_id int)   
	returns Decimal(10,2)
	as
   
	begin
		declare @data_base_name nvarchar(50)
		
		SELECT @data_base_name = DB_NAME(@data_base_id)
		
		declare @wynik Decimal(10,2)
		EXECUTE('SELECT 
			'+@wynik+' = (ISNULL(b.bo,0) + ISNULL(wn.sum_wn,0) - ISNULL(ma.sum_ma,0))
		FROM
		  (SELECT
				SUM(dekret.DeK_Kwota) sum_wn
			FROM
				'+@data_base_name+'.CDN.DekretyKonta dekret,
				'+@data_base_name+'.CDN.EwidDodNag ewidencja,
				'+@data_base_name+'.CDN.Konta konta,
				'+@data_base_name+'.CDN.Kategorie kategoria,
				'+@data_base_name+'.CDN.OkresyObrach okres 
			WHERE   
					dekret.DeK_Strona = 1  
				AND dekret.DeK_DataDok <= ewidencja.EDN_DataOpe
				AND dekret.DeK_AccId = konta.Acc_AccId
				AND ewidencja.EDN_NumerPelny = '+@nr_doc+'
				AND ewidencja.EDN_KatID = kategoria.Kat_KatID
(...)

Wywala mi błąd:

Msg 443, Level 16, State 14, Procedure GetSaldo, Line 11
Invalid use of a side-effecting operator 'EXECUTE STRING' within a function.

pozdrawiam i jeszcze raz dziękuję.

1

no tak, w funkcji nie mozesz uzywac dynamicznego sql. nie znam ladnego rozwiazania tego problemu.
z brzydkich - mozesz zamiast funkcji stworzyc procedure i zapisywac gdzies jej wynik.

edit: a czemu nie stworzyc tej funkcji w kazdej z baz danych i na poziomie klienta kontrolowac dla ktorej bazy jest to wywolywane?

0
katelx napisał(a):

edit: a czemu nie stworzyc tej funkcji w kazdej z baz danych i na poziomie klienta kontrolowac dla ktorej bazy jest to wywolywane?

No właśnie tego chciałem uniknąć ;-)
Chodzi o to, że tych baz jest dosyć dużo (kilkadziesiąt) i z czasem rośnie (co roku dochodzi przynajmniej kilka nowych). A dlatego, że SQL nie jest moją mocną stroną chciałem to obejść w ten sposób i o tym zapomnieć ;-).

0

jeśli to wszystko są bazy na jednym serwerze to rozważ opcję zrobienia jednego widoku, który będziesz musiał modyfikować co roku (no ale jak chcesz mieć dane z wszystkich baz, a te "dochodzą" to trzeba będzie zmieniać). Coś w ten deseń

SELECT 
           'baza1' baza,
           EDN_NumerPelny,
           (ISNULL(b.bo,0) + ISNULL(wn.sum_wn,0) - ISNULL(ma.sum_ma,0)) wynik
        FROM
          (SELECT
                SUM(dekret.DeK_Kwota) sum_wn,
                ewidencja.EDN_NumerPelny
            FROM
                baza1.CDN.DekretyKonta dekret,
                baza1.CDN.EwidDodNag ewidencja,
                baza1.CDN.Konta konta,
                baza1.CDN.Kategorie kategoria,
                baza1.CDN.OkresyObrach okres 
            WHERE   
                dekret.DeK_Strona = 1  
                AND dekret.DeK_DataDok <= ewidencja.EDN_DataOpe
                AND dekret.DeK_AccId = konta.Acc_AccId
                AND ewidencja.EDN_KatID = kategoria.Kat_KatID)
UNION
SELECT 
           'baza2' baza,
           EDN_NumerPelny,
           (ISNULL(b.bo,0) + ISNULL(wn.sum_wn,0) - ISNULL(ma.sum_ma,0)) wynik
        FROM
          (SELECT
                SUM(dekret.DeK_Kwota) sum_wn,
                ewidencja.EDN_NumerPelny
            FROM
                baza2.CDN.DekretyKonta dekret,
                baza2.CDN.EwidDodNag ewidencja,
                baza2.CDN.Konta konta,
                baza2.CDN.Kategorie kategoria,
                baza2.CDN.OkresyObrach okres 
            WHERE   
                dekret.DeK_Strona = 1  
                AND dekret.DeK_DataDok <= ewidencja.EDN_DataOpe
                AND dekret.DeK_AccId = konta.Acc_AccId
                AND ewidencja.EDN_KatID = kategoria.Kat_KatID)

na upartego można zrobić widoki najpierw na poszczególnych bazach z tego zapytania co ostatnio męczyliśmy :p a potem widoki z widoków. Teoretycznie wydajnościowo nie powinno być źle. Będzie trochę pracy przy nowej bazie (znaczy odpalić ze dwa-trzy skrypty). Jakby tak pomyśleć to SQL dla tego widoku można automatycznie generować. A co do zalet to masz jeden widok, w którym masz kwoty dla wszystkich baz i dla wszystkich EDN_NumerPelny - nic tylko pisać selekty i się cieszyć :)

0

A czemu to jest funkcja, a nie po prostu procedura?

0
abrakadaber napisał(a):

jeśli to wszystko są bazy na jednym serwerze to rozważ opcję zrobienia jednego widoku, który będziesz musiał modyfikować co roku (no ale jak chcesz mieć dane z wszystkich baz, a te "dochodzą" to trzeba będzie zmieniać).

Ho ho, ja nawet nie wiem czym jest widok :-P.
Dzięki za pomysł, ale to wygląda troszkę zbyt skomplikowanie :) -> chyba wolałbym kopiować listę funkcji i wrzucać do poszczególnych baz, gdy te zostaną dodane :).

somekind napisał(a):

A czemu to jest funkcja, a nie po prostu procedura?

A w procedurze można wykonywać dynamicznego SQL-a?
Czy taką procedurę będzie można wywołać wewnątrz funkcji?
No i dochodzi problem zwracania wartości. Fakt, że można utworzyć jakąś zmienną globalną i do niej przypisywać wynik, ale czy to jest bezpieczne?
Załóżmy, że dwie osoby na dwóch różnych komputerach (połączonych z serwerem) wykonają tą procedurę (funkcję z tą procedurą) w tej samej chwili -> czy nie ma ryzyka, że jedna z tych osób otrzyma wynik tej drugiej? Albo inaczej: czy wywoływanie funkcji/procedur na serwerze odbywa się w tym samym wątku czy w dwóch niezależnych?

Dziękuję wszystkim za udział w dyskusji ;)

A tak przy okazji:
Z wyzwalaczami nigdy nie miałem do czynienia - dowiedziałem się o nich przy okazji studiowania info o funkcjach/procedurach, ale czy nie dałoby się zrobić takiego wyzwalacza, który dodawałby taką funkcję w raz z tworzeniem kolejnej bazy danych?
Chociaż to taki pomysł na później - najpierw wolę spróbować czegoś mniej egzotycznego ;)

0
Księgowy napisał(a):

A w procedurze można wykonywać dynamicznego SQL-a?

Tak.

Czy taką procedurę będzie można wywołać wewnątrz funkcji?

Nie.

Skoro chcesz operować na danych z tabeli, to powinieneś użyć procedury. Funkcje służą do przetwarzania wejścia w wyjście w oderwaniu od danych w bazie.

No i dochodzi problem zwracania wartości. Fakt, że można utworzyć jakąś zmienną globalną i do niej przypisywać wynik, ale czy to jest bezpieczne? Załóżmy, że dwie osoby na dwóch różnych komputerach (połączonych z serwerem) wykonają tą procedurę (funkcję z tą procedurą) w tej samej chwili -> czy nie ma ryzyka, że jedna z tych osób otrzyma wynik tej drugiej?

Nie sądzę.

Albo inaczej: czy wywoływanie funkcji/procedur na serwerze odbywa się w tym samym wątku czy w dwóch niezależnych?

Co za różnica? Z punktu widzenia użytkownika ważne są transakcje, a nie szczegóły implementacji serwera.

1 użytkowników online, w tym zalogowanych: 0, gości: 1