Текущее время: Сб, июл 19 2025, 19:30

Часовой пояс: UTC + 3 часа


Правила форума


ВНИМАНИЕ!

Вопросы по SAP Query и Quick View - сюда



Начать новую тему Ответить на тему  [ 1 сообщение ] 
Автор Сообщение
 Заголовок сообщения: SAP на Oracle по Linux link с SQL сервером
СообщениеДобавлено: Вт, авг 27 2013, 22:14 
Менеджер
Менеджер
Аватара пользователя

Зарегистрирован:
Чт, мар 09 2006, 10:12
Сообщения: 565
Откуда: Волгодонск
Пол: Мужской
Задача:
Получение данных из БД на MS SQL сервере, в том числе получение Dataset-а из хранимой процедуры

Ландшафт:
SAP на oracle под Linux
MS SQL сервер по виндой

Решение:
Собственно простое, был сделан линк из oracl-а на sql сервер - как это делалось не знаю, это к админам

а вот дальше начинаются танцы с программистским бубном:
В принципе если надо было бы только простые запросы на выборку делать ... как-то пережить можно было бы, хотя правильно нарисовать запрос для линка ещё то удовольствие
Но вот получение Dataset-а возвращаемого хранимкой, задача ... однозначно без бубна не разрешимая

В конце концов было сделано такое, более менее универсальное решение:
в sql сервере созданы две таблицы sap_query и sap_result,
SAP вставляет запись в таблицу sap_query, в качестве данных передаётся текст sql запроса
на таблицу sap_query навешен тригер срабатывающий при вставке, он выполняет запрос передаваемый в вставляемой записи, результат выполнения запроса помещается в sap_result
SAP считывает результат из таблицы sap_result

