المشكلة مع المتغيرات ذات الصلة: كيفية تحويل محسن من العدو إلى صديق

مؤلف المقال هو فيكتور فارلاموف ( varlamovVp18 ) ، OCP.
تم نشر المقال الأصلي في 07/07/2017.
شكر خاص لمؤلف الترجمة - brutaltag .

ينفّذ نظام الإبلاغ الخاص بنا عادة المئات من الطلبات المطولة التي تحدثها الأحداث المختلفة. معلمات الاستعلام عبارة عن قائمة من العملاء وفاصل زمني (يوميًا ، أسبوعيًا ، شهريًا). بسبب البيانات غير المتساوية في الجداول ، يمكن أن ينتج استعلام واحد صفًا واحدًا أو مليون صفًا ، وفقًا لمعلمات التقرير (لدى العملاء المختلفين أعداد مختلفة من الصفوف في جداول الحقائق). يتم إعداد كل تقرير على شكل حزمة ذات وظيفة رئيسية تأخذ معلمات الإدخال ، وتقوم بإجراء تحويلات إضافية ، ثم تفتح مؤشر ثابت مع المتغيرات المرتبطة بها ، وتُرجع هذا المؤشر المفتوح أخيرًا. تم تعيين المعلمة DB CURSOR_SHARING إلى FORCE.
في مثل هذه الحالة ، يتعين على المرء أن يتعامل مع الأداء الضعيف ، سواء في حالة إعادة استخدام خطة الاستعلام من قِبل المُحسِّن ، وعندما يتم تحليل الاستعلام تمامًا باستخدام معلمات في شكل حرفية . قد تسبب متغيرات Bound خطة استعلام دون المستوى الأمثل.

في كتابه "ممارسات خبير أوراكل" ، يقدم أليكس غورباتشوف قصة ممتعة رواها توم كايت. كل يوم الاثنين الممطر ، كان على المستخدمين التعامل مع خطة استعلام معدلة. من الصعب تصديق ذلك ، لكنه كان:
"وفقًا لملاحظات المستخدم النهائي ، عندما هطلت الأمطار بشدة يوم الاثنين ، كان أداء قاعدة البيانات فظيعًا. في أي يوم آخر من أيام الأسبوع أو يوم الاثنين لم تكن هناك مشاكل دون مطر. من محادثة مع DBA ، علمت Tom Kite أن الصعوبات استمرت حتى اضطرت قاعدة البيانات إلى إعادة التشغيل ، وبعد ذلك أصبح الأداء طبيعياً. كان هذا هو الحل: الأمطار الاثنين - إعادة التشغيل. "

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

SQL> CREATE TABLE VVP_HARD_PARSE_TEST(C1 NUMBER, C2 NUMBER, C3 VARCHAR2(300)); TABLE created. SQL> INSERT INTO VVP_HARD_PARSE_TEST SELECT ROWNUM C1, CASE WHEN LEVEL < 9 THEN 1 WHEN MOD(ROWNUM, 100)=99 THEN 99 ELSE 1000000 END C2, RPAD('A', 300, 'A') C3 FROM DUAL CONNECT BY LEVEL CREATE INDEX IND_VVP_HARD_PARSE_TEST_C2 ON VVP_HARD_PARSE_TEST(C2); INDEX created. SQL> EXEC DBMS_STATS.GATHER_TABLE_STATS(OWNNAME => USER, TABNAME => 'VVP_HARD_PARSE_TEST', CASCADE => TRUE, METHOD_OPT=>'FOR ALL INDEXED COLUMNS SIZE 254'); PL/SQL PROCEDURE successfully completed. SQL> SELECT histogram FROM user_tab_columns WHERE table_name = 'VVP_HARD_PARSE_TEST' AND column_name = 'C2'; HISTOGRAM --------- FREQUENCY SQL> SELECT c2, COUNT(*) FROM VVP_HARD_PARSE_TEST GROUP BY c2 ORDER BY 1; C2 COUNT(*) ----------------------- 1 8 99 10000 1000000 989992 


