Skip to content

Commit

Permalink
Generate SQL plan baseline from SGA too
Browse files Browse the repository at this point in the history
  • Loading branch information
rammpeter committed Dec 17, 2024
1 parent 0f99011 commit 94b865b
Show file tree
Hide file tree
Showing 5 changed files with 206 additions and 102 deletions.
4 changes: 2 additions & 2 deletions app/controllers/dba_history_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2186,7 +2186,7 @@ def generate_baseline_creation
snap_corrected_warning = ''
if min_snap_id == max_snap_id
min_snap_id -= 1
snap_corrected_warning = "\n-- End snapshot ID (#{max_snap_id}) must be greater than begin snapshot ID ((#{max_snap_id}).\nTherfore begin snapshot ID has been adjusted to #{min_snap_id}!\n\n"
snap_corrected_warning = "\n-- End snapshot ID (#{max_snap_id}) must be greater than begin snapshot ID ((#{max_snap_id}).\n-- Therfore begin snapshot ID has been adjusted to #{min_snap_id}!\n\n"
end

dbms_sqltune = get_db_version >= '18' ? 'DBMS_SQLSET' : 'DBMS_SQLTUNE' # DBMS_SQLSET contains similar functions like DBMS_SQLTUNE but without requiring Tuning Pack
Expand Down Expand Up @@ -2223,7 +2223,7 @@ def generate_baseline_creation
END LOOP;
-- Create new SQL Tuning Set (STS)
DBMS_SQLTUNE.CREATE_SQLSET(sqlset_name => '#{sts_name}', description => 'Panorama: SQL Tuning Set for loading plan into SQL Plan Baseline');
#{dbms_sqltune}.CREATE_SQLSET(sqlset_name => '#{sts_name}', description => 'Panorama: SQL Tuning Set for loading plan into SQL Plan Baseline');
-- Populate STS from AWR, using a time duration when the desired plan was used
-- Specify the sql_id in the basic_filter (other predicates are available, see documentation)
Expand Down
118 changes: 82 additions & 36 deletions app/controllers/dba_sga_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1877,44 +1877,15 @@ def list_dbms_xplan_display_multiple_children

# Show possibilities to influence the sql plan
def influence_sql_plan
@sql_id = params[:sql_id]
@user_name = params[:user_name]
@min_snap_id = params[:min_snap_id] # only set if called from DBA_Hist_SQLStat view
@max_snap_id = params[:max_snap_id] # only set if called from DBA_Hist_SQLStat view
@sql_id = prepare_param :sql_id
@user_name = prepare_param :user_name
@min_snap_id = prepare_param :min_snap_id # only set if called from DBA_Hist_SQLStat view
@max_snap_id = prepare_param :max_snap_id # only set if called from DBA_Hist_SQLStat view
@dbid = prepare_param_dbid # only set if called from DBA_Hist_SQLStat view
@force_matching_signature = params[:force_matching_signature]
@exact_matching_signature = params[:exact_matching_signature]
@force_matching_signature = prepare_param :force_matching_signature
@exact_matching_signature = prepare_param :exact_matching_signature
@plan_hash_value = prepare_param :plan_hash_value # only set if called from SGA view with unique plan hash value

@methods = []

@methods << { type: :sql_tuning_advisor,
description: "Run Oracle's builtin SQL Tuning Advisor for this SQL to automatically find a better execution plan.\n\nADVISOR privilege is needed for the user to run the SQL Tuning Advisor.\nCREATE ANY SQL PROFILE privilege is needed to create a SQL profile from the result of the Tuning Advisor.",
option_pack_needed: "Enterprise Edition + Tuning Pack"
}

@methods << { type: :plan_baseline,
description: "Generate script to fix exactly one execution plan as SQL-baseline for this SQL.#{"\nYou may try to load SQLplan baseline from cursor cache instead (from current SGA)." if PanoramaConnection.edition == :standard || PanoramaConnection.management_pack_license != :diagnostics_and_tuning_pack}",
option_pack_needed: "Enterprise Edition + #{ get_db_version < '18' ? "Tuning Pack" : "Diagnostics Pack"}"
}

@methods << { type: :sql_profile,
description: "Generates commands for manual creation of a SQL profile.\nThis allows to inject your own defined optimizer hints to the SQL.",
option_pack_needed: "Enterprise Edition + Tuning Pack"
}

@methods << { type: :sql_patch,
description: "Generates commands for creation of a SQL patch.\nThis allows to inject your own defined optimizer hints to the SQL.\nSimilar to SQL profile, but without the need for licensed Tuning Pack.",
option_pack_needed: "None"
}

