Tuesday, April 14, 2009

PL/SQL Cursor For Loop with Bulk Collect and Limit

Here is an cursor example of how to use a BULK COLLECT with a LIMIT clause. DevNote: %NOTFOUND check must be at the end of the loop

declare

   PROCEDURE bulk_with_limit (
      n_start_in     IN    CONTRACT.CONTRACT_ID%TYPE,
      n_end_in       IN    CONTRACT.CONTRACT_ID%TYPE,
      limit_in       IN    PLS_INTEGER := 100
   )
   IS
      CURSOR contracts_cur
      IS
         SELECT *
         FROM contract
         WHERE contract_id between n_start_in and n_end_in;

      TYPE contract_trec IS TABLE OF contracts_cur%ROWTYPE INDEX BY PLS_INTEGER;

      l_contracts   contract_trec;

   BEGIN
      OPEN contracts_cur;

      LOOP
         FETCH contracts_cur
            BULK COLLECT 
            INTO l_contracts 
            LIMIT limit_in;

         -- Perform business logic ... 
         FOR i IN 1 .. l_contracts.COUNT LOOP
            dbms_output.put_line(lpad(' ',5 - length(i))||i||' : '||l_contracts(i).contract_id);
         END LOOP;

         -- DevNote: %NOTFOUND check must be at the end of the loop 
         EXIT WHEN contracts_cur%NOTFOUND;

      END LOOP;

      CLOSE contracts_cur;
   END bulk_with_limit;

begin
   bulk_with_limit(n_start_in => 10, n_end_in => 100000, limit_in => 100);
end;

Monday, March 30, 2009

Count occurences of a character in a string (Oracle)

Recently, I needed to see how many times the "@" [at sign] was in a particular column more than once.

select
   :str as "string",
   :chr as "character",
   length(:str) - length(translate(:str,chr(0)||:chr,chr(0))) as "count"
from dual;
select *
from 
(
   select
      ia.internet_address_id as "InternetAddressID",
      ia.internet_address as "string",
      '@' as "character",
      length(ia.internet_address) - length(translate(ia.internet_address,chr(0)||'@',chr(0))) as "occurences"
   from internet_address ia
)
where "occurences" > 1;

Wednesday, January 21, 2009

Syntax Highlighting Posted Code Snippets

For years, I have struggled with how to share my code snippets on the web. I wanted the "fancy" look, but I didn't want to waste a bunch of time converting my code to html/css.

I'd see some sites that had a nice look, but when I looked under the covers, they were invoking proprietary server side engines to generate the the "pretty print code".

Today, I found the answer on GoogleCode: Syntaxhighlighter, a free DYNAMIC Javascript-based syntax highlighter.

The only change that I made was to go

From:

<pre>
... some C# code here ...
</pre>
To:
<pre name="code" class="C#">
... some C# code here ...
</pre>
It supports:
  • C#
  • Java
  • XML
  • PHP
  • Python
  • SQL
  • VB
  • C++
  • CSS
  • Html
  • Delphi
I'm really impressed with how well it looks.

P.S. For those using Blogger/BlogSpot, I had to host the Javascript (.js) files on GoogleSites.

Tuesday, January 20, 2009

Redirects (Html, ASP.Net, JSP, ...)

Html (not generally recommended)

<html>
<head>
<META HTTP-EQUIV="Refresh"CONTENT="0; URL=http://www.new-url.com">
</head>
<body>
This page is redirecting your browser to www.new-url.com<br>
Please, click <a href="http://www.new-url.com">here</a> if redirection doesn't work.
</body>
</html>
ASP.Net
<script runat="server">
private void Page_Load(object sender, System.EventArgs e)
{   
   Response.Status = "301 Moved Permanently";
   Response.AddHeader("Location","http://www.new-url.com/");
}
</script>
JSP
<%
response.setStatus(301);
response.setHeader("Location", "http://www.new-url.com/" );
response.setHeader("Connection", "close" );
%>

Tuesday, December 02, 2008

Outlook - Red "X" on Image Paste

Delete the contents of the folder that is at "string value" RegEdit
Path : My Computer\HKEY_CURRENT_USER\Software\Microsoft\Office\11.0\Outlook\Service
String Name : OutlookSecureTempFolder
String Value : D:\Documents and Settings\JVogel\Local Settings\Temporary Internet Files\OLK41\

Tuesday, November 25, 2008

Html Fieldset Rounded Corners - Internet Explorer (IE) and Firefox

I don't usually prefer anything about Internet Explorer. However, IE's default presentation for the Html fieldset tag just looks nicer. Firefox, on the otherhand, defaults to an ugly square box presentation. So, to try to get Firefox to look more like IE (something I never thought that I would say) ...

