В
отличие от межзапросного параллелизма ([14]), означающего одновременное
выполнение разных запросов на нескольких потоках (threads) операционной
системы, внутризапросный (intraquery) параллелизм был реализован в SQL
Server 7.0 впервые. Внутризапросный параллелизм, как следует из его
названия, есть возможность распараллеливания процесса обработки одного
запроса по нескольким потокам, что позволяет эффективно использовать
многопроцессорные архитектуры при обработке сложных запросов.
Использование внутризапросного параллелизма не требует специального
разбиения данных при их хранении, а также внесения каких-либо изменений
в их структуру или текст запроса. Процессор запросов рассматривает
параллелизм наряду с другими этапами стратегии построения оптимального
плана. Основными критериями при принятии решения о паралельном
выполнении запроса выступают количество активных пользователей,
доступная память и предположительный объем данных, обрабатываемых
запросом. Очевидно, что параллельное выполнение простого запроса по
сравнительно небольшому числу записей невыгодно, так как потребует
больше памяти, нежели последовательное. При этом приходится забирать
потоки, которые в противном случае могли бы быть использованы для
поддержки большего числа пользователей. Общее правило можно
сформулировать так: в OLTP-системах, характеризующихся большим
количеством пользователей, обстреливающих SQL Server многочисленными
короткими транзакциями, он будет отдавать предпочтение последовательным
планам, расходуя поточный пул (см. опцию max worker threads) на
пользовательские соединения. В OLAP-приложениях, для которых, напротив,
характерны массивные долгоиграющие транзакции, а число пользователей
относительно невелико, процессор запросов прибегнет к параллельным
планам. Например, запрос select * from sales_fact_1997 union select * from sales_fact_1998 (количество записей в таблице, как мы помним, 86837) выполняется по следующему плану:
|-Parallelism(Gather Streams)
|-Hash Match(Union)
|-Parallelism(Repartition Streams, PARTITIONCOLUMNS:
(sales_fact_1997.product_id, sales_fact_1997.time_id, sales_fact_1997.customer_id,
sales_fact_1997.promotion_id, sales_fact_1997.store_id, sales_fact_1997.store_sales,
sales_fact_1997.store_
| |-Table Scan(FoodMart..sales_fact_1997)
|-Sort(DISTINCT ORDER BY: (sales_fact_1998.product_id asc, sales_fact_1998.time_id asc,
sales_fact_1998.customer_id asc, sales_fact_1998.promotion_id asc,
sales_fact_1998.store_id asc, sales_fact_1998.store_sales asc,
sales_fact_1998.store_cos
|-Parallelism(Repartition Streams, PARTITIONCOLUMNS: (sales_fact_1998.product_id,
sales_fact_1998.time_id, sales_fact_1998.customer_id, sales_fact_1998.promotion_id,
sales_fact_1998.store_id, sales_fact_1998.store_sales, sales_fact_1998.s
|-Table Scan(FoodMart..sales_fact_1998)
Лист.2.1
Параллельное выполнение запроса достигается введением в план
специальных операторов параллелизма, к которым относятся Distribute
(произвести разбиение данных на несколько потоков (streams)), Gather
(собрать результаты обработки данных c предыдущих шагов на нескольких
потоках) и Repartition (перераспределить данные по потокам). Запросы на
обновление (UPDATE / INSERT / DELETE) выполняются последовательно,
однако подчитка данных в них может производиться в параллельном режиме.
Специфика динамических курсоров предполагает строго последовательный
план выполнения. В то же время для заполнения статических и
keyset-курсоров может использоваться межзапросный параллелизм.
В
смешанных приложениях может возникнуть необходимость провести
количественную грань между понятиями простого и сложного запроса. Это
делается с помощью конфигурационного параметра cost threshold for
parallelism, который характеризует пороговую стоимость запроса, начиная
с которой оптимизатор начинает рассматривать возможность использования
параллельного плана выполнения. Запросы, чья стоимость не превышает
пороговой величины, всегда будут выполняться последовательно. Значение
этой опции по умолчанию равно 5. Ее также можно модифицировать из меню
Server Properties (закладка Processor) в SQL Enterprise Manager.
Ключевым
понятием параллельного выполнения является степень параллелизма (Degree
of Parallelism, или DOP), иными словами, количество процессоров,
которые будут задействованы для одновременной обработки запроса.
Отметим, что эффект внутризапросного параллелизма будет проявляться
только на машинах, где для работы SQL Server отведено два и более
процессоров (см. опцию affinity mask ([1])). Для настройки DOP
используется конфигурационный параметр max degree of parallelism,
который может принимать значения от 0 до 32: 1 запрещает
внутризапросный параллелизм, 0 (по умолчанию) означает, что при
построении параллельных планов процессор запросов будет использовать
максимально доступное на данный момент число процессоров. Количество
потоков, на которых начинается выполнение запроса в параллельном режиме
запрос, остается неизменным до момента его окончания. Вместе с тем,
необходимо иметь в виду, что оптимальный план может изменяться в
зависимости от конфигурации и загрузки SQL Server, так что тот же самый
запрос через некоторое время может выполняться с другой DOP, в
частности, последовательно. Просмотр DOP осуществляется при помощи
соответствующего подкласса события в SQL Profiler.
Наряду с
обычными потоками SQL Server 7.0 обладает возможностью использовать
волокна (fibers) Windows NT - особый вид легковесных потоков, из
которых может состоять thread. Легковесность заключается в особенностях
планирования (scheduling). Переключение между потоками требует перехода
в режим ядра операционной системы, что само по себе является довольно
дорогостоящей операцией, в то время как переключение волокон происходит
в контексте приложения. SQL Server использует волокна вместо потоков,
если конфигурационный параметр lightweight pooling установлен в 1.
Рассмотрим
типичную СУБД с позиций способа хранения информации и набора сервисов
для ее обработки и выборки. Никто не мешает нам написать СОМ-сервер,
инкапсулирующий основные сервисы по доступу и обработке данных, причем
не обязательно реляционных, а, например, иерархических, таких как
электронная почта, служба каталогов и т.д., или вообще
неструктурированных. Как внутри него эти службы будут реализованы -
личное дело СОМ-сервера, поскольку никто, кроме самих данных, не знает,
как их лучше всего обрабатывать. Единственным требованием к нему будет
поддержка стандартных интерфейсов, своего рода обязательство объекта
обеспечивать декларируемый набор методов с описанными параметрами и
типами возвращаемых значений. Это необходимо, чтобы клиентское
приложение с помощью одинаковых методов могло одновременно работать с
разными типами данных, не требуя переделки. Такой набор стандартных
интерфейсов был разработан и получил название OLE DB ([7]). Наш
СОМ-сервер в этой ситуации будет называться OLE DB-провайдером.
Отталкиваясь от привычных аналогий, провайдер можно уподобить драйверу
ODBC. Однако необходимо иметь в виду различия на прикладном и системном
уровне. Первое - технология OLE DB нацелена на обеспечение доступа к
данным любой природы, а не обязательно реляционным. Во вторых, ODBC -
это набор Сшных функций. Если мы пишем приложение, скажем, на VB и
хотим вместо RDO (Remote Data Objects - тонкая обертка над ODBC для
придания более дружественного интерфейса) использовать ODBC в чистом
виде, никаких проблем нет. Сделаем declare соответствующих функций - и
вперед. OLE DB, как уже упоминалось, есть совокупность интерфейсов,
построенных в соответствии с СОМ, поэтому они принципиально не
достижимы из Automation-языков программирования. Для того, чтобы
функциональность OLE DB была доступна из ASP, Visual Basic (VBScript,
VBA), Visual FoxPro и др., была написана IDispatch-обертка вокруг OLE
DB, получившая название ADO (ActiveX Data Objects). В ADO 1.5 была
включена служба удаленного доступа (RDS), позволяющая располагать
объекты на промежуточном слое между клиентом и сервером. RDS
поддерживает прикладные протоколы HTTP и DCOM, то есть в качестве
сервера приложений может использоваться Internet Information Server
(IIS) или Microsoft Transaction Server (MTS). Компоненты ADO 2.0,
входящие в состав Visual Studio 6, дополнены возможностями асинхронной
обработки событий, локального кэша на клиенте, иерархического множества
записей (с помощью провайдера MSDataShape) и элементами управления для
поддержки ADODB.Recordset. Еще одно отличие заключается в том, что
ODBC-драйвер для источника, не поддерживающего SQL (например, текстовый
файл), должен нести в себе эквивалент SQL-машины, даже если клиенту не
потребуется никакой другой функциональности, кроме простого множества
записей. Нулевой уровень OLE DB предполагает лишь самые общие базовые
возможности. Для их расширения применяются сервисные компоненты,
которые могут реализовывать, например, процессор запросов, механизм
поддержки курсоров и т.д. Так, механизм курсоров принимает rowset,
который нулевой уровень поставляет в строго последовательном виде и
дополняет его возможностями прокрутки в обоих направлениях. Нулевой
уровень и сервисные компоненты под управлением SCM (Service Control
Manager) образуют первый уровень. Таким образом, архитектура OLE DB
включает провайдера, потребителя и слой сервисных компонент между ними.
За
довольно короткий срок различными фирмами были написаны OLE
DB-провайдеры для большинства распространенных серверов баз данных,
настольных СУБД, ODBC, Active Directory, Index Server и др. Этот список
продолжает пополняться (см., например, http://www.microsoft.com/data/oledb/products/product.htm). Многие из них вместе с примерами их использования распространяются вместе с Data Access SDK 2.0 (http://www.microsoft.com/data/download.htm).
Там же можно найти инструментарий для написания собственного OLE
DB-провайдера с помощью Visual C++, Visual J++, Visual Basic и т.д.
(OLE DB Simple Provider Toolkit). Все они входят в OLE DB SDK 1.5.
Расширение OLE DB for OLAP используется в качестве средства доступа к
многомерной информации, например, к Microsoft OLAP Services, входящих в
состав SQL Server 7.0. Более того, OLE DB является "родным" интерфейсом
SQL Server 7.0, т.е. тем интерфейсом, посредством которого процессор
запросов общается с механизмом хранения. DB-Library поддерживается
путем эмуляции через OLE DB, но развиваться, по-видимому, в дальнейшем
уже не будет. Механизм хранения SQL Server 7.0 представляется для
процессора запросов просто источником данных, одним из многих OLE
DB-достижимых, не более того. Если мы запустим sp_linkedservers, то
увидим, что наш собственный сервер рассматривается процессором запросов
как еще один присоединенный сервер. На момент написания этих строк в
SQL Server 7.0 поддерживались следующие внешние источники: ODBC, MS
Access и ISAM, AS/400 и VSAM, Oracle 7.3 и выше , MS Index Server , OLE
DB for OLAP и, собственно, OLE DB-провайдер для SQL Server 7.0 (в
ранних документах, посвященных тематике универсального доступа, может
значиться как Luxor). Здесь необходимо отметить, что провайдер к SQL
Server 7.0 можно использовать в версиях 6.х, предварительно запустив на
них скрипт instcat.sql, находящийся в mssql7\install. Этот скрипт,
очевидно, не требуется запускать, если вы планируете осуществлять связь
с предыдущими версиями через OLE DB поверх ODBC.
В
SQL Server 7.0 существуют два основных способа работы с удаленным
источником: через прилинкованный сервер (linked server) и через имя,
данное по ходу дела (ad hoc name). Прилинкованный сервер есть
определяемый заранее виртуальный сервер, описание которого включает в
себя информацию об OLE DB-провайдере данного источника и свойства
соединения, указывающие провайдеру, где находится источник.
Прилинкованные сервера являются единицами администрирования внешних
источников данных. Они могут определяться как in-process (исполняющиеся
в процессе SQL Server) или out-of-process (как самостоятельные
exe-модули). Сервер in-process работает, очевидно, быстрее, однако его
целесообразно выбирать для проверенных провайдеров. Несмотря на
стандартный характер большинства свойств соединения, каждый провайдер
обладает возможностью приема дополнительных значимых параметров
(например, для геопространственных данных). Эти параметры также
прописываются в прилинкованном сервере и называются атрибутами.
Предположим,
в разных доменах у нас находятся два сервера: alexeysh_desk версии 7.0
и alexeysh_lapt версии 6.5. Если между доменами не установлены
доверительные отношения, то named pipes, по умолчанию являющиеся
основным прикладным протоколом взаимодействия, следует заменить на,
скажем, TCP/IP Sockets. Для этого с помощью Client Network Utility
следует добавить alexeysh_lapt в список серверов и указать, что
конкретно для него мы выбираем сетевую библиотеку TCP/IP. Соединимся с
alexeysh_lapt и запустим скрипт instcat.sql, который добавит новые типы
и системные таблицы в SQL Server 6.5. Теперь мы можем определить его
как прилинкованный сервер по отношению к alexeysh_desk. На соединении с
alexeysh_desk выполним следующий скрипт: sp_addlinkedserver @server= N"alexeysh_lapt", @srvproduct= "SQL Server"
Параметрами
процедуры служат: @server - имя, под которым источник будет значиться в
списке прилинкованных серверов и через которое мы будем к нему
обращаться (N перед именем означает формат Unicode); @srvproduct -
название продукта: например, если это SQL Server, то @srvproduct="SQL
Server"; если Oracle, то "Oracle". Для Jet и ODBC это пустая строка.
Указание @srvproduct в явном виде избавляет нас в данном случае от
необходимости ввода других параметров. Аналогичные действия могли бы
быть выполнены, например, так:
sp_addlinkedserver @server="MySQL65", @srvproduct="", @provider="SQLOLEDB", @datasrc="alexeysh_lapt", @catalog="pubs"
К другим параметрам относятся: @provider - кодовое
обозначение провайдера (SQLOLEDB - SQL Server, MSDASQL - ODBC, MSDAORA
- Oracle, Microsoft.Jet.OLEDB.3.51, Microsoft.Jet. OLEDB.4.0 - Access,
MSIDXS - Index Server и т.д.); @datasrc - имя источника данных (для SQL
Server это серверное имя линкуемого сервера, для Access - имя
.mdb-файла с указанием полного пути, для ODBC - DSN и т.д.). Имя
источника данных может быть также задано среди передаваемых провайдеру
свойств. Например, вместо @datasrc="alexeysh_lapt", мы могли бы
передать его через параметр @provstr: sp_addlinkedserver @server=N"MySQL65", @srvproduct="", @provider="SQLOLEDB", @provstr="server=alexeysh_lapt;", @catalog="pubs"
Точно
так же в случае ODBC мы можем передать DSN или строку соединения (для
так называемого DSNless source). В частности, соединение с
alexeysh_lapt может идти не напрямую через провайдера для SQL Server, а
через провайдера для ODBC и ODBC-драйвер для SQL Server. Рассмотрим
примеры. С помощью ODBC заведем системный DSN по имени ааа для драйвера
SQL Server и сервера alexeysh_lapt. Протестируем его и убедимся в
работе соединения. Тогда alexeysh_lapt может быть прилинкован как
sp_addlinkedserver @server="MySQL65ODBC",
@srvproduct="", @provider="MSDASQL", @datasrc="aaa"
или то же самое без создания DSN:
sp_addlinkedserver @server="MySQL65ODBC",
@srvproduct="", @provider="MSDASQL", @provstr="driver={SQL Server};
server=alexeysh_lapt;uid=sa;pwd=;
database=pubs"
В общем случае в @provstr оговаривается специфичная для
провайдера информация, уникально идентифицирующая источник данных.
Параметр @catalog cоответствует свойству DBPROP_INIT_CATALOG при
инициализации OLE DB-провайдера. Для SQL Server - это активная база
данных. Более подробное описание параметров процедуры
sp_addlinkedserver можно найти в документации по Transact-SQL.
Когда
прилинкованный сервер участвует в распределенном запросе, локальный SQL
Server должен залогиниться на него под какой-то авторизующей
информацией, например, от имени действующего пользователя. Отображение
локальных логинов на удаленные осуществляется при помощи хранимой
процедуры sp_addlinkedsrvlogin. Примеры: sp_addlinkedsrvlogin
@rmtsrvname= "MySQL65", @useself="true" - все пользователи
alexeysh_desk будут ходить на alexeysh_lapt под их собственными именами
и паролями на alexeysh_desk, т.е. преобразование отсутствует
(@useself="true") .
sp_addlinkedsrvlogin @rmtsrvname= "MySQL65",
@useself="false", @locallogin=NULL, @rmtuser= "sa", @rmtpassword="" -
все логины alexeysh_desk (@locallogin=NULL) будут авторизовываться на
alexeysh_lapt как sa с пустым паролем. Если в @rmtpassword указать
какой-нибудь левый пароль, то при попытке обратиться к MySQL65 будет
выдано сообщение login failed. При аутентификации пользователей SQL
Server средствами Windows NT сервер alexeysh_desk попытается
прикинуться для alexeysh_lapt NTвым пользователем. Способность SQL
Server 7.0 эмулировать аутентифицирующую информацию пользователей
Windows NT носит название делегирования и доступна только когда и
локальный, и прилинкованный сервер работают под NT 5.0.
Обратная
операция (удаление отображения локального логина) выполняется с помощью
sp_droplinkedsrvlogin. Sp_dropserver @server= "MySQL65",
@droplogins="droplogins" удаляет сервер из списка прилинкованных с
одновременным удалением отображенных на него пользователей.
Проиллюстрируем
распределенные запросы на примере. В качестве объекта эксперимента
выберем модельную базу данных pubs, устанавливающуюся вместе с SQL
Server. Экспортируем таблицу titles в Excel. В состав SQL Server 7.0
включены службы преобразования данных (Data Transformation Services),
основным назначением которых является извлечение данных из операционных
источников, их очистка, унификация, проверка на непротиворечивость и
промежуточное агрегирование перед погружением их в хранилище. В среде
Microsoft Datawarehousing Framework, обеспечивающей жизненный цикл
хранилища, DTS управляют потоками данных и метаданных между MS SQL
Server и многомерным кубом OLAP Server, но в принципе они могут
работать с любыми OLE DB-источниками. Основной административной
единицей DTS служит пакет (package). По своей идеологии пакет DTS
родственен заданию (job) SQL Agent (бывший SQL Executive). Он состоит
из шагов, логика выполнения которых может ветвится в зависимости от
результата работы предыдущих шагов. Шаг соответствует элементарной
операции DTS, например, создание таблицы, копирование данных и т.д. В
качестве шагов могут использоваться операторы SQL, ActiveX-скрипты,
исполняемые файлы (.exe), насосы данных (data pumps) или какие-то
пользовательские действия в соответствии с интерфейсом IDTSCustomStep.
Сами пакеты хранятся в MS Repository, SQL Server или в виде persistent
СОМ-объектов. Поскольку нам требуется просто перекачать данные из SQL
Server в другой формат, проще всего прибегнуть к DTS Wizard
(контекстное меню таблицы в SQL Enterprise Manager -> task ->
Export from SQL). Аналогично, перенесем таблицу sales в предварительно
подготовленную базу данных fox_sales в Visual FoxPro 6.0, а таблицу
stores - в базу access_stores в MS Access 97. Что у нас еще доступно?
Текстовый файл. Давайте экспортируем таблицу publishers в
publishers.txt. Ниже приведен файл schema.ini для текстового драйвера
ODBC.
[publishers.txt]
ColNameHeader=False
Format=FixedLength
MaxScanRows=25
CharacterSet=OEM
Col1=PUB_ID Char Width 4
Col2=PUB_NAME Char Width 40
Col3=CITY Char Width 20
Col4=STATE Char Width 2
Col5=COUNTRY Char Width 30
Создадим прилинкованные сервера для каждого из перечисленных
источников. Таблицу Excel можно присоединять как через OLE
DB-провайдера для ODBC:
if exists (select srvname from master.dbo.sysservers where srvname="MyExcel97")
exec sp_dropserver @server= "MyExcel97", @droplogins="droplogins"
go
exec sp_addlinkedserver @server= "MyExcel97", @srvproduct="", @provider="MSDASQL.1",
@provstr="Driver={Microsoft Excel Driver (*.xls)};",@catalog="d:\temp\HetQueries\titles.xls"
(указание @catalog в явном виде, как мы помним, не является обязательным и может быть передано в свойствах провайдера:
@provstr="Driver={Microsoft Excel Driver (*.xls)}; DBQ=D:\TEMP\HetQueries\titles.XLS;"),
так и через OLE DB-провайдера для Jet:
exec sp_addlinkedserver
@server="MyExcel97",
@srvproduct="Jet 4.0", @provider= "Microsoft.Jet.OLEDB.4.0",
@datasrc="d:\temp\HetQueries\Titles.xls", @location=NULL, @provstr="Excel 5.0;"
В случае прилинковки через Jet, требуется задать отображение пользователя:
exec sp_addlinkedsrvlogin "MyExcel97", false, sa, "Admin", NULL
С текстовым файлом и таблицей FoxPro соединимся с помощью провайдера для ODBC:
exec sp_addlinkedserver
@server="MyText", @srvproduct="", @provider="MSDASQL.1",
@provstr="Driver={Microsoft Text Driver (*.txt; *.csv)};
DefaultDir=D:\TEMP\HetQueries"
exec sp_addlinkedserver
@server="MyVFP6", @srvproduct="", @provider="MSDASQL.1",
@provstr="Driver={Microsoft Visual FoxPro Driver};
UID=;PWD=;SourceDB=D:\TEMP\HetQueries\fox_sales.dbc; SourceType=DBC; Exclusive=No;
BackgroundFetch=Yes;Collate=Machine;"
а с базой данных в Access - через провайдера для Jet:
exec sp_addlinkedserver
@server="MyAccess97",
@srvproduct="Access 97", @provider ="Microsoft.Jet.OLEDB.4.0",
@datasrc="d:\temp\HetQueries\Access_stores.mdb"
В случае ODBC перед нами везде пример соединения без создания
DSN, когда вся необходимая информация (название драйвера,
местоположение файла) тут же передается провайдеру. Если вид строки
@provstr для какого-либо ODBC-драйвера заранее неизвестен, его легко
выяснить экспериментально, временно создав соответствующий DSN и
посмотрев, какие свойства передаются провайдеру при DSN-соединении.
Допустим, мы не знаем, как должна выглядеть @provstr для ODBC-драйвера
для Excel. Создадим на таблицу Excel DSN по имени ааа. Cледующий код
Dim cnn As New ADODB.Connection
cnn.Provider = "MSDASQL"
cnn.Open ("DSN=aaa")
Debug.Print cnn.ConnectionString
cnn.Close
даст нам строку соединения
Provider=MSDASQL.1;Connect Timeout=15;Extended Properties="DSN=aaa;
DBQ=D:\TEMP\HetQueries\titles.XLS;DefaultDir=D:\TEMP\HetQueries;
DriverId=790;FIL=excel 5.0; MaxBufferSize=512;PageTimeout=5;"; Locale Identifier=1049
В ней следует обратить внимание на Extended Properties.
Вместо DSN=aaa нужно поставить Driver={...}, в фигурных скобках
ставится название ODBC-драйвера точно в таком виде, как оно значится в
ODBC Data Source Administrator, закладка Drivers.
Механизм
разрешения имен SQL Server 7.0 поддерживает названия, состоящие из 4-х
частей: <имя прилинкованного
сервера>.<каталог>.<схема>. <имя объекта>,
например, MySQL65.pubs.dbo. authors. Некоторые провайдеры не требуют
обязательного присутствия всех частей или имеют для них значения по
умолчанию, такие части могут опускаться. Например, если мы
прилинковываем Excel через провайдера для ODBC, то имя листа (Sheet)
titles может выглядеть так: MyExcel97.[d:\temp\HetQueries\
titles]..[titles], если же через провайдера для Jet, то его можно
указать в виде MyExcel97...[titles]. По имени прилинкованного сервера
SQL Server на основе информации, прописанной в системные таблицы при
его (прилинкованного сервера) создании, идентифицирует провайдера и
отсылает ему оставшиеся три части имени. Этих сведений провайдеру
должно быть достаточно, чтобы однозначно определить объект в источнике.
Запросы
к прилинкованным серверам могут быть двух типов: с использованием имени
из 4-х частей, либо сквозные (passthrough). В качестве примера с
именами из 4-х частей преобразуем сообразно ситуации запрос на
Лист.1.1.2
select a.au_fname, a.au_lname, t.title, p.pub_name, s.qty, st.stor_name from authors a
inner join MySQL65.pubs.dbo.titleauthor ta on a.au_id=ta.au_id
inner join MyExcel97...[titles] t on ta.title_id=t.title_id
inner join MyText.[D:\TEMP\HetQueries]..[publishers.txt] p on t.pub_id=p.pub_id
inner join MyVFP6.[fox_sales]..[sales] s on s.title_id=t.title_id
inner join MyAccess97...[stores] st on s.stor_id=st.stor_id
Можно его выполнить и убедиться, что результат будет в
точности таким же, как и у его прототипа на Лист.1.1.2, где все данные
хранились на локальном сервере.
Сквозные запросы создаются при
помощи функции OpenQuery(). Первым аргументом этой функции выступает
имя прилинкованного сервера, вторым - собственно текст запроса: select
* from OpenQuery(Monarch, "select FileName from scope(""c:\Program
Files"")"). Запрос внутри OpenQuery() не проверяется и не анализируется
SQL Server"ом, а напрямую передается прилинкованному источнику так, как
есть. Отсутствие предобработки позволяет сэкономить время, но требует
аккуратности при составлении запроса. Пример: запросы
select * from OpenQuery(MyExcel97, "select * from titles where type=""business""") и
select * from OpenQuery(MyExcel97, "select * from titles") where type="business"
дают один и тот же результат, однако во втором запросе
проверку условия фильтрации выполняет SQL Server, а в первом - OLE
DB-провайдер. Первый запрос следует признать более эффективным,
поскольку в этом случае пересылать приходится меньшее количество данных.
Третий
вариант построения распределенного запроса (ad hoc name) позволяет
вообще обойтись без прилинкованного заранее сервера. Вызов функции
OpenRowset подменяет обращение к таблице. В параметрах должна быть
указана информация, позволяющая установить соединение с удаленным
источником, и сама таблица в виде
<каталог>.<схема>.<объект>, либо запрос. Пример:
select * from OpenRowset("MSDASQL", "Driver={Microsoft Excel Driver (*.xls)};
DBQ=D:\TEMP\HetQueries\titles.XLS;", "select * from titles where type=""business""")
При совместной работе с данными, относящимися как к SQL
Server, так и к удаленным источникам следует иметь в виду, что внешние
данные всегда доступны на чтение. Поддержка операций обновления внешних
данных зависит от уровня обслуживающего их провайдера. Над входящими в
тот же запрос данными, принадлежащими SQL Server, возможны любые
допустимые операции. Следовательно, внешние данные всегда могут быть
использованы для создания представлений и статических курсоров.
Обновление внешних данных через представления или keyset-курсоров
определяется возможностями провайдера. Динамические курсоры и операторы
DDL над внешними данными не поддерживаются. Конвертация данных
осуществляется путем приведения к ближайшему соответствующему типу,
определенному в стандартах OLE DB. Сортировка выполняется в
соответствии с порядком, заданным на локальном SQL Server. Участие
внешних данных в транзакциях зависит от того, реализованы ли в
провайдер интерфейсы поддержки транзакций. Например, если провайдер
поддерживает интерфейс ITransactionLocal, но не поддерживает
ITransactionJoin, данные удаленного источника можно включать в
локальные транзакции, но они не смогут наследовать контекст внешней
транзакции и, следовательно, не будут участвовать в распределенных
транзакциях ([7]). Поддержка провайдером интерфейса IDBSchemaRowset
позволяет SQL Server"у получать информацию о метаданных. Для этого
можно использовать системные хранимые процедуры sp_catalogs,
sp_tables_ex, sp_columns_ex, sp_table_privileges, sp_column_privileges,
sp_primarykeys, sp_foreignkeys, sp_indexes и др. Если провайдер
предоставляет информацию об имеющихся индексах, процессор запросов SQL
Server сможет точнее оценить распределенный запрос и оптимизировать его
выполнение. В плане запроса
select st.stor_name, st.city, s.ord_num, s.qty from stores st,
MyOracle..klm.sales s where s.stor_id=st.stor_id and s.qty>50
----------------
|-Merge Join(Inner Join,
MANY-TO-MANY MERGE:(s.stor_id)= (Expr1002)
ESIDUAL:(s.stor_id=Convert(st.stor_id)))
|-Remote Query(SELECT `s`.`ord_num` AS Col1007,`s`.`qty` AS Col1008,`s`.`stor_id`
AS Col1006 FROM `sales` s WHERE `s`.`qty`>(50) ORDER BY `s`.`stor_id` ASC)
|-Sort(ORDER BY: (Expr1002 asc))
|-Compute Scalar(Expr1002=Convert(st.stor_id))
|-Clustered Index Scan(pubs..stores.UPK_storeid AS st)
выделенным шрифтом показан подзапрос, который в
действительности SQL Server отсылает на сервер Oracle. Шаг Merge Join
получает от провайдера Oracle отфильтрованные по qty и отсортированные
по stor_id результаты запроса. Если бы таблица sales находилась,
скажем, в виде текстового файла, то в плане вместо Remote Query стояло
бы сканирование всей удаленной таблицы (выполняется провайдером) с
последующими шагами фильтрации и сортировки (выполняется процессором
запросов SQL Server).
Одним
из частных примеров применения технологии универсального доступа может
служить возможность полнотекстового поиска в SQL Server 7.0.
Полнотекстовый провайдер (full-text provider) представляет собой
промежуточное звено, посредством которого клиентское приложение
взаимодействует со службой полнотекстового поиска (Microsoft Search
Service). Служба полнотекстового поиска устанавливается как комопонент
SQL Server 7.0 стандартной (Standard) и коропоративной (Enterprise)
редакции и функционирует как сервис Windows NT. Настольная (Windows 9x)
редакция SQL Server способна использовать полнотекстовый поиск подобно
клиентам SQL Server, установившим соединение с сервером стандартной или
корпоративной редакции. Служба полнотекстового поиска создает каталоги
и полнотекстовые индексы. Каждая запись индекса содержит указатель на
запись таблицы, слова, ассоциированные с этой записью за вычетом
незначащих (noisy words), информацию о поле, которому они принадлежат,
и месте их нахождения в этом поле. В качестве указателя записи
используется первичный ключ (primary key) или кандидат (candidate). На
таблицу можно создать не более, чем один полнотекстовый индекс. Каждый
индекс находится в своем каталоге. База данных может иметь несколько
индексных каталогов, но каждый каталог должен соответствовать только
одной базе данных. Полнотекстовые индексы не допускаются над
представлениями, а также системными или временными таблицами. Возможны
два способа наполнения полнотекстовых индексов - Full Population
(применяется при начальном создании индекса, либо при существенном
изменении содержания индексированных полей) и Incremental Population.
Последний доступен для таблиц, имеющих поле timestamp. Изменение
содержания индексированных полей не влечет за собой немедленной
поправки полнотекстового индекса, так как последние довольно объемны и
при их постоянной модификации происходила бы заметная задержка. Вместо
этого можно оформить Incremental Population как задание (job),
выполняющееся с некоторой периодичностью с помощью SQL Agent. Каталог
является минимальной единицей обновления полнотекстовых индексов. Кроме
того, в функции службы полнотекстового поиска входит обработка
специальных конструктов в запросах (предикаты CONTAINS и FREETEXT), с
помощью которых осуществляется поиск отдельных слов и фраз, учет
расстояния между словами (NEAR), распознавание словоформ (FORMSOF) и
взвешивание по значимости (ISABOUT). В качестве примера создадим
таблицу с полем pgh типа text, в каждую запись которой положим
отдельный абзац этой статьи. Это можно сделать с помощью макроса:
Sub Macro1()
Dim cnn As Object
Set cnn = CreateObject("ADODB. Connection")
Dim rst As Object
Set rst = CreateObject("ADODB.Recordset")
With cnn
.Open "Provider=SQLOLEDB; Data Source=alexeysh_desk; User ID=sa; Password=;
Initial Catalog=pubs"
.Execute "CREATE TABLE MySQLPaper (id int IDENTITY (1, 1) CONSTRAINT
[PK_MySQLPaper] PRIMARY KEY NONCLUSTERED (id) ON [PRIMARY], pgh ntext, ts timestamp)
ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]"
For Each pgh In ActiveDocument.Paragraphs
.Execute "insert into mysqlpaper (ts) values (default)"
rst.Open "select pgh from MySQLPaper where id=@@identity", cnn, 1, 3, -1
rst.Fields(0) = pgh
rst.Update
rst.Close
Next
.Close
End With
End Sub
Таблица готова. Ассоциируем список незначащих слов (noisy words) с файлом noise.dat:
sp_configure "Language neutral full-text", 1 go
reconfigure
go
Делаем текущую базу данных доступной для полнотекстового поиска: exec sp_fulltext_database @action="enable"
Создаем новый полнотекстовый каталог SQLPaper по заданному пути: exec sp_fulltext_catalog @ftcat= "SQLPaper", @action="create", @path="d:\mssql7b3_dat\FTData"
Создаем
метаданные полнотекстового индекса и указываем, что идентификация
записей в таблице MySQLPaper должна происходить по ключу PK_MySQLPaper: exec sp_fulltext_table @tabname="MySQLPaper", @action= "create", @ftcat="SQLPaper", @keyname="PK_MySQLPaper"
Добавляем поле pgh как одно из тех, по которому будет происходить полнотекстовый поиск (индекс пока неактивен):
exec sp_fulltext_column @tabname="MySQLPaper", @colname="pgh", @action="add"
Регистрируем таблицу в каталоге полнотекстового поиска файловой системы: exec sp_fulltext_table @tabname="MySQLPaper", @action="activate"
Все
вышеперечисленные действия интерактивно удобнее выполнять с помощью
программы-мастера полнотекстовых индексов, которая создает каталог и
структуру индекса. Нам остается лишь наполнить индексы в данном
каталоге: exec sp_fulltext_catalog @ftcat="SQLPaper", @action="start_full"
После чего к таблице можно обращаться с запросами типа: select id, pgh from MySQLPaper where contains(pgh, ""полнотекст*" near "поиск"")
Из
плана выполнения следует, что процессор запросов SQL Server проверяет,
определен ли на поле pgh полнотекстовый индекс и преобразует оператор
SQL в последовательность действий над множеством записей. Обычно эти
действия через OLE DB-провайдера передаются механизму хранения SQL
Server. Однако действия, относящиеся к контекстному поиску, передаются
OLE DB-провайдеру службы полнотекстового поиска. Эта служба
осуществляет поиск по своим каталогам и содержащимся в них
полнотекстовым индексам и возвращает набор указателей на записи,
отвечающим условию поиска. Процессор запросов комбинирует его с
наборами, полученными от других провайдеров, в частности, механизма
хранения и строит окончательное множество результатов, которое
возвращается клиентскому приложению. Каталоги и индексы, находящиеся
под управлением службы полнотекстового поиска, не хранятся в базе
данных SQL Server, следовательно, их не охватывают операции резервного
копирования и восстановления. Подробнее об архитектуре и синтаксических
конструкциях полнотекстового поиска можно узнать, обратившись к
документации.
Служба полнотекстового поиска, входящая в состав
SQL Server 7.0 обеспечивает поиск по строковым и текстовым полям (в том
числе Unicode). Контекстный поиск по документам файловой системы или
опубликованным на Web-сервере осуществляется с помощью индексных
механизмов Microsoft Index Server или Microsoft Site Server и
рассмотренной нами технологии гетерогенных запросов (например, через
прилинкованные сервера). Для доступа к этим индексам используются OLE
DB-провайдеры (соответственно, для Index Server или Site Server), так
что описанная выше схема полнотекстового поиска сохраняется с точностью
до провайдера. Базовые синтаксические расширения полнотекстового поиска
в Transact-SQL одинаковы и поддерживаются каждым из трех упомянутых
провайдеров.
|