- Automatische Kompilierungsprotokollierung
- Was tun, wenn Sie eine Ansicht mit Parametern erstellen möchten?
- Verwenden dynamischer Statistiken in Abfragen
- So speichern Sie den Abfrageplan beim Einfügen von Daten über eine Datenbankverbindung
- Ausführen von Prozeduren in parallelen Sitzungen
- Reste durchziehen
- Mehrere Geschichten zu einer kombinieren
- Normalisierer
- Rendern im SVG-Format
- Oracle Metadata Search-Anwendung
Automatische Kompilierungsprotokollierung
In einigen Oracle-Datenbanken verfügt Sberbank über einen Kompilierungsauslöser, der merkt, wer, wann und was sich im Code von Serverobjekten geändert hat. Somit kann der Autor der Änderungen aus der Kompilierungsprotokolltabelle ermittelt werden. Ein Versionskontrollsystem wird ebenfalls automatisch implementiert. Wenn der Programmierer vergessen hat, die Änderungen an Git zu senden, wird dieser Mechanismus in jedem Fall abgesichert. Beschreiben wir ein Beispiel für die Implementierung eines solchen Systems der automatischen Kompilierungsprotokollierung. Eine der vereinfachten Versionen des Kompilierungsauslösers, der in Form der Tabelle ddl_changes_log in das Protokoll schreibt, sieht folgendermaßen aus:
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;
In diesem Trigger werden der Name und der neue Inhalt des kompilierten Objekts abgerufen, mit dem vorherigen Inhalt aus dem Datenwörterbuch ergänzt und in das Änderungsprotokoll geschrieben.
Was tun, wenn Sie eine Ansicht mit Parametern erstellen möchten?
Ein solcher Wunsch kann oft von einem Entwickler bei Oracle besucht werden. Warum ist es möglich, eine Prozedur oder Funktion mit Parametern zu erstellen, aber es gibt keine Ansichten mit Eingabeparametern, die für Berechnungen verwendet werden können? Oracle hat etwas, um dieses unserer Meinung nach fehlende Konzept zu ersetzen.
Schauen wir uns ein Beispiel an. Für jeden Tag soll es eine Tabelle mit Verkäufen nach Geschäftsbereichen geben.
create table DIVISION_SALES
(
division_id INTEGER,
dt DATE,
sales_amt NUMBER
);
Diese Abfrage vergleicht die Verkäufe nach Abteilungen über zwei Tage. In diesem Fall 30.04.2020 und 11.09.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;
Hier ist eine Ansicht, die ich schreiben möchte, um eine solche Anfrage zusammenzufassen. Ich möchte Daten als Parameter übergeben. Die Syntax lässt dies jedoch nicht zu.
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;
Eine solche Problemumgehung wird vorgeschlagen. Erstellen wir aus dieser Ansicht einen Typ für die Linie.
create type t_division_sales_report as object
(
division_id INTEGER,
dt1 DATE,
dt2 DATE,
sales_amt1 NUMBER,
sales_amt2 NUMBER
);
Und wir werden aus solchen Zeichenfolgen einen Typ für eine Tabelle erstellen.
create type t_division_sales_report_table as table of t_division_sales_report;
Schreiben wir anstelle einer Ansicht eine Pipeline-Funktion mit Datumseingabeparametern.
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;
Sie können sich so darauf beziehen:
select *
from table(func_division_sales(to_date('30.04.2020', 'dd.mm.yyyy'),
to_date('11.09.2020', 'dd.mm.yyyy')));
Diese Anfrage liefert das gleiche Ergebnis wie die Anfrage am Anfang dieses Beitrags mit explizit ersetzten Daten.
Pipeline-Funktionen können auch nützlich sein, wenn Sie einen Parameter innerhalb einer komplexen Anforderung übergeben müssen.
Stellen Sie sich beispielsweise eine komplexe Ansicht vor, in der das Feld1, nach dem Sie Daten filtern möchten, irgendwo tief in der Ansicht versteckt ist.
create or replace view complex_view as
select field1, ...
from (select field1, ...
from (select field1, ... from deep_table), table1
where ...),
table2
where ...;
Und eine Abfrage aus einer Ansicht mit dem festen Wert von Feld1 hat möglicherweise einen schlechten Ausführungsplan.
select field1, ... from complex_view
where field1 = 'myvalue';
Jene. Anstatt deep_table zuerst nach dem Bedingungsfeld1 = 'myvalue' zu filtern, kann die Abfrage zuerst alle Tabellen verbinden, eine unnötig große Datenmenge verarbeiten und dann das Ergebnis nach dem Bedingungsfeld1 = 'myvalue' filtern. Diese Komplexität kann vermieden werden, indem anstelle einer Pipeline-Ansicht eine Funktion mit einem Parameter erstellt wird, der Feld1 zugewiesen ist.
Verwenden dynamischer Statistiken in Abfragen
Es kommt vor, dass dieselbe Abfrage in der Oracle-Datenbank jedes Mal eine andere Datenmenge in den darin verwendeten Tabellen und Unterabfragen verarbeitet. Wie kann der Optimierer herausfinden, wie diesmal Tabellen verknüpft werden sollen und welche Indizes jedes Mal verwendet werden sollen? Stellen Sie sich beispielsweise eine Abfrage vor, die einen Teil der Kontostände, die sich seit dem letzten Laden geändert haben, mit dem Kontoverzeichnis verbindet. Der Teil des geänderten Kontostands variiert stark von Download zu Download und beläuft sich auf Hunderte von Zeilen, manchmal Millionen von Zeilen. Abhängig von der Größe dieses Teils ist es erforderlich, die geänderten Salden mit Konten entweder nach der Methode / * + use_nl * / oder nach der Methode / * + use_hash * / zu kombinieren. Es ist unpraktisch, Statistiken jedes Mal neu zu erfassen, insbesondere wenn sich die Anzahl der Zeilen von Laden zu Laden nicht in der verknüpften Tabelle, sondern in der verknüpften Unterabfrage ändert.Der Hinweis / * + dynamic_sampling () * / kann hier Abhilfe schaffen. Lassen Sie uns anhand einer Beispielanforderung zeigen, wie sich dies auswirkt. Lassen Sie die Tabelle change_balances die Änderungen an Salden und Konten enthalten - dem Verzeichnis der Konten. Wir verbinden diese Tabellen durch die Felder account_id, die in jeder der Tabellen verfügbar sind. Zu Beginn des Experiments werden wir weitere Zeilen in diese Tabellen schreiben und deren Inhalt nicht ändern.
Nehmen wir zunächst 10% der Änderungen in den Residuen in der Tabelle change_balances und sehen, wie der Plan dynamic_sampling verwendet:
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.
Wir sehen also, dass vorgeschlagen wird, die Tabellen change_balances und Accounts mit einem vollständigen Scan zu durchsuchen und sie mit einem Hash-Join zu verbinden.
Lassen Sie uns nun die Stichprobe von change_balances drastisch reduzieren. Nehmen wir 0,1% der verbleibenden Änderungen und sehen, wie der Plan dynamic_sampling verwendet:
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.
Dieses Mal wird die Kontentabelle mit verschachtelten Schleifen an die Tabelle change_balances angehängt, und ein Index wird zum Lesen von Zeilen aus Konten verwendet.
Wenn der Hinweis dynamic_sampling entfernt wird, bleibt der Plan im zweiten Fall derselbe wie im ersten Fall, und dies ist nicht optimal.
Details zum Hinweis dynamic_sampling und mögliche Werte für das numerische Argument finden Sie in der Dokumentation.
So speichern Sie den Abfrageplan beim Einfügen von Daten über eine Datenbankverbindung
Wir lösen dieses Problem. Der Datenquellenserver verfügt über Tabellen, die verknüpft und in das Data Warehouse geladen werden müssen. Angenommen, auf dem Quellserver wird eine Ansicht geschrieben, die alle erforderlichen ETL-Transformationslogiken enthält. Die Ansicht ist optimal geschrieben und enthält Hinweise für das Optimierungsprogramm, die vorschlagen, wie Tabellen verknüpft werden und welche Indizes verwendet werden sollen. Auf der Serverseite des Data Warehouse müssen Sie eine einfache Sache tun: Fügen Sie die Daten aus der Ansicht in die Zieltabelle ein. Und hier können Schwierigkeiten auftreten. Wenn Sie mit einem Befehl wie in die Zieltabelle einfügen
insert into dwh_table
(field1, field2)
select field1, field2 from vw_for_dwh_table@xe_link;
Dann kann die gesamte Logik des Abfrageplans, die in der Ansicht enthalten ist, aus der wir Daten über die Datenbankverbindung lesen, ignoriert werden. Alle in diese Ansicht eingebetteten Hinweise können ignoriert werden.
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.
Um den Abfrageplan in der Ansicht zu speichern, können Sie Daten über den Cursor in die Zieltabelle einfügen:
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;
Abfrage vom Cursor
select field1, field2 from vw_for_dwh_table@xe_link;
im Gegensatz zu einfügen
insert into dwh_table
(field1, field2)
select field1, field2 from vw_for_dwh_table@xe_link;
speichert den Plan der Anforderung, der in der Ansicht auf dem Quellserver abgelegt ist.
Ausführen von Prozeduren in parallelen Sitzungen
Oft besteht die Aufgabe darin, mehrere parallele Berechnungen von einer übergeordneten Prozedur aus zu starten und die übergeordnete Prozedur auszuführen, nachdem Sie darauf gewartet haben, dass jede abgeschlossen ist. Dies kann beim parallelen Rechnen nützlich sein, wenn die Serverressourcen dies zulassen. Es gibt viele Möglichkeiten, dies zu tun.
Beschreiben wir eine sehr einfache Implementierung eines solchen Mechanismus. Parallele Prozeduren werden in parallelen "einmaligen" Jobs ausgeführt, während die übergeordnete Prozedur in einer Schleife auf den Abschluss all dieser Jobs wartet.
Erstellen wir Tabellen mit Metadaten für diesen Mechanismus. Lassen Sie uns zunächst eine Tabelle mit Gruppen parallel laufender Prozeduren erstellen:
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 ' ';
Als Nächstes erstellen wir eine Tabelle mit Skripten, die parallel in Gruppen ausgeführt werden. Das Füllen dieser Tabelle kann entweder statisch oder dynamisch erstellt werden:
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. ';
Und wir werden eine Protokolltabelle erstellen, in der wir ein Protokoll darüber sammeln, welche Prozedur in welchem Job gestartet wurde:
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;
Geben wir nun den Code der Prozedur zum Starten paralleler Streams an:
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;
Lassen Sie uns überprüfen, wie die Prozedur run_in_parallel funktioniert. Erstellen wir eine Testprozedur, die wir in parallelen Sitzungen aufrufen.
create or replace procedure sleep(in_seconds integer) as
begin
sys.Dbms_Lock.Sleep(in_seconds);
end;
Geben Sie den Namen der Gruppe und die Tabelle mit Skripten ein, die parallel ausgeführt werden.
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');
Beginnen wir mit einer Gruppe paralleler Prozeduren.
begin
run_in_parallel(1);
end;
Wenn Sie fertig sind, sehen wir uns das Protokoll an.
select * from PARALLEL_PROC_LOG;
| RUN_ID | GRUPPEN-ID | PROC_SCRIPT | JOB-ID | STARTZEIT | END_TIME |
| 1 | 1 | Schlaf beginnen (5); Ende; | 1 | 09/11/2020 15:00:51 | 09/11/2020 15:00:56 |
| 1 | 1 | Schlaf beginnen (10); Ende; | 2 | 09/11/2020 15:00:51 | 09/11/2020 15:01:01 |
Wir sehen, dass die Ausführungszeit der Testprozedurinstanzen den Erwartungen entspricht.
Reste durchziehen
Beschreiben wir eine Variante zur Lösung eines ziemlich typischen Bankproblems, bei dem es darum geht, das Gleichgewicht zu halten. Angenommen, es gibt eine Tabelle mit Fakten zu Änderungen der Kontensalden. Es ist erforderlich, den aktuellen Kontostand für jeden Tag des Kalenders anzugeben (den letzten für den Tag). Solche Informationen werden häufig in Data Warehouses benötigt. Wenn an einem Tag keine Bewegungen in der Zählung aufgetreten sind, müssen Sie den letzten bekannten Rest wiederholen. Wenn die Datenmenge und die Rechenleistung des Servers dies zulassen, können Sie dieses Problem mithilfe einer SQL-Abfrage lösen, ohne auf PL / SQL zurückgreifen zu müssen. Die Funktion last_value (* Nullen ignorieren) over (Partition nach * order by *) hilft uns dabei, wodurch der letzte bekannte Rest auf nachfolgende Daten ausgedehnt wird, an denen keine Änderungen vorgenommen wurden.
Erstellen wir eine Tabelle und füllen sie mit Testdaten.
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);
Die folgende Abfrage löst unser Problem. Die Unterabfrage 'cld' enthält den Datumskalender, in der Unterabfrage 'ab' gruppieren wir die Salden für jeden Tag, in der Unterabfrage 'a' merken wir uns die Liste aller Konten und das Startdatum des Verlaufs für jedes Konto, in der Unterabfrage 'pre' für jedes Konto erstellen wir einen Kalender von Tagen ab dem Beginn Geschichten. Die letzte Anforderung fügt die letzten Salden für jeden Tag zum Kalender der aktiven Tage jedes Kontos hinzu und erweitert sie auf die Tage, an denen keine Änderungen vorgenommen wurden.
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;
Das Ergebnis der Abfrage ist wie erwartet.
| 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 | -fünf |
| 01.08.2020 | 2 | 72 | 0 |
| 09/01/2020 | 2 | 72 | 0 |
| 10.01.2020 | 2 | 72 | 0 |
Mehrere Geschichten zu einer kombinieren
Beim Laden von Daten in Speicher wird das Problem häufig gelöst, wenn Sie einen einzelnen Verlauf für eine Entität erstellen müssen, der einen separaten Verlauf der Attribute dieser Entität aufweist, die aus verschiedenen Quellen stammen. Angenommen, es gibt eine Entität mit dem Primärschlüssel primary_key_id, über die der Verlauf (start_dt - end_dt) seiner drei verschiedenen Attribute bekannt ist und der sich in drei verschiedenen Tabellen befindet.
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);
Ziel ist es, einen einzelnen Änderungsverlauf von drei Attributen in eine Tabelle zu laden.
Unten finden Sie eine Abfrage, die dieses Problem löst. Es bildet zunächst eine Diagonaltabelle q1 mit Daten aus verschiedenen Quellen für verschiedene Attribute (Attribute, die in der Quelle fehlen, werden mit Nullen gefüllt). Mit der Funktion last_value (* ignore nulls) wird die Diagonaltabelle dann zu einem einzigen Verlauf zusammengefasst, und die letzten bekannten Attributwerte werden auf die Daten erweitert, an denen keine Änderungen vorgenommen wurden:
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;
Das Ergebnis ist wie folgt:
| PRIMARY_KEY_ID | START_DT | END_DT | ATTRIBUTE1 | ATTRIBUTE2 | ATTRIBUTE3 |
| 1 | 01/01/2014 | 31.12.2014 | 7 | NULL | NULL |
| 1 | 01.01.2015 | 31.12.2015 | 8 | 4 | NULL |
| 1 | 01/01/2016 | 31.12.2016 | neun | fünf | zehn |
| 1 | 01.01.2017 | 31.12.2017 | neun | 6 | 20 |
| 1 | 01.01.2018 | 31.12.9999 | neun | 6 | dreißig |
| 2 | 01/01/2014 | 31.12.2014 | 17 | NULL | NULL |
| 2 | 01.01.2015 | 31.12.2015 | achtzehn | vierzehn | NULL |
| 2 | 01/01/2016 | 31.12.2016 | 19 | fünfzehn | 110 |
| 2 | 01.01.2017 | 31.12.2017 | 19 | Sechszehn | 120 |
| 2 | 01.01.2018 | 31.12.9999 | 19 | Sechszehn | 130 |
Normalisierer
Manchmal tritt das Problem auf, Daten zu normalisieren, die im Format eines begrenzten Feldes vorliegen. Zum Beispiel in Form einer Tabelle wie dieser:
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');
Diese Abfrage normalisiert die Daten, indem die durch Kommas verknüpften Felder als mehrere Zeilen eingefügt werden:
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;
Das Ergebnis ist wie folgt:
| ICH WÜRDE | VAL | COLUMN_VALUE |
| 1 | aaa | 1 |
| 1 | cccc | 2 |
| 1 | bb | 3 |
| 2 | ddd | 1 |
| 3 | fffff | 1 |
| 3 | e | 2 |
Rendern im SVG-Format
Oft besteht der Wunsch, die in der Datenbank gespeicherten numerischen Indikatoren irgendwie zu visualisieren. Erstellen Sie beispielsweise Diagramme, Histogramme und Diagramme. Spezialisierte Tools wie Oracle BI können helfen. Lizenzen für diese Tools können jedoch Geld kosten, und das Einrichten kann mehr Zeit in Anspruch nehmen als das Schreiben einer SQL-Abfrage "on the Knie" an Oracle, die das fertige Bild zurückgibt. Lassen Sie uns anhand eines Beispiels zeigen, wie Sie ein solches Bild mithilfe einer Abfrage schnell im SVG-Format zeichnen können.
Angenommen, wir haben eine Tabelle mit Daten
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 ist das Datum der Relevanz,
val ist ein numerischer Indikator, dessen Dynamik wir über die Zeit visualisieren,
Radius ist ein weiterer numerischer Indikator, den wir in Form eines Kreises mit einem solchen Radius zeichnen werden.
Lassen Sie uns ein paar Worte zum SVG-Format sagen. Es ist ein Vektorgrafikformat, das in modernen Browsern angezeigt und in andere Grafikformate konvertiert werden kann. Darin können Sie unter anderem Linien, Kreise zeichnen und Text schreiben:
<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>
Im Folgenden finden Sie eine SQL-Abfrage an Oracle, in der ein Diagramm aus den Daten in dieser Tabelle dargestellt wird. Hier enthält die const-Unterabfrage verschiedene konstante Einstellungen - Bildgröße, Anzahl der Beschriftungen auf den Diagrammachsen, Linien- und Kreisfarben, Schriftgrößen usw. In der Unterabfrage gd1 konvertieren wir die Daten aus der Tabelle graph_data in die x- und y-Koordinaten in der Abbildung. Die Unterabfrage gd2 merkt sich die vorherigen Zeitpunkte, von denen aus die Linien zu neuen Punkten gezogen werden sollen. Der 'Header'-Block ist der Header des Bildes mit weißem Hintergrund. Der Block 'Vertikale Linien' zeichnet vertikale Linien. Die Blockbezeichnungen "Daten unter vertikalen Linien" befinden sich auf der x-Achse. Der Block 'Horizontale Linien' zeichnet horizontale Linien. Der Block 'Werte in der Nähe horizontaler Linien' kennzeichnet die Werte auf der y-Achse. Der Block 'Kreise' zeichnet Kreise mit dem angegebenen Radius in der Tabelle graph_data.Der Block 'graph data' erstellt aus der Tabelle graph_data ein Diagramm der Dynamik des Wertindikators. Der 'Fußzeilen'-Block fügt ein nachfolgendes Tag hinzu.
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;
Das Abfrageergebnis kann in einer Datei mit der Erweiterung * .svg gespeichert und in einem Browser angezeigt werden. Wenn Sie möchten, können Sie jedes der Dienstprogramme verwenden, um es in andere Grafikformate zu konvertieren, es auf den Webseiten Ihrer Anwendung zu platzieren usw.
Das Ergebnis ist das folgende Bild:
Oracle Metadata Search-Anwendung
Stellen Sie sich vor, Sie versuchen, etwas im Quellcode von Oracle zu finden, indem Sie Informationen auf mehreren Servern gleichzeitig anzeigen. Hier geht es um das Durchsuchen von Oracle-Datenwörterbuchobjekten. Der Arbeitsplatz für die Suche ist die Weboberfläche, über die der Benutzerprogrammierer die Suchzeichenfolge eingibt und die Kontrollkästchen aktiviert, auf denen Oracle-Server diese Suche durchführen sollen.
Die Websuchmaschine kann in Oracle-Serverobjekten gleichzeitig in mehreren verschiedenen Datenbanken der Bank nach einer Zeile suchen. Zum Beispiel können Sie suchen nach:
- 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.
Basierend auf den Suchergebnissen erhält der Benutzer Informationen darüber, auf welchem Server im Code welche Funktionen, Prozeduren, Pakete, Trigger, Ansichten usw. vorhanden sind. fand die erforderlichen Ergebnisse.
Beschreiben wir, wie eine solche Suchmaschine implementiert wird.
Die Client-Seite ist nicht kompliziert. Die Weboberfläche empfängt die vom Benutzer eingegebene Suchzeichenfolge, die Liste der zu suchenden Server und die Anmeldung des Benutzers. Die Webseite übergibt sie an eine auf dem Handler-Server gespeicherte Oracle-Prozedur. Der Verlauf von Anfragen an die Suchmaschine, d.h. Wer welche Anfrage ausgeführt hat, wird für alle Fälle protokolliert.
Nach dem Empfang einer Suchabfrage führt die Serverseite auf dem Oracle-Suchserver mehrere Prozeduren in parallelen Jobs aus, die die folgenden Datenwörterbuchansichten für die Datenbankverknüpfungen auf den ausgewählten Oracle-Servern nach der erforderlichen Zeichenfolge durchsuchen: dba_col_comments, dba_jobs, dba_mviews, dba_objects, dba_scheduler_jobs, dba_tabsource, dba_scheduler_obs , dba_views. Jede der Prozeduren schreibt, wenn etwas gefunden wurde, das gefundene in die Suchergebnistabelle (mit der entsprechenden Suchabfrage-ID).
Wenn alle Suchvorgänge abgeschlossen sind, gibt der Client-Teil dem Benutzer alles, was in die Suchergebnistabelle geschrieben ist, mit der entsprechenden Suchabfrage-ID.
Aber das ist nicht alles. Neben der Suche im Oracle-Datenwörterbuch wurde auch die Suche im Informatica PowerCenter-Repository in den beschriebenen Mechanismus integriert. Informatica PowerCenter ist ein beliebtes ETL-Tool, mit dem die Sberbank verschiedene Informationen in Data Warehouses lädt. Informatica PowerCenter verfügt über eine gut dokumentierte Open Source-Repository-Struktur. In diesem Repository können Sie auf dieselbe Weise wie im Oracle-Datenwörterbuch nach Informationen suchen. Welche Tabellen und Felder werden in dem mit Informatica PowerCenter entwickelten Download-Code verwendet? Was finden Sie in Porttransformationen und expliziten SQL-Abfragen? Alle diese Informationen sind in den Strukturen des Repositorys verfügbar und können gefunden werden. Für PowerCenter-Kenner schreibe ich, dass unsere Suchmaschine die folgenden Repository-Speicherorte nach Zuordnungen, Sitzungen oder Workflows durchsucht.Enthält die Suchzeichenfolge irgendwo: SQL-Überschreibung, Mapplet-Attribute, Ports, Quelldefinitionen in Mappings, Quelldefinitionen, Zieldefinitionen in Mappings, Zieldefinitionen, Mappings, Mapplets, Workflows, Worklets, Sitzungen, Befehle, Ausdrucksports, Sitzungsinstanzen, Quelldefinitionsfelder, Zieldefinitionsfelder, E-Mail-Aufgaben.
: , SberProfi DWH/BigData.
SberProfi DWH/BigData , Hadoop, Teradata, Oracle DB, GreenPlum, BI Qlik, SAP BO, Tableau .