fieldset { 
   -moz-border-radius-bottomleft:7px;
   -moz-border-radius-bottomright:7px;
   -moz-border-radius-topleft:5px;
   -moz-border-radius-topright:7px;
   border-radius: 3px;
}
I confess this only addresses IE and Firefox. Supposedly, Safari and Chrome will support rounded corners under their CSS3 support (eta?). I tried -webkit-border-radius: 3px;, but it never rendered correctly in the versions of Safari and Chrome that I have. I'm also assuming that Opera falls in this same category.

If you absolutely must have rounded corners: http://www.spiffycorners.com. However, I caution you against the author's approach because it requires too much Html for an effect that will be cleanly implemented in CSS3. Again, this is only a nice to have.

Example:

Title contents

Wednesday, November 05, 2008

ASP.Net - Dynamically Adding a CSS/StyleSheet Reference at run-time

Per Michael, I have removed a technique that was only available in Beta 1.

protected void Page_Init(object sender, EventArgs e)
{
   HtmlLink link = new HtmlLink();
   link.Href = "~/StyleSheet.css";
   link.Attributes.Add("rel", "stylesheet");
   link.Attributes.Add("type", "text/css");
   Page.Header.Controls.Add(link);
}

Tuesday, October 07, 2008

Print.css

Recently, I ran into a case where a user's printout of an order confirmation was cropping a portion along the rights side of the page.

By default, almost all browers print in "Scale-to-Fit" mode.  This particular user happened to be using Print at "100%".

Changed our html/aspx page to the following...

Html

<head id="head" runat="server">
   <link href="/shared/css/style.css" type="text/css" rel="stylesheet">
   <link href="/shared/css/print.css" type="text/css" rel="stylesheet" media="print">
<head>

CSS

body
{
background: white;
margin: 0;
font-size: 0.75em;
width: 660px;
}

#PageContainer
{
width:640px;
}

a:link, a:visited
{
color: #520;
background: transparent;
font-weight: bold;
text-decoration: underline;
}

Tuesday, August 12, 2008

Getting the current Oracle version (SQL Select)

Queries :

select *
from v$version;

select *
from product_component_version;
Results :
Oracle Database 11g Enterprise Edition Release 11.1.0.6.0 - 64bit Production PL/SQL Release 11.1.0.6.0 - Production CORE 11.1.0.6.0 Production TNS for Solaris: Version 11.1.0.6.0 - Production NLSRTL Version 11.1.0.6.0 - Production

PRODUCTVERSIONSTATUS
NLSRTL11.1.0.6.0Production
Oracle Database 11g Enterprise Edition11.1.0.6.064bit Production
PL/SQL11.1.0.6.0Production
TNS for Solaris:11.1.0.6.0Production

Thursday, August 07, 2008

gzip Http Compression in IIS

References URLS:

In order to enable compression (herein gzip), there are several steps that have to be completed.

Enable Compression in IIS

  • From the IIS snap-in, right-click on the Web Sites node and click on Properties
  • Select the Service tab - Enable Compress application files
  • Enable Compress static files
  • Change Temporary Directory to the folder that you created above, or leave it at it's default
  • Set the max size of the temp folder to something that the hard drive can handle. e.g. 1000.
  • Save and close the Web Site Properties dialog

Note: The temporary compress directory is only used for static pages. Dynamic pages aren't saved to disk and are recreated every time so there is some CPU overhead used on every page request for dynamic content.

<IIsCompressionScheme Location ="/LM/W3SVC/Filters/Compression/deflate"
  HcCompressionDll="%windir%\system32\inetsrv\gzip.dll"
  HcCreateFlags="0"
  HcDoDynamicCompression="TRUE"
  HcDoOnDemandCompression="TRUE"
  HcDoStaticCompression="FALSE"
  HcDynamicCompressionLevel="0"
  HcFileExtensions="htm
    html
    txt
    css"
  HcOnDemandCompLevel="10"
  HcPriority="1"
  HcScriptFileExtensions="asp
    dll
    exe
    aspx
    asmx
    js"
>
</IIsCompressionScheme>
<IIsCompressionScheme Location ="/LM/W3SVC/Filters/Compression/gzip"
  HcCompressionDll="%windir%\system32\inetsrv\gzip.dll"
  HcCreateFlags="1"
  HcDoDynamicCompression="TRUE"
  HcDoOnDemandCompression="TRUE"
  HcDoStaticCompression="TRUE"
  HcDynamicCompressionLevel="0"
  HcFileExtensions="htm
    html
    txt
    css"
  HcOnDemandCompLevel="10"
  HcPriority="1"
  HcScriptFileExtensions="asp
    dll
    exe
    aspx
    asmx
    js"