таблица sap_query
Code:
CREATE TABLE [dbo].[sap_query](
   [sap_id] [int] NOT NULL,
   [sql] [varchar](max) NOT NULL,
   [ret_structure] [varchar](1000) NOT NULL,
   [ret_fields] [varchar](1000) NULL,
   [Base64] [bit] NULL,
CONSTRAINT [PK_sap_query] PRIMARY KEY CLUSTERED
(
   [sap_id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]


тригер на вставку для sap_query
Code:
CREATE TRIGGER [dbo].[exec_sap_sql]
   ON  [dbo].[sap_query]
   AFTER INSERT
AS
BEGIN
   SET NOCOUNT ON;
   
   declare @sap_id int
   declare @sql nvarchar(max)
   declare @ret_structure varchar(1100)
   declare @ret_fields varchar(1100)
   declare @str varchar(max)
   declare @Base64 bit
   declare @fsBase64 varchar(15)
   
   declare @pos int
   declare @column_name varchar(50)
   declare @Delimiter varchar(1) = ','
   
   if ((OBJECT_ID ('tempdb.dbo.##tmp_exec_sap_sql')) IS NOT NULL) drop table ##tmp_exec_sap_sql
   
   declare ins_cur cursor for
   select sap_id, sql, ret_structure, ret_fields, Base64
     from inserted
   open ins_cur
   fetch next from ins_cur into @sap_id, @sql, @ret_structure, @ret_fields, @Base64
   while (@@FETCH_STATUS <> -1) begin   
      set @str = 'create table ##tmp_exec_sap_sql(' + @ret_structure + ')'
      exec(@str)
   
      if IsNull(@Base64, 0) = 1 begin
         set @sql = dbo.FromBase64(@sql)
         set @fsBase64 = 'dbo.ToBase64'
      end
      else set @fsBase64 = ''
      
      insert into ##tmp_exec_sap_sql exec sp_executesql @sql
            
      set @str = ''
      if @ret_fields is Null begin
         select @str = @str + 'convert(varchar(200),IsNull(['+column_name+'],''''))+''|''+'
           from tempdb.INFORMATION_SCHEMA.COLUMNS
          where TABLE_NAME = '##tmp_exec_sap_sql'
           order by ORDINAL_POSITION
      end
      else begin
         set @ret_fields = @ret_fields + ','
         while Len(@ret_fields) > 0 begin      
            set @pos = CharIndex(@Delimiter, @ret_fields)
            set @column_name = RTrim(LTrim(left(@ret_fields, @pos - 1)))
      
            if Len(@column_name) > 0 begin
               set @str = @str + 'convert(varchar(200),IsNull(['+ @column_name +'],''''))+''|''+'
            end
            set @ret_fields = SubString(@ret_fields, @pos + 1, len(@ret_fields))
         end
      end   
   
      set @str = 'select '''+ convert(varchar(10), @sap_id) +''' as sap_id, '+ @fsBase64+'(' + @str + ''''') as res from ##tmp_exec_sap_sql'
      insert into sap_result (sap_id, res)
      exec (@str)
      
      drop table ##tmp_exec_sap_sql
      
      fetch next from ins_cur into @sap_id, @sql, @ret_structure, @ret_fields, @Base64
   end
   close ins_cur
   deallocate ins_cur
END


таблица sap_result
Code:
CREATE TABLE [dbo].[sap_result](
   [id] [int] IDENTITY(1,1) NOT NULL,
   [sap_id] [int] NOT NULL,
   [res] [varchar](max) NOT NULL
) ON [PRIMARY]


обнаружилась проблема с кодировкой... она где-то терялась при взаимодействии систем
возможно это можно как-то настроить/исправит ... в общем решил сделать с помощью base64 кодировки
Code:
-- Author:  V.U.M.Sastry Sagi
-- Create date: 09/24/2010
-- Description: Decrypts the string from Base64 Format
CREATE FUNCTION [dbo].[FromBase64] (@data varchar(max))
   RETURNS varchar(max)
AS
BEGIN
   DECLARE
   @Output varchar(max),
   @Bits varbinary(3)
-- declare vars.
   DECLARE @XmlData xml
-- construct an xml var.
   SET @XmlData = CAST('<data>' + @data + '</data>' as xml)
-- base64 decode the @data.
   SELECT @Output= CONVERT(varchar(max),
   @XmlData.value('(data)[1]', 'varbinary(max)'))
   RETURN @Output
END

CREATE FUNCTION [dbo].[ToBase64] (@Input varchar(max))
   RETURNS varchar(max)
AS
BEGIN
   DECLARE
   @Output varchar(max),
   @Bits varbinary(3),
   @Pos int
   SET @Pos = 1
   SET @Output = ''
   WHILE @Pos <= Len(@Input) BEGIN
      SET @Bits = Convert(varbinary(3), Substring(@Input, @Pos, 3))
      SET @Output = @Output +
      Substring('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
      Substring(@Bits, 1, 1) / 4 + 1, 1)
      SET @Output = @Output +
      Substring('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
      Substring(@Bits, 1, 1) % 4 * 16 +
      Substring(@Bits, 2, 1) / 16 + 1, 1)
      SET @Output = @Output +
      Substring('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
      Substring(@Bits, 2, 1) % 16 * 4 +
      Substring(@Bits, 3, 1) / 64 + 1, 1)
      SET @Output = @Output +
      Substring('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
      Substring(@Bits, 3, 1) % 64 + 1, 1)
      SET @Pos = @Pos + 3
   END
   RETURN (Left(@Output, Len(@Output) - 3 + Len(@Bits)) + Replicate('=', 3 - Len(@Bits)))
END


в SAPe для удобства работы со всем этим был написан ФМ
Code:
FUNCTION zlink_execute_query.
*"----------------------------------------------------------------------
*"*"Локальный интерфейс:
*"  IMPORTING
*"     REFERENCE(CON_NAME) TYPE  DBCON-CON_NAME OPTIONAL
*"     REFERENCE(LINK)
*"     REFERENCE(SQL)
*"     REFERENCE(SQL_RET_STRUCTURE)   
*"     REFERENCE(RET_FIELDS) OPTIONAL 
*"  CHANGING
*"     REFERENCE(RET)
*"----------------------------------------------------------------------

* SQL_RET_STRUCTURE - структура результата запроса
* она нужна для того чтоб создать временную таблицу в которую будет помещён результат запроса
* а затем перегружен в sap_result

* RET_FIELDS - не обязательное - возвращаемые поля из запроса
* нужно в тех случаях когда надо вернуть не все поля возвращаемые запросом,
* актуально в основном для хранимых процедур

  PERFORM link_select USING con_name link sql sql_ret_structure ret_fields ret.
ENDFUNCTION.


-------------------------
FUNCTION-POOL zlink_to_mssql.               "MESSAGE-ID ..

*&---------------------------------------------------------------------*
*&  Include           ZLINK2MSSQL
*&---------------------------------------------------------------------*

* для преобразования из Base64 FORM convert
DATA: utils TYPE REF TO cl_http_utility.
DATA: conv_int TYPE REF TO cl_abap_conv_obj.
DATA: conv_out TYPE REF TO cl_abap_conv_obj.

DATA: con_ref TYPE REF TO cl_sql_connection.
DATA: qry TYPE REF TO cl_sql_statement.

*&---------------------------------------------------------------------*
*&      Form  init_link
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
FORM init_link USING con_name.
  CHECK qry IS INITIAL.

  IF con_name IS INITIAL.
    CREATE OBJECT qry.
  ELSE.
    con_ref = cl_sql_connection=>get_connection( con_name ).
    qry = con_ref->create_statement( ).
  ENDIF.

  CREATE OBJECT utils.

  CREATE OBJECT conv_int
    EXPORTING
      incode           = '1504'
      miss             = 'S' " prc_miss
      broken           = 'M' " prc_broken
      use_f1           = 'X'
      substc           = '00035' " prc_sapno
    EXCEPTIONS
      invalid_codepage = 1
      internal_error   = 2.

  CREATE OBJECT conv_out
    EXPORTING
      outcode          = '1504'
      miss             = 'S' " prc_miss
      broken           = 'M' " prc_broken
      use_f1           = 'X'
      substc           = '00035' " prc_sapno
    EXCEPTIONS
      invalid_codepage = 1
      internal_error   = 2.
ENDFORM.                    "init

*&---------------------------------------------------------------------*
*&      Form  link_select
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
*      -->SQL                text
*      -->SQL_RET_STRUCTURE  text
*      -->SQL_RET_FIELDS     text
*      -->RET                text
*----------------------------------------------------------------------*
FORM link_select USING con_name link sql sql_ret_structure ret_fields ret.
* Так уж получилось что по нормальному, данные из MS SQL Сервера через link от oracle не взять
* причин две:
* 1 DataSet от хранимой процедуры получить не возможно
* 2 Кодировка данных, возможно это можно как-то настроить, но наш админ не смог, для того чтоб победить эту проблему решил использовать Base64 кодировку
* Работает след образом:

  DATA: sap_id(10) TYPE c.
  DATA: xsql TYPE string.
  DATA: qry_res TYPE REF TO cl_sql_result_set.
  DATA: BEGIN OF itres OCCURS 0,
          val TYPE string,
        END   OF itres.
  DATA: res_ref TYPE REF TO data.
  FIELD-SYMBOLS <res> LIKE LINE OF itres.
  DATA: eres TYPE i.

  DATA: btyp TYPE c.
  DATA: typ TYPE c.

  DATA: itw TYPE STANDARD TABLE OF string.
  FIELD-SYMBOLS <w> LIKE LINE OF itw.

  DATA: ret_wa_ref TYPE REF TO data.
  FIELD-SYMBOLS <ret_tab> TYPE STANDARD TABLE.
  FIELD-SYMBOLS <ret_wa> TYPE ANY.
  FIELD-SYMBOLS <fs> TYPE ANY.

  PERFORM init_link USING con_name.

  sap_id(4) = sy-datum+4.
  sap_id+4(6) = sy-uzeit.

  xsql = sql.
  PERFORM to_base64 USING xsql.

  IF ret_fields IS INITIAL.
    CONCATENATE 'insert into "sap_query"@' link ' ("sap_id", "Base64", "sql", "ret_structure") values (`' sap_id '`, 1,`' xsql '`,`' sql_ret_structure '`)' INTO xsql.
  ELSE.
    CONCATENATE 'insert into "sap_query"@' link ' ("sap_id", "Base64", "sql", "ret_structure", "ret_fields") values (`' sap_id '`, 1,`' xsql '`,`' sql_ret_structure '`,`' ret_fields '`)' INTO xsql.
  ENDIF.

  TRANSLATE xsql USING '`'''.
  eres = qry->execute_update( xsql ).

  CONCATENATE 'select "res" from "sap_result"@' link ' where "sap_id" =#' sap_id INTO xsql.
  TRANSLATE xsql USING '# '.

  GET REFERENCE OF itres[] INTO res_ref.
  qry_res = qry->execute_query( xsql ).
  qry_res->set_param_table( res_ref ).
  qry_res->next_package( ).
  qry_res->close( ).

  CONCATENATE 'delete from "sap_query"@tpp where "sap_id" =' sap_id INTO xsql SEPARATED BY space.
  eres = qry->execute_update( xsql ).

  CONCATENATE 'delete from "sap_result"@tpp where "sap_id" =' sap_id INTO xsql SEPARATED BY space.
  eres = qry->execute_update( xsql ).

* Обрабатываем результат
  DESCRIBE FIELD ret TYPE btyp.
  IF btyp = 'h'.
    ASSIGN ret TO <ret_tab>.
    CREATE DATA ret_wa_ref LIKE LINE OF <ret_tab>.
    ASSIGN ret_wa_ref->* TO <ret_wa>.
    DESCRIBE FIELD <ret_wa> TYPE typ.
  ELSE.
    typ = btyp.
    ASSIGN ret TO <ret_wa>.
  ENDIF.

  IF typ CA 'uv'. " Структуры
    typ = 'u'.
  ENDIF.

  LOOP AT itres  ASSIGNING <res>.
    IF btyp = 'h'.
      APPEND INITIAL LINE TO <ret_tab> ASSIGNING <ret_wa>.
    ENDIF.

    PERFORM from_base64 USING <res>-val.
    SPLIT <res>-val AT '|' INTO TABLE itw.
    LOOP AT itw ASSIGNING <w>.
      IF typ = 'u'. " Структура
        ASSIGN COMPONENT sy-tabix OF STRUCTURE <ret_wa> TO <fs>.
        IF sy-subrc <> 0. EXIT. ENDIF.
        <fs> = <w>.
      ELSE.
        <ret_wa> = <w>.
        EXIT.
      ENDIF.
    ENDLOOP.

    IF btyp <> 'h'. EXIT. ENDIF.
  ENDLOOP.
ENDFORM.                    "link_select

*&---------------------------------------------------------------------*
*&      Form  from_base64
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
*      -->VAL        text
*----------------------------------------------------------------------*
FORM from_base64 USING val.
  DATA: sin TYPE string.
  DATA: s   TYPE string.
  DATA: sout TYPE string.

  CHECK NOT val IS INITIAL.

  sin = val.

  CALL METHOD utils->decode_base64
    EXPORTING
      encoded = sin
    RECEIVING
      decoded = s.

  CALL METHOD conv_int->convert
    EXPORTING
      inbuff         = s
      inbufflg       = 0
      outbufflg      = 0
    IMPORTING
      outbuff        = sout
    EXCEPTIONS
      internal_error = 1
      OTHERS         = 2.

  val = sout.
ENDFORM.                    "convert

*&---------------------------------------------------------------------*
*&      Form  to_base64
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
*      -->VAL        text
*----------------------------------------------------------------------*
FORM to_base64 USING val.
  DATA: sin TYPE string.
  DATA: s   TYPE string.
  DATA: sout TYPE string.

  CHECK NOT val IS INITIAL.

  sin = val.

  CALL METHOD conv_out->convert
    EXPORTING
      inbuff         = sin
      inbufflg       = 0
      outbufflg      = 0
    IMPORTING
      outbuff        = s
    EXCEPTIONS
      internal_error = 1
      OTHERS         = 2.

  CALL METHOD utils->encode_base64
    EXPORTING
      unencoded = s
    RECEIVING
      encoded   = sout.

  val = sout.
ENDFORM.                                                    "to_base64


пример использования
Code:
  CONCATENATE 'EXEC t_proc_get_mat_from_plan @mode = 3, @item_kind = NULL, @start_date = `' sbudat-low '`, @end_date = `' sbudat-high '`' INTO sql.
  TRANSLATE sql USING '`'''.

  CONCATENATE
    '  order_code VARCHAR(20)'
    ', item_number INT'
    ', item_code VARCHAR(80)'
    ', obj_uom_code VARCHAR(3)'
    ', obj_uom VARCHAR(15)'
    ', line_kind INT'
    ', quantity NUMERIC(15,3) NOT NULL'
    ', Price NUMERIC(15,2) NOT NULL'
  INTO srs.

  CALL FUNCTION 'ZLINK_EXECUTE_QUERY'
    EXPORTING
      con_name                = 'ZZZ'
      link                    = 'zzz'
      sql                     = sql
      sql_ret_structure       = srs
*     RET_FIELDS              =
    CHANGING
      ret                     = itk[].


нюанс:
обязательно сделайте в DBCO отдельное соединения с базой oracle, иначе ошибка которая может возникнуть при выполнении запроса через линк, может поставит раком всю базу oracle

_________________
Изображение Попытка не пытка


Принять этот ответ
Вернуться к началу
 Профиль  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ 1 сообщение ] 

Часовой пояс: UTC + 3 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: Yandex [Bot]


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  
cron
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
Русская поддержка phpBB