10 حيل للعمل مع Oracle

هناك العديد من ممارسات Oracle في Sberbank قد تجدها مفيدة. أعتقد أن بعضًا منها مألوف لك ، لكننا لا نستخدم فقط أدوات ETL للتحميل ، ولكن أيضًا إجراءات Oracle المخزنة. تطبق Oracle PL / SQL الخوارزميات الأكثر تعقيدًا لتحميل البيانات في المخازن ، حيث تحتاج إلى "الشعور بكل بايت".



  • التسجيل التلقائي للترجمة
  • ماذا تفعل إذا كنت تريد إنشاء عرض مع المعلمات
  • استخدام الإحصائيات الديناميكية في الاستعلامات
  • كيفية حفظ خطة الاستعلام عند إدخال البيانات عبر ارتباط قاعدة البيانات
  • تشغيل الإجراءات في جلسات متوازية
  • سحب بقايا الطعام
  • الجمع بين عدة قصص في قصة واحدة
  • عادي
  • التقديم بتنسيق SVG
  • تطبيق Oracle Metadata Search


التسجيل التلقائي للترجمة



في بعض قواعد بيانات Oracle ، يوجد في Sberbank مشغل تجميع يتذكر من ومتى وما تغير في كود كائنات الخادم. وبالتالي ، يمكن تحديد مؤلف التغييرات من جدول سجل التجميع. يتم أيضًا تنفيذ نظام التحكم في الإصدار تلقائيًا. على أي حال ، إذا نسي المبرمج إرسال التغييرات إلى Git ، فستعمل هذه الآلية على التحوط. دعنا نصف مثالاً على تنفيذ مثل هذا النظام من التسجيل التلقائي للترجمة. يبدو أحد الإصدارات المبسطة لمشغل الترجمة الذي يكتب في السجل في شكل جدول ddl_changes_log كما يلي:



create table DDL_CHANGES_LOG
(
  id               INTEGER,
  change_date      DATE,
  sid              VARCHAR2(100),
  schemaname       VARCHAR2(30),
  machine          VARCHAR2(100),
  program          VARCHAR2(100),
  osuser           VARCHAR2(100),
  obj_owner        VARCHAR2(30),
  obj_type         VARCHAR2(30),
  obj_name         VARCHAR2(30),
  previous_version CLOB,
  changes_script   CLOB
);

create or replace trigger trig_audit_ddl_trg
  before ddl on database
declare
  v_sysdate              date;
  v_valid                number;
  v_previous_obj_owner   varchar2(30) := '';
  v_previous_obj_type    varchar2(30) := '';
  v_previous_obj_name    varchar2(30) := '';
  v_previous_change_date date;
  v_lob_loc_old          clob := '';
  v_lob_loc_new          clob := '';
  v_n                    number;
  v_sql_text             ora_name_list_t;
  v_sid                  varchar2(100) := '';
  v_schemaname           varchar2(30) := '';
  v_machine              varchar2(100) := '';
  v_program              varchar2(100) := '';
  v_osuser               varchar2(100) := '';
begin
  v_sysdate := sysdate;
  -- find whether compiled object already presents and is valid
  select count(*)
    into v_valid
    from sys.dba_objects
   where owner = ora_dict_obj_owner
     and object_type = ora_dict_obj_type
     and object_name = ora_dict_obj_name
     and status = 'VALID'
     and owner not in ('SYS', 'SPOT', 'WMSYS', 'XDB', 'SYSTEM')
     and object_type in ('TRIGGER', 'PROCEDURE', 'FUNCTION', 'PACKAGE', 'PACKAGE BODY', 'VIEW');
  -- find information about previous compiled object
  select max(obj_owner) keep(dense_rank last order by id),
         max(obj_type) keep(dense_rank last order by id),
         max(obj_name) keep(dense_rank last order by id),
         max(change_date) keep(dense_rank last order by id)
    into v_previous_obj_owner, v_previous_obj_type, v_previous_obj_name, v_previous_change_date
    from ddl_changes_log;
  -- if compile valid object or compile invalid package body broken by previous compilation of package then log it
  if (v_valid = 1 or v_previous_obj_owner = ora_dict_obj_owner and
     (v_previous_obj_type = 'PACKAGE' and ora_dict_obj_type = 'PACKAGE BODY' or
     v_previous_obj_type = 'PACKAGE BODY' and ora_dict_obj_type = 'PACKAGE') and
     v_previous_obj_name = ora_dict_obj_name and
     v_sysdate - v_previous_change_date <= 1 / 24 / 60 / 2) and
     ora_sysevent in ('CREATE', 'ALTER') then
    -- store previous version of object (before compilation) from dba_source or dba_views in v_lob_loc_old
    if ora_dict_obj_type <> 'VIEW' then
      for z in (select substr(text, 1, length(text) - 1) || chr(13) || chr(10) as text
                  from sys.dba_source
                 where owner = ora_dict_obj_owner
                   and type = ora_dict_obj_type
                   and name = ora_dict_obj_name
                 order by line) loop
        v_lob_loc_old := v_lob_loc_old || z.text;
      end loop;
    else
      select sys.dbms_metadata_util.long2clob(v.textlength, 'SYS.VIEW$', 'TEXT', v.rowid) into v_lob_loc_old
        from sys."_CURRENT_EDITION_OBJ" o, sys.view$ v, sys.user$ u
       where o.obj# = v.obj#
         and o.owner# = u.user#
         and u.name = ora_dict_obj_owner
         and o.name = ora_dict_obj_name;
    end if;
    -- store new version of object (after compilation) from v_sql_text in v_lob_loc_new
    v_n := ora_sql_txt(v_sql_text);
    for i in 1 .. v_n loop
      v_lob_loc_new := v_lob_loc_new || replace(v_sql_text(i), chr(10), chr(13) || chr(10));
    end loop;
    -- find information about session that changed this object
    select max(to_char(sid)), max(schemaname), max(machine), max(program), max(osuser)
      into v_sid, v_schemaname, v_machine, v_program, v_osuser
      from v$session
     where audsid = userenv('sessionid');
    -- store changes in ddl_changes_log
    insert into ddl_changes_log
      (id, change_date, sid, schemaname, machine, program, osuser,
       obj_owner, obj_type, obj_name, previous_version, changes_script)
    values
      (seq_ddl_changes_log.nextval, v_sysdate, v_sid, v_schemaname, v_machine, v_program, v_osuser,
       ora_dict_obj_owner, ora_dict_obj_type, ora_dict_obj_name, v_lob_loc_old, v_lob_loc_new);
  end if;
exception
  when others then
    null;
end;


في هذا المشغل ، يتم الحصول على الاسم والمحتويات الجديدة للكائن المترجم ، مع استكمالها بالمحتويات السابقة من قاموس البيانات ، وكتابتها في سجل التغيير.