>
</IIsCompressionScheme>

From a CMD prompt, iisreset stop

Now save metabase.xml

From a CMD prompt, iisreset

Wednesday, April 16, 2008

Prototyping in ASP.Net - asp:SqlDataSource Example

Recently, I've been doing a bunch of ASP.Net. One of ASP.Net's best features is its ability to be used for realistic rapid prototyping.

Here is a simple example that executes a SQL query and displays the results.

In the aspx... (be sure and notice the "$" in the connection string)

<asp:sqldatasource id="SqlDSCoreCoverages" runat="server" connectionstring="<%$ ConnectionStrings:ConnectionString_DEV %>" >"
       ProviderName="Oracle.DataAccess.Client"
       SelectCommand="
             select coverage_description, display_order
             from prod_cov
        whereid = :argProductVersionId
             order by display_order
       "
       EnableCaching="True">
    </asp:SqlDataSource>

<table cellpadding="3" style="border: 1px #333333 solid;">
                <tr><td>
                   <asp:gridview id="GridView1" runat="server" datasourceid="SqlDSCoreCoverages" autogeneratecolumns="False" gridlines="None">
                      <headerstyle cssclass="SectionHeader">
                      <columns>
                         <asp:BoundField DataField="coverage_description"  HeaderText="Included Core Coverages"
                            ReadOnly="True" SortExpression="coverage_description" />
                      </columns>
                   </asp:GridView>
                </td></tr>
             </table>

In the aspx.cs [codebehind] ...

this.SqlDSCoreCoverages.SelectParameters.Add("argProductVersionId", productVersionId.ToString());

this.SqlDSCoreCoverages.DataBind();

Thursday, April 10, 2008

Feuerstein's article on BULK COLLECT and %NOTFOUND

