11. Wyczyszczenie obiektówDodanie referencji do projektu jest konieczne, gdy chcemy skorzystać z pełni funkcjonalności edytora VBE oraz gdy jawnie deklarujemy typy zmiennych. W pewnych sytuacjach dodanie referencji przeszkadza bardziej niż pomaga, np. gdy próbujemy uruchomić makro na innej maszynie z niedoinstalowanymi komponentami. Taka sytuacja spowoduje błąd i makro się nie uruchomi. Swego rodzaju zabezpieczeniem jest korzystanie ze zmiennych obiektowych i tworzenie obiektu za pomocą instrukcji CreateObject. Poniżej przykład jawnego deklarowania zmiennych i niejawnego z wykorzystaniem Zmiennych obiektowych. Sub test_1() Dim aConn As ADODB.Connection Set aConn = New ADODB.Connection ' dalszy kod aplikacji Set aConn = Nothing End Sub Sub test_2() Dim aConn As Object Set aConn = CreateObject(
ADODB.Connection
) ' dalszy kod aplikacji Set aConn = Nothing End Sub Sporym minusem drugiego sposobu jest brak InteliSense w edytorze, niewątpliwą zaletą brak konieczności tworzenia referencji do biblioteki źródłowej. Otwarcie połączenia jest pierwszym etapem pracy z samą bazą. Żeby tego dokonać trzeba zbudować odpowiedni Connection String dostosowany do ustawień samej bazy danych. Przykładowe ciągi znajdują się poniżej: Dla połączenia z jawnie deklarowanym hasłem i nazwą użytkownika: aConn.ConnectionString =
Provider=SQLNCLI10;Server=.QLEXPRESS;Database=Baza;Uid=Użytkownik;Pwd=Hasło;” Dla połączenia z wykorzystaniem zintegrowanej metody logowania: aConn.ConnectionString =
Provider=SQLNCLI10;Server=.QLEXPRESS;Database=Baza;Trusted_Connection=yes;” W przypadku zaś, gdy chcemy zapytać o login I hasło lub wybrać metodę zintegrowaną trzeba posłużyć się pewnym trikiem z ustawieniem jednego parametru połączenia aConn.ConnectionString =
Provider=SQLNCLI10;Server=.QLEXPRESS;Database=Baza;
aConn.Properties(
Prompt
) = adPromptAlways Przykładowy kod z wykorzystaniem ostatniej metody: Sub test_3() Dim aConn As ADODB.Connection Set aConn = New ADODB.Connection aConn.ConnectionString =
Provider=SQLNCLI10;Server=.QLEXPRESS;Database=Baza;
aConn.Properties(
Prompt
) = adPromptAlways aConn.Open MsgBox aConn.State aConn.Close Set aComm = Nothing Set aConn = Nothing End Sub Komentarza wymaga tylko aConn.State – Właściwość ta mówi nam, że połączenie jest otwarte (wartość 1), zamknięte (wartość 0) lub jest w trakcie nawiązywania połączenia (wartość 2). Jest to również najszybsza i najpewniejsza metoda sprawdzenia czy rzeczywiście nawiązaliśmy połączenia z bazą danych. Konstrukcja ADO pozwala pomijać pewne kroki w określonych sytuacjach np. dozwolone jest stworzenie Recordset-u wprost z połączenia gdyż obiekt Connection zawiera metodę Execute. Wiąże się to jednak z wieloma ograniczeniami i jest przydatne tylko do bardzo prostych zapytań. Sub test_4() Dim aConn As ADODB.Connection Set aConn = New ADODB.Connection aConn.ConnectionString =
Provider=SQLNCLI10;Server=.QLEXPRESS;Database=Baza;
aConn.Properties(
Prompt
) = adPromptAlways aConn.Open aConn.Execute
DELETE FROM TABELA
, False aConn.Close Set aComm = Nothing Set aConn = Nothing End Sub Dlatego też często korzysta się z obiektu Command pozwalającego na bardziej zaawansowaną pracę z zapytaniami między innymi natywnie wspiera parametry oraz umożliwia parametryzowanie procedur oraz prostych zapytań SQl-owych. Przykład kodu parametryzującego kod SQL i wykonujący go dwukrotnie: Sub test_5() Dim aConn As ADODB.Connection Dim aComm As ADODB.Command Set aComm = New ADODB.Command Set aConn = New ADODB.Connection aConn.ConnectionString =
Provider=SQLNCLI10;Server=.QLEXPRESS;Database=Baza;
aConn.Properties(
Prompt
) = adPromptAlways aConn.Open Set aComm.ActiveConnection = aConn aComm.CommandType = adCmdText aComm.CommandText =
DELETE FROM TABELA WHERE ID = ?
aComm.Parameters.Append aComm.CreateParameter(, adInteger, adParamInput) aComm.Parameters(1).Value = 1 aComm.Execute False aComm.Parameters(1).Value = 2 aComm.Execute False aConn.Close Set aComm = Nothing Set aConn = Nothing End Sub UWAGA: Parametryzacja zapytania to zastąpienie fragmentu SQL-a za pomocą znaku zapytania. Pod taki znak następnie jest podstawiany parametr utworzony za pomocą kodu VBA lub też pobrany bezpośrednio z tablicy. aComm.Execute False,array(2) Gdzie Array(2) to tablica zawierająca parametry podstawiane w kolejności pojawienia się w tablicy do kolejnych znaków zapytania. Obiekt Command automatycznie pobiera listę parametrów dostępną dla procedury składowanej. Dzięki temu faktowi nie musimy tworzyć kolekcji parametrów samodzielnie. Sub test_6() Dim aConn As ADODB.Connection Dim aComm As ADODB.Command Set aComm = New ADODB.Command Set aConn = New ADODB.Connection aConn.ConnectionString =
Provider=SQLNCLI10;Server=.QLEXPRESS;Database=Baza;
aConn.Properties(
Prompt
) = adPromptAlways aConn.Open Set aComm.ActiveConnection = aConn aComm.CommandType = adCmdStoredProc aComm.CommandText =
dbo.usp_procedura
aComm.Parameters(
@parametr
).Value = 1 aComm.Execute False aConn.Close Set aComm = Nothing Set aConn = Nothing End Sub W przypadku gdyby wystąpiły jakieś problemy z pobraniem tej kolekcji trzeba sprawdzić czy użytkownik w kontekście, którego pracujemy ma uprawnienia Execute do wybranych obiektów: W przypadku gdyby się okazało, że nie posiadamy stosownych uprawnień możemy je nadać za pomocą T-SQL-a GRANT EXECUTE ON [dbo].[sp_ddopen] TO [guest] GRANT EXECUTE ON [dbo].[sp_sproc_columns] TO [guest] GO W sytuacji podbramkowej możemy samodzielnie stworzyć kolekcję parametrów na podstawie następującego zapytania SQL: Sub test_7() Dim aConn As ADODB.Connection Dim aComm As ADODB.Command Set aComm = New ADODB.Command Set aConn = New ADODB.Connection aConn.ConnectionString =
Provider=SQLNCLI10;Server=.QLEXPRESS;Database=Baza;
aConn.Properties(
Prompt
) = adPromptAlways aConn.Open Set aComm.ActiveConnection = aConn aComm.CommandType = adCmdStoredProc aComm.CommandText =
dbo.usp_procedura
aComm.Parameters.Append aComm.CreateParameter(
@parametr
, adInteger, adParamInput, 1) aComm.Parameters(
@parametr
).Value = 1 aComm.Execute False aConn.Close Set aComm = Nothing Set aConn = Nothing End Sub Przydatna może tu być tablica translacji miedzy typami danych VBA a SQL DataType EnumValueAccessSQLServeradBigInt20 BigInt (SQL Server 2000 +)adBinary128 BinaryTimeStampadBoolean11YesNoBitadChar129 CharadCurrency6CurrencyMoneySmallMoneyadDate7DateDateTimeadDBTimeStamp135DateTime (Access 97 (ODBC))DateTimeSmallDateTimeadDecimal14 adDouble5DoubleFloatadGUID72ReplicationID (Access 97 (OLEDB)), (Access 2000 (OLEDB))UniqueIdentifier (SQL Server 7.0 +)adIDispatch9 adInteger3AutoNumberIdentity (SQL Server 6.5)IntegerIntLong adLongVarBinary205OLEObjectImageadLongVarChar201Memo (Access 97)TextHyperlink (Access 97)adLongVarWChar203Memo (Access 2000 (OLEDB))NText (SQL Server 7.0 +)Hyperlink (Access 2000 (OLEDB))adNumeric131Decimal (Access 2000 (OLEDB))DecimalNumeric adSingle4SingleRealadSmallInt2IntegerSmallIntadUnsignedTinyInt17ByteTinyIntadVarBinary204ReplicationID (Access 97)VarBinaryadVarChar200Text (Access 97)VarCharadVariant12 Sql_Variant (SQL Server 2000 +)adVarWChar202Text (Access 2000 (OLEDB))NVarChar (SQL Server 7.0 +)adWChar130 NChar (SQL Server 7.0 +) Obiekt wyjściowy Recordset, który otrzymaliśmy po wykonaniu Execute staje się doskonałym półproduktem, z którym możemy zrobić wiele ciekawych rzeczy. W przypadku wykorzystania w Excelu uzyskujemy możliwość: Wypełnienia obiektu typu QueryTable (kwerenda). UWAGA: Odwołanie do obiektu Me oznacza, że kod znajduje się w skoroszycie a nie module Wypełnienia obiektu PivotCache (tabela przestawna). Sub test_8() Dim aConn As ADODB.Connection Dim aComm As ADODB.Command Dim aRs As ADODB.Recordset Set aComm = New ADODB.Command Set aConn = New ADODB.Connection aConn.ConnectionString =
Provider=SQLNCLI10;Server=.QLEXPRESS;Database=Baza;
aConn.Properties(
Prompt
) = adPromptAlways aConn.Open Set aComm.ActiveConnection = aConn aComm.CommandType = adCmdStoredProc aComm.CommandText =
dbo.usp_procedura
aComm.Parameters(
@parametr
).Value = 1 Set aRs = aComm.Execute With .PivotTables(
PivotTable1
).PivotCache Set .Recordset = aRs .Refresh End With aConn.Close Set aComm = Nothing Set aConn = Nothing End Sub Dane tabeli przestawnej są trzymane w obiekcie PivotCache. Obiekt ten może zawierać dane dla kilku różnych tabel przestawnych i wykresów przestawnych (kopii orginału), przez co ładowanie danych może się odbyć tylko raz. UWAGA: Obiekty przed wypełnieniem musi istnieć w arkuszu Bezpośredniego wklejenia danych do arkusza. Sub test_10() Dim aConn As ADODB.Connection Dim aComm As ADODB.Command Dim aRs As ADODB.Recordset Dim rTmp As Range Dim x As Integer Set aComm = New ADODB.Command Set aConn = New ADODB.Connection Set rTmp = Me.Range(
A1
) aConn.ConnectionString =
Provider=SQLNCLI10;Server=.QLEXPRESS;Database=Baza;
aConn.Properties(
Prompt
) = adPromptAlways aConn.Open Set aComm.ActiveConnection = aConn aComm.CommandType = adCmdText aComm.CommandText =
select * from sys.tables
Set aRs = aComm.Execute For x = 0 To aRs.Fields.Count - 1 With rTmp.Offset(0, x) .Value = aRs.Fields(x).Name .Interior.ColorIndex = 15 .Interior.Pattern = xlSolid End With Next ' rTmp.Offset(1).CopyFromRecordset aRs aConn.Close Set aComm = Nothing Set aConn = Nothing End Sub Metoda .CopyFromRecordset wkleja dane bez nagłówków. Dlatego też jest obecna pętla tworząca prosty nagłówek na podstawie kolekcji z nazwami kolumn. Pracy z poszczególnymi rekordami i np. wypełnienie listbox-a elementami. Sub test_11() Dim aConn As ADODB.Connection Dim aComm As ADODB.Command Dim aRs As ADODB.Recordset Dim x As Integer Set aComm = New ADODB.Command Set aConn = New ADODB.Connection x = 0 With Me.ListBox1 .Clear .ColumnCount = 3 End With aConn.ConnectionString =
Provider=SQLNCLI10;Server=.QLEXPRESS;Database=Baza;
aConn.Properties(
Prompt
) = adPromptAlways aConn.Open Set aComm.ActiveConnection = aConn aComm.CommandType = adCmdText aComm.CommandText =
select * from sys.tables
Set aRs = aComm.Execute While Not aRs.EOF With Me.ListBox1 .AddItem aRs.Fields(
object_id
).Value .List(x, 1) = aRs.Fields(
name
).Value .List(x, 2) = aRs.Fields(
type
).Value x = x + 1 aRs.MoveNext End With Wend aConn.Close Set aComm = Nothing Set aConn = Nothing End Sub Wyjaśnienia wymaga tylko Not aRs.EOF- jest to warunek zwracający logiczną prawdę w momencie dotarcia do końca zbioru rekordów. Przesuwanie jest natomiast realizowane za pomocą MoveNext. W przypadku zaś, gdy pracujemy z Accessem możemy wykonywać następujące operacje: Wykonać kod SQL bezpośrednio na serwerze bez konieczności przebudowy kwerendy przekazującej. Kod programu jst zupełnie analogony jak przykład 4 i 5. Przykład 11 zaś może służyć za wzór postępowania w przypadku pracy krokowej z Recordsetem. Programowo wypełnić kontroli Recordset-em – nie trzeba do niczego się odwoływać Sub test_12() Dim aConn As ADODB.Connection Dim aComm As ADODB.Command Dim aRs As ADODB.Recordset Set aComm = New ADODB.Command Set aConn = New ADODB.Connection aConn.ConnectionString =
Provider=SQLNCLI10;Server=.QLEXPRESS;Database=Baza;
aConn.Properties(
Prompt
) = adPromptAlways aConn.Open Set aComm.ActiveConnection = aConn aComm.CommandType = adCmdStoredProc aComm.CommandText =
dbo.usp_procedura
aComm.Parameters(
@parametr
).Value = 1 Set aRs = aComm.Execute Set Me.Combo0.Recordset = aRs Set aComm = Nothing Set aConn = Nothing End Sub Programowo wypełnić formularz danymi. Private aConn As ADODB.Connection Private aRs As ADODB.Recordset Sub test_13() Set aConn = New ADODB.Connection With aConn .ConnectionString =
Provider=SQLNCLI10;Server=.QLEXPRESS;Database=Baza;
.Properties(
Prompt
) = adPromptAlways .CursorLocation = adUseClient .Open End With Set aRs = New ADODB.Recordset With aRs .Open
TABELA
, aConn, adOpenKeyset, adLockPessimistic End With Set Me.Recordset = aRs End Sub Private Sub Form_Load() Call test_13 End Sub Private Sub Form_Unload(Cancel As Integer) aConn.Close aRs.Close Set aConn = Nothing Set aRs = Nothing End Sub W tym przypadku problematyczne może być zachowanie edytowalności danych. Efekt ten uzyskamy poprzez zmianę sposobu pobierania danych. Zamiast przedstawionego obiektu Command skorzystamy w tym przypadku z możliwości obiektu Recordset. W przykładzie tym widzimy także dwie dodatkowe procedury. Są one wykonywane podczas ładowania formularza (Form_Load) oraz zamykania (Form_Unload). Dzięki tym procedurom formularz automatycznie się wypełni danymi na starcie oraz zamknie połączenie z bazą na końcu pracy. UWAGA: Formularz wypełniony programowo i zapisany może zachować dziwny obiekt w DATASOURCE, co powoduje, że występują błędy podczas ponownego uruchamiania formularza. Rozwiązaniem jest wyczyszczenie tej właściwości formularza w trybie edycji i ponowny zapis. Czego nie możemy zaś zrobić to programowo wypełnić raportu za pomocą Recordset-u UWAGA: kontrolki i formularze po wypełnieniu programowym mają brzydką tendencję do wyświetlania komunikatu o braku możliwości połączenia z bazą danych. Można łatwo to ominąć ustawiając obiekt Connection takiego Recordset-u na Nothing. W przypadku wypełnionego formularza po takiej operacji tracimy możliwość edycji rekordów w bazie. Set aTmp = aRs Set aTmp.ActiveConnection = Nothing Set oObject.Recordset = aTmp W przypadku Access-a, niewątpliwą zaletą korzystania z rozwiązań stricte programowych jest brak konieczności definiowania linków tabel w pliku. Zmniejsza to ogólną ilość obiektów w projekcie oraz zwiększa bezpieczeństwo samej aplikacji. Dobrą praktyką jest przechowywanie obiektu połączenia w zmiennej i wielokrotne wykorzystanie. Zyskujemy dzięki temu czas, który byłby poświęcany na każdorazowe otwieranie połączenia na serwerze. Doskonale do tego nadają się zmienne globalne zdefiniowane w module: Public aRs As ADODB.Recordset Public aConn As ADODB.Connection Public aComm As ADODB.Command Kolejną dobrą praktyką jest dzielenie kodu na mniejsze fragmenty odpowiedzialne za poszczególne operacje. Problemem dla niektórych może być przekazywanie parametrów niebędących standardowymi zmiennymi. Zasadniczo przekazywanie obiektu, jako zmiennej do funkcji lub pobranie obiektu z funkcji nie różni się od podstawienia zwykłej zmiennej np. liczbowej. Trzeba tylko pamiętać o tym, że jak podstawiamy do jakiejś zmiennej obiekt nieważne czy z funkcji czy też z innego obiektu musimy skorzystać ze słowa kluczowego SET. Sub test_14() Dim aConn As ADODB.Connection Dim aRs As ADODB.Recordset Set aConn = New ADODB.Connection With aConn .ConnectionString =
Provider=SQLNCLI10;Server=.QLEXPRESS;Database=rar;
.Properties(
Prompt
) = adPromptAlways .CursorLocation = adUseClient .Open Set Me.Combo0.Recordset = fGetRecordset(aConn,
SELECT * FROM sys.TABLES
) .Close End With Set aConn = Nothing End Sub Function fGetRecordset(oConn As ADODB.Connection, sSql As String) As ADODB.Recordset Dim aComm As ADODB.Command Set aComm = New ADODB.Command With aComm Set .ActiveConnection = oConn .CommandType = adCmdText .CommandText = sSql Set fGetRecordset = .Execute End With Set aComm = Nothing End Function Private Sub Form_Load() test_14 End Sub Widzimy tu praktycznie wykorzystanie funkcji zwracającej Recordset po przekazaniu dwu parametrów: obiektu połączenia i ciągu tekstowego z zapytaniem SQL. Ostatnią kwestią, którą można poruszyć jest wykorzystanie Eventów związanych z obiektami ADO. Możemy z nich korzystać tylko i wyłącznie w momencie, gdy deklarujemy zmienną ze specjalnym słowem kluczowym WithEvents oraz gdy deklaracja taka odbywa się w klasie lub innym obiekcie. UWAGA: Eventy nie są dostępne w modułach. Po takim zabiegu w edytorze będą dostępne dodatkowe obiekty dostępne w rozwijanej liście. Zaś po wybraniu jakiegoś obiektu na liście po prawej stronie dostępne będą wszystkie zdarzenia, jakie generuje dany obiekt. UWAGA: Event – jest to zdarzenie, które może uruchomić procedurę. Gdy zajdą okoliczności, w których dane zdarzenie ma zadziałać to podejmowana jest próba uruchomienia procedury obsługującej takie zdarzenie, zaś program główny jest wstrzymany aż do momentu zakończenia działania procedury obsługującej. Obiekty Recordset i Command posiadają szereg Eventów pozwalających przechwytywać zdarzenia związane z korzystaniem z bazy danych. Do ciekawszych i bardziej przydatnych zdarzeń należą te związane z połączeniem z bazą, wykonaniem zapytania, transakcjami. Dzięki nim możemy w znacznym stopniu poszerzyć funkcjonalność naszych aplikacji. Private WithEvents aConn As ADODB.Connection Private WithEvents aRs As ADODB.Recordset Private aComm As ADODB.Command Sub test_15() Set aConn = New ADODB.Connection With aConn .ConnectionString =
Provider=SQLNCLI10;Server=.QLEXPRESS;Database=rar;
.Properties(
Prompt
) = adPromptAlways .CursorLocation = adUseClient .Open Set Me.Combo0.Recordset = fGetRecordset(aConn,
SELECT * FROM sys.TABLES
) .Close End With Set aConn = Nothing End Sub Function fGetRecordset(oConn As ADODB.Connection, sSql As String) As ADODB.Recordset Set aComm = New ADODB.Command With aComm Set .ActiveConnection = oConn .CommandType = adCmdText .CommandText = sSql Set fGetRecordset = .Execute End With Set aComm = Nothing End Function Private Sub aConn_ConnectComplete(ByVal pError As ADODB.Error, adStatus As ADODB.EventStatusEnum, ByVal pConnection As ADODB.Connection) Debug.Print adStatus End Sub Private Sub aConn_ExecuteComplete(ByVal RecordsAffected As Long, ByVal pError As ADODB.Error, adStatus As ADODB.EventStatusEnum, ByVal pCommand As ADODB.Command, ByVal pRecordset As ADODB.Recordset, ByVal pConnection As ADODB.Connection) Debug.Print adStatus End Sub Private Sub Form_Load() test_15 End Sub Niezwykle użyteczne Eventy zaprezentowane w tym przykładzie umożliwiają odczytanie statusu serwera po połączeniu się z serwerem - aConn_ConnectComplete, oraz odczyt statusu po wykonaniu jakiegokolwiek zapytania - aConn_ExecuteComplete. Dodatkowo Eventy te dostarczają szereg obiektów wykorzystywanych w trakcie trwania zapytania. Do najbardziej użytecznych zaliczymy pError gdyż w przypadku wystapienia błędu pomoże ona nam uzyskać pełną I wyczerpującą informację o zaistniałym błędzie. MSSQL MSSQL 2008 Express udostępnia mechanizmy pozwalające bezpośrednio wykorzystywać dane zawarte w plikach Excel-a i Access-a. Do tego celu możemy wykorzystać aż trzy mechanizmy różniące się zasadą działania oraz poziomem uprawnień koniecznych do skorzystania z danego mechanizmu. Do najpopularniejszego mechanizmu możemy zaliczy Linked Servers. Mechanizm ten wymaga zdefiniowania specjalnego obiektu nazywanego linkiem do bazy danych. Jest to zdefiniowana i przechowywana w bazie systemowej definicja połączenia do zdalnego źródła danych. Połączenie takie daje nam możliwość wykonywania zdalnych zapytań SQL i przekazania wyniku bezpośrednio do zapytania MSSQL. Do utworzenia takiego obiektu potrzebne są uprawnienia administratora serwera, gdyż z tak utworzonego obiektu będziemy mogli korzystać w każdej bazie danych. Do stworzenia obiektu możemy wykorzystać SQL SERVER MENAGMENT STUDIO: Rysunek 32 Lub też możemy skorzystać z T-SQL-a. EXEC master.dbo.sp_addlinkedserver @server = 'baza_xls', @srvproduct = 'Jet 4.0', @provider = 'Microsoft.Jet.OLEDB.4.0', @datasrc = 'c:estest.xls', @provstr = 'Excel 8.0' GO Tak utworzony obiekt możemy później wykorzystać w zapytaniu SQL SELECT * FROM baza_xls...[Arkusz1$] UWAGA: wykorzystujemy tu czteroczłonową nazwę tabeli. Pierwszy człon jest to nazwa linku reprezentującego zdalne źródło danych Dodatkową zaletą jest możliwość wykorzystania polecenia OPENQUERY. Polecenie to tworzy swego rodzaju wirtualną tabelę, której danymi są rekordy zwrócone przez zapytanie wewnętrzne. Przykładowe zapytanie: SELECT * FROM OPENQUERY (baza_xls, 'SELECT * FROM [Arkusz1$]') as t Lub SELECT * FROM OPENQUERY (baza_xls, 'SELECT * FROM [obszar_nazwany]') as t Analogicznie możemy korzystać z plików Access-a EXEC sp_addlinkedserver @server = 'baza_access', @provider = 'Microsoft.Jet.OLEDB.4.0', @srvproduct = 'OLE DB Provider for Jet', @datasrc = 'C:estaza.mdb' SELECT * FROM baza_access...tabela Lub SELECT * FROM OPENQUERY (baza_access,'SELECT * FROM TABELA') as t UWAGA: wadą stosowania OPENQUERY jest brak możliwości definiowania wewnętrznego zapytania w zmiennej. Zmusza to osobę korzystającą do parametryzowania całego zapytania po stronie SQL i wykonanie go za pomocą EXECUTE lub też sp_executesql. Niedogodność tą można ominąć wykorzystując składnię EXECUTE (…) AT LinkedServer Z kolei to rozwiązanie wymaga specyficznej konfiguracji samej definicji linku do źródła danych. Zamieszczone przykłady mają zastosowanie do plików Access-a i Excel-a do wersji 2003 włącznie. Pliki od wersji 2007 potrzebują nowszego sterownika oraz modyfikacji connection string-a podczas dodawania linków. Stosowne pliki znajdują się na stronie Microsoft-u pod nazwą: „2007 Office System Driver: Data Connectivity Components”. Wcześniejsze przykłady po modyfikacji wyglądałyby następująco: Dla Excel-a EXEC sp_addlinkedserver @server = 'baza_xls', @srvproduct= 'ExcelData', @provider= 'Microsoft.ACE.OLEDB.12.0', @datasrc= 'C:estest.xlsx', @provstr= 'EXCEL 12.0' ; Oraz dla Access-a EXEC sp_addlinkedserver @server = 'baza_accdb', @provider = 'Microsoft.ACE.OLEDB.12.0', @srvproduct = 'OLE DB Provider for ACE', @datasrc = 'C:estaza.accdb' Wykonanie zapytania bezpośrednio poprawiającego dane w Accessie. Pierwszym krokiem jest uruchomienie RPC (Remote Procedure Call) dla określonego linku: EXEC master.dbo.sp_serveroption @server=N'baza_access', @optname=N'rpc out', @optvalue=N'true' GO Przykładowe zapytanie SQL w dialekcie JET wykonane na MSSQL 2008 exec ('DELETE TableOut.[Employee ID], TableOut.[Employee Number] FROM TableOut WHERE (((TableOut.[Employee ID]) Is Null) AND ((TableOut.[Employee Number]) Is Null)); ') at baza_access Kolejnym niezwykle użytecznym mechanizmem jest możliwość parametryzowania zapytań. Przykład takiej parametryzacji: exec ('UPDATE TableOut SET TableOut.[Employee ID] = ? WHERE (((TableOut.[Employee ID]) Is Null));',3) at baza_access Parametrów może być więcej, i będą one podawane w kolejności umieszczenia. Kolejne parametry są rozdzielane przecinkiem. Ostatnim aspektem zdalnego wykonywania zapytań w bazie Accessowej jest uruchamianie kwerend parametrycznych z poziomu MSSQL: exec ('exec kw_update @nr = ? ',3) at baza_access Istnieje również możliwość wykonywania operacji na danych za pomocą T-SQL-a. Jest to jednak w pewnych sytuacjach wolniejsze i mniej wygodne. Powodem tego jest konieczność otwarcia zestawu danych za pomocą operacji SELECT. Przykładowe zapytanie przedstawiam poniżej: UPDATE OPENQUERY(baza_access, 'select * from TableOut') SET [Employee ID] = 4 WHERE ([Employee ID] = 3) Kolejne dwie metody podłączenia do zewnętrznego źródła danych to w zasadzie pewna wariacja już wspomnianych polegająca na tym, że nie ma konieczności definiowania linku w bazie danych, dlatego są określane mianem połączeń AD-HOC. Definicja połączenia jest zawarta w parametrach polecenia OPENDATASOURCE lub OPENROWSET. Uwaga: zew względów bezpieczeństwa połączenia AD-HOC są domyślnie wyłączone. Można je włączyć za pomocą SQL-a sp_configure 'show advanced options',1 reconfigure go sp_configure 'Ad Hoc Distributed Queries',1 reconfigure go Ponadto polecenia OPENDATASOURCE i OPENROWSET wymagają ponadto uprawnień administratora, co może być kłopotliwe w pewnych sytuacjach. SELECT * FROM OPENDATASOURCE('Microsoft.Jet.OLEDB.4.0', 'Data Source=C:estl..xls;Extended Properties=Excel 8.0')...[Arkusz1$] Lub SELECT * FROM OPENROWSET('Microsoft.Jet.OLEDB.4.0', 'Excel 8.0;Database=C:estl..xls', [Arkusz1$]) as t Wszystkie wymienione metody umożliwiają dwustronną komunikację ze źródłem danych o ile źródło to na to pozwoli. Do komunikacji tak jak można było już zauważyć wykorzystywane są standardowe sterowniki OLEDB dostępne w systemie. Problematyczne może być podpinanie się do danych zapisanych w nowym Excel-u lub Accessie, bez instalacji tego pakietu na maszynie z SQL serwerem. Można to rozwiązać poprzez doinstalowanie stosowych sterowników OLEDB w systemie. Istnieje również możliwość umieszczania danych w plikach Excel-a z poziomu MSSQL-a bez wykorzystywania dodatkowych narzędzi czy też jakiegoś oprogramowania niestandardowego. Przykładowa konstrukcja dołącza dane do podlinkowanego wcześniej pliku: INSERT INTO openquery(baza_xls, 'SELECT Name, Date FROM [Sheet1$]') SELECT [Name], GETDATE() FROM msdb.dbo.sysjobs Lub do wskazanego pliku. INSERT INTO OPENROWSET('Microsoft.Jet.OLEDB.4.0', 'Excel 8.0;Database=e:est202_testing.xls;', 'SELECT Name, Date FROM [Sheet1$]') SELECT [Name], GETDATE() FROM msdb.dbo.sysjobs UWAGA: Plik Excel-a musi istnieć przed rozpoczęciem eksportu W przypadku, gdy korzystamy z podlinkowanego pliku Excel-a oraz uruchomimy w tym linku obsługę RPC jesteśmy wstanie tworzyć oraz modyfikować strukturę danych na wzór normalnej bazy danych za pomocą instrukcji SQL. Przykład tworzenia nowej zakładki będącej swego rodzaju tabelą: exec (' CREATE TABLE Datads (Staff_Nox NUMBER, Salx CURRENCY, Namex TEXT, Boolyx BIT, Regionx NUMBER, Datex DATETIME) ') at baza_xls Lub też modyfikacja Tabeli: exec ('ALTER TABLE Datads ADD COLUMN UnitPrice CURRENCY') at baza_xls Możemy tu wykorzystać większość instrukcji z dialektu JET SQL o ile są wspierane przez Excel-a. Próba wykonania tego typu komendy skończy się wygenerowaniem błędu. Przykładowe zapytanie: exec ('ALTER TABLE Datads ADD COLUMN Item TEXT CONSTRAINT UniqueConstraint UNIQUE') at baza_xls Wygeneruje błąd: OLE DB provider
Microsoft.Jet.OLEDB.4.0
for linked server
baza_xls
returned message
Operation is not supported for this type of object.
. Msg 7215, Level 17, State 1, Line 1 Could not execute statement on remote server 'baza_xls'. Import i Export danych kreatorem: Import and Export Data (32-bit) Kreator ten instalowany wraz z instancją MSSQL 2008 Express umożliwia łaty transfer danych zarówno z jak i do serwer-a. Sam kreator jest bardzo intuicyjny i wymaga jedynie wskazania źródła danych np. pliku Excel oraz celu np. nazwanej instancji SQL Serwera za pośrednictwem SQL Server Native Client 10. Rysunek 33 Z dostępnych dodatkowych opcji mamy tu możliwość utworzenia nowej bazy danych lub też wybrania istniejącej Rysunek 34 Kolejnym krokiem jest wybranie opcji kopiowania wskazanych skoroszytów lub też mamy możliwość samodzielnego napisania zapytania SQL importującego dane Rysunek 35 W kolejnym kroku mamy możliwość podglądu danych oraz modyfikacji mapowań nazw kolumn oraz typów danych, jaka nastąpi w procesie przenoszenia danych między źródłem a bazą docelową Rysunek 36 Ostatni krok pozwala uruchomić przenoszenie danych. Rysunek 37 Końcowa informacja prze uruchomieniem transferu danych: Rysunek 38