ماذا تفعل إذا كنت تريد إنشاء عرض مع المعلمات



غالبًا ما يمكن لمطور في Oracle زيارة هذه الرغبة. لماذا من الممكن إنشاء إجراء أو وظيفة باستخدام معلمات ، ولكن لا توجد طرق عرض بمعلمات إدخال يمكن استخدامها في الحسابات؟ لدى Oracle شيء ليحل محل هذا المفهوم المفقود ، في رأينا.

لنلقي نظرة على مثال. يجب ألا يكون هناك جدول بالمبيعات حسب القسم لكل يوم.



create table DIVISION_SALES
(
  division_id INTEGER,
  dt          DATE,
  sales_amt   NUMBER
);


يقارن هذا الاستعلام المبيعات حسب القسم على مدار يومين. في هذه الحالة ، 04/30/2020 و 09/11/2020.



select t1.division_id,
       t1.dt          dt1,
       t2.dt          dt2,
       t1.sales_amt   sales_amt1,
       t2.sales_amt   sales_amt2
  from (select dt, division_id, sales_amt
          from division_sales
         where dt = to_date('30.04.2020', 'dd.mm.yyyy')) t1,
       (select dt, division_id, sales_amt
          from division_sales
         where dt = to_date('11.09.2020', 'dd.mm.yyyy')) t2
 where t1.division_id = t2.division_id;


إليكم وجهة نظر أود كتابتها لتلخيص مثل هذا الطلب. أود تمرير التواريخ كمعلمات. ومع ذلك ، فإن بناء الجملة لا يسمح بذلك.



create or replace view vw_division_sales_report(in_dt1 date, in_dt2 date) as
select t1.division_id,
       t1.dt          dt1,
       t2.dt          dt2,
       t1.sales_amt   sales_amt1,
       t2.sales_amt   sales_amt2
  from (select dt, division_id, sales_amt
          from division_sales
         where dt = in_dt1) t1,
       (select dt, division_id, sales_amt
          from division_sales
         where dt = in_dt2) t2
 where t1.division_id = t2.division_id;


تم اقتراح مثل هذا الحل. لنقم بإنشاء نوع للخط من هذا العرض.



create type t_division_sales_report as object
(
  division_id INTEGER,
  dt1         DATE,
  dt2         DATE,
  sales_amt1  NUMBER,
  sales_amt2  NUMBER
);


وسننشئ نوعًا لجدول من هذه السلاسل.



create type t_division_sales_report_table as table of t_division_sales_report;


بدلاً من طريقة العرض ، دعنا نكتب دالة مخططة مع معلمات إدخال التاريخ.



create or replace function func_division_sales(in_dt1 date, in_dt2 date)
  return t_division_sales_report_table
  pipelined as
begin
  for z in (select t1.division_id,
                   t1.dt          dt1,
                   t2.dt          dt2,
                   t1.sales_amt   sales_amt1,
                   t2.sales_amt   sales_amt2
              from (select dt, division_id, sales_amt
                      from division_sales
                     where dt = in_dt1) t1,
                   (select dt, division_id, sales_amt
                      from division_sales
                     where dt = in_dt2) t2
             where t1.division_id = t2.division_id) loop
    pipe row(t_division_sales_report(z.division_id,
                                     z.dt1,
                                     z.dt2,
                                     z.sales_amt1,
                                     z.sales_amt2));
  end loop;
end;


يمكنك الرجوع إليها على النحو التالي:



select *
  from table(func_division_sales(to_date('30.04.2020', 'dd.mm.yyyy'),
                                 to_date('11.09.2020', 'dd.mm.yyyy')));


سيعطينا هذا الطلب نفس نتيجة الطلب في بداية هذا المنشور بتواريخ بديلة صريحة.

يمكن أن تكون الوظائف المخططة مفيدة أيضًا عندما تحتاج إلى تمرير معلمة داخل استعلام معقد.

على سبيل المثال ، ضع في اعتبارك طريقة عرض معقدة يكون فيها الحقل 1 ، الذي تريد تصفية البيانات بواسطته ، مخفيًا في مكان ما في عمق العرض.



create or replace view complex_view as
 select field1, ...
   from (select field1, ...
           from (select field1, ... from deep_table), table1
          where ...),
        table2
  where ...;


وقد يكون للاستعلام من طريقة عرض ذات قيمة ثابتة لـ field1 خطة تنفيذ سيئة.



select field1, ... from complex_view
 where field1 = 'myvalue';


أولئك. بدلاً من التصفية الأولى لـ deep_table بواسطة حقل الشرط 1 = "myvalue" ، يمكن للاستعلام أولاً ضم جميع الجداول ومعالجة كمية كبيرة من البيانات غير الضرورية ، ثم تصفية النتيجة حسب الحقل الشرط 1 = "myvalue". يمكن تجنب هذا التعقيد إذا قمنا بدلاً من العرض المبني على خطوط الأنابيب بإنشاء وظيفة بمعامل يتم تعيين قيمته للحقل 1.



استخدام الإحصائيات الديناميكية في الاستعلامات



يحدث أن نفس الاستعلام في قاعدة بيانات Oracle يعالج في كل مرة كمية مختلفة من البيانات في الجداول والاستعلامات الفرعية المستخدمة فيها. كيف يمكنك جعل المُحسِّن يكتشف طريقة ربط الجداول هذه المرة والفهارس التي يجب استخدامها في كل مرة؟ ضع في اعتبارك ، على سبيل المثال ، استعلامًا يربط جزءًا من أرصدة الحسابات التي تغيرت منذ آخر تنزيل إلى دليل الحساب. يختلف جزء أرصدة الحسابات المتغيرة اختلافًا كبيرًا من تنزيل إلى تنزيل ، حيث يصل إلى مئات الأسطر ، وأحيانًا ملايين السطور. اعتمادًا على حجم هذا الجزء ، يلزم دمج الأرصدة المتغيرة مع الحسابات إما عن طريق / * + use_nl * / طريقة ، أو عن طريق طريقة / * + use_hash * /. من غير الملائم إعادة تجميع الإحصائيات في كل مرة ، خاصةً إذا تغير عدد الصفوف من تحميل إلى تحميل ليس في الجدول المرتبط ، ولكن في الاستعلام الفرعي المرتبط.يمكن للتلميح / * + dynamic_sampling () * / أن ينقذ هنا. دعنا نظهر كيف يؤثر ذلك ، باستخدام طلب مثال. دع الجدول change_balances يحتوي على التغييرات في الأرصدة والحسابات - دليل الحسابات. ننضم إلى هذه الجداول من خلال حقول account_id المتوفرة في كل من الجداول. في بداية التجربة ، سنكتب المزيد من الصفوف في هذه الجداول ولن نغير محتوياتها.