Steven Feuerstien wrote an informative article (http://www.oracle.com/technology/oramag/oracle/08-mar/o28plsql.html) on using BULK COLLECT with LIMIT in a "reasonable" manner. Much of this content is drawn from that article. Basically, the use the LIMIT construct requires a secondary loop to process the smaller "batch". Our DBAs require us to do this kind of "batching" to put less strain on Replication. Smaller COMMITs going across the pipe rather than a 100,000 COMMIT. In otherwords, "commit every x number of rows." [Search engine fodder]

PROCEDURE process_all_rows (limit_in IN PLS_INTEGER DEFAULT 100)
IS
   CURSOR employees_cur
   IS
       SELECT * FROM employees;

   TYPE employees_aat IS TABLE OF employees_cur%ROWTYPE
       INDEX BY PLS_INTEGER;

   l_employees employees_aat;
BEGIN  
   OPEN employees_cur;
   LOOP
       FETCH employees_cur
           BULK COLLECT INTO l_employees LIMIT limit_in;

       FOR indx IN 1 .. l_employees.COUNT LOOP
           update_compensation (l_employees(indx));
       END LOOP;

       COMMIT; -- Commit batch of size LIMIT 

       EXIT WHEN l_employees.COUNT < limit_in;

   END LOOP;

   CLOSE employees_cur;
END process_all_rows;
To summarize his second article, don't use %NOTFOUND with BULK COLLECT and LIMIT. Use .COUNT. Secondary Example
set serveroutput on;
clear;

declare
  CURSOR contracts_cur
   IS
       SELECT * FROM contract where rownum <= 50;

   TYPE t_contract IS TABLE OF contracts_cur%ROWTYPE
       INDEX BY PLS_INTEGER;

   l_contract t_contract;
   
BEGIN  
   OPEN contracts_cur;
   LOOP
       FETCH contracts_cur
           BULK COLLECT INTO l_contract LIMIT 10;

       FOR indx IN 1 .. l_contract.COUNT LOOP
        common_func.display_output(indx||' : '||l_contract(indx).contract_id);
       END LOOP;

       EXIT WHEN l_contract.COUNT = 0;

   END LOOP;

   CLOSE contracts_cur;
   
   EXCEPTION 
    WHEN OTHERS THEN 
       common_func.display_output(SQLERRM);
   
END;

Thursday, March 20, 2008

Handling ORA-04068: Existing state of packages has been discarded

How to deal with the frustrating ORA-04068: existing state of packages has been discarded error that crops up while in the circular cycle of "code..execute..debug".

ORA-04068: existing state of packages has been discarded
ORA-04061: existing state of package "PKG.PROC" has been invalidated
ORA-04065: not executed, altered or dropped package "PKG.PROC"
ORA-06508: PL/SQL: could not find program unit being called
Previously, the only thing I knew to do was logout and then log back in. This works, but it is kind of a waste of time. I knew there had to be a better way because my PL/SQL Editor (PL/SQL Developer) doesn't require this step. I probably could have asked the guys at AllAround, but I think that I found the answer:
begin
DBMS_SESSION.RESET_PACKAGE;
-- execute routine... 
end;
Thanks, Jason

Tuesday, January 15, 2008

Upsert / Merge within the Same Table (Oracle)

Okay, I'm a huge fan of using Oracle's %ROWTYPE to define records structures. I love writing code like the following:

DECLARE

   rec_contract  CONTRACT%ROWTYPE := Null;

BEGIN

   -- ... retrieval logic and business logic ...

   -- "Set" logic
   UPDATE CONTRACT
   SET ROW = rec_contract
   WHERE CONTRACT_ID = rec_contract.CONTRACT_ID;

   IF (SQL%ROWCOUNT = 0) THEN
      INSERT INTO CONTRACT
      VALUES rec_contract;
   END IF;
END;
Code like the above is really handle for writing "setters". It can easily be extended to handle DELETE logic too. Now, recently I need to copy a bunch of data within the table but with a different primary key. I started to write a bunch of code like the above, but I just needed some one of code. To cut to the chase, the code below is an example of how to do an "UPSERT" (like a MERGE) but within the same table [which is impossible with the MERGE command]. Source:
drop table test;
clear;

create table test(
   id number(10),
 name varchar2(50)
);

insert into test(id,name) values (1,'First row');

insert into test
   using select 2,'Row two' from dual;

insert into test
   using select 3,'Row three - unmodified' from dual;

commit;

select * from test;

update test set (id,name) = (select 3,'Row three - modified' from test where id = 3) where id = 3;

commit;

select * from test;
Results:
Table created
 
1 row inserted
 
1 row inserted
 
1 row inserted
 
Commit complete
 
         ID NAME
----------- --------------------------------------------------
          1 First row
          2 Row two
          3 Row three - unmodified
 
1 row updated
 
Commit complete
 
         ID NAME
----------- --------------------------------------------------
          1 First row
          2 Row two
          3 Row three - modified

Wednesday, October 17, 2007

Suzuki Katana - Tire Pressure

I kept misplacing my notes about the tire pressure for my 2001 Katana 750... so I'm putting them here. 220 35/38 250 36.5/39.5 Per http://www.drivegreen.com/Motorcycle_Tire_Pressure/SUZUKI.shtml, GSX750 Katana (1989-2002) 34 36

Friday, October 12, 2007

Oracle: InstantClient, TNSPING, and PL/SQL Developer

Well, anyone that has tried to use tnsping with Oracle's InstantClient (http://www.oracle.com/technology/tech/oci/instantclient/index.html) was in for a big nasty surprise... it's not included. Not only is not included, you can't download a version that is compatible with InstantClient. I've been a fan of tnsping for some time. It has always been valuable as a quick way to test the "reachability" factor of a particular database. I now have to admit that it's not required since SQLPlus technically can perform this functionality. Well, habits die hard. Goal: Get InstantClient working with tnsping and AllAroundAutomation's PL/SQL Developer (http://www.allroundautomations.nl/plsqldev.html). Solution: Disclaimer:

  1. I don't want to tell you how long it took me to figure this all out, but it was way too long.
  2. It's an unacceptable solution from a long-term maintenance, support standpoint.
Steps:
  1. Download the appropriate InstantClient "base" (in other words, English-only or International).
  2. Download the InstantClient SQL-Plus "add-on".
  3. Extract both Zip files into the same directory [for this example, "c:\oracle\instantclient"].
  4. At this point, we haven't done anything special.
  5. Define an environment variable oracle_home that points to the location of your InstantClient directory (e.g.: "c:\oracle\instantclient").
  6. Create a subdirectory called "network" (c:\oracle\instantclient\network).
  7. In "network", create a subdirectory called "admin".
  8. If desired, put your tnsnames.ora and sqlnet.ora in c:\oracle\instantclient\network\admin [technically, this isn't required but it follows Oracle's "standards"].
  9. Define an environment variable tns_admin that points to the location of your tnsnames.ora (if you are using one).
  10. In "network", create a subdirectory called "mesg".
  11. From an Oracle 10.x Fat client installation, copy tnsus.msb into c:\oracle\instantclient\network\mesg. This is the file that tnsping uses to "lookup" the message text while running.
  12. At this point, you should be able to fire up PL/SQL Dev and still have things work.
  13. Okay, now the weird part starts.
  14. Create a subdirectory called "bin" (c:\oracle\instantclient\bin), and yes, it must be called bin.
  15. From an Oracle 10.x Fat client installation the following files into c:\oracle\instantclient\bin:
    • heteroxa10.dll
    • oci.dll
    • ocijdbc10.dll
    • ociw32.dll
    • oraclient10.dll
    • oracommon10.dll
    • oracore10.dll
    • orageneric10.dll
    • orahasgen10.dll
    • oraldapclnt10.dll
    • oran10.dll
    • orancds10.dll
    • orancrypt10.dll
    • oranhost10.dll
    • oranl10.dll
    • oranldap10.dll
    • oranls10.dll
    • orannzsbb10.dll
    • oranro10.dll
    • orantcp10.dll
    • orantns10.dll
    • oraocci10.dll
    • oraociicus10.dll
    • oraocr10.dll
    • oraocrb10.dll
    • oraplp10.dll
    • orapls10.dll
    • ORASLAX10.DLL
    • orasnls10.dll
    • orasql10.dll
    • Orasqlplusic10.dll
    • oraunls10.dll
    • orauts.dll
    • orauts_imp.dll
    • oravsn10.dll
    • oraxml10.dll
    • sqora32.dll
    • sqoras32.dll
    • sqresja.dll
    • sqresus.dll
    • tnsping.exe
  16. Now, copy all the files from c:\oracle\instantclient into c:\oracle\instantclient\bin except those that already exist.
At this point, InstantClient is "valid", PL/SQL Developer works [but it's actually using c:\oracle\instantclient\bin, and tnsping should work from c:\oracle\instantclient\bin. Talk about smells bad. So if you want tnsping for InstantClient, create this nightmare-ish drink from this recipe or complain to Oracle and ask for a InstantClient-compatible version of tnsping. Jason

Thursday, August 09, 2007

Google Search

I recently discovered that my Blog wasn't being picked up correctly by Google. A little odd since Blogger/Blogspot is owned by Google. I hoping that it is just an issue with bad keywords/meta-tags.

Monday, February 19, 2007

Rails : Maintaining an Oracle connection (preventing a timeout)

Kubo posted a good note about how to maintain an "active" connection to Oracle from Rails. Add the following code at the end of 'config/environment.rb' as a workaround.

Thread.new(ActiveRecord::Base.connection.raw_connection) do |conn|
  # run a SQL every one hour.
  while true
    sleep(3600)
    conn.exec('select 1 from dual') {}
  end
end

Friday, February 02, 2007

Ruby : WebService - GetQuote

require 'soap/wsdlDriver'

wsdl = "http://www.webservicex.net/stockquote.asmx?wsdl"

params = {:symbol => "IBM"}

drv = SOAP::WSDLDriverFactory.new(wsdl).create_rpc_driver
drv.wiredump_dev = STDOUT if $DEBUG
result = drv.GetQuote(params)

puts "\n\nGetQuote : "+result.inspect+"\n\n"

if (result != nil)

 puts "\n\Result.getQuoteResult.methods : "+result.getQuoteResult.methods.sort.join("\n").to_s+"\n\n"
#   puts "Symbol : "+result.getQuoteResult.stockQuotes.Stock.Symbol

end

Wednesday, January 31, 2007

Ruby - Code Snippet - Methods List

Just a snippet how to print out a sorted list of an object's methods.

puts "\n\object.methods : "+ object.methods.sort.join("\n").to_s+"\n\n"

Wednesday, December 13, 2006

Ruby - Default Values for Method Arguments

>> def test(args)
>  >>   puts args.values_at(:a, :b, :c)
>  >> end
> => nil
>  >> test :a => 1, :c => 2

def test(args)
>>   args = { "a" => "default a",
?>            "b" => "default b",
?>            "c" => "default c" }.merge(args)
>>   puts args.values_at("a", "b", "c")
>> end
=> nil
>> test "a" => "some a", "c" => "some c"
some a
default b
some c
=> nil
Source :
def test(args)
  args[:a] ||= 'defaultA'
  args[:b] ||= 'defaultB'
  args[:c] ||= 'defaultC'
  puts args.indexes(:a, :b, :c)
end
Result: Breaks when...
test :a => false
test :a => nil
test 'a' => true

Ruby - How do I catch a missing method on a passed block

J.Vogel - Copied... from Erik V. - http://www.erikveen.dds.nl/

module Kernel
  def block_with_method_missing(block1, &block2)
    class << block1
      self
    end.module_eval do
      define_method(:method_missing, &block2)
      define_method(:call){block1.instance_eval(&block1)}
    end

    block1
  end
end

class Foo
  def bar(&block)
    block = method_missing_inspect(block)

    block.call
  end

  def method_missing_inspect(block)
    block_with_method_missing(block) do |method_name, *args|
      p [:mm, method_name, args]
    end
  end
end

Foo.new.bar{baz(1, 2, 3)}

Wednesday, December 06, 2006

Rails : View - Partials

Partials are awesome...

<%= render :partial => "coverages"  unless @base_coverages.blank? %>

Rails - Automatic Reloading and Models

I'm fairly new to RoR. I have to admit that I'm pretty excited about exiting the Code-Compile-Deploy-Run cycle because of config.cache_classes = false in \environments\development.rb. However, I've discovered that doesn't apply to Models. Models are loaded at server startup. Therefore, we're back to the stop..start to pickup Models changes. I haven't tried either of these yet! Two options:

Ruby: View - Inserting Text

<%= "hello" %>
or
<%= concat("hello", binding) %>

Rails: Snippet - View - Render a Object into a Table

Please don't get all bent out of shape. My Rails code isn't perfect, but I wanted to post some of my snippets that do work!

<% if (@base_coverages != nil) && (@base_coverages.length > 0) %>
<>
<% @base_coverages.each do |i| %>
<>">
  <><%= i.coverage_id %>< /td>
  <><%= i.external_description %>< /td>
< /tr>
<% end %>
< /tbody>< /table>
<% end %>

Ruby : Defaulted optional arguments

I'd seen this technique, but I'd lost it.

def placeholder(requiredarg, options = {})
  # set default options
  o = { :class => 'placeholder', :tag => 'p' }.merge(options)

  ...
end

Tuesday, December 05, 2006

Rails - Value Checking

These are all options posted to me:

  • return [] unless myvalue
  • return unless arguments.all?

    'arguments.all?' will return true if and only if none of the collection members is false or nil.

    If an argument can be false, you could do this:

    return unless arguments.all? { |arg| !arg.nil? }
  • raise ArgumentError, "Parameter X is required" if x.blank?

Wednesday, November 29, 2006

Enterprise Ruby / Rails : Links to Review

This is just a blog note to myself to review :

Jason

Monday, November 27, 2006

PL/SQL : Example using SysTimeStamp (milliseconds for SysDate)

Oracle's SYSDATE only goes to the second. If you want more precision, then you need to use the new SYSTIMESTAMP (available starting in Oracle 9). Source :

declare

ltimestamp_start       timestamp;
ltimestamp_stop        timestamp;
linterval_diff         interval day to second;

ldt_temp               date;

begin

ltimestamp_start := systimestamp;
dbms_output.put_line(ltimestamp_start);

  -- Routine to performance test... 
  for i in 1 .. 10000 loop
    select sysdate into ldt_temp from dual;
  end loop;

ltimestamp_stop := systimestamp;
dbms_output.put_line(ltimestamp_stop);

linterval_diff := ltimestamp_stop - ltimestamp_start;

dbms_output.put_line(CHR(10)||LPAD('=',22,'=')||CHR(10));
dbms_output.put_line('  Runtime Difference'||CHR(10)||LPAD('=',22,'='));
dbms_output.put_line(
     '     Days : '||EXTRACT(DAY FROM linterval_diff)||CHR(10)||
     '    Hours : '||EXTRACT(HOUR FROM linterval_diff)||CHR(10)||
     '  Minutes : '||EXTRACT(MINUTE FROM linterval_diff)||CHR(10)||
     '  Seconds : '||EXTRACT(SECOND FROM linterval_diff) );
end;
Results :
27-NOV-06 01.26.50.118424 PM
27-NOV-06 01.26.51.125141 PM

======================
Runtime Difference
======================
Days : 0
Hours : 0
Minutes : 0
Seconds : 1.006717

PL/SQL: Joinrows - Convert multiple [returned cursor] rows into one row

This routine converts multiple rows into a single row. The trick is to pass the data via a SYS_REFCURSOR (see the CURSOR call in the example). Example Usage :

select joinrows(cursor(select contract_id from contract where rownum < 10 )) from dual;
CREATE OR REPLACE FUNCTION joinrows(
 sysref_cursor_in      sys_refcursor,
 s_delimiter_in        VARCHAR2 := ',' )
RETURN VARCHAR2
IS
   s_value            VARCHAR2(32767) := Null;
   s_result           VARCHAR2(32767) := Null;
   s_delimiter        VARCHAR2(100) := NVL(s_delimiter_in,',');
BEGIN

   LOOP
      FETCH sysref_cursor_in
         INTO s_value;

      EXIT WHEN sysref_cursor_in%NOTFOUND;

      IF (s_result IS NOT NULL) THEN
       s_result := s_result||s_delimiter;
      END IF;

      s_result := s_result||s_value;

   END LOOP;

   RETURN s_result;

END joinrows;

Results:

0,2,3,9,37,404,818,991,1178

Tuesday, November 21, 2006

Ruby: Invoking a PL/SQL Package with Array args

I'm fairly new to Ruby (and subsequently to Rails). I would be normally be labeled an "Enterprise Developer"... yada, yada. Anyway, I'm trying to invoke a Oracle PL/SQL Package from Ruby that has IN and OUT arguments that are arrays (TABLE OF VARCHAR2 INDEX BY BINARY-INTEGER, in Oracle PL/SQL terms). We have a fairly extensive library of PL/SQL that I want to reuse. This technique also works with Oracle Types (CREATE OR REPLACE TYPE PROD_TYPES.TYPE_STRING_ARRAY AS TABLE OF VARCHAR2(2000);).

Related Posts :

I've spent several weeks trying to come up with a solution. First, the best solution would be for Oracle to write the OCI driver for Ruby. OCI8 [http://rubyforge.org/projects/ruby-oci8/] (by Kubo Takehiro) is wonderful, but he is still a person. For companies to adopt and support Ruby/Rails, the support needs to be more robust. Upper Management and Operations resist open source. Single person supported software is easier for them to dismiss. I would prefer that Oracle had an OCI expert write and maintain the library (along the lines of their support for PHP). If this were the case, then the driver could support returning arrays natively.

My PL/SQL Packages :

CREATE OR REPLACE PACKAGE common_func IS
TYPE string_table IS TABLE OF VARCHAR2(2000) INDEX BY BINARY_INTEGER;
END common_func;
/
CREATE OR REPLACE PACKAGE BODY common_func
IS
BEGIN
   NULL;
END common_func;
/
create or replace package ruby_test is

function f_ruby(s in number,t out varchar2,st out common_func.string_table)

end ruby_test;
/
create or replace package body ruby_test is

function f_ruby(s in number,t out varchar2,st out common_func.string_table)
return varchar2
is
begin
   t := 'outta here';
   st(1) := 'array 1';
   st(2) := 'array 2';
   return 'Ruby rocks '||TO_CHAR(NVL(s,5))||' times!';
end;

begin
   null;
end ruby_test;
Desired Ruby :
cursor = conn.parse("BEGIN :result := ruby_test.f_ruby(s => :in,t => :out,st => st); END;")

cursor.bind_param(':result', nil, String, 100)
cursor.bind_param(':in', 10)
cursor.bind_param(':out', nil, String, 100)
# cursor.bind_param(':out_array', Array, 100) <= I tried this too!
cursor.bind_param(':out_array', String[], 100)

cursor.exec()

p cursor[':result'] # => 'Ruby rocks 10 times!'
p cursor[':out'] # => 'outta here'
p cursor[':out_array'] # => 'st(1) = array 1, st(2) = array 2' !!! Fails
First Solution : Convert the array to a string, bind to that string, and then split the delimited string apart on the Ruby side. I don't care for this solution for a couple of reasons :
  • Size issue at 32K
  • What delimiter should I use? How do I know that it will be the "right" delimiter?
First Solution (Revised) : I could switch from a string bind argument to a CLOB. OCI8 supports CLOBs, but I couldn't get it to work. The documentation is incomplete, and I'm not fluent enough in Ruby. Hints back to my issue with the library being solely maintained.

Current Solution : Convert the string array to a reference cursor (SYS_REFCURSOR) and use OCI8's bind to OCI::CURSOR support. Type :

CREATE OR REPLACE TYPE PROD_TYPES.TYPE_STRING_ARRAY AS TABLE OF VARCHAR2(2000)
Cursor Package :
CREATE OR REPLACE PACKAGE cursor_func IS
/**
Converts an array into a SYS_REFCURSOR (System Reference Cursor)

@Return
{*} SysRefCursor Success
{*} Exception Error
*/
FUNCTION f_array_to_SYSREFCURSOR(
   st_array_in       IN   common_func.STRING_TABLE )
RETURN SYS_REFCURSOR;
PRAGMA RESTRICT_REFERENCES(f_array_to_sysrefcursor,WNDS,TRUST);

END cursor_func;
/
CREATE OR REPLACE PACKAGE BODY cursor_func IS

/**
Converts an array into a SYS_REFCURSOR (System Reference Cursor)

@Return
{*} SysRefCursor Success
{*} Exception Error
*/
FUNCTION f_array_to_SYSREFCURSOR(
   st_array_in       IN   common_func.STRING_TABLE )
RETURN SYS_REFCURSOR
IS
   lsysrefcursor_array  SYS_REFCURSOR;
   le_error      EXCEPTION;

   lst_prodtypes    prod_types.type_string_array;

BEGIN

   lst_prodtypes := common_func.convert_table(st_array_in);

  OPEN lsysrefcursor_array FOR
     SELECT column_value FROM TABLE(cast(lst_prodtypes AS prod_types.type_string_array));

   RETURN lsysrefcursor_array;

END f_array_to_SYSREFCURSOR;

BEGIN
   Null;
END cursor_func;
Ruby Source :
plsql = conn.parse(
"DECLARE "+
"   st common_func.string_table; " +
"BEGIN "+
"   :result := ruby_test.f_ruby(s => :in,t => :out, st => st); " +
"   :cst := cursor_func.f_array_to_SYSREFCURSOR(st_array_in => st); " +
"END;")

plsql.bind_param(':result', nil, String, 100)
plsql.bind_param(':in', 10)
plsql.bind_param(':out', nil, String, 100)
plsql.bind_param(':cst', OCI8::Cursor)

plsql.exec()

puts "\nResults from returning a SysRefCursor\n"

p plsql[':result'] # => 'Ruby rocks 10 times!'
p plsql[':out'] # => 'outta here'

cursor = plsql[':cst']

plsql.close

x = ''
while r = cursor.fetch()
x = x + r.join(', ') + "\n"
end
cursor.close() # <= Don't forgot this

puts x
And success, finally!
Results from returning a SysRefCursor
"Ruby rocks 10 times"
"outta here"
array 1
array 2
Warning : There are two potential gotchas to this solution :
  • If the developer forgets the cursor.close statement, the transaction could be jeopardized if there are too many cursors opened. I don't remember which ORA-##### this is.
  • A single database session could run into issues with just having too many reference cursors open at any one instant.
Native support for arrays would be a blessing.

Posts of Interest :

Monday, November 13, 2006

Rails: Hello World / HelloWorld

Assuming that Ruby and Rails are already installed...

Go to the appropriate top level directory [where you want to create your new application]. For me, c:\develop\rubysrc.
  1. rails helloworld
  2. cd helloworld
  3. ruby script\generate controller helloworld
  4. You can either:
    • Use the controller DIRECTLY...
    class HelloController < ApplicationController
    def index
    render_text << EOF
    <html>
    <head>
    <title>Rails HelloWorld from Controller</title>
    </head>
    <body>
    HelloWorld from Controller
    </body>
    </html>
    EOF
    end
    end
    
    • Use a view... Create a file called index.rhtml in app/views/helloworld, containing "Hello World"
    <html>
    <head>
    <title>Rails HelloWorld from View</title>
    </head>
    <body>
    HelloWorld from View
    </body>
    </html>
    
  5. ruby script\server
  6. Navigate to http://localhost:3000/helloworld in your browser and be greeted with your friendly application: "Hello World"

Tuesday, November 07, 2006

PHP: Autofilled Forms

Source URL : http://www.onlamp.com/pub/a/php/2006/03/16/autofill-forms.html

This looks like a clever way to design PHP forms (be sure and check out the forums for issues).

Friday, November 03, 2006

Oracle: SQL - Converting one row into two rows [multiple rows]

Problem : A single row needs to be converted into two rows.

Solution 1 :

Prerequite :

CREATE OR REPLACE TYPE PROD_TYPES.TYPE_STRING_ARRAY AS TABLE OF VARCHAR2(2000)
Function : This routine handles an unlimited number of desired rows as opposed to Solution 2.
CREATE OR REPLACE FUNCTION split (
 s_delimited_list_in     VARCHAR2,
 s_delimiter_in          VARCHAR2 := ',')
RETURN prod_types.type_string_array PIPELINED
IS
/*
@Usage Example:
select * from table(split('one,two,three'));
*/
 l_idx               PLS_INTEGER;
 l_list              VARCHAR2(32767) := s_delimited_list_in;
 l_value             VARCHAR2(32767);
 ls_delimiter        VARCHAR2(100) := NVL(s_delimiter_in,',');
BEGIN
LOOP
 l_idx := INSTR(l_list,ls_delimiter);

 IF (l_idx > 0) THEN

    PIPE ROW(SUBSTR(l_list,1,l_idx-1));
    l_list := SUBSTR(l_list,l_idx+LENGTH(ls_delimiter));
  
 ELSE

    PIPE ROW(l_list);
    EXIT;
  
 END IF;
END LOOP;

RETURN;

END SPLIT;

Sorry, there was a typo in the above script. It was fixed on 2/9/2009. Jason

The reverse of "split" is "joinrows".

Solution 2 :

Here is a simple example of converting one row into multiple rows. "atomic_contract" is returning a single row, but we need two columns from it to be returned as two rows.

select *
from (
select ac.*,
decode(t.row_count,1,ac.sk_seller,
                  2,ac.sk_buyer, null) sk_customer
from atomic_contract ac,
( select 1 row_count from dual
union all
select 2 row_count from dual) t
where contract_id = 3562777)

Friday, October 27, 2006

JavaScript and AJAX