بمعنى آخر ، لدينا جدول VVP_HARD_PARSE_TEST به مليون صف ، حيث يكون الحقل في 10000 حالة C2 = 99 ، 8 سجلات مع C2 = 1 ، والباقي مع C2 = 1،000،000. يشير الرسم البياني عبر الحقل C2 إلى مُحسِّن Oracle حول توزيع البيانات هذا. يُعرف هذا الموقف بالتوزيع غير المتساوي للبيانات ، ويمكن أن يساعدك الرسم البياني في اختيار خطة الاستعلام الصحيحة بناءً على البيانات المطلوبة.

نلاحظ استفسارات بسيطة لهذا الجدول. من الواضح للطلب

SELECT * FROM VVP_HARD_PARSE_TEST WHERE c2 = :p

إذا كانت p = 1 ، فسيكون INDEX RANGE SCAN هو الخيار الأفضل ، بالنسبة للحالة p = 1000000 ، من الأفضل استخدام FULL TABLE SCAN. استعلامات Query1 و Query1000000 متطابقة ، باستثناء النص في التعليقات ، يتم ذلك للحصول على معرفات خطة استعلام مختلفة.

 DECLARE p NUMBER; v NUMBER; BEGIN V := 0; p := 1000000; FOR rec IN (SELECT /*+query1000000*/ * FROM VVP_HARD_PARSE_TEST WHERE c2 = p) LOOP V := v + 1; END LOOP; dbms_output.put_line(v); v : =0; p := 1; FOR rec IN (SELECT /*+query1000000*/ * FROM VVP_HARD_PARSE_TEST WHERE c2 = p) LOOP V := v + 1; END LOOP; dbms_output.put_line(v); ----------------- V := 0; p := 1; FOR rec IN (SELECT /*+query1*/ * FROM VVP_HARD_PARSE_TEST WHERE c2 = p) LOOP V := v + 1; END LOOP; dbms_output.put_line(v); v := 0; p := 1000000; FOR rec IN (SELECT /*+query1*/ * FROM VVP_HARD_PARSE_TEST WHERE c2 = p) LOOP V := v + 1; END LOOP; dbms_output.put_line(v); END; 

الآن دعونا نلقي نظرة على خطط الاستعلام:

 SQL> SELECT sql_id, child_number, executions, plan_hash_value, sql_text FROM v$sql WHERE sql_text LIKE 'SELECT % * FROM VVP_HARD_PARSE_TEST WHERE C2%'; SQL_ID CHILD_NUMBER EXECUTIONS PLAN_HASH_VALUE SQL_TEXT ------------------------------------------------- 7rqnhhp6pahw2 0 2 2782757451 SELECT /*+query1000000*/ * FROM VVP_HARD_PARSE_TEST WHERE C2 = :B1 7xwt28hvw3u9s 0 2 2463783749 SELECT /*+query1*/ * FROM VVP_HARD_PARSE_TEST WHERE C2 = :B1 SQL> SELECT * FROM TABLE(dbms_xplan.display_cursor(sql_id => '7rqnhhp6pahw2', format => 'basic +peeked_binds')); SELECT /*+query1000000*/ * FROM VVP_HARD_PARSE_TEST WHERE C2 = :B1 PLAN hash VALUE: 2782757451 ------------------------------------------------- | Id | Operation | Name | ------------------------------------------------- | 0 | SELECT STATEMENT | | | 1 | TABLE ACCESS FULL| VVP_HARD_PARSE_TEST | ------------------------------------------------- Peeked Binds (IDENTIFIED BY position): -------------------------------------- 1 - :B1 (NUMBER): 1000000 SQl> SELECT * FROM TABLE(dbms_xplan.display_cursor(sql_id => '7xwt28hvw3u9s', format => 'basic +peeked_binds')); SELECT /*+query1*/ * FROM VVP_HARD_PARSE_TEST WHERE C2 = :B1 PLAN hash VALUE: 2463783749 ------------------------------------------------------------------ | Id | Operation | Name | ------------------------------------------------------------------ | 0 | SELECT STATEMENT | | | 1 | TABLE ACCESS BY INDEX ROWID| VVP_HARD_PARSE_TEST | | 2 | INDEX RANGE SCAN | IND_VVP_HARD_PARSE_TEST_C2 | ------------------------------------------------------------------ Peeked Binds (IDENTIFIED BY position): -------------------------------------- 1 - :B1 (NUMBER): 1 