أولاً ، لنأخذ 10٪ من التغييرات في القيم المتبقية في جدول change_balances ونرى ما الذي ستستخدمه الخطة Dynamic_sampling:



SQL> EXPLAIN PLAN
  2   SET statement_id = 'test1'
  3   INTO plan_table
  4  FOR  with c as
  5   (select /*+ dynamic_sampling(change_balances 2)*/
  6     account_id, balance_amount
  7      from change_balances
  8     where mod(account_id, 10) = 0)
  9  select a.account_id, a.account_number, c.balance_amount
 10    from c, accounts a
 11   where c.account_id = a.account_id;

Explained.

SQL>
SQL> SELECT * FROM table (DBMS_XPLAN.DISPLAY);
Plan hash value: 874320301

----------------------------------------------------------------------------------------------
| Id  | Operation          | Name            | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |                 |  9951K|   493M|       |   140K  (1)| 00:28:10 |
|*  1 |  HASH JOIN         |                 |  9951K|   493M|  3240K|   140K  (1)| 00:28:10 |
|*  2 |   TABLE ACCESS FULL| CHANGE_BALANCES |   100K|  2057K|       |  7172   (1)| 00:01:27 |
|   3 |   TABLE ACCESS FULL| ACCOUNTS        |    10M|   295M|       |   113K  (1)| 00:22:37 |
----------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("ACCOUNT_ID"="A"."ACCOUNT_ID")
   2 - filter(MOD("ACCOUNT_ID",10)=0)

Note
-----
   - dynamic sampling used for this statement (level=2)

20 rows selected.


لذلك ، نرى أنه من المقترح المرور عبر جداول change_balances والحسابات باستخدام مسح كامل والانضمام إليها باستخدام صلة تجزئة.

الآن دعونا نقلل بشكل كبير من العينة من change_balances. لنأخذ 0.1٪ من التغييرات المتبقية ونرى ما ستستخدمه الخطة Dynamic_sampling:



SQL> EXPLAIN PLAN
  2   SET statement_id = 'test2'
  3   INTO plan_table
  4  FOR  with c as
  5   (select /*+ dynamic_sampling(change_balances 2)*/
  6     account_id, balance_amount
  7      from change_balances
  8     where mod(account_id, 1000) = 0)
  9  select a.account_id, a.account_number, c.balance_amount
 10    from c, accounts a
 11   where c.account_id = a.account_id;

Explained.

SQL>
SQL> SELECT * FROM table (DBMS_XPLAN.DISPLAY);
Plan hash value: 2360715730

-------------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name                   | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |                        | 73714 |  3743K| 16452   (1)| 00:03:18 |
|   1 |  NESTED LOOPS                |                        |       |       |            |          |
|   2 |   NESTED LOOPS               |                        | 73714 |  3743K| 16452   (1)| 00:03:18 |
|*  3 |    TABLE ACCESS FULL         | CHANGE_BALANCES        |   743 | 15603 |  7172   (1)| 00:01:27 |
|*  4 |    INDEX RANGE SCAN          | IX_ACCOUNTS_ACCOUNT_ID |   104 |       |     2   (0)| 00:00:01 |
|   5 |   TABLE ACCESS BY INDEX ROWID| ACCOUNTS               |    99 |  3069 |   106   (0)| 00:00:02 |
-------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - filter(MOD("ACCOUNT_ID",1000)=0)
   4 - access("ACCOUNT_ID"="A"."ACCOUNT_ID")

Note
-----
   - dynamic sampling used for this statement (level=2)

22 rows selected.


في هذه المرة ، يتم إرفاق جدول الحسابات بجدول change_balances باستخدام حلقات متداخلة ويتم استخدام فهرس لقراءة الصفوف من الحسابات.

إذا تمت إزالة تلميح dynamic_sampling ، ففي الحالة الثانية ستبقى الخطة كما هي في الحالة الأولى ، وهذا ليس هو الأمثل.

يمكن العثور على تفاصيل حول تلميح dynamic_sampling والقيم المحتملة لوسيطته الرقمية في الوثائق.



كيفية حفظ خطة الاستعلام عند إدخال البيانات عبر ارتباط قاعدة البيانات



نحن نحل هذه المشكلة. يحتوي خادم مصدر البيانات على جداول يلزم ربطها وتحميلها في مستودع البيانات. لنفترض أن العرض مكتوب على الخادم المصدر ، والذي يحتوي على كل منطق تحويل ETL الضروري. تمت كتابة العرض على النحو الأمثل ، ويحتوي على تلميحات للمحسن تقترح كيفية ربط الجداول والفهارس التي يجب استخدامها. على جانب الخادم في مستودع البيانات ، عليك أن تفعل شيئًا بسيطًا - أدخل البيانات من العرض في الجدول الهدف. وهنا يمكن أن تنشأ صعوبات. إذا أدخلت في الجدول الهدف باستخدام أمر مثل



insert into dwh_table
  (field1, field2)
  select field1, field2 from vw_for_dwh_table@xe_link;


، ثم يمكن تجاهل كل منطق خطة الاستعلام الواردة في طريقة العرض التي نقرأ منها البيانات عبر رابط قاعدة البيانات. يمكن تجاهل كل التلميحات المضمنة في هذا العرض.



SQL> EXPLAIN PLAN
  2   SET statement_id = 'test'
  3   INTO plan_table
  4  FOR  insert into dwh_table
  5    (field1, field2)
  6    select field1, field2 from vw_for_dwh_table@xe_link;

Explained.

SQL>
SQL> SELECT * FROM table (DBMS_XPLAN.DISPLAY);
Plan hash value: 1788691278

-------------------------------------------------------------------------------------------------------------
| Id  | Operation                | Name             | Rows  | Bytes | Cost (%CPU)| Time     | Inst   |IN-OUT|
-------------------------------------------------------------------------------------------------------------
|   0 | INSERT STATEMENT         |                  |     1 |  2015 |     2   (0)| 00:00:01 |        |      |
|   1 |  LOAD TABLE CONVENTIONAL | DWH_TABLE        |       |       |            |          |        |      |
|   2 |   REMOTE                 | VW_FOR_DWH_TABLE |     1 |  2015 |     2   (0)| 00:00:01 | XE_LI~ | R->S |
-------------------------------------------------------------------------------------------------------------

Remote SQL Information (identified by operation id):
----------------------------------------------------

   2 - SELECT /*+ OPAQUE_TRANSFORM */ "FIELD1","FIELD2" FROM "VW_FOR_DWH_TABLE" "VW_FOR_DWH_TABLE"
       (accessing 'XE_LINK' )


16 rows selected.


لحفظ خطة الاستعلام في طريقة العرض ، يمكنك استخدام إدراج البيانات في الجدول الهدف من المؤشر:



declare
  cursor cr is
    select field1, field2 from vw_for_dwh_table@xe_link;
  cr_row cr%rowtype;
