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

Часовой пояс: 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 часа


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

Сейчас этот форум просматривают: нет зарегистрированных пользователей


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

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