كما ترون ، يتم إنشاء خطة للطلبات المختلفة مرة واحدة فقط ، في وقت التنفيذ الأول (يوجد مؤشر فرعي واحد فقط مع CHILD_NUMBER = 0 لكل طلب). يتم تنفيذ كل طلب مرتين (EXECUTION = 2). أثناء التحليل الثابت ، يسترد Oracle قيم المتغيرات المرتبطة ويختار خطة وفقًا لهذه القيم. لكنه يستخدم نفس الخطة للتشغيل التالي ، على الرغم من تغير المتغيرات ذات الصلة في الجولة الثانية. يتم استخدام الخطط غير المثلى - Query1000000 مع المتغير C2 = 1 يستخدم FULL TABLE SCAN بدلاً من INDEX RANGE SCAN ، والعكس بالعكس.

من الواضح أن إصلاح التطبيق واستخدام المعلمات كحرفية في الاستعلام هو الطريقة الأنسب لحل المشكلة ، ولكنه يؤدي إلى SQL ديناميكي مع أوجه القصور المعروفة. هناك طريقة أخرى لتعطيل استعلام المتغيرات ذات الصلة ( ALTER SESSION SET "_OPTIM_PEEK_USER_BINDS" = FALSE ) أو حذف المدرج التكراري ( الارتباط ).

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

