We had an outage on an important application last Thursday. A particular SQL statement locked up our database with library cache: mutex X waits. I worked with Oracle support to find a bug that caused the issue and we came up with a good workaround. The bug caused a bunch of shared cursor entries. So, I wanted to run a test on a test database to recreate the excess shared cursor entries. I wanted to run the SQL query that caused the outage a bunch of times. Also, we embed the SQL query inside a PL/SQL procedure so I wanted to run the query by calling the procedure. So, I needed to come up with a bunch of calls to the procedure using realistic data as a test script. This blog post is about the decision I had to make about creating the test script. Would I use SQL or Python to quickly hack together my test script? I thought it would be interesting to write about my choice because I am working on my Python for the Oracle DBA talk that encourages Oracle DBAs to learn Python. In this situation I turned to SQL instead of Python so what does that say about the value of Python for Oracle DBAs?
Let me lay out the problem that I needed to solve. Note that I was trying to get this done quickly and not spend a lot of time coming up with the perfect way to do it. I had over 6000 sets of bind variable values that the problem query has used in the past. I used my bind2.sql script to get some sample bind variable values for the problem query. The output of bind2.sql was in this format:
2017-11-27 15:08:56 :B1 1 2017-11-27 15:08:56 :B2 ABC 2017-11-27 15:08:56 :B3 JAFSDFSF 2017-11-27 15:08:56 :B4 345 2017-11-27 15:08:56 :B5 6345 2017-11-27 15:08:56 :B6 10456775 2017-11-27 15:08:56 :B7 34563465 2017-11-27 15:08:56 :B8 433 2017-11-27 15:09:58 :B1 1 2017-11-27 15:09:58 :B2 JUL 2017-11-27 15:09:58 :B3 KSFJSDJF 2017-11-27 15:09:58 :B4 234 2017-11-27 15:09:58 :B5 234253 2017-11-27 15:09:58 :B6 245 2017-11-27 15:09:58 :B7 66546 2017-11-27 15:09:58 :B8 657576 2017-11-27 15:10:12 :B1 1 2017-11-27 15:10:12 :B2 NULL 2017-11-27 15:10:12 :B3 NULL 2017-11-27 15:10:12 :B4 45646 2017-11-27 15:10:12 :B5 43 2017-11-27 15:10:12 :B6 3477 2017-11-27 15:10:12 :B7 6446 2017-11-27 15:10:12 :B8 474747
I needed to convert it to look like this:
exec myproc(34563465,10456775,345,433,6345,'JAFSDFSF','ABC',1,rc); exec myproc(66546,245,234,657576,234253,'KSFJSDJF','JUL',1,rc); exec myproc(6446,3477,45646,474747,43,'NULL','NULL',1,rc);
I gave myself maybe a minute or two to decide between using SQL or Python. I choose SQL. All I did was insert the data into a table and then manipulate it using SQL statements. Note that the order of the arguments in the procedure call is not the same as the order of the bind variable numbers. Also, some are character and some are number types.
Here is the SQL that I used:
drop table bindvars; create table bindvars (datetime varchar2(20), varname varchar2(2), varvalue varchar2(40)); insert into bindvars values ('2017-11-27 15:08:56','B1','1'); insert into bindvars values ('2017-11-27 15:08:56','B2','ABC'); insert into bindvars values ('2017-11-27 15:08:56','B3','JAFSDFSF'); insert into bindvars values ('2017-11-27 15:08:56','B4','345'); insert into bindvars values ('2017-11-27 15:08:56','B5','6345'); insert into bindvars values ('2017-11-27 15:08:56','B6','10456775'); insert into bindvars values ('2017-11-27 15:08:56','B7','34563465'); insert into bindvars values ('2017-11-27 15:08:56','B8','433'); insert into bindvars values ('2017-11-27 15:09:58','B1','1'); insert into bindvars values ('2017-11-27 15:09:58','B2','JUL'); insert into bindvars values ('2017-11-27 15:09:58','B3','KSFJSDJF'); insert into bindvars values ('2017-11-27 15:09:58','B4','234'); insert into bindvars values ('2017-11-27 15:09:58','B5','234253'); insert into bindvars values ('2017-11-27 15:09:58','B6','245'); insert into bindvars values ('2017-11-27 15:09:58','B7','66546'); insert into bindvars values ('2017-11-27 15:09:58','B8','657576'); insert into bindvars values ('2017-11-27 15:10:12','B1','1'); insert into bindvars values ('2017-11-27 15:10:12','B2','NULL'); insert into bindvars values ('2017-11-27 15:10:12','B3','NULL'); insert into bindvars values ('2017-11-27 15:10:12','B4','45646'); insert into bindvars values ('2017-11-27 15:10:12','B5','43'); insert into bindvars values ('2017-11-27 15:10:12','B6','3477'); insert into bindvars values ('2017-11-27 15:10:12','B7','6446'); insert into bindvars values ('2017-11-27 15:10:12','B8','474747'); commit; drop table bindvars2; create table bindvars2 as select b1.varvalue b1, b2.varvalue b2, b3.varvalue b3, b4.varvalue b4, b5.varvalue b5, b6.varvalue b6, b7.varvalue b7, b8.varvalue b8 from bindvars b1, bindvars b2, bindvars b3, bindvars b4, bindvars b5, bindvars b6, bindvars b7, bindvars b8 where b1.datetime = b2.datetime and b1.datetime = b3.datetime and b1.datetime = b4.datetime and b1.datetime = b5.datetime and b1.datetime = b6.datetime and b1.datetime = b7.datetime and b1.datetime = b8.datetime and b1.varname = 'B1' and b2.varname = 'B2' and b3.varname = 'B3' and b4.varname = 'B4' and b5.varname = 'B5' and b6.varname = 'B6' and b7.varname = 'B7' and b8.varname = 'B8'; select 'exec myproc('|| B7||','|| B6||','|| B4||','|| B8||','|| B5||','''|| B3||''','''|| B2||''','|| B1||',rc);' from bindvars2;
I hacked the insert statements together with my Textpad text editor and then loaded the rows into a little table. Then I built a new table which combines the 8 rows for each call into a single row with a column for each bind variable. Finally I queried the second table generating the procedure calls with single quotes, commas and other characters all in the right place.
Now that the rush is past and my testing is done I thought I would hack together a quick Python script to do the same thing. If I had chosen Python how would have I done it without spending a lot of time making it optimal? Here is what I came up with:
Instead of insert statements I pulled the data into a multi-line string constant. Then I split it into a list of strings with each string representing a line. Then I split each line into space delimited strings so each line would have date,time,bind variable name, bind variable value. Finally I looped through each set of 8 lines extracting the bind variable values and then printing the bind variables in the correct order and format.
These are two quickly hacked together solutions. I think the key point is how I stored the data. With SQL I used tables. With Python I used lists. I’m not sure which I like better in this case. I’ve been doing SQL longer but Python wasn’t really harder. I guess my decision under pressure to use SQL shows that I still have more comfort with the SQL way of doing things, but my after the fact Python hacking shows that the Python solution was not any harder. FWIW.
Bobby
Pingback: Should I use SQL or Python? - SSWUG.ORG
Interesting. I find myself using Python quite often to generate longwinded SQL statements which are saved to a text file, then I use a CLI tool (snowsql cli interface) to call to the DB and generate a csv. Usually if I need to do a bunch of regex counts to get new columns for each pattern, and maybe have dozens or hundreds of patterns. Then csv –> pandas –> get on with the thing.
Interesting. Thank your for our reply. The scripting that you describe reminds me of some of the scripts that we have on our Oracle datawarehouses. But they use SQL, PL/SQL, and shell scripts instead of Python. I’m so used to only having those kinds of tools I’m still growing in my use of Python.