CREATE OR REPLACE PROCEDURE numeros (entra1 NUMBER, entra2 NUMBER)
IS
v_num1 NUMBER;
v_num2 NUMBER;
BEGIN
v_num1:=entra1;
v_num2:=entra2;
END;
-----------------------------------------------------------------------
CREATE OR REPLACE PROCEDURE somando
IS
v_soma NUMBER;
v_num1 NUMBER;
v_num2 NUMBER;
BEGIN
numeros(40,60);
v_soma:=(v_num1+v_num2);
DBMS_OUTPUT.PUT_LINE('O valor da soma de ' ||v_num1||' e '||v_num2||' é:'||v_soma);
END somando;
Why I can't catch the values from the first procedure "numeros", when I execute the procedure "somando" the result is empty " ".
Assuming this is PL/SQL, I believe there are a few possible workarounds for this. The easiest being to make output parameter variables in the procedure to get the numbers like so:
CREATE OR REPLACE PROCEDURE numeros(
entra1 IN NUMBER,
entra2 IN NUMBER,
out_entra1 OUT NUMBER,
out_entra2 OUT NUMBER)
IS
v_num1 NUMBER;
v_num2 NUMBER;
BEGIN
v_num1 := entra1;
v_num2 := entra2;
out_entra1 := v_num1;
out_entra2 := v_num2;
END numeros;
----------------------------
CREATE OR REPLACE PROCEDURE somando IS
v_soma NUMBER;
v_num1 NUMBER;
v_num2 NUMBER;
BEGIN
dbms_output.enable();
numeros(40,60,v_num1,v_num2);
v_soma := (v_num1+v_num2);
DBMS_OUTPUT.PUT_LINE('O valor da soma de ' ||v_num1||' e '||v_num2||' é:'||v_soma);
END somando;
I believe a more recommended method would be to place these two procedures in a package and declare global variables in the header, but I'm not sure what your set up is like.
You can either change the procedure to a function that returns the value, or specify one of the arguments to the procedure to be IN OUT
Related
I am doing a job interview PL/SQL question, and I am really stuck. Could someone help?
Create stored procedure to return substrings from input string value. Substring delimiter shall be input parameter.
Task description
Stored procedure shall have input parameters:
• STRING
• DELIMITER //any symbol
• STRING_NUMBER //number of substring to be returned
Input example: STRING => ‘one,two,three’,
DELIMITER => ‘e’,
STRING_NUMBER => null
Output shall be: ‘on’
‘,two,thr’
‘’
‘’
If STRING_NUMBER => 2, output shall be: ‘,two,thr’
EDIT:
First, I am trying to trim a string using TRIM function, but that doesn't work. Why?
CREATE OR REPLACE PROCEDURE substring
(STRNG IN VARCHAR2,DELIMITER IN VARCHAR2)
IS
instring VARCHAR2(100);
BEGIN
instring:= TRIM(DELIMITER FROM STRNG);
DBMS_OUTPUT.PUT_LINE(instring);
END;
set serveroutput on
BEGIN
substring('marc','a');
END;
EDIT 2:
This does the part on the job:
CREATE OR REPLACE PROCEDURE substring
(STRNG IN VARCHAR2,DELIMITER IN VARCHAR2)
IS
instring VARCHAR2(100);
BEGIN
instring:= REPLACE(STRNG,DELIMITER);
DBMS_OUTPUT.PUT_LINE(instring);
END;
set serveroutput on
BEGIN
substring('marc','a');
END;
Here is my own solution:
CREATE OR REPLACE PROCEDURE substring
(STRNG IN VARCHAR2,DELIMITER IN VARCHAR2, STRING_NUMBER IN NUMBER)
IS
replace_string VARCHAR2(100);
instring NUMBER;
comma_counter NUMBER;
substring VARCHAR2(100);
BEGIN
replace_string:= REPLACE(STRNG,DELIMITER);
comma_counter:=REGEXP_COUNT(STRNG,',');
instring:=INSTR(STRNG,',',1,comma_counter-STRING_NUMBER+1);
substring:=SUBSTR(replace_string,instring);
DBMS_OUTPUT.PUT_LINE(replace_string);
DBMS_OUTPUT.PUT_LINE(','||substring);
END;
set serveroutput on
BEGIN
substring('one,two,three,four','e',2);
END;
I have a Service developed in Delphi with DataSnap and Tethering that sends me information to connected clients. Now, some of the fields are float, when you convert them to string with the function "FormatFloat ('$, 0. ###', field)" it gives me another format, ie it does not send me in the format I have configured In Windows, "." For thousands separator and "," for decimals, but on the contrary. I want 15674.45 to be $ 15.647,45 and not $ 15,647.45. But I do not want to force the format.
procedure TServerContainerSGV40.tapServicioResourceReceived(const Sender: TObject; const AResource: TRemoteResource);
var
identifier, hint, cadena: string;
ID_PRODUCTO: Integer;
codigo, descripcion: string;
ppp, stock, precio_venta: Real;
begin
if AResource.ResType = TRemoteResourceType.Data then
begin
identifier := Copy(AResource.Hint, 1, Pos('}', AResource.Hint));
hint := AResource.Hint.Replace(identifier, '');
cadena := AResource.Value.AsString;
if cadena = 'Get IP' then EnviarCadena(AResource.Hint, 'Envío IP', GetLocalIP);
if hint = 'Datos Producto' then
begin
if cadena.Length > 0 then
begin
with usGetDatosProducto do
begin
ParamByName('CODIGO').AsString := cadena;
Execute;
ID_PRODUCTO := ParamByName('ID_PRODUCTO').AsInteger;
codigo := ParamByName('CODIGO').AsString;
descripcion := ParamByName('DESCRIPCION').AsString;
ppp := ParamByName('PPP').AsFloat;
stock := ParamByName('STOCK').AsFloat;
precio_venta := ParamByName('PRECIO_VENTA').AsFloat;
end;
if ID_PRODUCTO > 0 then
begin
cadena := Format('%s;%s;;PRECIO:'#9'%s;P.P.P.:'#9'%s;STOCK:'#9'%s', [
codigo, descripcion, FormatFloat('$ ,0', precio_venta),
FormatFloat('$ ,0.##', ppp), FormatFloat(',0.###', stock)
]);
EnviarCadena(identifier, 'Envío Datos Producto', cadena);
end
else
EnviarCadena(identifier, 'Mostrar Mensaje', 'Código de Producto No Existe');
end;
end;
end;
end;
By default, FormatFloat() uses the global SysUtils.ThousandsSeparator and SysUtils.DecimalSeparator variables, which are initialized from OS settings at program startup:
FormatFloat('$#,##0.00', field);
If you want to force a specific format regardless of OS settings, use the overloaded version of FormatFloat() that takes a TFormatSettings as input:
var
fmt: TFormatSettings;
fmt := TFormatSettings.Create;
fmt.ThousandsSeparator := '.';
fmt.DecimalSeparator := ',';
FormatFloat('$#,##0.00', field, fmt);
In Delphi versions from D2009 (at least) you can specify format settings for given operation and initialize these settings either by Windows default settings or modify needed formatting fields.
function FormatFloat(const Format: string; Value: Extended): string; overload;
function FormatFloat(const Format: string; Value: Extended;
const FormatSettings: TFormatSettings): string; overload;
And I wonder - is it impossible to form all string with only Format function?
Am learning how to use insert into statements and, with my access database, am trying to insert a single record. The table I'm inserting a new record into has three fields: StockID (AutoN), Description (Text), Cost (Number). I've looked at previous posts but the posted solutions seem to go beyond my basic level of Insert Into...which is what I'm interested in. Anyway, here is my code...
adoQuery1.Active := true;
adoQuery1.SQL.Clear;
adoQuery1.SQL.Add('INSERT INTO Stock (StockID,Description,Cost) VALUES (4,Cheese,5)');
adoQuery1.open;
adoQuery1.Close;
It compiles fine, but when press a command button to invoke the above, I get the following message:
'ADOQuery1: "Missing SQL property".'
what am I doing wrong?
Thanks, Abelisto. Your last post looks complex indeed...but I did my own little version since your last solution got me up and running. It works so I'm very chuffed. Am now going to focus on DELETE FROM using combobox (for field selection) and user value. Here was my solution I got working... ;)
x:=strtoint(txtStockID.Text);
y:=txtDescription.Text;
z:=strtoCurr(txtCost.Text);
adoQuery1.SQL.Clear;
adoQuery1.SQL.Add('INSERT INTO tblStock (StockID,Description,Cost)');
adoQuery1.SQL.Add('VALUES (:StockID,:Description,:Cost)'); // ':StockID' denotes a parameter
adoQuery1.Parameters.ParamByName('StockID').Value:= x;
adoQuery1.Parameters.ParamByName('Description').Value:= y;
adoQuery1.Parameters.ParamByName('Cost').Value:= z;
adoQuery1.ExecSQL;
adoQuery1.Close;
Using parameters is more efficient then constant SQL statements.
Additional to my comments here is some useful functions which I using frequently to call SQL statements with parameters (Maybe it will be useful for you too):
function TCore.ExecQuery(const ASQL: String; const AParamNames: array of string;
const AParamValues: array of Variant): Integer;
var
q: TADOQuery;
i: Integer;
begin
if Length(AParamNames) <> Length(AParamValues) then
raise Exception.Create('There are different number of parameter names and values.');
q := GetQuery(ASQL) as TADOQuery;
try
for i := Low(AParamNames) to High(AParamNames) do
SetParamValue(q, AParamNames[i], AParamValues[i]);
q.ExecSQL;
Result := q.RowsAffected;
finally
q.Free;
end;
end;
function TCore.GetQuery(const ASQL: String): TDataSet;
begin
Result := TADOQuery.Create(Self);
(Result as TADOQuery).CommandTimeout := 0;
(Result as TADOQuery).Connection := Connection;
(Result as TADOQuery).SQL.Text := ASQL;
end;
procedure TCore.SetParamValue(AQuery: TDataSet; const AName: string; const AValue: Variant);
var
i: Integer;
q: TADOQuery;
begin
q := AQuery as TADOQuery;
for i := 0 to q.Parameters.Count - 1 do
if AnsiSameText(AName, q.Parameters[i].Name) then
begin
case VarType(AValue) of
varString, varUString:
q.Parameters[i].DataType := ftString;
varInteger:
q.Parameters[i].DataType := ftInteger;
varInt64:
q.Parameters[i].DataType := ftLargeint;
end;
q.Parameters[i].Value := AValue;
end;
end;
And usage example in your case:
Core.ExecQuery(
'INSERT INTO Stock (StockID, Description, Cost) VALUES (:PStockID, :PDescription, :PCost)',
['PStockID', 'PDescription', 'PCost'],
[4, 'Cheese', 5]);
I have a TOraQuery with SQL defined something like this
SELECT ML.ID, Ml.detail1, Ml.detail2
FROM MY_LIST ML
WHERE
ML.ID in (:My_IDS)
If I was to build this query on the fly, I'd naturally end up with something like this:
SELECT ML.ID, Ml.detail1, Ml.detail2
FROM MY_LIST ML
WHERE
ML.ID in (14001,14002,14003)
However, I'd like to pass in 14001,14002,14003 as a parameter.
myListQuery.Active := False;
myListQuery.ParamByName('My_IDS').AsString := '14001,14002,14003';
myListQuery.Active := True;
But of course that generates an ORA-01722: invalid number. Do I have any other option other than building up the query on the fly.
AFAIK, it is not possible directly.
You'll have to convert the list into a SQL list in plain text.
For instance:
function ListToText(const Args: array of string): string; overload;
var
i: integer;
begin
result := '(';
for i := 0 to high(Args) do
result := result+QuotedStr(Args[i])+',';
result[length(result)] := ')';
end;
function ListToText(const Args: array of integer): string; overload;
var
i: integer;
begin
result := '(';
for i := 0 to high(Args) do
result := result+IntToStr(Args[i])+',';
result[length(result)] := ')';
end;
To be used as such:
SQL.Text := 'select * from myTable where intKey in '+ListToText([1,2,3]);
SQL.Text := 'select * from myTable where stringKey in '+ListToText(['a','b','c']);
Or in your case:
myListQuery.SQL.Text := 'SELECT ML.ID, Ml.detail1, Ml.detail);
myListQuery.SQL.Add('FROM MY_LIST ML ');
myListQuery.SQL.Add('WHERE ');
myListQuery.SQL.Add('ML.ID in ') + ListToText([14001,14002,14003]);
You can do it but it requires some additional setup. Hopefully this works with your version of Oracle.
Create a table type
Create a function that converts your string to your table type
Use CAST in the subquery. Pass your value to the bind variable using the same thing you have in your code (i.e. ParamByName('').AsString).
create or replace type myTableType as table of varchar2 (255);
create or replace function in_list( p_string in varchar2 ) return myTableType as
l_string long default p_string || ',';
l_data myTableType := myTableType();
n number;
begin
loop
exit when l_string is null;
n := instr( l_string, ',' );
l_data.extend;
l_data(l_data.count) :=
ltrim( rtrim( substr( l_string, 1, n-1 ) ) );
l_string := substr( l_string, n+1 );
end loop;
return l_data;
end;
select * from THE ( select cast( in_list(:MY_BIND_VARIABLE) as mytableType ) from dual ) a
If this works for you, credit for the answer and example code goes to Tom Kyte from Oracle who runs asktom.com. https://asktom.oracle.com/pls/asktom/f?p=100:11:0%3a%3a%3a%3aP11_QUESTION_ID:210612357425
You can use a "Macro"
Not quite what I was looking for, but it's a hair closer to a parameter than building the SQL on the fly.
Create the TOraQuery like this
SELECT ML.ID, Ml.detail1, Ml.detail2
FROM MY_LIST ML
WHERE
ML.ID in (&My_IDS)
Now I can pass in 14001,14002,14003 as a macro.
myListQuery.Active := False;
myListQuery.MacroByName('My_IDS').value := '14001,14002,14003';
myListQuery.Active := True;
Is there a method in Delphi to check if a string is a number without raising an exception?
its for int parsing.
and an exception will raise if one use the
try
StrToInt(s);
except
//exception handling
end;
function TryStrToInt(const S: string; out Value: Integer): Boolean;
TryStrToInt converts the string S, which represents an integer-type number in either decimal or hexadecimal notation, into a number, which is assigned to Value. If S does not represent a valid number, TryStrToInt returns false; otherwise TryStrToInt returns true.
To accept decimal but not hexadecimal values in the input string, you may use code like this:
function TryDecimalStrToInt( const S: string; out Value: Integer): Boolean;
begin
result := ( pos( '$', S ) = 0 ) and TryStrToInt( S, Value );
end;
var
s: String;
iValue, iCode: Integer;
...
val(s, iValue, iCode);
if iCode = 0 then
ShowMessage('s has a number')
else
ShowMessage('s has not a number');
Try this function StrToIntDef()
From help
Converts a string that represents an integer (decimal or hex notation) to a number with error default.
Pascal
function StrToIntDef(const S: string; Default: Integer): Integer;
Edit
Just now checked the source of TryStrToInt() function in Delphi 2007. If Delphi 7 dont have this function you can write like this. Its just a polished code to da-soft answer
function TryStrToInt(const S: string; out Value: Integer): Boolean;
var
E: Integer;
begin
Val(S, Value, E);
Result := E = 0;
end;
XE4 and newer:
for ch in s do
TCharacter.IsNumber(ch);
Don't forget:
uses System.Character
In delphi 7 you can use the Val procedure. From the help:
Unit: System
Delphi syntax: procedure Val(S; var V; var Code: Integer);
S is a string-type expression; it must be a sequence of characters that form a signed real number.
V is an integer-type or real-type variable. If V is an integer-type variable, S must form a whole number.
Code is a variable of type Integer.
If the string is invalid, the index of the offending character is stored in Code; otherwise, Code is set to zero. For a null-terminated string, the error position returned in Code is one larger than the actual zero-based index of the character in error.
use this function
function IsNumber(N : String) : Boolean;
var
I : Integer;
begin
Result := True;
if Trim(N) = '' then
Exit(False);
if (Length(Trim(N)) > 1) and (Trim(N)[1] = '0') then
Exit(False);
for I := 1 to Length(N) do
begin
if not (N[I] in ['0'..'9']) then
begin
Result := False;
Break;
end;
end;
end;
For older Delphi versions from delphi 5 help example:
uses Dialogs;
var
I, Code: Integer;
begin
{ Get text from TEdit control }
Val(Edit1.Text, I, Code);
{ Error during conversion to integer? }
if Code <> 0 then
MessageDlg('Error at position: ' + IntToStr(Code), mtWarning, [mbOk], 0);
else
Canvas.TextOut(10, 10, 'Value = ' + IntToStr(I));
end;
In some languages decimal separators are different (for example, '.' is used in English and ',' is used in Russian). For these cases to convert string to real number the following procedure is proposed:
function TryStrToFloatMultiLang(const S : String; out Value : Extended) : Boolean;
var
dc : char;
begin
Result := false;
dc := DecimalSeparator;
DecimalSeparator := '.';
try
Result := TryStrToFloat(S, Value);
except
DecimalSeparator := ',';
Result := TryStrToFloat(S, Value);
end;
DecimalSeparator := dc;
end;
Update
As #Pep mentioned TryStrToFloat catch exceptions, but it returns boolean value. So the correct code is:
function TryStrToFloatMultiLang(const S : String; out Value : Extended) : Boolean;
var
dc : char;
begin
Result := false;
dc := DecimalSeparator;
DecimalSeparator := '.';
Result := TryStrToFloat(S, Value);
if not Result then begin
DecimalSeparator := ',';
Result := TryStrToFloat(S, Value);
end;
DecimalSeparator := dc;
end;
When you using procedure
val(s, i, iCode);
and set value xd ....
val('xd', i, iCode)
as a result we obtain: 13
standard unit Variants
function VarIsNumeric(v:Variant):Boolean