I want to make stored procedure which going to check Oracle db tables and do statistics gathering for them.
I have made this so far, and I would like to now how can I catch a result of execution for each line in a same loop?
declare
comm varchar2(200);
cursor c1 is
select owner, table_name, num_rows, last_analyzed from dba_tables
where num_rows>=500000
and owner not in ('SYS','SYSTEM')
and last_analyzed <= sysdate -7
order by 3 desc;
begin
FOR V1 IN C1 LOOP
comm:= 'EXEC DBMS_STATS.gather_table_stats( '''||V1.OWNER||''','''||V1.TABLE_NAME||''' , estimate_percent => DBMS_STATS.auto_sample_size);';
EXECUTE IMMEDIATE comm;
end loop;
end;
From a purely syntactical perspective, your code could be something like this:
begin
for r in (
select t.owner, t.table_name, t.num_rows
from dba_tables t
left join dba_external_tables x
on x.owner = t.owner and x.table_name = t.table_name
where t.owner not in ('SYS','SYSTEM')
and t.temporary = 'N'
and x.table_name is null
and t.num_rows >= 500000
and t.last_analyzed <= sysdate -7
)
loop
begin
dbms_stats.gather_table_stats(r.owner, r.table_name);
exception
when others then
dbms_output.put_line('Error gathering stats on table '|| r.owner||'.'||r.table_name||': '|| sqlerrm);
end;
end loop;
end;
I have excluded external and temporary tables, and failures are skipped.
However, I'm not sure this is a good approach.
Are you sure you want to gather stats for every table in every other schema?
What should happen with tables that don't have any stats?
Are you sure you want to use the default method_opt setting for every table? Some tables might have carefully crafted histograms and extended statistics column groups, or they might deliberately avoid histograms altogether.
Could there be tables with huge volumes that will take many hours to process? If so, are you ready for your script to run for several days?
I tried and this will do for now.... Thank you for your replies
declare
comm varchar2(200);
cursor c1 is
select owner, table_name, num_rows, last_analyzed from dba_tables
where num_rows>=500000
and owner not in ('SYS','SYSTEM')
and last_analyzed <= sysdate -7
order by 3 desc;
begin
FOR V1 IN C1 LOOP
begin
comm := 'begin DBMS_STATS.gather_table_stats( '''||V1.OWNER||''','''||V1.TABLE_NAME||''' , estimate_percent => DBMS_STATS.auto_sample_size); end;';
--dbms_output.put_line('begin DBMS_STATS.gather_table_stats( '''||V1.OWNER||''' , '''||V1.TABLE_NAME||''' , estimate_percent => DBMS_STATS.auto_sample_size); end;') ;
EXECUTE IMMEDIATE comm ;
dbms_output.put_line( V1.TABLE_NAME || '------Ok------') ;
exception
when others then
dbms_output.put_line(V1.TABLE_NAME || '------Not ok------' || 'SQLERRM: '|| SQLERRM || ' Format error stack : '||dbms_utility.format_error_stack) ;
end;
end loop;
end;
Related
I have a procedure with the following lines:
The value of ITERATOR is set dynamically in a loop
DECLARE V_LOSS_ID integer;
DECLARE V_STATE_CODE CHARACTER(2);
DECLARE V_CLASS_CODE CHARACTER(4);
SET V_QUERY = 'set (?,?,?) = (select LOSS_ID, STATE_CODE, CLASS_CODE from LOSS L limit 1 offset '|| ITERATOR || ' )';
PREPARE STMT FROM V_QUERY;
EXECUTE STMT into V_LOSS_ID, V_STATE_CODE, V_CLASS_CODE;
I am getting the following error in iSeries db2 -
[SQL0104] Token V_LOSS_ID was not valid. Valid tokens: SQL DESCRIPTOR.
However, this procedure works on DB2 LUW database. Facing this issue only on DB2 iseries as400 database
EXECUTE in Db2 for IBM i has different functionality.
So, use something like below:
DECLARE ITERATOR ...;
DECLARE V_LOSS_ID integer;
DECLARE V_STATE_CODE CHARACTER(2);
DECLARE V_CLASS_CODE CHARACTER(4);
DECLARE C1 CURSOR FOR STMT;
...
--SET V_QUERY = 'set (?,?,?) = (select LOSS_ID, STATE_CODE, CLASS_CODE from LOSS L limit 1 offset '|| ITERATOR || ' )';
SET V_QUERY = 'select LOSS_ID, STATE_CODE, CLASS_CODE from LOSS L limit 1 offset '|| ITERATOR;
PREPARE STMT FROM V_QUERY;
--EXECUTE STMT into V_LOSS_ID, V_STATE_CODE, V_CLASS_CODE;
OPEN C1;
FETCH C1 INTO V_LOSS_ID, V_STATE_CODE, V_CLASS_CODE;
CLOSE C1;
can someone help with my Proc code here? I am trying to execute this proc in DB12 Z/OS. Below is the body of my proc
CREATE PROCEDURE DEL_TBL_TEST23(IN TBL_NM VARCHAR(100))
DYNAMIC RESULT SETS 1
LANGUAGE SQL MODIFIES SQL DATA
BEGIN
DECLARE SQLCODE INTEGER;
DECLARE BDE_COUNTER INTEGER DEFAULT 0;
DECLARE V1 VARCHAR(50);
DECLARE V2 VARCHAR(100);
DECLARE V3 VARCHAR(100);
DECLARE V4 VARCHAR(100);
DECLARE V5 VARCHAR(200);
DECLARE V6 VARCHAR(500);
DECLARE T VARCHAR(500);
SET V1='DELETE FROM';
SET V2= TBL_NM;
SET V3='WHERE LN_NO IN (SELECT LN_NO FROM';
SET V4= TBL_NM;
SET V5='WHERE REC_CHNG_CD=''T''';
SET V6='ORDER BY LN_NO FETCH FIRST 10000 ROWS ONLY)';
SET T = V1||V2||V3||V4||V5||V6 ;
DEL_LOOP:
LOOP
SET BDE_COUNTER=BDE_COUNTER + 1;
EXECUTE IMMEDIATE T;
COMMIT;
IF SQLCODE = 100 THEN
LEAVE DEL_LOOP;
END IF;
END LOOP DEL_LOOP;
COMMIT;
END
I want to delete some rows from a tablename, which I will provide while executing it. My proc here keeps on running in loops. Please help me know what am I doing wrong here?
Note that there is only one SQLCODE that always reflects the result of the last SQL-Statement. So instead of checking the SQLCODE from EXECUTE IMMEDIATE T you are seeing that of the COMMIT that will never be 100.
So this might work:
DEL_LOOP:
LOOP
SET BDE_COUNTER=BDE_COUNTER + 1;
EXECUTE IMMEDIATE T;
IF SQLCODE = 100 THEN
LEAVE DEL_LOOP;
END IF;
COMMIT;
END LOOP DEL_LOOP;
COMMIT;
Create or replace procedure total_test_inside()
RETURNS REFTABLE(testtabl)
LANGUAGE NZPLSQL
AS
BEGIN_PROC
DECLARE
prod_id integer;
lkp_weighted_prc numeric(20,3);
rec record;
BEGIN
FOR rec IN select prod_id from weight
LOOP
select weightprice into lkp_weighted_prc from weight;
call total_amort_test(lkp_weighted_prc);
execute immediate 'insert into ' ||REFTABLENAME || ' values(' || lkp_weighted_prc || ')';
END loop;
return REFTABLE;
END;
END_PROC;
call total_test_inside();
Can someone guide on why I'm unable to loop which is taking just last prod_id from weight table in netezza.
Thanks in advance
I'm going to go out on a limb with what I think you're trying to accomplish here.
Since you are already doing a FOR loop here, there's no need to do another select to get weightprice, and in fact that will probably just give you the same first value over and over again.
FOR rec IN select prod_id from weight
LOOP
select weightprice into lkp_weighted_prc from weight;
call total_amort_test(lkp_weighted_prc);
execute immediate 'insert into ' ||REFTABLENAME || ' values(' || lkp_weighted_prc || ')';
END loop;
What I think you want is this. Here I've added weightprice to the select statement in the FOR definition, and removed the seemingly superfluous SELECT. Then I use "rec." qualifier to reference the single record that is stepped over in the FOR loop.
BEGIN
FOR rec IN select prod_id, weightprice lkp_weighted_prc from weight
LOOP
call total_amort_test(rec.lkp_weighted_prc);
execute immediate 'insert into ' ||REFTABLENAME || ' values(' || rec.lkp_weighted_prc || ')';
END loop;
I'm currently using AdoQuery's and append post commands. But for data security I want to change my code with insert into and update table name...
But I have a lot of forms and tables...
Because of that I think maybe someone has already developed code for generating insert statements.
Actually I have found a way but I'm stuck.
I have query1. it contains the fieldlist.
I'm creating a parameter list in another query from this fieldlist.
I'm updating the parameters field by field.
This is not very convenient
Can someone give me a easy ways to do this.
Note: I prefer coding this job with only standard components. I don't want to install additional components.
Maybe not the reply you want. I think you need to raise the abstraction level. You need to skip SQL. An ORM framework can do this for you. It maybe feels like a big step for you but I promise it is also a relief to just use code like:
Person.name := 'Bob';
Invoice.customer.address.street := 'Abbey road';
Edit1.text := Invoice.customer.name;
To actually update database you need to call an update method that differ depending on framework. For a list of frameworks see here. I am also aware of TMS Aurelius. I use Bold on daily use. Bold also have features like OCL, derived attributes and links in the model, some boldaware components (it updates whenever db changes). But it has one big disadvantage. It is only available for D2006/D2007. I am working for a solution on this because I think it is the best and most mature ORM framework for Delphi. See also my blog on Bold for Delphi. Ask if you have questions!
You take the fieldlist from your query.
Create a new query with parameters.
And fill in the values.
Something like this:
const
TableNameEscapeStart = '['; //SQL server, use '`' for MySQL
TableNameEscapeEnd = ']'; //SQL server, use '`' for MySQL
FieldNameEscapeStart = '[';
FieldNameEscapeEnd = ']';
function CreateInsertStatementFromTable1ToTable2(Table1, Table2: TTable): String;
var
i: integer;
comma: string;
begin
i:= 0;
Result:= 'INSERT INTO '+TableNameEscapeStart + Table2.TableName + TableNameEscapeEnd + ' (';
comma:= ' , '
while i < Table1.FieldCount do begin
if (i = Table1.FieldCount -1) then begin comma:= ' '; end;
Result:= Result + FieldNameEscapeStart + Table1.Fields.Field[i].Name + FieldNameEscapeEnd + comma;
end;
Result:= Result +' ) VALUES ( ';
i:= 0;
comma:= ' , '
while i < Table1.FieldCount do begin
if (i = Table1.FieldCount -1) then begin comma:= ' '; end;
Result:= Result +':' + IntToStr(i+1) + comma;
end; {while}
Result:= Result + ' ); ';
end;
There are three avenues for SQL injection here.
1. The field values
2. The table name
3. The field names
The first is covered by the use of parameters.
The second and third are covered, because you're using the table and field names of the table directly.
If you don't have a trusted source of table and fields names, then you need to compare these against the table and fieldnames obtained directly from the table.
See: Delphi - prevent against SQL injection
You insert the data using ParamByName (slowly) or more efficiently using Param[i] where i starts at 0.
In MySQL it's even easier:
If table1 and table2 have the same fields, the following SQL will insert all data in table2 into table1:
INSERT INTO table1 SELECT * FROM table2;
I have a problem when trying to apply a filter to a ADO dataset in Delphi XE2 ...
Filter := ' [Name] like ''%john'' ';
It raises an exception:
Project Test.exe raised exception class EOleException with message
'Arguments are of the wrong type, are out of acceptable range,
or are in conflict with one another'. Process stopped.
but when using:
Filter := ' [Name] like ''john%'' '
it works fine!
why?
The Operator can only be one of the following:
= < > <= >= <> LIKE
If you use the LIKE operator, you can also use the * or % wildcards as the last character in the string or as the first and last character in the string.
http://www.devguru.com/technologies/ado/quickref/recordset_filter.html
You can catch the filter on the event OnFilterRecord:
procedure TForm1.ADODataSet1FilterRecord(DataSet: TDataSet; var Accept: Boolean);
var
iPos: Integer;
begin
iPos:= pos('john',ADODataSet1name.AsString);
if (iPos>0) and
(iPos = length(ADODataSet1name.AsString)-3) then
begin
Accept:= True;
end
else
begin
Accept:= False;
end;
end;
or
function TForm1.IsLastCriteria(AText: String): Boolean;
var
iPos: Integer;
begin
iPos:= pos(AText,ADODataSet1name.AsString);
Result:= (iPos>0) and
(iPos = length(ADODataSet1name.AsString)-length(AText)-1);
end;
procedure TForm1.ADODataSet1FilterRecord(DataSet: TDataSet; var Accept: Boolean);
begin
Accept:= IsLastCriteria('john');
end;
AS. I still ask you to read http://www.catb.org/esr/faqs/smart-questions.html#beprecise and describe your environment accordingly.
What is database server ?
What version are MDAC/ADO components?
What the query is ?
What is Name column type in SQL ?
You are "upping" the comments, that si nice. But you don't answer the questions. And that is not nice. We are not ESPers, we canot read you mind.
I put you few suggestions in comments above. Did you tried them ? Did they worked ? To quote them:
Maybe you can update ADO/MDAC to 2.8sp1 version. Referenced MSDN KB articles are told to apply to MDAC up to 2.7 version. Maybe 2.8sp1 no more has that limitation.
Maybe you can use some data-server specific tricks like copying last 4 letters of Name into a separate column.
Maybe you can move that condition into SQL SELECT WHERE clause and re-open the query.
Maybe there is event-handler like TBDEDataSet.OnFilterRecord
There are 4 workarounds and you either did not tried them or did not reported the results. Not nice.
Idea #1 is self-explanatory
Idea #3 was detailed by #SertacAkyuz as far as he could do it, giving lack of information about your program.
Idea #4 was detailed by #Ravaut123
Idea #2 is outlined below
Assuming your "ADO database" is backed by Microsoft Access, and according to http://www.databasedev.co.uk/access-sql-string-functions.html ...
Or using MS SQL and according to http://msdn.microsoft.com/en-us/library/ms177532.aspx ...
Or...
Query.SQL.Text := 'select Right(Name, 4) as name_tail, * from table where ...'
See that extra column added to query! 4 is length for "john" there.
With query like that the following filtering conditions are to be equivalent:
Filter := ' [Name] like ''%john'' ';
Filter := ' [name_tail] = ''john'' ';
However, if on some rows Name column is shorter than 4 letters, i don't know what RIGHT function would do. Maybe it will truncate the result, or maybe throw an error and abort the query. The latter maybe - depending on the real data - maybe can be alleviated by padding with spaces to the 4 length like
LTrim(Right(' ' || Name, 4)) as name_tail
That is for you to test, since only you know the details of your environment.
The simplest answer to this question was mentioned in comments.
If you used asterisk instead of percent symbol, everything would work great
Filter := ' [Name] like ''*john'' ';