begin
  open cr;
  loop
    fetch cr
      into cr_row;
    insert into dwh_table
      (field1, field2)
    values
      (cr_row.field1, cr_row.field2);
    exit when cr%notfound;
  end loop;
  close cr;
end;


استعلام من المؤشر



select field1, field2 from vw_for_dwh_table@xe_link;


على عكس الإدراج



insert into dwh_table
  (field1, field2)
  select field1, field2 from vw_for_dwh_table@xe_link;


سيحفظ خطة الطلب الموضوعة في العرض على الخادم المصدر.



تشغيل الإجراءات في جلسات متوازية



غالبًا ما تكون المهمة هي بدء عدة عمليات حسابية متوازية من بعض الإجراءات الأبوية ، وبعد انتظار اكتمال كل منها ، استمر في تنفيذ الإجراء الأصلي. يمكن أن يكون هذا مفيدًا في الحوسبة المتوازية إذا سمحت موارد الخادم بذلك. هناك طرق عديدة للقيام بذلك.

دعونا نصف تنفيذًا بسيطًا جدًا لمثل هذه الآلية. سيتم تنفيذ الإجراءات الموازية في وظائف متوازية "لمرة واحدة" ، بينما سينتظر الإجراء الأصلي في حلقة لإكمال جميع هذه الوظائف.

لنقم بإنشاء جداول مع بيانات وصفية لهذه الآلية. بادئ ذي بدء ، دعنا نصنع جدولًا بمجموعات من إجراءات التشغيل المتوازي:



create table PARALLEL_PROC_GROUP_LIST
(
  group_id   INTEGER,
  group_name VARCHAR2(4000)
);
comment on column PARALLEL_PROC_GROUP_LIST.group_id
  is '    ';
comment on column PARALLEL_PROC_GROUP_LIST.group_name
  is '    ';


بعد ذلك ، سننشئ جدولًا يحتوي على نصوص سيتم تنفيذها بالتوازي في مجموعات. يمكن أن يكون ملء هذا الجدول ثابتًا أو يتم إنشاؤه ديناميكيًا:



create table PARALLEL_PROC_LIST
(
  group_id    INTEGER,
  proc_script VARCHAR2(4000),
  is_active   CHAR(1) default 'Y'
);
comment on column PARALLEL_PROC_LIST.group_id
  is '    ';
comment on column PARALLEL_PROC_LIST.proc_script
  is 'Pl/sql    ';
comment on column PARALLEL_PROC_LIST.is_active
  is 'Y - active, N - inactive.          ';


وسنقوم بعمل جدول سجل ، حيث سنجمع سجلًا بالإجراء الذي تم بدء العمل فيه:



create table PARALLEL_PROC_LOG
(
  run_id      INTEGER,
  group_id    INTEGER,
  proc_script VARCHAR2(4000),
  job_id      INTEGER,
  start_time  DATE,
  end_time    DATE
);
comment on column PARALLEL_PROC_LOG.run_id
  is '   run_in_parallel';
comment on column PARALLEL_PROC_LOG.group_id
  is '    ';
comment on column PARALLEL_PROC_LOG.proc_script
  is 'Pl/sql    ';
comment on column PARALLEL_PROC_LOG.job_id
  is 'Job_id ,      ';
comment on column PARALLEL_PROC_LOG.start_time
  is '  ';
comment on column PARALLEL_PROC_LOG.end_time
  is '  ';

create sequence Seq_Parallel_Proc_Log;


الآن دعنا نعطي رمز الإجراء لبدء التدفقات المتوازية:



create or replace procedure run_in_parallel(in_group_id integer) as
  --        parallel_proc_list.
  --  -    parallel_proc_list
  v_run_id             integer;
  v_job_id             integer;
  v_job_id_list        varchar2(32767);
  v_job_id_list_ext    varchar2(32767);
  v_running_jobs_count integer;
begin
  select seq_parallel_proc_log.nextval into v_run_id from dual;
  -- submit jobs with the same parallel_proc_list.in_group_id
  -- store seperated with ',' JOB_IDs in v_job_id_list
  v_job_id_list     := null;
  v_job_id_list_ext := null;
  for z in (select pt.proc_script
              from parallel_proc_list pt
             where pt.group_id = in_group_id
               and pt.is_active = 'Y') loop
    dbms_job.submit(v_job_id, z.proc_script);
    insert into parallel_proc_log
      (run_id, group_id, proc_script, job_id, start_time, end_time)
    values
      (v_run_id, in_group_id, z.proc_script, v_job_id, sysdate, null);
    v_job_id_list     := v_job_id_list || ',' || to_char(v_job_id);
    v_job_id_list_ext := v_job_id_list_ext || ' union all select ' ||
                         to_char(v_job_id) || ' job_id from dual';
  end loop;
  commit;
  v_job_id_list     := substr(v_job_id_list, 2);
  v_job_id_list_ext := substr(v_job_id_list_ext, 12);
  -- loop while not all jobs finished
  loop
    -- set parallel_proc_log.end_time for finished jobs
    execute immediate 'update parallel_proc_log set end_time = sysdate where job_id in (' ||
                      v_job_id_list_ext ||
                      ' minus select job from user_jobs where job in (' ||
                      v_job_id_list ||
                      ') minus select job_id from parallel_proc_log where job_id in (' ||
                      v_job_id_list || ') and end_time is not null)';
    commit;
    -- check whether all jobs finished
    execute immediate 'select count(1) from user_jobs where job in (' ||
                      v_job_id_list || ')'
      into v_running_jobs_count;
    -- if all jobs finished then exit
    exit when v_running_jobs_count = 0;
    -- sleep a little
    sys.dbms_lock.sleep(0.1);
  end loop;
end;


دعنا نتحقق من كيفية عمل الإجراء run_in_parallel. لنقم بإنشاء إجراء اختبار نسميه في جلسات متوازية.



create or replace procedure sleep(in_seconds integer) as
begin
  sys.Dbms_Lock.Sleep(in_seconds);
end;


دعنا نملأ اسم المجموعة والجدول بالبرامج النصية التي سيتم تنفيذها بالتوازي.



insert into PARALLEL_PROC_GROUP_LIST(group_id, group_name) values(1, ' ');

insert into PARALLEL_PROC_LIST(group_id, proc_script, is_active) values(1, 'begin sleep(5); end;', 'Y');
insert into PARALLEL_PROC_LIST(group_id, proc_script, is_active) values(1, 'begin sleep(10); end;', 'Y');


لنبدأ مجموعة من الإجراءات المتوازية.



begin
  run_in_parallel(1);
end;


عند الانتهاء ، دعونا نرى السجل.



select * from PARALLEL_PROC_LOG;