انظر ماذا سنفعل الآن. بعد تحليل بياناتنا ، نقرر تقسيم العملاء إلى ثلاث فئات - كبيرة ومتوسطة وصغيرة (LMS أو 9-5-1) - وفقًا لعدد المعاملات أو المعاملات خلال العام. أيضًا ، يعتمد عدد الأسطر في التقرير بشكل صارم على الفترة: شهري - كبير ، أسبوعي - متوسط ​​، يومي - صغير أو 9-5-1. علاوة على ذلك ، فإن الحل بسيط - سنجعل سياسة الأمان تعتمد على كل فئة وعلى كل فترة. لذلك ، لكل طلب نحصل عليه 9 مؤشرات الطفل المحتملة. علاوة على ذلك ، فإن الاستعلامات ذات السياسات المختلفة ستقودنا إلى نفس معرفات الاستعلام ، مما يجعل من الممكن تنفيذ SQL PLAN MANAGEMENT (خط الأساس لخطة sql).

 SQL> CREATE TABLE HARD_PARSE_TABLE AS SELECT * FROM dual; TABLE created. SQL> CREATE TABLE CLIENTS_HP_STATISTICS (client_seqno NUMBER, client_id VARCHAR2(255), cnt_year NUMBER); TABLE created. SQL> INSERT INTO CLIENTS_HP_STATISTICS (client_seqno, client_id, cnt_year) VALUES (1, 'SMALL CLIENT', 8); 1 ROW inserted. SQL> INSERT INTO CLIENTS_HP_STATISTICS (client_seqno, client_id, cnt_year) VALUES (99, 'MIDDLE CLIENT', 50001); 1 ROW inserted. SQL> INSERT INTO CLIENTS_HP_STATISTICS (client_seqno, client_id, cnt_year) VALUES (1000000,'LARGE CLIENT', 989992); 1 ROW inserted. SQL> CREATE OR REPLACE PACKAGE FORCE_HARD_PARSE_PKG IS gc_small CONSTANT NUMBER := 1; gc_middle CONSTANT NUMBER := 5; gc_large CONSTANT NUMBER := 9; gc_client_middle CONSTANT NUMBER := 50000; gc_client_large CONSTANT NUMBER := 500000; gc_daterange_middle CONSTANT NUMBER := 10; gc_daterange_large CONSTANT NUMBER := 50; FUNCTION FORCE_HARD_PARSE(in_schema VARCHAR2, in_object VARCHAR2) RETURN VARCHAR2; PROCEDURE SET_PREDICATE (n NUMBER); PROCEDURE SET_PREDICATES (p_daterange NUMBER DEFAULT NULL, p_clientrange NUMBER DEFAULT NULL); PROCEDURE CALC_PREDICATE; PROCEDURE CALC_PREDICATES(p_date_interval NUMBER DEFAULT 1, p_client_seqno NUMBER DEFAULT NULL, p_client_id VARCHAR2 DEFAULT NULL, p_client_seqno_list VARCHAR2 DEFAULT NULL ); END FORCE_HARD_PARSE_PKG; PACKAGE created. SQL> CREATE OR REPLACE PACKAGE BODY FORCE_HARD_PARSE_PKG IS g_predicate NUMBER; -- g_daterange || 0 || g_clientrange g_daterange NUMBER; -- 1 - small, 5 - middle, 9 - large g_clientrange NUMBER; -- 1 - small, 5 - middle, 9 - large -- FUNCTION FORCE_HARD_PARSE(in_schema VARCHAR2, in_object VARCHAR2) RETURN VARCHAR2 IS BEGIN IF NVL(g_predicate, 0) = 0 THEN RETURN NULL; ELSE RETURN TO_CHAR(g_predicate, 'TM') || ' = ' || TO_CHAR(g_predicate, 'TM'); END IF; END FORCE_HARD_PARSE; -- PROCEDURE SET_PREDICATE (n NUMBER) IS BEGIN g_predicate := n; END; PROCEDURE SET_PREDICATES (p_daterange NUMBER DEFAULT NULL, p_clientrange NUMBER DEFAULT NULL) IS BEGIN IF p_daterange IS NOT NULL THEN g_daterange := p_daterange; CALC_PREDICATE; END IF; IF p_clientrange IS NOT NULL THEN g_clientrange := p_clientrange; CALC_PREDICATE; END IF; END SET_PREDICATES; PROCEDURE CALC_PREDICATE IS BEGIN g_predicate := NVL(g_daterange, 0) * 100 + NVL(g_clientrange, 0); END CALC_PREDICATE; PROCEDURE CALC_PREDICATES (p_date_interval NUMBER DEFAULT 1, p_client_seqno NUMBER DEFAULT NULL, p_client_id VARCHAR2 DEFAULT NULL, p_client_seqno_list VARCHAR2 DEFAULT NULL) IS v_cnt NUMBER; BEGIN IF p_date_interval IS NOT NULL THEN g_daterange := CASE WHEN p_date_interval < gc_daterange_middle THEN gc_small WHEN p_date_interval < gc_daterange_large THEN gc_middle ELSE gc_large END; CALC_PREDICATE; END IF; IF COALESCE(p_client_seqno, p_client_id, p_client_seqno_list) IS NOT NULL THEN SELECT NVL(SUM(cnt_year), 0) AS cnt INTO v_cnt FROM CLIENTS_HP_STATISTICS t WHERE 1=1 AND (p_client_seqno IS NULL OR p_client_seqno = t.client_seqno) AND (p_client_id IS NULL OR p_client_id = t.client_id) AND (p_client_seqno_list IS NULL OR t.client_seqno IN (SELECT SUBSTR(s, CASE WHEN LEVEL > 1 THEN INSTR(s, ',', 1, LEVEL - 1 ) + 1 ELSE 1 END, INSTR(s, ',', 1, LEVEL) – CASE WHEN LEVEL > 1 THEN INSTR(s, ',', 1, LEVEL – 1) + 1 ELSE 1 END) FROM (SELECT p_client_seqno_list||',' AS s FROM DUAL) CONNECT BY INSTR(s, ',', 1, LEVEL) > 0)); g_clientrange := CASE WHEN v_cnt > gc_client_large THEN gc_large WHEN v_cnt > gc_client_middle THEN gc_middle ELSE gc_small END; CALC_PREDICATE; END IF; END CALC_PREDICATES; END FORCE_HARD_PARSE_PKG; PACKAGE BODY created. SQL> EXEC DBMS_RLS.ADD_POLICY (USER, 'HARD_PARSE_TABLE', 'HARD_PARSE_POLICY', USER, 'FORCE_HARD_PARSE_PKG.FORCE_HARD_PARSE', 'select'); PL/SQL PROCEDURE successfully completed. 