if get_db_version >= '12.1'
@methods << { type: :sql_translation,
description: "Generates a script for using SQL translation framework.\nThis allows to replace all parts of the SQL statement as long as the number and types of bind variables remain the same,",
option_pack_needed: "Enterprise Edition"
}
end

@methods.each {|m| m.extend(SelectHashHelper) }
render_partial
end

Expand Down Expand Up @@ -1976,6 +1947,81 @@ def run_sql_tuning_advisor
render_partial
end

def generate_sql_plan_baseline_from_sga
sql_id = prepare_param :sql_id
plan_hash_value = prepare_param :plan_hash_value
force_matching_signature = prepare_param :force_matching_signature
exact_matching_signature = prepare_param :exact_matching_signature

result = "
-- Build SQL plan baseline for SQL-ID = '#{sql_id}', plan hash value = #{plan_hash_value}
-- Generated with Panorama at #{Time.now}
SET SERVEROUTPUT ON;
DECLARE
cur sys_refcursor;
hit_count PLS_INTEGER;
sql_handle VARCHAR2(30);
plan_name VARCHAR2(30);
BEGIN
hit_count := DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE (
sql_id => '#{sql_id}',
plan_hash_value => #{plan_hash_value},
fixed => 'YES',
enabled => 'YES'
);
-- Get access criteria for the new created baseline based on exact- or force-signature
SELECT SQL_Handle, Plan_Name INTO sql_handle, plan_name FROM DBA_SQL_Plan_Baselines
WHERE (Signature = #{force_matching_signature} OR Signature = #{exact_matching_signature})
AND Created > SYSDATE-0.1;
-- Set the description according to your choice (max. 500 chars)
hit_count := DBMS_SPM.ALTER_SQL_PLAN_BASELINE(SQL_Handle => sql_handle, Plan_Name => plan_name, Attribute_Name => 'description', Attribute_Value => 'Baseline generated by Panorama ...');
-- You can check the existence of the baseline now by executing:
-- SELECT * FROM dba_sql_plan_baselines;
-- or by looking at SQL details page for your SQL with Panorama
-- Next commands remove currently existing cursors of this SQL from SGA to ensure hard parse with SQL plan baseline at next execution
"

if PanoramaConnection.rac?
result << " -- Because your system uses RAC you should execute the following block with DBMS_SHARED_POOL.PURGE once again connected on the appropriate RAC-instance\n"
instances = sql_select_all(["SELECT Inst_ID FROM gv$SQLArea WHERE SQL_ID=?", sql_id])
result << " -- at generation time of this script the SQL is loaded in SGA at this RAC instances: #{instances.map{|i| i.inst_id }.join(', ')}\n"
end

result << "
FOR cu IN (SELECT RAWTOHEX(Address) Address, Hash_Value FROM v$SQLArea WHERE SQL_ID='#{sql_id}') LOOP
DBMS_SHARED_POOL.PURGE (cu.address||', '||cu.hash_value, 'C');
DBMS_OUTPUT.PUT_LINE('Existing cursor for SQL-ID=#{sql_id} removed from SGA');
END LOOP;
END;
/
-- ######### to remove an existing SQL plan baseline execute the following:
-- Get the SQL-Handle of your baseline from Panorama's selections or by
-- SELECT * FROM dba_sql_plan_baselines WHERE SQL_Text LIKE '%<your SQL>%';
-- Drop all baselines of the SQL with the given SQL-Handle
-- DECLARE
-- v_dropped_plans number;
-- BEGIN
-- v_dropped_plans := DBMS_SPM.DROP_SQL_PLAN_BASELINE (sql_handle => 'SQL_b6b0d1c71cd1807b');
-- DBMS_OUTPUT.PUT_LINE('dropped ' || v_dropped_plans || ' plans');
-- END;
-- /
"

respond_to do |format|
format.html {render :html => render_code_mirror(result) }
end

end

def create_profile_from_sql_tuning_advisor_task
@task_name = prepare_param :task_name
PanoramaConnection.sql_execute ["BEGIN DBMS_SQLTUNE.accept_sql_profile(task_name => ?, name => ?, task_owner => USER, replace => TRUE); END;", @task_name, @task_name]
Expand Down
167 changes: 112 additions & 55 deletions app/views/dba_sga/_influence_sql_plan.html.erb
Original file line number Diff line number Diff line change
@@ -1,69 +1,126 @@
<%
@update_area = get_unique_area_id

show_action = proc do |rec|
case rec.type
when :sql_tuning_advisor
disabled = PanoramaConnection.management_pack_license != :diagnostics_and_tuning_pack
title = "Run Oracle's builtin SQL Tuning Advisor for this SQL to automatically find a better execution plan."
title = "SQL Tuning Advisor requires Enterprise Edition with Diagnostics and Tuning Pack." if PanoramaConnection.management_pack_license != :diagnostics_and_tuning_pack
@methods = []

ajax_submit('SQL Tuning Advisor', {
:controller => :dba_sga,
:action => :show_sql_tuning_advisor,
:update_area => @update_area,
:sql_id => @sql_id,
min_snap_id: @min_snap_id,
max_snap_id: @max_snap_id,
dbid: @dbid
}, :disabled => disabled, :title=> title)
#### sql_tuning_advisor
disabled = PanoramaConnection.management_pack_license != :diagnostics_and_tuning_pack
title = "Run Oracle's builtin SQL Tuning Advisor for this SQL to automatically find a better execution plan."
title = "SQL Tuning Advisor requires Enterprise Edition with Diagnostics and Tuning Pack." if PanoramaConnection.management_pack_license != :diagnostics_and_tuning_pack
action = ajax_submit('SQL Tuning Advisor', {
:controller => :dba_sga,
:action => :show_sql_tuning_advisor,
:update_area => @update_area,
:sql_id => @sql_id,
min_snap_id: @min_snap_id,
max_snap_id: @max_snap_id,
dbid: @dbid
}, :disabled => disabled, :title=> title)
@methods << { type: :sql_tuning_advisor,
action: action.html_safe,
description: "Run Oracle's builtin SQL Tuning Advisor for this SQL to automatically find a better execution plan.\n\nADVISOR privilege is needed for the user to run the SQL Tuning Advisor.\nCREATE ANY SQL PROFILE privilege is needed to create a SQL profile from the result of the Tuning Advisor.",
option_pack_needed: "Enterprise Edition + Diagnostics and Tuning Pack"
}

when :plan_baseline then
#### plan_baseline
title = t(:dba_history_list_sql_detail_historic_create_baseline_hint, :default=>"Generate script to fix exactly this execution plan as SQL-baseline for this SQL.")
desc_add = ""
option_pack_needed = ""
if @min_snap_id && @max_snap_id # called from AWR view
desc_add << "\nBases on AWR data for choosen time period."
if get_db_version < '18'
disabled = PanoramaConnection.edition == :standard || PanoramaConnection.management_pack_license != :diagnostics_and_tuning_pack
title = t(:dba_history_list_sql_detail_historic_create_baseline_hint, :default=>"Generate script to fix exactly this execution plan as SQL-baseline for this SQL.")
title = "Generation of SQL plan baseline from AWR requires Enterprise Edition with Diagnostics and Tuning Pack.\nYou may try to load SQLplan baseline from cursor cache instead (from current SGA)." if PanoramaConnection.edition == :standard || PanoramaConnection.management_pack_license != :diagnostics_and_tuning_pack

ajax_submit('SQL plan baseline', {
:controller => :dba_history,
:action => :select_plan_hash_value_for_baseline,
:update_area => @update_area,
:sql_id => @sql_id,
:min_snap_id => @min_snap_id,
:max_snap_id => @max_snap_id,
:force_matching_signature => @force_matching_signature.to_s,
:exact_matching_signature => @exact_matching_signature.to_s
}, :disabled => disabled, :title=> title)
when :sql_profile then
ajax_submit('SQL Profile', {
:controller => :dba_sga,
:action => :generate_sql_profile,
:update_area => @update_area,
:sql_id => @sql_id,
}, :title=> "Generate script to create SQL profile for this SQL.")
when :sql_patch then
ajax_submit('SQL Patch', {
:controller => :dba_sga,
:action => :generate_sql_patch,
:update_area => @update_area,
:sql_id => @sql_id,
}, :title=> t(:dba_history_list_sql_detail_historic_create_sql_patch_hint, :default=>"Generate script to create SQL patch for this SQL."))
when :sql_translation then
ajax_submit('SQL Translation', {
:controller => :dba_sga,
:action => :generate_sql_translation,
:update_area => @update_area,
:sql_id => @sql_id,
:user_name => @user_name,
}, :title=> t(:dba_history_list_sql_detail_historic_create_translation_hint, :default=>"Generate script to create profile for SQL-translation-framework for this SQL."))
else "Unsupported type #{rec.type}"
desc_add << "\n\nGeneration of SQL plan baseline from AWR requires Enterprise Edition with Diagnostics and Tuning Pack.\nYou may try to load SQL plan baseline from cursor cache instead (from current SGA)." if disabled
option_pack_needed = "Enterprise Edition + Diagnostics and Tuning Pack"
else
disabled = PanoramaConnection.edition == :standard || PanoramaConnection.management_pack_license != :diagnostics_pack
desc_add << "\n\nGeneration of SQL plan baseline from AWR requires Enterprise Edition with Diagnostics Pack.\nYou may try to load SQLplan baseline from cursor cache instead (from current SGA)." if disabled
option_pack_needed = "Enterprise Edition + Diagnostics Pack"
end
action = ajax_submit('SQL plan baseline', {
:controller => :dba_history,
:action => :select_plan_hash_value_for_baseline,
:update_area => @update_area,
:sql_id => @sql_id,
:min_snap_id => @min_snap_id,
:max_snap_id => @max_snap_id,
:force_matching_signature => @force_matching_signature.to_s,
:exact_matching_signature => @exact_matching_signature.to_s
}, :disabled => disabled, :title=> title)
else # called from SGA view
desc_add << "\nBases on current SGA data."
disabled = @plan_hash_value.nil?
desc_add << "\n\nGeneration of SQL plan baseline from SGA requires a unique plan hash value.\nPlease choose a SQL child cursor view with a unique plan hash value." if disabled
option_pack_needed = "none"

action = ajax_submit('SQL plan baseline', {
:controller => :dba_sga,
:action => :generate_sql_plan_baseline_from_sga,
:update_area => @update_area,
:sql_id => @sql_id,
plan_hash_value: @plan_hash_value,
:force_matching_signature => @force_matching_signature.to_s,
:exact_matching_signature => @exact_matching_signature.to_s
}, :disabled => disabled, :title=> title)
end
@methods << { type: :plan_baseline,
action: action.html_safe,
description: "Generate script to fix exactly one execution plan as SQL-baseline for this SQL.#{desc_add}",
option_pack_needed: option_pack_needed
}

#### sql_profile
action = ajax_submit('SQL Profile', {
:controller => :dba_sga,
:action => :generate_sql_profile,
:update_area => @update_area,
:sql_id => @sql_id,
}, :title=> "Generate template script to create a SQL profile for this SQL.")

@methods << { type: :sql_profile,
action: action.html_safe,
description: "Generates commands for manual creation of a SQL profile.\nThis allows to inject your own defined optimizer hints to the SQL.",
option_pack_needed: "Enterprise Edition + Tuning Pack"
}

#### sql_patch
action = ajax_submit('SQL Patch', {
:controller => :dba_sga,
:action => :generate_sql_patch,
:update_area => @update_area,
:sql_id => @sql_id,
}, :title=> t(:dba_history_list_sql_detail_historic_create_sql_patch_hint, :default=>"Generate template script to create SQL patch for this SQL."))

@methods << { type: :sql_patch,
action: action.html_safe,
description: "Generates commands for creation of a SQL patch.\nThis allows to inject your own defined optimizer hints to the SQL.\nSimilar to SQL profile, but without the need for licensed Tuning Pack.",
option_pack_needed: "None"
}

if get_db_version >= '12.1'
#### sql_translation
action = ajax_submit('SQL Translation', {
:controller => :dba_sga,
:action => :generate_sql_translation,
:update_area => @update_area,
:sql_id => @sql_id,
:user_name => @user_name,
}, :title=> t(:dba_history_list_sql_detail_historic_create_translation_hint, :default=>"Generate template script to create profile for SQL-translation-framework for this SQL."))

@methods << { type: :sql_translation,
action: action.html_safe,
description: "Generates a script for using SQL translation framework.\nThis allows to replace all parts of the SQL statement as long as the number and types of bind variables remain the same,",
option_pack_needed: "Enterprise Edition"
}
end

@methods.each {|m| m.extend(SelectHashHelper) }

column_options =
[
{ caption: "Action", data: show_action, title: "Action to generate the script snippet to execute"},
{ caption: "Description", data: proc{|rec| rec.description }, title: "Description of influencing activity"},
{ caption: "Option pack needed", data: proc{|rec| rec.option_pack_needed }, title: "Needed licensing of option pack to use this function"},
{ caption: "Action", data: proc{|rec| rec[:action]}, title: "Action to generate the script snippet to execute"},
{ caption: "Description", data: proc{|rec| rec.description}, title: "Description of influencing activity"},
{ caption: "Option pack needed", data: proc{|rec| rec.option_pack_needed }, title: "Needed licensing of option pack to use this function"},
]
%>
<%= gen_slickgrid(@methods,
Expand Down
Loading

0 comments on commit 94b865b

Please sign in to comment.