Хранимая процедура SQL Server 2016 для функционирования - PullRequest
0 голосов
/ 01 ноября 2018

Я создал хранимую процедуру, которая использует временную таблицу, чтобы получить массивные результаты XML-карт Bing и преобразовать их в varchar, чтобы я мог использовать ее для извлечения нескольких полей. Причина его преобразования в varchar заключается в том, что XML был слишком большим для sp_OAMethod, а моя переменная всегда была пустой.

set @serviceUrl = 'https://dev.virtualearth.net/REST/v1/Routes/Truck?wp.0=' + @ToAddress  + '&wp.1=' + @FromAddress + '&vehicleHazardousMaterials=Flammable&output=xml&key=XXX-000-XXX'

Exec sp_OACreate 'MSXML2.XMLHTTP', @Object OUT;
Exec sp_OAMethod @Object, 'open', NULL, 'get',@serviceUrl, 'false'
Exec sp_OAMethod @Object, 'send'
Exec sp_OAMethod @Object, 'responseText', @ResponseText OUTPUT

exec @returnCode = sp_oamethod @Object, 'read', @ResponseText out, -1

Create table #tmp(dt xml)

insert into #tmp
    exec @hr = sp_OAGetProperty @Object, 'responseXML.XML'

Set @ResponseText = Convert(varchar(max), (SELECT dt from #tmp))
Drop Table #tmp 

select REPLACE(SUBSTRING(@ResponseText,PATINDEX('%<TravelDistance>%',@ResponseText),21),'<TravelDistance>','') Miles

  ,REPLACE(REPLACE(SUBSTRING(@ResponseText,PATINDEX('%<TravelDuration>%',@ResponseText),21),'<TravelDuration>',''),'<','')/60 TravelMinutes

Выше не полный код; Я серьезно отредактировал его, чтобы его было легче читать.

Я уверен, что есть лучший способ сделать это, но это работает, и это очень увлекательно.

Однако планировалось создать функцию, которая могла бы использоваться вместе с другими специальными запросами для извлечения тех же данных. Невозможно в качестве функции из-за временной таблицы или заполнения таблицы внутри функции.

Так есть ли у кого-нибудь способ сделать это возможным?

1 Ответ

0 голосов
/ 02 ноября 2018

Вы действительно должны использовать CLR для этого. Вы далеки от правильной хранимой процедуры, и процедуры sp_OAxxx лучше избегать. Вы неправильно обрабатываете код возврата, используете неверный компонент HTTP и не анализируете результаты с помощью анализатора XML SQL Server. Изначально может показаться, что реализация CLR - это больше работы, но поддержание кода, использующего хранимые процедуры sp_OAxxx и взаимодействие COM, всегда будет болезненным.

Очень немногие люди понимают, как работает этот код, как его изменить, а также является ли он безопасным и надежным. Следующий человек, которому принадлежит ваша кодовая база, вероятно, не один из этих людей Нехорошо иметь такой код в своем проекте.

Если вы действительно используете CLR, вам все равно не следует использовать функцию (хотя технически это возможно). Вы не должны выполнять какой-либо внешний доступ в середине запроса.

В любом случае, вот некоторые исправления к существующему подходу:

declare @serviceURL varchar(max)
declare @returnCode int
declare @errorMessage nvarchar(max)
declare @Object int
declare @hr int
declare @key varchar(2000) = 'Aoj...nFS'
declare @FromAddress nvarchar(max) = '7000 N SH 161, Irving TX 75039'

declare @ToAddress nvarchar(max) = '8617 Garland Rd, Dallas, TX 75218'

set @serviceUrl = 'https://dev.virtualearth.net/REST/v1/Routes/Truck?wp.0=' + @ToAddress  + '&wp.1=' + @FromAddress + '&vehicleHazardousMaterials=Flammable&output=xml&key=' + @key


exec @hr = sp_OACreate 'MSXML2.ServerXMLHTTP', @Object OUT;
IF @hr <> 0
begin
    set @errorMessage = concat('sp_OACreate failed ', convert(varchar(20),cast(@hr as varbinary(4)),1));
    throw 60000, @errorMessage, 1;
end;
begin try
    Exec @hr = sp_OAMethod @Object, 'open', NULL, 'get',@serviceUrl, 'false'
    IF @hr <> 0
    begin
        set @errorMessage = concat('open failed ', convert(varchar(20),cast(@hr as varbinary(4)),1));
        throw 60000, @errorMessage, 1;
    end;

    Exec @hr = sp_OAMethod @Object, 'send'
    IF @hr <> 0
    begin
        set @errorMessage = concat('send failed ', convert(varchar(20),cast(@hr as varbinary(4)),1));
        throw 60000, @errorMessage, 1;
    end;

    declare @responseCode int;
    Exec @hr = sp_OAGetProperty @Object, 'status', @responseCode out
    IF @hr <> 0 or @responseCode <> 200
    begin
        set @errorMessage = concat('send failed hr:', convert(varchar(20),cast(@hr as varbinary(4)),1),' http response code: ', @responseCode);
        throw 60000, @errorMessage, 1;
    end;

    declare @tmp table(doc xml)
    insert into @tmp
    exec @hr = sp_OAGetProperty @Object, 'ResponseXML.XML'
    IF @hr <> 0
    begin
        set @errorMessage = concat('ResponseXML.XML failed ', convert(varchar(20),cast(@hr as varbinary(4)),1));
        throw 60000, @errorMessage, 1;
    end;



    WITH XMLNAMESPACES (DEFAULT 'http://schemas.microsoft.com/search/local/ws/rest/v1')
    select doc.value('(//TravelDistance)[1]', 'float') TravelDistance,
           doc.value('(//DistanceUnit)[1]', 'nvarchar(20)') DistanceUnit,
           doc.value('(//TravelDuration)[1]', 'float') TravelDuration,
           doc.value('(//DurationUnit)[1]', 'nvarchar(20)') DurationUnit
    from @tmp;
end try
begin catch
    exec @hr = sp_OADestroy  @Object ;
    throw;    
end catch
...