RUN_ID معرف مجموعة PROC_SCRIPT JOB_ID وقت البدء وقت النهاية
1 1 تبدأ النوم (5) ؛ النهاية؛ 1 09/11/2020 15:00:51 09/11/2020 15:00:56
1 1 يبدأ النوم (10) ؛ النهاية؛ 2 09/11/2020 15:00:51 09/11/2020 15:01:01


نرى أن وقت تنفيذ حالات إجراء الاختبار يلبي التوقعات.



سحب بقايا الطعام



دعونا نصف البديل لحل مشكلة مصرفية نموذجية إلى حد ما "سحب التوازن". لنفترض أن هناك جدولاً بحقائق التغييرات في أرصدة الحسابات. مطلوب للإشارة إلى رصيد الحساب الجاري لكل يوم من أيام التقويم (آخر يوم في اليوم). غالبًا ما تكون هناك حاجة إلى مثل هذه المعلومات في مستودعات البيانات. إذا لم تكن هناك حركات في العد في يوم ما ، فأنت بحاجة إلى تكرار آخر الباقي المعروف. إذا كانت كمية البيانات وقوة الحوسبة للخادم تسمح بذلك ، فيمكنك حل هذه المشكلة باستخدام استعلام SQL ، دون اللجوء إلى PL / SQL. ستساعدنا الدالة last_value (* تجاهل القيم الخالية) على (التقسيم حسب * الترتيب حسب *) في هذا الأمر ، والتي ستمتد آخر الباقي المعروف إلى التواريخ اللاحقة التي لم تحدث فيها تغييرات.

لنقم بإنشاء جدول ونملأه ببيانات الاختبار.



create table ACCOUNT_BALANCE
(
  dt           DATE,
  account_id   INTEGER,
  balance_amt  NUMBER,
  turnover_amt NUMBER
);
comment on column ACCOUNT_BALANCE.dt
  is '     ';
comment on column ACCOUNT_BALANCE.account_id
  is ' ';
comment on column ACCOUNT_BALANCE.balance_amt
  is '  ';
comment on column ACCOUNT_BALANCE.turnover_amt
  is '  ';

insert into account_balance(dt, account_id, balance_amt, turnover_amt) values(to_date('01.01.2020 00:00:00','dd.mm.yyyy hh24.mi.ss'), 1, 23, 23);
insert into account_balance(dt, account_id, balance_amt, turnover_amt) values(to_date('05.01.2020 01:00:00','dd.mm.yyyy hh24.mi.ss'), 1, 45, 22);
insert into account_balance(dt, account_id, balance_amt, turnover_amt) values(to_date('05.01.2020 20:00:00','dd.mm.yyyy hh24.mi.ss'), 1, 44, -1);
insert into account_balance(dt, account_id, balance_amt, turnover_amt) values(to_date('05.01.2020 00:00:00','dd.mm.yyyy hh24.mi.ss'), 2, 67, 67);
insert into account_balance(dt, account_id, balance_amt, turnover_amt) values(to_date('05.01.2020 20:00:00','dd.mm.yyyy hh24.mi.ss'), 2, 77, 10);
insert into account_balance(dt, account_id, balance_amt, turnover_amt) values(to_date('07.01.2020 00:00:00','dd.mm.yyyy hh24.mi.ss'), 2, 72, -5);


الاستعلام أدناه يحل مشكلتنا. يحتوي الاستعلام الفرعي "cld" على تقويم التواريخ ، في الاستعلام الفرعي "ab" نقوم بتجميع أرصدة كل يوم ، في الاستعلام الفرعي "أ" نتذكر قائمة جميع الحسابات وتاريخ بدء السجل لكل حساب ، في الاستعلام الفرعي "قبل" لكل حساب نقوم بتكوين تقويم بالأيام من بدايته قصص. يضيف الطلب الأخير الأرصدة الأخيرة لكل يوم إلى تقويم الأيام النشطة لكل حساب ويمدها إلى الأيام التي لم تكن هناك تغييرات فيها.



with cld as
 (select /*+ materialize*/
   to_date('01.01.2020', 'dd.mm.yyyy') + level - 1 dt
    from dual
  connect by level <= 10),
ab as
 (select trunc(dt) dt,
         account_id,
         max(balance_amt) keep(dense_rank last order by dt) balance_amt,
         sum(turnover_amt) turnover_amt
    from account_balance
   group by trunc(dt), account_id),
a as
 (select min(dt) min_dt, account_id from ab group by account_id),
pre as
 (select cld.dt, a.account_id from cld left join a on cld.dt >= a.min_dt)
select pre.dt,
       pre.account_id,
       last_value(ab.balance_amt ignore nulls) over(partition by pre.account_id order by pre.dt) balance_amt,
       nvl(ab.turnover_amt, 0) turnover_amt
  from pre
  left join ab
    on pre.dt = ab.dt
   and pre.account_id = ab.account_id
 order by 2, 1;


نتيجة الاستعلام كما هو متوقع.

DT ACCOUNT_ID BALANCE_AMT TURNOVER_AMT
01.01.2020 1 23 23
02.01.2020 1 23 0
03/01/2020 1 23 0
04/01/2020 1 23 0
01/05/2020 1 44 21
06.01.2020 1 44 0
07.01.2020 1 44 0
01/08/2020 1 44 0
09/01/2020 1 44 0
10.01.2020 1 44 0
01/05/2020 2 77 77
06.01.2020 2 77 0
07.01.2020 2 72 -خمسة
01/08/2020 2 72 0
09/01/2020 2 72 0
10.01.2020 2 72 0


الجمع بين عدة قصص في قصة واحدة



عند تحميل البيانات في المخازن ، غالبًا ما يتم حل المشكلة عندما تحتاج إلى إنشاء سجل واحد لكيان ما ، مع وجود سجل منفصل لسمات هذا الكيان التي تأتي من مصادر مختلفة. لنفترض أن هناك كيانًا له مفتاح أساسي primary_key_id ، والذي يُعرف عنه التاريخ (start_dt - end_dt) لسماته الثلاثة المختلفة ، الموجود في ثلاثة جداول مختلفة.



create table HIST1
(
  primary_key_id INTEGER,
  start_dt       DATE,
  attribute1     NUMBER
);

insert into HIST1(primary_key_id, start_dt, attribute1) values(1, to_date('2014-01-01','yyyy-mm-dd'), 7);
insert into HIST1(primary_key_id, start_dt, attribute1) values(1, to_date('2015-01-01','yyyy-mm-dd'), 8);
insert into HIST1(primary_key_id, start_dt, attribute1) values(1, to_date('2016-01-01','yyyy-mm-dd'), 9);
insert into HIST1(primary_key_id, start_dt, attribute1) values(2, to_date('2014-01-01','yyyy-mm-dd'), 17);
insert into HIST1(primary_key_id, start_dt, attribute1) values(2, to_date('2015-01-01','yyyy-mm-dd'), 18);
insert into HIST1(primary_key_id, start_dt, attribute1) values(2, to_date('2016-01-01','yyyy-mm-dd'), 19);

