W nawiązaniu do tematu http://cdn.3lance.pl/viewtopic.php?f=13&t=1428 zamieszczam przykładowe rozwiązanie, wraz z opisem.
CREATE TRIGGER [CDN].[_Kopiowanie_z_wysylki]
ON [CDN].[Atrybuty]
FOR INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
-- Dla zmiany wartości atrybutu
IF UPDATE (Atr_Wartosc)
BEGIN
-- Jeżeli ID klasy atrybutu jest równe X (u mnie 79 - atrybut na dokumentach PKA/WKA z numerem wysyłki)
-- i numer wysyłki jest na parametrze ustalony:
IF EXISTS (SELECT inserted.Atr_ID FROM inserted WHERE inserted.Atr_Atkid = 79 AND inserted.Atr_Wartosc <> '' AND inserted.Atr_Wartosc <> '<Brak>')
BEGIN
DECLARE @GIDWysylka INT -- GID numer wysyłki
DECLARE @ObiNumer INT -- GID obiektu dla którego uruchamiany jest wyzwalacz
DECLARE @ObiTyp INT -- GID Typ obiektu dla którego uruchamiany jest wyzwalacz
DECLARE @Wysylka VARCHAR(30) -- Wartość atrybutu z numerem wysyłki
DECLARE @param1 VARCHAR(100) -- Wartość parametru pierwszego
DECLARE @param2 VARCHAR(100) -- Wartość parametru drugiego
DECLARE @param3 VARCHAR(100) -- Wartość parametru trzeciego
-- Pobranie wartości atrybutu z numerem wysyłki
SET @Wysylka = (SELECT Atr_wartosc FROM inserted)
-- Pobranie GID-u obiektu (w tym przypadku PKA/WKA) dla którego zmieniana jest wartość atrybutu.
SET @ObiNumer = (SELECT Atr_ObiNumer FROM inserted)
-- Pobranie GID typ-u obiektu (w tym przypadku PKA/WKA) dla którego zmieniana jest wartość atrybutu.
SET @ObiTyp = (SELECT Atr_ObiTyp FROM inserted)
-- Pobranie GID-u wysyłki o określonym numerze (w moim przypadku seria jest pusta więc ją pomijam).
-- Generalnie jest to zależne od tego w jaki sposób uzupełniamy wartość atrybutu z numerem wysyłki.
-- Dla mnie seria zawsze jest pusta, a więc nie biorę jej pod uwagę - numer wysyłki na atrybucie określam poprzez wybór z listy za pomocą zapytania:
-- SELECT cast(Wys_numer as varchar(5)) + '/' + cast(Wys_rok as varchar(4)) as Numer FROM CDN.Wysylki
-- Stąd też wyjściowy sposób zwrócenia GID-u dla moich wysyłek:
SET @GIDWysylka = (SELECT isnull(WYS_GIDNumer,0) FROM CDN.Wysylki
WHERE WYS_GIDTyp = 337
AND WYS_Numer = cast(substring(@Wysylka,1,charindex('/',@Wysylka)-1) as int)
AND WYS_Rok = cast(substring(@Wysylka,charindex('/',@Wysylka)+1,4) as int))
-- Pobranie wartości pierwszego parametru z wysyłki, który będziemy kopiować
SET @param1 = (SELECT isnull(Atr_Wartosc,'') FROM CDN.Atrybuty
WHERE Atr_ObiTyp = 337 AND Atr_ObiNumer = @GIDWysylka AND Atr_AtkID = 80)
-- Pobranie wartości drugiego parametru z wysyłki, który będziemy kopiować
SET @param2 = (SELECT isnull(Atr_Wartosc,'') FROM CDN.Atrybuty
WHERE Atr_ObiTyp = 337 AND Atr_ObiNumer = @GIDWysylka AND Atr_AtkID = 81)
-- Pobranie wartości trzeciego parametru z wysyłki, który będziemy kopiować
SET @param3 = (SELECT isnull(Atr_Wartosc,'') FROM CDN.Atrybuty
WHERE Atr_ObiTyp = 337 AND Atr_ObiNumer = @GIDWysylka AND Atr_AtkID = 82)
-- Skopiowanie wartości pierwszego parametru na obiekt docelowy
UPDATE CDN.Atrybuty
SET Atr_Wartosc = @param1
WHERE Atr_ObiTyp = @ObiTyp AND CDN.Atrybuty.Atr_ObiNumer = @ObiNumer AND Atr_AtkID = 80
-- Skopiowanie wartości drugiego parametru na obiekt docelowy
UPDATE CDN.Atrybuty
SET Atr_Wartosc = @param2
WHERE Atr_ObiTyp = @ObiTyp AND CDN.Atrybuty.Atr_ObiNumer = @ObiNumer AND Atr_AtkID = 81
-- Skopiowanie wartości trzeciego parametru na obiekt docelowy
UPDATE CDN.Atrybuty
SET Atr_Wartosc = @param3
WHERE Atr_ObiTyp = @ObiTyp AND CDN.Atrybuty.Atr_ObiNumer = @ObiNumer AND Atr_AtkID = 82
END
END
END
Zaznaczam, iż wyzwalacz ma charakter edukacyjny i z tym wiąże się deklarowanie tylu zmiennych oraz jego dość rozbudowana forma. To samo można osiągnąć przy użyciu jednego UPDATE FROM SELECT, aczkolwiek uznałem, że nie dla wszystkich byłoby to czytelne.
Pole Atr_AtkID to oczywiście ID atrybutów które nas interesują - w moim przypadku są to ID ze zbioru {79,80,81,82} - w waszym zapewne będą inne i wymaga to sprawdzenia. Wyzwalacz skopiuje wartość danego atrybutu tylko w przypadku gdy ten atrybut będzie istniał na docelowym elemencie.
Jak na razie to pierwszy trigger jaki tu zamieszczam, aczkolwiek zapewne nie ostatni, jako iż jest to ciekawa funkcja SQL Server, która daje administratorowi dużo możliwości.
środa, 27 października 2010
poniedziałek, 25 października 2010
Sklejanie pól tekstowych - unikanie kursora.
Przeglądając, jak co dzień, forum 3lance, zauważyłem temat gdzie jeden z użytkowników umieścił funkcję w której wykorzystywał kursor do sklejania wartości tekstowych. http://cdn.3lance.pl/viewtopic.php?f=13&t=1435 Tak troszkę mnie tknęło, bo przecież generalnie kursor mistrzem wydajności nie jest i w miarę możliwości winno się go unikać. Napisałem więc swoje rozwiązanie, które jest bardziej wydajne - być może się wam przyda :)
Przykładowy kod generujący "sklejone" płatności dla pierwszych 49 GID-ów w tabeli płatności.
SELECT DISTINCT tab1.TrP_GIDNumer,
STUFF((SELECT ',' + LTRIM(RTRIM(tab2.TrP_FormaNazwa)) FROM CDN.TraPlat AS tab2 WHERE tab2.TrP_GIDNumer = tab1.TrP_GIDNumer FOR XML PATH('')), 1, 1, '') AS Formy
FROM CDN.TraPlat AS tab1 WHERE trp_GIDNumer < 50
ORDER BY tab1.TrP_GIDNumer
Rozwiązanie trochę bardziej eleganckie, no i pozbyliśmy się nieszczęsnego kursora :P Na dzisiaj to by było na tylko, bo czasu brak :)
EDIT: Jeszcze mały edit - podane przeze mnie rozwiązanie będzie działać od SQL w wersji 2005.
Przykładowy kod generujący "sklejone" płatności dla pierwszych 49 GID-ów w tabeli płatności.
SELECT DISTINCT tab1.TrP_GIDNumer,
STUFF((SELECT ',' + LTRIM(RTRIM(tab2.TrP_FormaNazwa)) FROM CDN.TraPlat AS tab2 WHERE tab2.TrP_GIDNumer = tab1.TrP_GIDNumer FOR XML PATH('')), 1, 1, '') AS Formy
FROM CDN.TraPlat AS tab1 WHERE trp_GIDNumer < 50
ORDER BY tab1.TrP_GIDNumer
Rozwiązanie trochę bardziej eleganckie, no i pozbyliśmy się nieszczęsnego kursora :P Na dzisiaj to by było na tylko, bo czasu brak :)
EDIT: Jeszcze mały edit - podane przeze mnie rozwiązanie będzie działać od SQL w wersji 2005.
piątek, 22 października 2010
HASP i jego aktualizacja
Będąc kiedyś na szkoleniu, spotkałem się rozmową dwóch Panów, którzy byli przekonani, iż serwisu klucza HASP (a w zasadzie licencji na kluczu) nie da się zaktualizować poprzez połączenie pulpitu zdalnego - trzeba się udać do klienta i dopiero można działać. Nie jest to do końca zgodne z prawdą. Otóż w momencie kiedy zalogujemy się na nasz serwer w sesji terminalowej, sam serwis klucza nie będzie widoczny:
Udajemy się więc do narzędzia administracyjne/pulpity zdalne. Tworzymy nowe połączenie do serwera na którym obecnie jesteśmy (nawet bez licencji terminalowych Win 2003/2008 Serwer ma 2 licencje dla administratora, więc zalogowanie będzie możliwe) i logujemy się już bardziej "lokalnie" do tego samego serwera. Efekt końcowy wygląda następująco:
Jesteśmy w stanie tak zaktualizować klucz, jak i zwalniać z niego "zawieszone" licencje. Czemu tak się dzieje ? Otóż sesja wewnętrzna nie jest do końca traktowana jak połączenie pulpitu zdalnego i dzięki temu widzi wszystkie lokalne porty i usługi na nich działające.
Co prawda nie jest to odkrycie Ameryki, ale myślę, że komuś może się przydać - może akurat kogoś wybawiłem przed 50 kilometrową wycieczką tylko dlatego, że coś nie działa :)
EDIT: W ramach uzupełnienia, Adam S uświadomił mnie, iż ten sam efekt można uzyskać poprzez wywołanie mstsc /admin - na pewno jest szybciej :) Niemniej jednak zostawiam obie możliwości.
Udajemy się więc do narzędzia administracyjne/pulpity zdalne. Tworzymy nowe połączenie do serwera na którym obecnie jesteśmy (nawet bez licencji terminalowych Win 2003/2008 Serwer ma 2 licencje dla administratora, więc zalogowanie będzie możliwe) i logujemy się już bardziej "lokalnie" do tego samego serwera. Efekt końcowy wygląda następująco:
Jesteśmy w stanie tak zaktualizować klucz, jak i zwalniać z niego "zawieszone" licencje. Czemu tak się dzieje ? Otóż sesja wewnętrzna nie jest do końca traktowana jak połączenie pulpitu zdalnego i dzięki temu widzi wszystkie lokalne porty i usługi na nich działające.
Co prawda nie jest to odkrycie Ameryki, ale myślę, że komuś może się przydać - może akurat kogoś wybawiłem przed 50 kilometrową wycieczką tylko dlatego, że coś nie działa :)
EDIT: W ramach uzupełnienia, Adam S uświadomił mnie, iż ten sam efekt można uzyskać poprzez wywołanie mstsc /admin - na pewno jest szybciej :) Niemniej jednak zostawiam obie możliwości.
czwartek, 21 października 2010
Wyświetlanie komunikatów błędów w XL (i nie tylko).
Nie raz, nie dwa, zdarzało się, iż podczas pracy z XL (np. przy wywoływaniu wydruku) przywitał nas komunikat o błędzie. Problem w tym, że sam błąd nie był opisany, a zamiast tego witał nas komunikat "Kod dostawcy bazy danych = X", gdzie X jest liczbą naturalną. Jeśli nie wiemy co tak naprawdę oznacza dany komunikat, warto zapoznać się z tabelą systemową sys.messsages w kontekście bazy danych . Tak więc wywołanie z poziomu np. SSMS:
SELECT message_id, language_id, severity, is_event_logged, text
FROM sys.messages
zwróci nam informację o wszystkich błędach zdefiniowanych we wspomnianej tabeli.
Skoro wyskoczył nam ten jeden konkretny (X) to najłatwiej znaleźć go poprzez dopisanie do naszego zapytania klauzuli WHERE (WHERE message_id = X) i ograniczenie wyników do języka, który nie wygląda dla nas jak stado znaczków - w moim przypadku wybrałem angielski (AND language_ID = 1033). Otrzymamy więc prościutkie zapytanie, np.
SELECT severity, text
FROM sys.messages
WHERE language_id = 1033 AND message_id = 120
które zwróci nam informację na ile poważny jest błąd (kolumna severity) i jego opis (text). Dla ciekawskich zamieszczam jeszcze link do opisu tabeli wg. Microsoft-u http://msdn.microsoft.com/en-us/library/ms187382.aspx.
Niezbyt obszerny ten post, ale od czegoś trzeba zacząć :)
SELECT message_id, language_id, severity, is_event_logged, text
FROM sys.messages
zwróci nam informację o wszystkich błędach zdefiniowanych we wspomnianej tabeli.
Skoro wyskoczył nam ten jeden konkretny (X) to najłatwiej znaleźć go poprzez dopisanie do naszego zapytania klauzuli WHERE (WHERE message_id = X) i ograniczenie wyników do języka, który nie wygląda dla nas jak stado znaczków - w moim przypadku wybrałem angielski (AND language_ID = 1033). Otrzymamy więc prościutkie zapytanie, np.
SELECT severity, text
FROM sys.messages
WHERE language_id = 1033 AND message_id = 120
które zwróci nam informację na ile poważny jest błąd (kolumna severity) i jego opis (text). Dla ciekawskich zamieszczam jeszcze link do opisu tabeli wg. Microsoft-u http://msdn.microsoft.com/en-us/library/ms187382.aspx.
Niezbyt obszerny ten post, ale od czegoś trzeba zacząć :)
Wstęp...
Tytułem wstępu wypada powiedzieć coś o sobie. Mam na imię Mateusz i właśnie rozpoczynam prowadzenie tego oto bloga - wraz z czasem będzie się zmieniać zarówno jego treść, jak i wygląd. Jak długo będę go prowadzić, nie wiem, aczkolwiek docelowo mam zamiar stworzyć coś swojego, a nie działać w oparciu o szablon. No tak.... Ale o czym będzie ten blog ? W założeniu poświęcony on będzie zagadnieniom związanym z technologiami Microsoftu - SQL Server i .NET. Jako, że na co dzień pracuję w firmie działającej z Comarch XL, a wcześniej, przez ponad 2 lata byłem programisto-wdrożeniowcem w firmie wdrażającej to oprogramowanie, wpadnie tutaj sporo informacji i o tym. Swoją wiedzę rozwijam z każdym kolejnym dniem i w związku z tym chce się nią po części podzielić poprzez zamieszczanie tutaj wpisów - będą to tak drobne informacje, jak i rozwiązania problemów z którymi się borykam, oraz ciekawostki które odnalazłem w odmętach internetu, a uznałem je za interesujące. Daleko mi do doskonałości w tym co robię, ale myślę, że to co tutaj zamieszczę przyda się niejednej osobie. Pozdrawiam i miłej lektury !
Subskrybuj:
Posty (Atom)