الآن ، إذا أردنا تضمين هذه التكنولوجيا في التقرير ، فنحن بحاجة إلى إضافة HARD_PARSE_TABLE إلى الاستعلام (لن يؤدي ذلك إلى تدميره قليلاً) والاتصال بـ CALC_PREDICATES قبل تنفيذ الاستعلام الرئيسي.

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

 DECLARE p NUMBER; v NUMBER; BEGIN V := 0; p := 1000000; FORCE_HARD_PARSE_PKG.SET_PREDICATE(1000000); FOR rec IN (SELECT /*+query_hp1000000*/ * FROM VVP_HARD_PARSE_TEST, HARD_PARSE_TABLE WHERE c2 = p) LOOP V := v + 1; END LOOP; dbms_output.put_line(v); v := 0; p := 1; FORCE_HARD_PARSE_PKG.SET_PREDICATE(1); FOR rec IN (SELECT /*+query_hp1000000*/ * FROM VVP_HARD_PARSE_TEST, HARD_PARSE_TABLE WHERE c2 = p) LOOP V := v + 1; END LOOP; dbms_output.put_line(v); ----------------- V := 0; p := 1; FORCE_HARD_PARSE_PKG.SET_PREDICATE(1); FOR rec IN (SELECT /*+query_hp1*/ * FROM VVP_HARD_PARSE_TEST, HARD_PARSE_TABLE WHERE c2 = p) LOOP V := v + 1; END LOOP; dbms_output.put_line(v); v := 0; p := 1000000; FORCE_HARD_PARSE_PKG.SET_PREDICATE(1000000); FOR rec IN (SELECT /*+query_hp1*/ * FROM VVP_HARD_PARSE_TEST, HARD_PARSE_TABLE WHERE c2 = p) LOOP V := v + 1; END LOOP; dbms_output.put_line(v); END; 

