I have a Stored Procedure that seems to be very slow.
Executing it in Oracle SQL Developer;
SET TIMING ON;
DECLARE
CUR_OUT UTILS.T_CURSOR;
P_ARTTYID NUMBER;
P_ORDERST VARCHAR2(200);
P_DRUMNO VARCHAR2(200);
P_SHIPPINGNO VARCHAR2(200);
P_DELIVERYDATEFROM DATE;
BEGIN
P_ARTTYID := 2;
P_ORDERST := '3';
P_DRUMNO := '611-480';
P_SHIPPINGNO := NULL;
P_DELIVERYDATEFROM := '2005-01-01';
C_T_ORDER_GETOVERVIEW(
CUR_OUT => CUR_OUT,
P_ARTTYID => P_ARTTYID,
P_ORDERST => P_ORDERST,
P_DRUMNO => P_DRUMNO,
P_SHIPPINGNO => P_SHIPPINGNO,
P_DELIVERYDATEFROM => P_DELIVERYDATEFROM
);
--DBMS_OUTPUT.PUT_LINE('CUR_OUT = ' || CUR_OUT); -- Doesn´t work ;|
END;
Gives the "Statement output"
anonymous block completed
139ms elapsed
Now the problem is when I call it from my VB.NET application using
Microsoft.Practices.EnterpriseLibrary.Data.Database.ExecuteReader() that returns a System.Data.IDataReader and the following function to convert the IDataReader to a DataSet.
Public Shared Function ConvertIDataReaderToDataSet(ByVal reader As IDataReader) As DataSet
Dim schemaTable As DataTable = reader.GetSchemaTable()
Dim dataTable As DataTable = New DataTable
For intCounter As Integer = 0 To schemaTable.Rows.Count - 1
Dim dataRow As DataRow = schemaTable.Rows(intCounter)
Dim columnName As String = CType(dataRow("ColumnName"), String)
Dim column As DataColumn = New DataColumn(columnName, CType(dataRow("DataType"), Type))
dataTable.Columns.Add(column)
Next
Dim dataSet As DataSet = New DataSet
dataSet.Tables.Add(dataTable)
'dataSet.Load(reader, LoadOption.OverwriteChanges, dataTable) ' DEBUG
While reader.Read()
Dim dataRow As DataRow = dataTable.NewRow()
For intCounter As Integer = 0 To reader.FieldCount - 1
dataRow(intCounter) = reader.GetValue(intCounter)
Next
dataTable.Rows.Add(dataRow)
End While
Return dataSet
End Function
Debugging and stepping through the function ends at the line for "While reader.Read()".
Also tried another version using DataSet.Load() but with the same result.
Found this thread on MSDN where others with the same problem seems to have solved it by tuning their queries by adding indexes.
How can I continue investigating the issue when it seems like the procedure works (responds within ~100 - 200ms) and the IDataReader.Read() just ends (or continues in the background?)
Can I time the procedure in another (better) way?
Can there be any table or transaction locks involved?
All advices are highly appreciated :)
Your test in SQL Developer is simply measuring the time required to open the cursor. Opening the cursor does not cause Oracle to actually execute the query-- that does not happen until you fetch data from the cursor and each time you fetch, Oracle will continue processing the query to get the next set of rows. Oracle does not, in general, need to execute the entire query at any point in time. To be a comparable test, your PL/SQL block would need to fetch all the data from the cursor. Something like
DECLARE
CUR_OUT UTILS.T_CURSOR;
P_ARTTYID NUMBER;
P_ORDERST VARCHAR2(200);
P_DRUMNO VARCHAR2(200);
P_SHIPPINGNO VARCHAR2(200);
P_DELIVERYDATEFROM DATE;
BEGIN
P_ARTTYID := 2;
P_ORDERST := '3';
P_DRUMNO := '611-480';
P_SHIPPINGNO := NULL;
P_DELIVERYDATEFROM := '2005-01-01';
C_T_ORDER_GETOVERVIEW(
CUR_OUT => CUR_OUT,
P_ARTTYID => P_ARTTYID,
P_ORDERST => P_ORDERST,
P_DRUMNO => P_DRUMNO,
P_SHIPPINGNO => P_SHIPPINGNO,
P_DELIVERYDATEFROM => P_DELIVERYDATEFROM
);
LOOP
FETCH cur_out
INTO <<list of variables to fetch data into>>;
EXIT WHEN cur_out%notfound;
END LOOP;
--DBMS_OUTPUT.PUT_LINE('CUR_OUT = ' || CUR_OUT); -- Doesn´t work ;|
END;
Are you saying that in your .Net code, the reader.Read() line never returns? Or are you saying your code aborts at that point?
Related
Below is a function I have in VB that will run a stored proc. The stored proc has 2 out parameters. One called #Success and one called #Message.
I am trying to set the success and message parameters to the values of the corresponding output params from the stored proc. I don't know much about VB.NET. How can I achieve this based off of the function I have currently written?
Public Function InvalidateCertificate(ByVal context As DbContextBase,
ByVal certificateId As System.String,
ByRef success As System.Boolean,
ByRef message As System.String) As Int32
Dim successParameter As New SqlParameter("#Success", success) With {.Direction = ParameterDirection.InputOutput, .Value = success}
Dim messageParameter As New SqlParameter("#Message", message) With {.Direction = ParameterDirection.InputOutput, .Value = message}
Dim parameters() As SqlParameter = {New SqlParameter("#CertificationValue", certificateId), successParameter, messageParameter}
Dim results As Int32 = context.ExecuteProcedure("Orders.spInvalidateCertificate", parameters)
success = DirectCast(successParameter.Value, System.Boolean)
message = DirectCast(messageParameter.Value, System.String)
Return 0
End Function
Stored proc:
ALTER PROCEDURE [Orders].[spInvalidateCertificate]
-- Add the parameters for the stored procedure here
#CertificationValue VARCHAR(20),
#Success BIT OUTPUT,
#Message VARCHAR(50) OUTPUT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Log the call to this procedure
EXEC [Logs].[spLogStoredProcedureCall] ##PROCID;
--Check to see if CertificationValue exists or is invalid
IF(select CertificationValue from
[Certificate].CertificateOrderDetailImageXref where CertificationValue =
#CertificationValue) is null
BEGIN
SET #Success = 0
SET #Message = 'Certification does not exist or is already
invalidated.'
END
ELSE
BEGIN
-- Insert statements for procedure here
UPDATE Certificate.CertificateOrderDetailImageXref
SET CertificationValue = CONCAT(#CertificationValue,'-VOID')
WHERE CertificationValue = #CertificationValue
SET #Success = 1
SET #Message = 'Success, certification has been invalidated.'
END;
END
GO
To note, what's actually happening is that only the first letter of the message is being returned. so if its a success, and the sql output should be "success!" I just get "S". If the certification does not exist I just get "C".
I am just new in oracle and using procedure and still learning, but I have a problem how can I populate the combo box using store procedure? I already have a code but when I run it, it shows an error IndexOutOfRangedException was unhandled and Cannot find Column 1.
Here's my code
(Procedure/Oracle Sql:)
procedure cmbbox_location (o_output out o_refcur)
as
o_cur o_refcur;
begin
open o_cur for
select city_id, city_name from city;
o_output := o_cur;
end cmbbox_location;
(program/vb.net)
Private Sub Main_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
instantiate_dev()
ora_conn.Open()
populate_cmbbox_loc()
End Sub
Private Sub populate_cmbbox_loc()
instantiate_dev()
ora_conn.Open()
qr.populate_location()
cmblocation.DataSource = dt
cmblocation.ValueMember = dt.Columns(0).ColumnName
cmblocation.DisplayMember = dt.Columns(1).ColumnName
cmblocation.Text = ""
cmblocation.SelectedIndex = 0
End Sub
Public Function populate_location()
dt = New DataTable
bg.GetProcDataTable(connStr, "Location_Package.cmbbox_location")
cmd.Parameters.Add(New OracleParameter("O_OUTPUT", OracleDbType.RefCursor)).Direction = ParameterDirection.Output
adap_or.SelectCommand = cmd
adap_or.Fill(dt)
Return dt
ora_conn.Close()
End Function
The error you are getting indicates that you are indexing something that doesn't exist, it's possible/probable your stored procedure isn't returning anything. To address William Robertson's concern about the ref cursor:
There are any number of ways to output a ref cursor. The one I use the most is a ref cursor type defined in the package definition, for example:
CREATE OR REPLACE PACKAGE Location_Package IS
TYPE DataOutCursor IS REF CURSOR;
procedure cmbbox_location (o_output out DataOutCursor);
END Location_Package;
/
Then in the procedure in the package body:
CREATE OR REPLACE PACKAGE BODY Location_Package IS
procedure cmbbox_location (o_output out DataOutCursor) as
begin
open o_output for
select city_id, city_name from city;
end;
END;
/
I assume the error is occurring here:
cmblocation.ValueMember = dt.Columns(0).ColumnName
cmblocation.DisplayMember = dt.Columns(1).ColumnName
It is always best to make sure the data table has data first so wrap the combo box lines with:
IF dt.Rows.Count > 0 Then
' ..... assign the data table to the combo box
Else
' Some error message ...
End IF
This will get rid of the unhandled exception and help you to figure out the issue.
On last thing, your function needs to return a value (datatable):
Public Function populate_location() as Datatable
dt = New DataTable
bg.GetProcDataTable(connStr, "Location_Package.cmbbox_location")
cmd.Parameters.Add(New OracleParameter("O_OUTPUT", OracleDbType.RefCursor)).Direction = ParameterDirection.Output
adap_or.SelectCommand = cmd
adap_or.Fill(dt)
Return dt
ora_conn.Close()
End Function
Is it possible to return from a HSQLDB Stored procedure before reaching its end?
I've tried:
IF (condition) THEN
RETURN;
END IF;
This doesn't compile.
Neither with EXIT or LEAVE instead of RETURN.
CREATE PROCEDURE PRO (VAR INT)
mylabel: BEGIN ATOMIC
...
IF VAR = 3 THEN
LEAVE mylabel;
END IF;
...
END
I have this Delphi code:
function EnDeCrypt(const Value : String) : String;
var
CharIndex : integer;
begin
Result := Value;
for CharIndex := 1 to Length(Value) do
Result[CharIndex] := chr(not(ord(Value[CharIndex])));
end;
how can it be translated to .Net?
If the function that RRUZ provided (now deleted) is really what you want (and I'm still a little sceptical of the encoding issues) then you can write it like this:
Private Function EnDeCrypt(ByVal Value As String) As String
Dim transformed = Encoding.Unicode.GetBytes(Value).Select( _
Function(item) Not item)
Return Encoding.Unicode.GetString(transformed.ToArray())
End Function
I've been given this simple VB application and library which I'm told can open a door/turnstyle attached to the printer port at 0x378 base address.
'Inp and Out declarations for port I/O using inpout32.dll.
Public Declare Function Inp Lib "inpout32.dll" Alias "Inp32" _
(ByVal PortAddress As Integer) _
As Integer
Public Declare Sub Out Lib "inpout32.dll" Alias "Out32" _
(ByVal PortAddress As Integer, _
ByVal Value As Integer)
------------------------------------------------------------------------------------
Option Explicit
Dim Value As Integer
Dim PortAddress As Integer
Private Sub cmdWriteToPort_Click()
'Write to a port.
Out PortAddress, Value
'Read back and display the result.
Text1.Text = Inp(PortAddress)
Value = Value + 1
If Value = 255 Then Value = 0
End Sub
Private Sub Form_Load()
'Test program for inpout32.dll
Value = 0
'Change PortAddress to match the port address to write to:
'(Usual parallel-port addresses are &h378, &h278, &h3BC)
PortAddress = &H378
End Sub
However I need to re-write it in Delphi 5 to integrate in to my application.
Is it possible to access the same library through D5?
Am I in the right direction with the following code?
//Inp and Out declarations for port I/O using library
function Inp(PortAddress:String); external 'inpout32.dll.dll'
begin
return ??
end;
procedure Output(PortAddress:String;Value:Integer); external 'inpout32.dll.dll'
procedure TForm1.FormActivate(Sender: TObject);
begin
//Test program for inpout32.dll
Value := 0;
//Change PortAddress to match the port address to write to:
//(Usual parallel-port addresses are &h378, &h278, &h3BC)
PortAddress := '&H378';
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
//Write to a port.
Output(PortAddress, Value);
//Read back and display the result.
Edit1.Text := Inp(PortAddress);
Value := Value + 1;
if Value = 255 then
Value := 0;
end;
I'm not sure exactly how to declare the library functions and what to declare the variables as (&H378 is obviously not an integer)
thanks
PortAddress is declared as an Integer, so don't use strings. Your code should look something like this:
//Inp and Out declarations for port I/O using inpout32.dll.
function Inp(PortAddress: Integer): Integer; stdcall; external 'inpout32.dll' name 'Inp32';
procedure Output(PortAddress, Value: Integer); stdcall; external 'inpout32.dll' name 'Out32';
procedure TForm1.FormActivate(Sender: TObject);
begin
//Test program for inpout32.dll
Value := 0;
//Change PortAddress to match the port address to write to:
//(Usual parallel-port addresses are $378, $278, $3BC)
PortAddress := $378;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
//Write to a port.
Output(PortAddress, Value);
//Read back and display the result.
Edit1.Text := IntToStr(Inp(PortAddress));
Value := Value + 1;
if Value = 255 then
Value := 0;
end;
You should completely drop using inpout32.dll since it's only used to directly access printer port and complicates your code conversion. You can do the same much more efficient with ZLOPRTIO Delphi library.