create table HIST2
(
  primary_key_id INTEGER,
  start_dt       DATE,
  attribute2     NUMBER
);
 
insert into HIST2(primary_key_id, start_dt, attribute2) values(1, to_date('2015-01-01','yyyy-mm-dd'), 4);
insert into HIST2(primary_key_id, start_dt, attribute2) values(1, to_date('2016-01-01','yyyy-mm-dd'), 5);
insert into HIST2(primary_key_id, start_dt, attribute2) values(1, to_date('2017-01-01','yyyy-mm-dd'), 6);
insert into HIST2(primary_key_id, start_dt, attribute2) values(2, to_date('2015-01-01','yyyy-mm-dd'), 14);
insert into HIST2(primary_key_id, start_dt, attribute2) values(2, to_date('2016-01-01','yyyy-mm-dd'), 15);
insert into HIST2(primary_key_id, start_dt, attribute2) values(2, to_date('2017-01-01','yyyy-mm-dd'), 16);

create table HIST3
(
  primary_key_id INTEGER,
  start_dt       DATE,
  attribute3     NUMBER
);
 
insert into HIST3(primary_key_id, start_dt, attribute3) values(1, to_date('2016-01-01','yyyy-mm-dd'), 10);
insert into HIST3(primary_key_id, start_dt, attribute3) values(1, to_date('2017-01-01','yyyy-mm-dd'), 20);
insert into HIST3(primary_key_id, start_dt, attribute3) values(1, to_date('2018-01-01','yyyy-mm-dd'), 30);
insert into HIST3(primary_key_id, start_dt, attribute3) values(2, to_date('2016-01-01','yyyy-mm-dd'), 110);
insert into HIST3(primary_key_id, start_dt, attribute3) values(2, to_date('2017-01-01','yyyy-mm-dd'), 120);
insert into HIST3(primary_key_id, start_dt, attribute3) values(2, to_date('2018-01-01','yyyy-mm-dd'), 130);


الهدف هو تحميل سجل تغيير واحد من ثلاث سمات في جدول واحد.

يوجد أدناه استعلام يحل هذه المشكلة. يقوم أولاً بتكوين جدول قطري q1 مع بيانات من مصادر مختلفة لسمات مختلفة (تمتلئ السمات الغائبة في المصدر بالقيم الخالية). بعد ذلك ، باستخدام الدالة last_value (* تجاهل القيم الخالية) ، يتم طي الجدول المائل في سجل واحد ، ويتم تمديد آخر قيم السمات المعروفة إلى التواريخ التي لم تكن هناك تغييرات فيها:



select primary_key_id,
       start_dt,
       nvl(lead(start_dt - 1)
           over(partition by primary_key_id order by start_dt),
           to_date('9999-12-31', 'yyyy-mm-dd')) as end_dt,
       last_value(attribute1 ignore nulls) over(partition by primary_key_id order by start_dt) as attribute1,
       last_value(attribute2 ignore nulls) over(partition by primary_key_id order by start_dt) as attribute2,
       last_value(attribute3 ignore nulls) over(partition by primary_key_id order by start_dt) as attribute3
  from (select primary_key_id,
               start_dt,
               max(attribute1) as attribute1,
               max(attribute2) as attribute2,
               max(attribute3) as attribute3
          from (select primary_key_id,
                       start_dt,
                       attribute1,
                       cast(null as number) attribute2,
                       cast(null as number) attribute3
                  from hist1
                union all
                select primary_key_id,
                       start_dt,
                       cast(null as number) attribute1,
                       attribute2,
                       cast(null as number) attribute3
                  from hist2
                union all
                select primary_key_id,
                       start_dt,
                       cast(null as number) attribute1,
                       cast(null as number) attribute2,
                       attribute3
                  from hist3) q1
         group by primary_key_id, start_dt) q2
 order by primary_key_id, start_dt;


النتيجة هكذا:

PRIMARY_KEY_ID START_DT END_DT السمة 1 السمة 2 السمة 3
1 01/01/2014 12/31/2014 7 لا شيء لا شيء
1 01.01.2015 31.12.2015 8 4 لا شيء
1 01/2016 31/12/2016 تسع خمسة عشرة
1 01.01.2017 31.12.2017 تسع 6 20
1 01.01.2018 31/12/1999 م تسع 6 ثلاثين
2 01/01/2014 12/31/2014 17 لا شيء لا شيء
2 01.01.2015 31.12.2015 الثامنة عشر أربعة عشرة لا شيء
2 01/2016 31/12/2016 19 خمسة عشر 110
2 01.01.2017 31.12.2017 19 السادس عشر 120
2 01.01.2018 31/12/1999 م 19 السادس عشر 130


عادي



في بعض الأحيان تنشأ مشكلة تطبيع البيانات التي تأتي في شكل حقل محدد. على سبيل المثال ، في شكل جدول مثل هذا:



create table DENORMALIZED_TABLE
(
  id  INTEGER,
  val VARCHAR2(4000)
);

insert into DENORMALIZED_TABLE(id, val) values(1, 'aaa,cccc,bb');
insert into DENORMALIZED_TABLE(id, val) values(2, 'ddd');
insert into DENORMALIZED_TABLE(id, val) values(3, 'fffff,e');


يعمل هذا الاستعلام على تسوية البيانات عن طريق لصق الحقول المرتبطة بفاصلة كسطر متعددة:



select id, regexp_substr(val, '[^,]+', 1, column_value) val, column_value
  from denormalized_table,
       table(cast(multiset
                  (select level
                     from dual
                   connect by regexp_instr(val, '[^,]+', 1, level) > 0) as
                  sys.odcinumberlist))
 order by id, column_value;


النتيجة هكذا:

هوية شخصية VAL COLUMN_VALUE
1 aaa 1
1 cccc 2
1 ب 3
2 ddd 1
3 fffff 1
3 ه 2


التقديم بتنسيق SVG



غالبًا ما تكون هناك رغبة في تصور المؤشرات الرقمية المخزنة في قاعدة البيانات بطريقة ما. على سبيل المثال ، قم بإنشاء الرسوم البيانية والرسوم البيانية والمخططات. يمكن أن تساعد الأدوات المتخصصة مثل Oracle BI. ولكن قد تكلف تراخيص هذه الأدوات أموالاً ، وقد يستغرق إعدادها وقتًا أطول من كتابة استعلام SQL "على الركبة" إلى Oracle ، والذي سيعيد الصورة النهائية. دعنا نوضح بمثال كيفية رسم مثل هذه الصورة بسرعة بتنسيق SVG باستخدام استعلام.

افترض أن لدينا جدولاً بالبيانات