لنلقِ نظرة على خطط التنفيذ:

 SQL> SELECT sql_id, child_number, executions, plan_hash_value, sql_text, s.* FROM v$sql s WHERE sql_text LIKE 'SELECT % * FROM VVP_HARD_PARSE_TEST, HARD_PARSE_TABLE WHERE c2%' ORDER BY 1,2; SQL_ID CHILD_NUMBER EXECUTIONS PLAN_HASH_VALUE SQL_TEXT -------------------------------------------------------------------------------- 7wva3uqbgh4qf 0 1 1136240498 SELECT /*+query_hp1000000*/ * FROM VVP_HARD_PARSE_TEST, HARD_PARSE_TABLE WHERE C2 = :B1 7wva3uqbgh4qf 1 1 3246475190 SELECT /*+query_hp1000000*/ * FROM VVP_HARD_PARSE_TEST, HARD_PARSE_TABLE WHERE C2 = :B1 8cju3tfjvwm1p 0 1 3246475190 SELECT /*+query_hp1*/ * FROM VVP_HARD_PARSE_TEST, HARD_PARSE_TABLE WHERE C2 = :B1 8cju3tfjvwm1p 1 1 1136240498 SELECT /*+query_hp1*/ * FROM VVP_HARD_PARSE_TEST, HARD_PARSE_TABLE WHERE C2 = :B1 -- SQL> SELECT * FROM TABLE(dbms_xplan.display_cursor(sql_id => '7wva3uqbgh4qf', cursor_child_no => 0, format => 'basic +peeked_binds')); SELECT /*+query_hp1000000*/ * FROM VVP_HARD_PARSE_TEST, HARD_PARSE_TABLE WHERE C2 = :B1 PLAN hash VALUE: 1136240498 ---------------------------------------------------- | Id | Operation | Name | ---------------------------------------------------- | 0 | SELECT STATEMENT | | | 1 | MERGE JOIN CARTESIAN| | | 2 | TABLE ACCESS FULL | HARD_PARSE_TABLE | | 3 | BUFFER SORT | | | 4 | TABLE ACCESS FULL | VVP_HARD_PARSE_TEST | ---------------------------------------------------- Peeked Binds (IDENTIFIED BY position): -------------------------------------- 1 - :B1 (NUMBER): 1000000 -- SQL> SELECT * FROM TABLE(dbms_xplan.display_cursor(sql_id => '7wva3uqbgh4qf', cursor_child_no => 1, format => 'basic +peeked_binds')); SELECT /*+query_hp1000000*/ * FROM VVP_HARD_PARSE_TEST, HARD_PARSE_TABLE WHERE C2 = :B1 PLAN hash VALUE: 3246475190 -------------------------------------------------------------------- | Id | Operation | Name | -------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | 1 | MERGE JOIN CARTESIAN | | | 2 | TABLE ACCESS FULL | HARD_PARSE_TABLE | | 3 | BUFFER SORT | | | 4 | TABLE ACCESS BY INDEX ROWID| VVP_HARD_PARSE_TEST | | 5 | INDEX RANGE SCAN | IND_VVP_HARD_PARSE_TEST_C2 | -------------------------------------------------------------------- Peeked Binds (IDENTIFIED BY position): -------------------------------------- 1 - :B1 (NUMBER): 1 -- SQl> SELECT * FROM TABLE(dbms_xplan.display_cursor(sql_id => '8cju3tfjvwm1p', cursor_child_no => 0, format => 'basic +peeked_binds')); SELECT /*+query_hp1*/ * FROM VVP_HARD_PARSE_TEST, HARD_PARSE_TABLE WHERE C2 = :B1 PLAN hash VALUE: 3246475190 -------------------------------------------------------------------- | Id | Operation | Name | -------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | 1 | MERGE JOIN CARTESIAN | | | 2 | TABLE ACCESS FULL | HARD_PARSE_TABLE | | 3 | BUFFER SORT | | | 4 | TABLE ACCESS BY INDEX ROWID| VVP_HARD_PARSE_TEST | | 5 | INDEX RANGE SCAN | IND_VVP_HARD_PARSE_TEST_C2 | -------------------------------------------------------------------- Peeked Binds (IDENTIFIED BY position): -------------------------------------- 1 - :B1 (NUMBER): 1 -- SQL> SELECT * FROM TABLE(dbms_xplan.display_cursor(sql_id => '8cju3tfjvwm1p', cursor_child_no => 1, format => 'basic +peeked_binds')); SELECT /*+query_hp1*/ * FROM VVP_HARD_PARSE_TEST, HARD_PARSE_TABLE WHERE C2 = :B1 PLAN hash VALUE: 1136240498 ---------------------------------------------------- | Id | Operation | Name | ---------------------------------------------------- | 0 | SELECT STATEMENT | | | 1 | MERGE JOIN CARTESIAN| | | 2 | TABLE ACCESS FULL | HARD_PARSE_TABLE | | 3 | BUFFER SORT | | | 4 | TABLE ACCESS FULL | VVP_HARD_PARSE_TEST | ---------------------------------------------------- Peeked Binds (IDENTIFIED BY position): -------------------------------------- 1 - :B1 (NUMBER): 1000000 

تبدو رائعة! يتم تنفيذ كل استعلام مرتين ، باستخدام مؤشرات فرعية مختلفة وخطط مختلفة. بالنسبة إلى المعلمة C2 = 1،000،000 ، نرى FULL TABLE SCAN في كلا الاستعلامات ، وللمعلمة C1 = 1 ، نرى دائمًا INDEX RANGE SCAN.

في النهاية ، أعطي حلاً لقضية الاثنين الممطرة:

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

وبعض المشاهدات المفيدة للنظام:
dba_tab_histograms, all_tab_histograms, user_tab_histograms
v$vpd_policy
v$sql_bind_capture
dba_hist_sqlbind

Source: https://habr.com/ru/post/ar434444/


All Articles