Oracle
 sql >> डेटाबेस >  >> RDS >> Oracle

पीएल/एसक्यूएल कर्सर के लिए परिवर्तनीय/शाब्दिक प्रतिस्थापन?

नीचे दिया गया फ़ंक्शन GV$SQL_BIND_CAPTURE के डेटा का उपयोग करते हुए, बाइंड वैरिएबल को हाल के शाब्दिकों से बदल देता है। Oracle बाइंड मेटाडेटा हमेशा उपलब्ध नहीं होता है, इसलिए नीचे दिया गया फ़ंक्शन सभी प्रश्नों के साथ काम नहीं कर सकता है।

फ़ंक्शन बनाएं:

create or replace function get_sql_with_literals(p_sql_id varchar2) return clob authid current_user is
/*
    Purpose: Generate a SQL statement with literals, based on values in GV$SQL_BIND_CAPTURE.
        This can be helpful for queries with hundreds of bind variables (or cursor sharing),
        and you don't want to spend minutes manually typing each variable.
*/
    v_sql_text clob;
    v_names sys.odcivarchar2list;
    v_values sys.odcivarchar2list;
begin
    --Get the SQL_ID and text.
    --(Use dynamic SQL to simplify privileges.  Your user must have access to GV$ views,
    -- but you don't need to have them directly granted to your user, role access is fine.)
    execute immediate
    q'[
        select sql_fulltext
        from gv$sql
        --There may be multiple rows, for clusters or child cursors.
        --Can't use distinct with CLOB SQL_FULLTEXT, but since the values will be the same
        --we can pick any one of the rows.
        where sql_id = :p_sql_id
            and rownum = 1
    ]'
    into v_sql_text
    using p_sql_id;

    --Try to find the binds from GV$SQL_MONITOR.  If the values exist, this is the most accurate source.
    execute immediate
    q'[
        --Get the binds for the latest run.
        select
            case
                when name like ':SYS_%' then ':"' || substr(name, 2) || '"'
                else name
            end name,
            case
                when dtystr like 'NUMBER%' then nvl(the_value, 'NULL')
                when dtystr like 'VARCHAR2%' then '''' || the_value || ''''
                when dtystr like 'DATE%' then 'to_date('''||the_value||''', ''MM/DD/YYYY HH24:MI:SS'')'
                --From: https://ardentperf.com/2013/11/19/convert-rawhex-to-timestamp/
                when dtystr like 'TIMESTAMP%' then
                    'to_timestamp('''||
                        to_char( to_number( substr( the_value, 1, 2 ), 'xx' ) - 100, 'fm00' ) ||
                        to_char( to_number( substr( the_value, 3, 2 ), 'xx' ) - 100, 'fm00' ) ||
                        to_char( to_number( substr( the_value, 5, 2 ), 'xx' ), 'fm00' ) ||
                        to_char( to_number( substr( the_value, 7, 2 ), 'xx' ), 'fm00' ) ||
                        to_char( to_number( substr( the_value, 9, 2 ), 'xx' )-1, 'fm00' ) ||
                        to_char( to_number( substr( the_value,11, 2 ), 'xx' )-1, 'fm00' ) ||
                        to_char( to_number( substr( the_value,13, 2 ), 'xx' )-1, 'fm00' ) ||
                        ''', ''yyyymmddhh24miss'')'
                else 'Unknown type: '||dtystr
            end the_value
        from
        (
            select xmltype.createXML(binds_xml) binds_xml
            from
            (
                select binds_xml, last_refresh_time, max(last_refresh_time) over () max_last_refresh_time
                from gv$sql_monitor
                where sql_id = :p_sql_id
                    and binds_xml is not null
            )
            where last_refresh_time = max_last_refresh_time
                and rownum = 1
        ) binds
        cross join
        xmltable('/binds/bind' passing binds.binds_xml
            columns
                name varchar2(128) path '@name',
                dtystr varchar2(128) path '@dtystr',
                the_value varchar2(4000) path '/'
        )
        --Match longest names first to avoid matching substrings.
        --For example, we don't want ":b1" to be matched to ":b10".
        order by length(name) desc, the_value
    ]'
    bulk collect into v_names, v_values
    using p_sql_id;


    --Use gv$sql_bind_capture if there was nothing from SQL Monitor.
    if v_names is null or v_names.count = 0 then
        --Get bind data.
        execute immediate
        q'[
            select
                name,
                --Convert to literals that can  be plugged in.
                case
                    when datatype_string like 'NUMBER%' then nvl(value_string, 'NULL')
                    when datatype_string like 'VARCHAR%' then '''' || value_string || ''''
                    when datatype_string like 'DATE%' then 'to_date('''||value_string||''', ''MM/DD/YYYY HH24:MI:SS'')'
                    --TODO: Add more types here
                end value
            from
            (
                select
                    datatype_string,
                    --If CURSOR_SHARING=FORCE, literals are replaced with bind variables and use a different format.
                    --The name is stored as :SYS_B_01, but the actual string will be :"SYS_B_01".
                    case
                        when name like ':SYS_%' then ':"' || substr(name, 2) || '"'
                        else name
                    end name,
                    position,
                    value_string,
                    --If there are multiple bind values captured, only get the latest set.
                    row_number() over (partition by name order by last_captured desc nulls last, address) last_when_1
                from gv$sql_bind_capture
                where sql_id = :p_sql_id
            )
            where last_when_1 = 1
            --Match longest names first to avoid matching substrings.
            --For example, we don't want ":b1" to be matched to ":b10".
            order by length(name) desc, position
        ]'
        bulk collect into v_names, v_values
        using p_sql_id;
    end if;

    --Loop through the binds and replace them.
    for i in 1 .. v_names.count loop
        v_sql_text := replace(v_sql_text, v_names(i), v_values(i));
    end loop;

    --Return the SQL.
    return v_sql_text;
end;
/

फ़ंक्शन चलाएँ:

Oracle केवल बाइंड वैरिएबल के पहले उदाहरण को कैप्चर करता है। मौजूदा बाइंड डेटा को साफ़ करने के लिए प्रक्रिया चलाने से पहले इस कथन को चलाएँ। इस कथन को उत्पादन में चलाने में सावधानी बरतें, यह अस्थायी रूप से सिस्टम को धीमा कर सकता है क्योंकि यह कैश्ड योजनाओं को खो देता है।

alter system flush shared_pool;

अब SQL_ID खोजें। SQL कितना सामान्य या अद्वितीय है, इसके आधार पर यह मुश्किल हो सकता है।

select *
from gv$sql
where lower(sql_fulltext) like lower('%unique_string%')
    and sql_fulltext not like '%quine%';

अंत में, एसक्यूएल को प्रक्रिया में प्लग करें और इसे कोड को अक्षर के साथ वापस करना चाहिए। दुर्भाग्य से SQL ने सभी स्वरूपण खो दिए। इसके आसपास कोई आसान रास्ता नहीं है। यदि यह एक बड़ा सौदा है तो आप प्रक्रिया में चर को बदलने के लिए पीएल/स्कोप का उपयोग करके संभावित रूप से कुछ बना सकते हैं लेकिन मुझे लगता है कि यह हास्यास्पद रूप से जटिल होगा। उम्मीद है कि आपके IDE में कोड ब्यूटिफायर होगा।

select get_sql_with_literals(p_sql_id => '65xzbdjubzdqz') sql
from dual;

एक प्रक्रिया के साथ पूरा उदाहरण:

मैंने आपके स्रोत कोड को संशोधित किया है और विशिष्ट पहचानकर्ता जोड़े हैं ताकि प्रश्नों को आसानी से पाया जा सके। मैंने एक संकेत का उपयोग किया क्योंकि पार्स किए गए प्रश्नों में नियमित टिप्पणियां शामिल नहीं हैं। उदाहरण को और अधिक यथार्थवादी बनाने के लिए मैंने स्ट्रिंग्स और तिथियों को शामिल करने के लिए डेटा प्रकारों को भी बदल दिया है।

drop table test1 purge;
create table test1(col1 number, col2 varchar2(100), col3 date);

create or replace procedure test_procedure is
    C_Constant constant date := date '2000-01-01';
    v_output1 number;
    v_output2 varchar2(100);
    v_output3 date;

    CURSOR cFunnyCursor (
      v1 NUMBER,
      v2 VARCHAR2
    ) IS
    SELECT /*+ unique_string_1 */ * FROM TEST1
    WHERE  col1  = v1
    AND    col2 != v2
    AND    col3  = C_CONSTANT;
begin
    open cFunnyCursor(3, 'asdf');
    fetch cFunnyCursor into v_output1, v_output2, v_output3;
    close cFunnyCursor;
end;
/

begin
    test_procedure;
end;
/

select *
from gv$sql
where lower(sql_fulltext) like lower('%unique_string%')
    and sql_fulltext not like '%quine%';

परिणाम:

select get_sql_with_literals(p_sql_id => '65xzbdjubzdqz') sql
from dual;

SQL
---
SELECT /*+ unique_string_1 */ * FROM TEST1 WHERE COL1 = 3 AND COL2 != 'asdf' AND COL3 = to_date('01/01/2000 00:00:00', 'MM/DD/YYYY HH24:MI:SS') 


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. webLogic सर्वर के मूल JSF कार्यान्वयन को अक्षम करें और myfaces कार्यान्वयन का उपयोग करें

  2. डेटाबेस बनाते समय ओवरसब्सक्राइब शाब्दिक/लंबाई ट्री त्रुटि

  3. एसक्यूएल भुगतान वितरण

  4. Oracle 12c मूल्यों द्वारा पहचाना गया

  5. php का उपयोग कर संग्रहीत प्रक्रिया से पैरामीटर प्राप्त नहीं कर सकता