create table graph_data(dt date, val number, radius number);

insert into graph_data(dt, val, radius) values (to_date('01.01.2020','dd.mm.yyyy'), 12, 3);
insert into graph_data(dt, val, radius) values (to_date('02.01.2020','dd.mm.yyyy'), 15, 4);
insert into graph_data(dt, val, radius) values (to_date('05.01.2020','dd.mm.yyyy'), 17, 5);
insert into graph_data(dt, val, radius) values (to_date('06.01.2020','dd.mm.yyyy'), 13, 6);
insert into graph_data(dt, val, radius) values (to_date('08.01.2020','dd.mm.yyyy'),  3, 7);
insert into graph_data(dt, val, radius) values (to_date('10.01.2020','dd.mm.yyyy'), 20, 8);
insert into graph_data(dt, val, radius) values (to_date('11.01.2020','dd.mm.yyyy'), 18, 9);


dt هو تاريخ الصلة ،

val هو مؤشر رقمي ، ديناميكيات نتخيلها بمرور الوقت ،

نصف القطر هو مؤشر رقمي آخر سنرسمه في شكل دائرة بنصف قطر كهذا.

دعنا نقول بضع كلمات عن تنسيق SVG. إنه تنسيق رسومات متجه يمكن عرضه في المتصفحات الحديثة وتحويله إلى تنسيقات رسوم أخرى. في ذلك ، من بين أشياء أخرى ، يمكنك رسم خطوط ودوائر وكتابة نص:



<line x1="94" x2="94" y1="15" y2="675" style="stroke:rgb(150,255,255); stroke-width:1px"/>
<circle cx="30" cy="279" r="3" style="fill:rgb(255,0,0)"/>
<text x="7" y="688" font-size="10" fill="rgb(0,150,255)">2020-01-01</text>


يوجد أدناه استعلام SQL إلى Oracle الذي يرسم رسمًا بيانيًا من البيانات الموجودة في هذا الجدول. هنا يحتوي الاستعلام الفرعي const على إعدادات ثابتة متنوعة - حجم الصورة ، وعدد الملصقات على محاور الرسم البياني ، وألوان الخطوط والدوائر ، وأحجام الخطوط ، إلخ. في طلب البحث الفرعي gd1 ، نقوم بتحويل البيانات من جدول Graph_data إلى إحداثيات x و y في الشكل. يتذكر طلب البحث الفرعي gd2 النقاط السابقة في الوقت المناسب ، والتي يجب من خلالها رسم الخطوط إلى نقاط جديدة. كتلة "الرأس" هي رأس الصورة بخلفية بيضاء. كتلة "الخطوط العمودية" ترسم خطوطًا عمودية. تواريخ تسميات الكتل "التواريخ تحت الأسطر الرأسية" على المحور السيني. كتلة "الخطوط الأفقية" ترسم خطوطًا أفقية. تسمي كتلة "القيم بالقرب من الخطوط الأفقية" القيم الموجودة على المحور ص. ترسم كتلة "الدوائر" دوائر نصف القطر المحدد في جدول Graph_data.تُنشئ كتلة "بيانات الرسم البياني" رسمًا بيانيًا لديناميكيات مؤشر val من جدول بيانات الرسم البياني من الخطوط. تضيف كتلة "التذييل" علامة لاحقة.



with const as
 (select 700 viewbox_width,
         700 viewbox_height,
         30 left_margin,
         30 right_margin,
         15 top_margin,
         25 bottom_margin,
         max(dt) - min(dt) + 1 num_vertical_lines,
         11 num_horizontal_lines,
         'rgb(150,255,255)' stroke_vertical_lines,
         '1px' stroke_width_vertical_lines,
         10 font_size_dates,
         'rgb(0,150,255)' fill_dates,
         23 x_dates_pad,
         13 y_dates_pad,
         'rgb(150,255,255)' stroke_horizontal_lines,
         '1px' stroke_width_horizontal_lines,
         10 font_size_values,
         'rgb(0,150,255)' fill_values,
         4 x_values_pad,
         2 y_values_pad,
         'rgb(255,0,0)' fill_circles,
         'rgb(51,102,0)' stroke_graph,
         '1px' stroke_width_graph,
         min(dt) min_dt,
         max(dt) max_dt,
         max(val) max_val
    from graph_data),
gd1 as
 (select graph_data.dt,
         const.left_margin +
         (const.viewbox_width - const.left_margin - const.right_margin) *
         (graph_data.dt - const.min_dt) / (const.max_dt - const.min_dt) x,
         const.viewbox_height - const.bottom_margin -
         (const.viewbox_height - const.top_margin - const.bottom_margin) *
         graph_data.val / const.max_val y,
         graph_data.radius
    from graph_data, const),
gd2 as
 (select dt,
         round(nvl(lag(x) over(order by dt), x)) prev_x,
         round(x) x,
         round(nvl(lag(y) over(order by dt), y)) prev_y,
         round(y) y,
         radius
    from gd1)
/* header */
select '<?xml version="1.0" encoding="UTF-8" standalone="no"?>' txt
  from dual
union all
select '<svg version="1.1" width="' || viewbox_width || '" height="' ||
       viewbox_height || '" viewBox="0 0 ' || viewbox_width || ' ' ||
       viewbox_height ||
       '" style="background:yellow" baseProfile="full" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events">'
  from const
union all
select '<title>Test graph</title>'
  from dual
union all
select '<desc>Test graph</desc>'
  from dual
union all
select '<rect width="' || viewbox_width || '" height="' || viewbox_height ||
       '" style="fill:white" />'
  from const
union all
/* vertical lines */
select '<line x1="' ||
       to_char(round(left_margin +
                     (viewbox_width - left_margin - right_margin) *
                     (level - 1) / (num_vertical_lines - 1))) || '" x2="' ||
       to_char(round(left_margin +
                     (viewbox_width - left_margin - right_margin) *
                     (level - 1) / (num_vertical_lines - 1))) || '" y1="' ||
       to_char(round(top_margin)) || '" y2="' ||
       to_char(round(viewbox_height - bottom_margin)) || '" style="stroke:' ||
       const.stroke_vertical_lines || '; stroke-width:' ||
       const.stroke_width_vertical_lines || '"/>'
  from const
connect by level <= num_vertical_lines
union all
/* dates under vertical lines */
select '<text x="' ||
       to_char(round(left_margin +
                     (viewbox_width - left_margin - right_margin) *
                     (level - 1) / (num_vertical_lines - 1) - x_dates_pad)) ||
       '" y="' ||
       to_char(round(viewbox_height - bottom_margin + y_dates_pad)) ||
       '" font-size="' || font_size_dates || '" fill="' || fill_dates || '">' ||
       to_char(min_dt + level - 1, 'yyyy-mm-dd') || '</text>'
  from const
