Memfilter kumpulan rekaman
Di bagian 5 dari seri kami, kami akan mempelajari bagaimana Microsoft Access menangani filter yang diterapkan dan mengintegrasikannya ke dalam kueri ODBC. Di artikel sebelumnya, kita melihat bagaimana Access akan memformulasi SELECT
pernyataan dalam perintah SQL ODBC. Kami juga melihat di artikel sebelumnya bagaimana Access akan mencoba memperbarui baris dengan menerapkan WHERE
klausa berdasarkan kunci dan jika berlaku, versi baris. Namun, kita perlu mempelajari bagaimana Access akan menangani filter yang disediakan untuk kueri Access dan menerjemahkannya di lapisan ODBC. Ada pendekatan berbeda yang dapat digunakan Access bergantung pada bagaimana kueri Access dirumuskan dan Anda akan mempelajari cara memprediksi bagaimana Access akan menerjemahkan kueri Access ke dalam kueri ODBC untuk predikat filter berbeda yang diberikan.
Terlepas dari bagaimana Anda benar-benar menerapkan filter — baik itu secara interaktif melalui formulir atau perintah pita lembar data atau klik menu kanan, atau secara terprogram menggunakan VBA atau menjalankan kueri tersimpan — Access akan mengeluarkan kueri ODBC SQL yang sesuai untuk melakukan pemfilteran. Secara umum, Access akan mencoba memfilter sejauh mungkin dari jarak jauh. Namun, itu tidak akan memberi tahu Anda jika tidak dapat melakukannya. Sebagai gantinya, jika Access tidak dapat mengekspresikan filter menggunakan sintaks SQL ODBC, Access akan mencoba melakukan pemfilteran sendiri dengan mengunduh seluruh konten tabel dan mengevaluasi kondisinya secara lokal. Itu bisa menjelaskan mengapa terkadang Anda mungkin menemukan kueri yang berjalan dengan cepat tetapi dengan satu perubahan kecil, melambat hingga merangkak. Bagian ini diharapkan dapat membantu Anda memahami kapan hal ini dapat terjadi dan cara menanganinya sehingga Anda dapat membantu Access sejauh mungkin ke sumber data untuk menerapkan filter.
Untuk artikel ini, kami akan menggunakan kueri tersimpan tetapi informasi yang dibahas di sini harus tetap berlaku untuk metode penerapan filter lainnya.
Filter statis
Kami akan memulai dengan mudah dan membuat kueri tersimpan dengan filter hard-coded.
SELECT c.CityID ,c.CityName ,c.StateProvinceID FROM Cities AS c WHERE c.CityName="Boston";Jika kita membuka kueri, kita akan melihat SQL ODBC ini di jejak:
SQLExecDirect: SELECT "c"."CityID" FROM "Application"."Cities" "c" WHERE ("CityName" = 'Boston' )Selain perubahan sintaks, semantik kueri tidak berubah; filter yang sama dilewatkan apa adanya. Perhatikan bahwa hanya
CityID
dipilih karena secara default kueri menggunakan recordset tipe-dinaset yang telah kita bahas di bagian sebelumnya. Filter dengan parameter sederhana
Mari kita ubah SQL untuk menggunakan parameter sebagai gantinya:
PARAMETERS SelectedCityName Text ( 255 ); SELECT c.CityID ,c.CityName ,c.StateProvinceID FROM Cities AS c WHERE c.CityName=[SelectedCityName];Jika kita menjalankan query dan memasukkan “Boston” pada nilai parameter prompt seperti yang ditunjukkan, kita akan melihat ODBC trace SQL berikut:
SQLExecDirect: SELECT "c"."CityID" FROM "Application"."Cities" "c" WHERE ("CityName" = ? )Perhatikan bahwa kita akan mengamati perilaku yang sama dengan referensi kontrol atau tautan subformulir. Jika kita menggunakan ini sebagai gantinya:
SELECT c.CityID ,c.CityName ,c.StateProvinceID FROM Cities AS c WHERE c.CityName=[Forms]![frmSomeForm]![txtSomeText];Kami masih akan mendapatkan ODBC SQL terlacak yang sama seperti yang kami lihat dengan kueri berparameter asli. Itu masih terjadi meskipun kueri kami yang dimodifikasi tidak memiliki
PARAMETERS
penyataan. Ini menunjukkan bahwa Access dapat mengenali bahwa referensi kontrol tersebut yang nilainya dapat berubah dari waktu ke waktu, paling baik diperlakukan sebagai parameter saat merumuskan ODBC SQL. Itu juga berfungsi untuk fungsi VBA. Kita dapat menambahkan fungsi VBA baru:
Public Function GetSelectedCity() As String GetSelectedCity = "Boston" End FunctionKami menyesuaikan kueri yang disimpan untuk menggunakan fungsi VBA baru:
WHERE c.CityName=GetSelectedCity();Jika Anda menelusuri ini, Anda akan melihat bahwa itu masih sama. Jadi, kami telah menunjukkan bahwa terlepas dari apakah input adalah parameter eksplisit, referensi ke kontrol, atau hasil dari fungsi VBA, Access akan memperlakukan semuanya sebagai parameter dari kueri SQL ODBC yang akan dijalankan pada kepentingan. Itu hal yang baik karena kami mendapatkan kinerja yang lebih baik secara umum ketika kami dapat menggunakan kembali kueri dan cukup mengubah parameter.
Namun, ada satu lagi skenario umum yang biasanya disiapkan oleh pengembang Access dan yang membuat SQL dinamis dengan kode VBA, biasanya dengan menggabungkan string dan kemudian mengeksekusi string yang digabungkan. Mari gunakan kode VBA berikut:
Public Sub GetSelectedCities() Dim db As DAO.Database Dim rs As DAO.Recordset Dim fld As DAO.Field Dim SelectedCity As String Dim SQLStatement As String SelectedCity = InputBox("Enter a city name") SQLStatement = _ "SELECT c.CityID, c.CityName, c.StateProvinceID " & _ "FROM Cities AS c " & _ "WHERE c.CityName = '" & SelectedCity & "';" Set db = CurrentDb Set rs = db.OpenRecordset(SQLStatement) Do Until rs.EOF For Each fld In rs.Fields Debug.Print fld.Value; Next Debug.Print rs.MoveNext Loop End SubODBC SQL yang dilacak untuk
OpenRecordset
adalah sebagai berikut: SQLExecDirect: SELECT "c"."CityID" FROM "Application"."Cities" "c" WHERE ("CityName" = 'Boston' )Tidak seperti contoh sebelumnya, ODBC SQL tidak diparameterisasi. Access tidak memiliki cara untuk mengetahui bahwa 'Boston' diisi secara dinamis saat runtime oleh
VBA.InputBox
. Kami hanya menyerahkan SQL yang dibangun dari Access 'POV, hanya pernyataan SQL statis. Dalam hal ini, kami mengalahkan parameterisasi kueri. Penting untuk diketahui bahwa saran populer yang diberikan kepada pengembang Access adalah bahwa SQL yang dibangun secara dinamis lebih baik daripada menggunakan kueri parameter karena menghindari masalah di mana mesin Access dapat menghasilkan rencana eksekusi berdasarkan satu nilai parameter yang mungkin sebenarnya kurang optimal untuk yang lain. nilai parameter. Untuk detail lebih lanjut tentang fenomena itu, saya mendorong Anda untuk membaca tentang masalah "parameter-sniffing". Perhatikan bahwa ini adalah masalah umum untuk mesin database apa pun, bukan hanya Access. Namun, dalam kasus Access, SQL dinamis bekerja lebih baik karena jauh lebih murah hanya untuk menghasilkan rencana eksekusi baru. Sebaliknya, mesin RDBMS mungkin memiliki strategi tambahan untuk menangani masalah dan mungkin lebih sensitif untuk memiliki terlalu banyak rencana eksekusi satu kali karena dapat berdampak negatif pada caching-nya.
Untuk alasan itu, kueri berparameter dari Access terhadap sumber ODBC mungkin lebih disukai daripada SQL dinamis. Karena Access akan memperlakukan kontrol referensi pada formulir atau fungsi VBA yang tidak memerlukan referensi kolom sebagai parameter, Anda tidak memerlukan parameter eksplisit di sumber rekaman atau sumber baris Anda. Namun, jika Anda menggunakan VBA untuk mengeksekusi SQL, biasanya lebih baik menggunakan ADO yang juga memiliki dukungan parameterisasi yang jauh lebih baik. Dalam hal membangun sumber rekaman atau sumber baris dinamis, menggunakan kontrol tersembunyi pada formulir/laporan bisa menjadi cara mudah untuk membuat parameter kueri. Namun, jika kueri sangat berbeda, membangun SQL dinamis di VBA dan menetapkannya ke properti recordsource/rowsource secara efektif memaksa kompilasi ulang penuh dan oleh karena itu menghindari penggunaan rencana eksekusi buruk yang tidak akan berkinerja baik untuk set input saat ini. Anda dapat menemukan rekomendasi dalam artikel yang membahas WITH RECOMPILE
SQL Server membantu dalam memutuskan apakah akan memaksa kompilasi ulang atau menggunakan kueri berparameter.
Menggunakan fungsi dalam pemfilteran SQL
Di bagian sebelumnya, kita melihat bahwa pernyataan SQL yang berisi fungsi VBA diparameterisasi sehingga Access dapat menjalankan fungsi VBA dan menggunakan output sebagai input ke kueri berparameter. Namun, tidak semua fungsi bawaan berperilaku seperti ini. Mari gunakan UCase()
sebagai contoh untuk memfilter kueri. Selanjutnya kita akan menerapkan fungsi tersebut pada sebuah kolom.
SELECT c.CityID ,c.CityName ,c.StateProvinceID FROM Cities AS c WHERE UCase([c].[CityName])="BOSTON";Jika kita melihat ODBC SQL yang dilacak, kita akan melihat ini:
SQLExecDirect: SELECT "c"."CityID" FROM "Application"."Cities" "c" WHERE ({fn ucase("CityName" )}= 'BOSTON' )Pada contoh sebelumnya, Access dapat sepenuhnya membuat parameter dari
GetSelectedCity()
karena tidak memerlukan input dari kolom yang dirujuk dalam kueri. Namun, UCase()
membutuhkan sebuah masukan. Apakah kami menyediakan UCase("Boston")
, Access juga akan membuat parameter ini. Namun, input adalah referensi kolom, yang Access tidak dapat dengan mudah membuat parameter. Namun, Access dapat mendeteksi bahwa UCase()
adalah salah satu fungsi skalar ODBC yang didukung. Karena kami lebih suka melakukan remote sebanyak mungkin ke sumber data, Access melakukan hal itu dengan menjalankan versi ucase
ODBC .
Jika kita kemudian membuat fungsi VBA khusus yang mengemulasi UCase()
fungsi:
Public Function MyUCase(InputValue As Variant) As String MyUCase = UCase(InputValue) End Functiondan mengubah pemfilteran dalam kueri menjadi:
WHERE MyUCase([c].[CityName])="BOSTON";Inilah yang kami dapatkan:
SQLExecDirect: SELECT "CityName" ,"c"."CityID" FROM "Application"."Cities" "c"Access tidak dapat melakukan remote fungsi VBA kustom
MyUCase
kembali ke sumber data. Namun, SQL kueri yang disimpan adalah legal sehingga Access harus memenuhinya. Untuk melakukan ini, akhirnya mengunduh set lengkap CityName
dan CityID
yang sesuai untuk masuk ke fungsi VBA MyUCase()
dan mengevaluasi hasilnya. Akibatnya, kueri sekarang bekerja jauh lebih lambat karena Access sekarang meminta lebih banyak data dan melakukan lebih banyak pekerjaan.
Meskipun kami menggunakan UCase()
dalam contoh ini, kita dapat dengan jelas melihat bahwa umumnya lebih baik untuk melakukan pekerjaan jarak jauh sebanyak mungkin ke sumber data. Tetapi bagaimana jika kita memiliki fungsi VBA kompleks yang tidak dapat ditulis ulang ke dalam dialek SQL asli sumber data? Meskipun saya pikir skenario ini cukup langka, itu layak untuk dipertimbangkan. Misalkan kita dapat menambahkan filter untuk mempersempit kumpulan kota yang dikembalikan.
SELECT c.CityID ,c.CityName ,c.StateProvinceID FROM Cities AS c WHERE c.CityName LIKE "Bos*" AND MyUCase([c].[CityName])="BOSTON";ODBC SQL yang dilacak akan keluar seperti ini:
SQLExecDirect: SELECT "CityName" ,"c"."CityID" FROM "Application"."Cities" "c" WHERE ("CityName" LIKE 'Bos%' )Access dapat melakukan remote
LIKE
kembali ke sumber data, yang menghasilkan kembali kumpulan data yang jauh lebih kecil. Itu masih akan melakukan evaluasi lokal dari MyUCase()
pada kumpulan data yang dihasilkan. Kueri berjalan lebih cepat hanya karena kumpulan data yang dikembalikan lebih kecil. Ini memberi tahu kami jika kami menghadapi skenario yang tidak diinginkan di mana kami tidak dapat dengan mudah memfaktorkan ulang fungsi VBA kompleks dari kueri, kami masih dapat mengurangi efek buruk dengan menambahkan filter yang dapat diremote untuk mengurangi kumpulan rekaman awal untuk Access bekerja dengannya.
Catatan tentang sargability
Dalam contoh sebelumnya, kami menerapkan fungsi skalar pada kolom. Itu berpotensi membuat kueri sebagai "non-sargable" yang berarti bahwa mesin database tidak dapat mengoptimalkan kueri menggunakan indeks untuk mencari dan menemukan kecocokan. Bagian "sarg" dari kata "sargability" mengacu pada "Search ARGument". Misalkan kita memiliki indeks yang ditentukan pada sumber data di atas tabel:
CREATE INDEX IX_Cities_CityName ON Application.Cities (CityName);Ekspresi seperti
UCASE(CityName)
mencegah mesin database untuk dapat menggunakan indeks IX_Cities_CityName
karena mesin dipaksa untuk mengevaluasi setiap baris satu per satu untuk menemukan kecocokan, seperti yang dilakukan Access dengan fungsi VBA khusus. Beberapa mesin database seperti SQL Server versi terbaru mendukung pembuatan indeks berdasarkan ekspresi. Jika kami ingin mengoptimalkan kueri menggunakan UCASE()
fungsi transact-SQL, kita dapat menyesuaikan definisi indeks: CREATE INDEX IX_Cities_Boston_Uppercase ON Application.Cities (CityName) WHERE UCASE(CityName) = 'BOSTON';Ini memungkinkan SQL Server untuk menangani kueri dengan
WHERE UCase(CityName) = 'BOSTON'
sebagai query sargable karena sekarang dapat menggunakan indeks IX_Cities_Boston_Uppercase
untuk mengembalikan catatan yang cocok. Namun, jika kueri cocok dengan 'CLEVELAND'
bukannya 'BOSTON'
, sargability hilang. Terlepas dari mesin basis data mana yang sebenarnya Anda gunakan, selalu lebih baik untuk merancang dan menggunakan kueri yang dapat diurutkan sedapat mungkin untuk menghindari masalah kinerja. Kueri penting harus memiliki indeks yang mencakup untuk memberikan kinerja terbaik. Saya mendorong Anda untuk mempelajari lebih lanjut tentang sargability dan meliputi indeks untuk membantu Anda menghindari merancang kueri yang sebenarnya non-sargable.
Kesimpulan
Kami meninjau bagaimana Access menangani penerapan filter dari Access SQL ke dalam kueri ODBC. Kami juga mengeksplorasi kasus yang berbeda di mana Access akan mengubah berbagai jenis referensi menjadi parameter, memungkinkan Access untuk melakukan evaluasi di luar lapisan ODBC dan meneruskannya sebagai input ke dalam pernyataan ODBC yang disiapkan. Kami juga melihat apa yang terjadi ketika tidak dapat diparameterisasi, biasanya karena mengandung referensi kolom sebagai input. Itu dapat memiliki konsekuensi pada kinerja selama migrasi ke server SQL.
Untuk fungsi tertentu, Access mungkin bisa mengonversi ekspresi untuk menggunakan fungsi skalar ODBC, yang memungkinkan Access untuk remote ekspresi ke sumber data ODBC. Salah satu konsekuensinya adalah jika implementasi fungsi skalar berbeda, hal itu dapat menyebabkan kueri berperilaku berbeda atau dapat bekerja lebih cepat/lambat. Kami melihat bagaimana fungsi VBA, bahkan fungsi sederhana yang membungkus fungsi skalar yang dapat diremote dapat mengalahkan upaya untuk meremote ekspresi. Kami juga mempelajari bahwa jika kami memiliki situasi di mana kami tidak dapat memfaktorkan ulang fungsi VBA yang kompleks dari kueri/sumber rekaman/sumber baris Access, kami setidaknya dapat mengurangi unduhan yang mahal dengan menambahkan filter tambahan pada kueri yang dapat diremote untuk mengurangi jumlah data yang dikembalikan.
Pada artikel berikutnya kita akan melihat bagaimana penggabungan ditangani oleh Access.
Mencari bantuan dengan Microsoft Access? Hubungi pakar kami hari ini di 773-809-5456 atau email kami di [email protected].