connect by level <= num_vertical_lines
union all
/* horizontal lines */
select '<line x1="' || to_char(round(left_margin)) || '" x2="' ||
       to_char(round(viewbox_width - right_margin)) || '" y1="' ||
       to_char(round(top_margin +
                     (viewbox_height - top_margin - bottom_margin) *
                     (level - 1) / (num_horizontal_lines - 1))) || '" y2="' ||
       to_char(round(top_margin +
                     (viewbox_height - top_margin - bottom_margin) *
                     (level - 1) / (num_horizontal_lines - 1))) ||
       '" style="stroke:' || const.stroke_horizontal_lines ||
       '; stroke-width:' || const.stroke_width_horizontal_lines || '"/>'
  from const
connect by level <= num_horizontal_lines
union all
/* values near horizontal lines */
select '<text text-anchor="end" x="' ||
       to_char(round(left_margin - x_values_pad)) || '" y="' ||
       to_char(round(viewbox_height - bottom_margin -
                     (viewbox_height - top_margin - bottom_margin) *
                     (level - 1) / (num_horizontal_lines - 1) +
                     y_values_pad)) || '" font-size="' || font_size_values ||
       '" fill="' || fill_values || '">' ||
       to_char(round(max_val / (num_horizontal_lines - 1) * (level - 1), 2)) ||
       '</text>'
  from const
connect by level <= num_horizontal_lines
union all
/* circles */
select '<circle cx="' || to_char(gd2.x) || '" cy="' || to_char(gd2.y) ||
       '" r="' || gd2.radius || '" style="fill:' || const.fill_circles ||
       '"/>'
  from gd2, const
union all
/* graph data */
select '<line x1="' || to_char(gd2.prev_x) || '" x2="' || to_char(gd2.x) ||
       '" y1="' || to_char(gd2.prev_y) || '" y2="' || to_char(gd2.y) ||
       '" style="stroke:' || const.stroke_graph || '; stroke-width:' ||
       const.stroke_width_graph || '"/>'
  from gd2, const
union all
/* footer */
select '</svg>' from dual;


يمكن حفظ نتيجة الاستعلام في ملف بامتداد * .svg وعرضها في المستعرض. إذا رغبت في ذلك ، يمكنك استخدام أي من الأدوات المساعدة لتحويلها إلى تنسيقات رسوم أخرى ، ووضعها على صفحات الويب الخاصة بتطبيقك ، وما إلى ذلك.

والنتيجة هي الصورة التالية:







تطبيق Oracle Metadata Search



تخيل محاولة العثور على شيء ما في التعليمات البرمجية المصدر على Oracle من خلال البحث عن معلومات على عدة خوادم في وقت واحد. يتعلق هذا بالبحث خلال كائنات قاموس بيانات Oracle. مكان العمل للبحث هو واجهة الويب ، حيث يقوم مبرمج المستخدم بإدخال سلسلة البحث وتحديد مربعات الاختيار على خوادم Oracle لإجراء هذا البحث.

محرك بحث الويب قادر على البحث عن صف في كائنات خادم أوراكل في وقت واحد في عدة قواعد بيانات مختلفة للبنك. على سبيل المثال ، يمكنك البحث عن:

  • Oracle 61209, ?
  • accounts ( .. database link)?
  • , , ORA-20001 “ ”?
  • IX_CLIENTID - SQL-?
  • - ( .. database link) , , , ..?
  • - - ? .
  • Oracle ? , wm_concat Oracle. .
  • - , , ? , Oracle sys_connect_by_path, regexp_instr push_subq.


بناءً على نتائج البحث ، يتم إعطاء المستخدم معلومات حول أي خادم في رمز الوظائف ، والإجراءات ، والحزم ، والمشغلات ، والعروض ، وما إلى ذلك. وجدت النتائج المطلوبة.

دعنا نصف كيف يتم تنفيذ محرك البحث هذا.



جانب العميل ليس معقدًا. تتلقى واجهة الويب سلسلة البحث التي أدخلها المستخدم ، وقائمة الخوادم المراد البحث عنها ، وتسجيل دخول المستخدم. تمررهم صفحة الويب إلى إجراء أوراكل المخزن على خادم المعالج. تاريخ الطلبات إلى محرك البحث ، أي الذي نفذ الطلب الذي تم تسجيله فقط في حالة.



بعد تلقي استعلام بحث ، يقوم جانب الخادم على خادم بحث أوراكل بتنفيذ العديد من الإجراءات في وظائف متوازية تقوم بمسح طرق عرض قاموس البيانات التالية على روابط قاعدة البيانات على خوادم أوراكل المحددة بحثًا عن السلسلة المطلوبة: ، dba_views. كل من الإجراءات ، إذا وجدت شيئًا ما ، يكتب ما تم العثور عليه في جدول نتائج البحث (مع معرف استعلام البحث المقابل).



عند الانتهاء من جميع إجراءات البحث ، يمنح جزء العميل المستخدم كل ما هو مكتوب في جدول نتائج البحث مع معرف استعلام البحث المقابل.

لكن هذا ليس كل شيء. بالإضافة إلى البحث في قاموس بيانات Oracle ، تم أيضًا ربط البحث في مستودع Informatica PowerCenter في الآلية الموضحة. Informatica PowerCenter هي أداة ETL شائعة يستخدمها Sberbank لتحميل معلومات متنوعة في مستودعات البيانات. لدى Informatica PowerCenter بنية مستودع مفتوحة وموثقة جيدًا. في هذا المستودع ، من الممكن البحث عن المعلومات بنفس الطريقة المستخدمة في قاموس بيانات أوراكل. ما هي الجداول والحقول المستخدمة في كود التنزيل الذي تم تطويره باستخدام Informatica PowerCenter؟ ما الذي يمكن العثور عليه في تحويلات المنفذ واستعلامات SQL الصريحة؟ كل هذه المعلومات متوفرة في هياكل المستودع ويمكن العثور عليها. لخبراء PowerCenter ، سأكتب أن محرك البحث الخاص بنا يقوم بمسح مواقع المستودعات التالية بحثًا عن التعيينات أو الجلسات أو سير العمل ،تحتوي على سلسلة البحث في مكان ما: sql override ، سمات mapplet ، المنافذ ، تعريفات المصدر في التعيينات ، تعريفات المصدر ، تعريفات الهدف في التعيينات ، target_definitions ، التعيينات ، mapplets ، سير العمل ، worklets ، الجلسات ، الأوامر ، منافذ التعبير ، مثيلات الجلسة ، حقول تعريف المصدر ، حقول تعريف الهدف ، مهام البريد الإلكتروني.



: , SberProfi DWH/BigData.



SberProfi DWH/BigData , Hadoop, Teradata, Oracle DB, GreenPlum, BI Qlik, SAP BO, Tableau .



All Articles