create procedure InsertEmp
(
IN in_eno INTEGER,
IN in_fname VARCHAR(30),
IN in_lanme VARCHAR(30),
IN in_DOB DATE,
IN in_JoinedDate DATE,
IN in_DepartmentNo BYTEINT
)
BEGIN
SELECT * FROM EMPLOYEE;
END;
While executing above procedure through unix server facing
Failure 3706 Syntax error: Invalid SQL statement.
You have to define an INTO clause in the procedure you posted, in order to compile in any environment (BTEQ, SQL*Assistant, et al).
Roughly like this:
create table EMPLOYEE (eno INTEGER);
replace procedure InsertEmp
(
IN in_eno INTEGER
)
BEGIN
DECLARE l_eno INTEGER;
SELECT top 1 eno INTO l_eno FROM EMPLOYEE;
END;
Related
I have this items in my dbase file (.dbf)
INDICE NOME COR ESTILO ESCALA
100 SAOJOAO 18 0,00
I need to change column name of INDICE to ID, so I use this code:
while not ADOQuery1.Eof do
begin
Adoquery1.Edit;
ADOQuery1.FieldByName('NOME').TEXT:= 'ID';
Adoquery1.Post;
ADOQuery1.Next;
end;
When I run the above I get these results:
INDICE NOME COR ESTILO ESCALA
ID SAOJOAO 18 0,00
Connection string used:
Driver={Microsoft dBASE Driver (*.dbf)};DriverID=277;Dbq=C:_workspace\projects\DBFEditor\temp
I have a system that need import dbf file and only recognize a file which have id column name.
The demo project below shows a way to do what you seem to want. I don't claim that
it's the most efficient way or the best way, but it's probably as simple as you're likely to get.
If you were wanting just to change the displayed name of a field in a Delphi application, for example in the column header of a DBGrid, you could do that by changing the DisplayLabel property of the field in question (AdoQuery1.FieldByName('INDICE').DisplayLabel := 'ID'), as I said in a comment earlier. But in your latest edit, it seems that what you actually want to do is to change the name of the INDICE column as it seen by a program reading the datafile to ID. To do that, you have to make an alteration to the on-disk structure of your .DBF file. This is what my code below does.
It uses a User DSN set up for the MS ODBC driver for dBase files as the target of the AdoConnection's connection string.
Ideally, I would have liked the find a flavour of the ALTER TABLE Sql statement
which would simply rename the INDICE column, but the MS dBase driver doesn't seem
to support that, because it generated an exception when I tried. So instead, my code works by making a copy of the table and its contents, with the INDICE column renamed to ID.
In short, the program
Creates a table MATest with a first column named INDICE and a couple of other columns and inserts a single row into it. This is just to set up a table to work from.
Creates a second table MATest2 with the same structure as the MATest one, except that the first
column is named ID rather than INDICE.
Populates the MATest2 table by copying all the rows from MATest, using an INSERT INTO Sql statement.
The important steps for what you want to do are carried out in the btnCreateTableCopyClick
procedure. Note that you will have to comment out the first two lines, which drop the
table MATest2 the first time you run the app, otherwise it will complain, cryptically,
that MATest2 can't be dropped because it doesn't exist.
I leave it to you to adapt the code as necessary to your data.
Code:
type
TForm1 = class(TForm)
ADOConnection1: TADOConnection;
btnCreateSrcTable: TButton;
ADOQuery1: TADOQuery;
btnOpenSrcTable: TButton;
DataSource1: TDataSource;
DBGrid1: TDBGrid;
DBNavigator1: TDBNavigator;
btnDropTable: TButton;
btnCreateTableCopy: TButton;
procedure btnCreateSrcTableClick(Sender: TObject);
procedure btnDropTableClick(Sender: TObject);
procedure btnOpenSrcTableClick(Sender: TObject);
procedure btnCreateTableCopyClick(Sender: TObject);
private
protected
public
procedure CreateSourceTable;
end;
[...]
procedure TForm1.btnCreateTableCopyClick(Sender: TObject);
var
Sql : String;
begin
Sql := 'drop table MATest2';
AdoConnection1.Execute(Sql);
Sql := 'create table MATest2(ID int, AName char(20), AValue char(20))';
AdoConnection1.Execute(Sql);
Sql := 'insert into MATest2 select INDICE, AName, AValue from MATest';
AdoConnection1.Execute(Sql);
end;
procedure TForm1.btnCreateSrcTableClick(Sender: TObject);
begin
CreateSourceTable;
end;
procedure TForm1.btnDropTableClick(Sender: TObject);
var
Sql : String;
begin
// Sql := 'drop table MATest';
// AdoConnection1.Execute(Sql);
end;
procedure TForm1.btnOpenSrcTableClick(Sender: TObject);
begin
AdoQuery1.Open;
end;
procedure TForm1.btnCreateTableCopyClick(Sender: TObject);
var
Sql : String;
begin
Sql := 'drop table MATest2';
AdoConnection1.Execute(Sql);
Sql := 'create table MATest2(ID int, AName char(20), AValue char(20))';
AdoConnection1.Execute(Sql);
Sql := 'insert into MATest2 select INDICE, AName, AValue from MATest';
AdoConnection1.Execute(Sql);
end;
procedure TForm1.CreateSourceTable;
var
Sql : String;
begin
Sql := 'create table MATest(INDICE int, AName char(20), AValue char(20))';
AdoConnection1.Execute(Sql);
Sql := 'insert into MATest(INDICE, AName, AValue) values(1, ''aaa'', ''vvv'')';
AdoConnection1.Execute(Sql);
end;
Obviously it would be better to generate your data with the ID fieldname in the first place and avoid all this, but presumably there is a good reason why you can't.
i'm trying to execute a stored procedure dynamically since i got alof of them based on a simple number, so i created another procedure to do this, but i keep getting the erro on the title of my question, here is my procedure:
PROCEDURE P_EXEC_REG(p_Register IN VARCHAR2, p_LineBuff IN VARCHAR2, p_User IN VARCHAR2) IS
l_Procedure VARCHAR2(50);
l_Sentence VARCHAR2(500);
BEGIN
l_Procedure := 'P_REG_' || TRIM(p_Register);
l_Sentence := 'BEGIN CTS.PK_INTEGRATE_MANIFESTO.' || l_Procedure || '(:A, :B); END;';
EXECUTE IMMEDIATE l_Sentence
USING IN p_LineBuff, p_User;
END;
Since we have hundreds of procedures, it is a pain to do it with if statements, hope somebody can help me out on this, also, i will show you how i am calling this:
PROCEDURE P_LOAD_FILE(p_Linebuff IN VARCHAR2, p_User IN VARCHAR2) IS
l_Register VARCHAR2(3);
BEGIN
BEGIN
SELECT SUBSTR(p_Linebuff,1,3)
INTO l_Register
FROM DUAL;
EXCEPTION
WHEN NO_DATA_FOUND THEN
--p_result := false;
NULL;
END;
CTS.PK_INTEGRATE_MANIFESTO.P_EXEC_REG(l_Register, p_Linebuff, p_User);
EXCEPTION
WHEN OTHERS THEN
NULL;
END;
Thank you guys, i found the answer to this issue thanks to #John Heller's comment, with that command i could notice that it was in fact executing, but when i was sending the dynamic parameter, which isl_Register, i wasnt checking for null values, so it went straight to EXECUTE IMMEDIATE with a procedure with a null parameter at the end. Now it looks like this:
PROCEDURE P_LOAD_FILE(p_Linebuff IN VARCHAR2, p_User IN VARCHAR2, p_Error OUT BOOLEAN, p_Message OUT VARCHAR2) IS
l_MessageLevel NUMBER(1);
l_Register VARCHAR2(3);
l_Command VARCHAR2(50);
l_Procedure VARCHAR2(4000);
l_Error NUMBER(1);
BEGIN
IF p_Linebuff IS NOT NULL OR p_Linebuff <> '' THEN
BEGIN
SELECT SUBSTR(p_Linebuff,1,3)
INTO l_Register
FROM DUAL;
IF LENGTH(TRIM(l_Register)) = 3 THEN
P_EXEC_REG(l_Register, p_Linebuff, p_User, l_Error, p_Message);
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
NULL;
END;
END IF;
EXCEPTION
WHEN OTHERS THEN
p_Error := TRUE;
p_Message := 'Sucedio un error al procesar el archivo.';
END;
Thanks for the help, and sorry if it was a dumb question :)
I´m having trouble to fetch IDENTITY columns with ADO in Delphi XE2.
Question
The ADO Fields property doesen´t return/contain any IDENTITY columns.
Is there a property or something in the connection that will enable to fetch IDENTITY columns or is this a known bug ?
Scenario
I have a stored procedure (in SYBASE ASE), that fetch 1 row with 4 columns.
One of the column is a IDENTITY column.
In this example: ADOQuery.Fields.Count will return 3 and not 4. The IDENTITY column is missing.
Ex:
procedure TForm1.FormCreate(Sender: TObject);
var
test: string;
Password: String;
UserName: string;
ServerName : string;
NoRec: integer;
MyValue: integer;
begin
Password:='xxxx';
UserName:='yyyy';
ServerName := 'zzzzz';
ADOConnection := TADOConnection.Create(Application);
with ADOConnection do
begin
CommandTimeout := 0;
IsolationLevel := ilSerializable;
Attributes := [xaAbortRetaining];
KeepConnection := True;
LoginPrompt := False;
ConnectionString := 'Provider=ASEOLEDB.1;Password=' + Password +
';Persist Security Info=True' + ';User ID=' +
UserName + ';Data Source=' + ServerName;
end;
if not Assigned(ADOQuery) then
begin
ADOQuery := TADOQuery.Create(Application);
with ADOQuery do
begin
CommandTimeout := 0;
DisableControls;
CacheSize := 500;
Connection := ADOConnection;
CursorType := ctOpenForwardOnly;
end;
end;
ADOConnection.Open();
ADOQuery.SQL.Text:= 'sp_echo';
ADOQuery.Open;
NoRec:=ADOQuery.Fields.Count; //This will return: 3, the IDENTITY col is missing
//Trying to fetch the column this way, will fail (the field doesen´t exist):
//MyValue:=ADOQUery.FieldByName('col_1').AsInteger
end;
Here is script, table, stored procedure for this example
create table tbl_echo
(
col_1 numeric(10,0) identity,
col_2 varchar(255),
col_3 int,
col_4 int
)
insert into tbl_echo (col_2, col_3, col_4) select 'testing', 100, 200
create proc sp_echo
as
select top 1 * from tbl_echo
You have two different ways to get what you want:
You declare an output parameter in your SP and, after inserting, assign ##IDENTITY to this parameter. Execute such a SP by using a TADOStoredProc component, not a TADOQuery component. After the SP finnishes, when the control comes back to your Delphi code you just read the value of such a parameter.
You simply select ##IDENTITY, what will give you a result set with a single row with a single column containing the generated value. In this case you can use a TADOQuery, but in your Delphi code you use Open (not ExecSQL) to retrieve such a resultset and just read the value from the only field is has.
Okey, I´ve tried to use some other providers with the same result.
One simple solution for this problem is just to cast the IDENTITY column to an integer in my procedure/query. By doing that it works like expected.
create proc sp_echo
as
select top 1 cast(col_1 as int) as col_1, col_2, col_3, col_4 from tbl_echo
I have two records with the same fields and one of them has a set of procedures. Why the Size of both records is the same?
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
type
TData = record
Age : Byte;
Id : Integer;
end;
TData2 = record
Age : Byte;
Id : Integer;
procedure foo1;
procedure foo2;
procedure foo3;
end;
procedure TData2.foo1;
begin
end;
procedure TData2.foo2;
begin
end;
procedure TData2.foo3;
begin
end;
begin
try
Writeln('SizeOf(TData) = '+ IntToStr(SizeOf(TData)));
Writeln('SizeOf(TData2) = '+ IntToStr(SizeOf(TData2)));
Readln;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
That's because the record itself only carries with the data that composes the record and no procedures or functions. The procedures and functions are a kind of syntactic sugar to avoid passing the record itself as a parameter: the self variable that is automagically added by the compiler for you.
Each method you declare in a record have another parameter for the record itself, for example:
TData2 = record
Age : Byte;
Id : Integer;
procedure Foo1;
procedure Foo2(SomeParam: Integer);
end;
is changed to something equivalent to:
PData2 = ^TData2;
TData2 = record
Age : Byte;
Id : Integer;
end;
procedure TData2_Foo1(Self: PData2);
procedure TData2_Foo2(Self: PData2; SomeParam: Integer);
end each call you make is also changed, for example:
var
Data: TData2;
begin
Data.Foo1;
Data.Foo2(1);
end;
is changed for something equivalent to:
var
Data: TData2;
begin
TData2_Foo1(#Data);
TData2_Foo1(#Data, 1);
end;
I have no Delphi at hand to check if the parameter is added at the beginning or at the end of your parameter list, but I hope you get the idea.
Of course there's no real syntax for this, since it is done on the fly by the compiler and thus, for example, the procedure names are not changed. I did that in a try to make my answer easy to understand.
Procedures do not take space. Compiler will hook them up properly. Their addresses do not need to be in memory at runtime for each record. If you look at the representation of TData2 in memory you will not find the procedures.
I am using Delphi 2010 with a dbgrid bound via ADO to a table in an Access mdb Database.
This table is filtered based on clicks in a radio group box.
The following code colour codes the rows based on the data in the table, but fails with an error dialogue box stating
"" is not a valid integer value
if the filter returns a null dataset. I thought I had allowed for not invoking colour setting if no records returned but it appears not to work ; see code below
procedure TMainForm.DBGridAbsenceDrawColumnCell(Sender: TObject;
const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
begin
//Need to add code to detect if record exists
with DBGridAbsence.Canvas do
begin
RecordCountLabel.Caption.Text := 'Absence Records: ' + IntToStr(ADOTblAbsence.RecordCount);
font.color:=clBlack;
brush.color:=clMoneyGreen;
If ( ADOTblAbsence.State <> dsInsert ) and ( ADOTblAbsence.RecordCount > 0 ) then
begin
Font.Color := StringToColor(ADOTblAbsenceForeground.AsString);
Brush.Color:= StringToColor(ADOTblAbsenceBackground.asstring);
end;
DBGridAbsence.DefaultDrawColumnCell(Rect, DataCol, Column, State);
end;
end;
Any and all help gratefully recieved. Note I have also tried conditionals such as
if Trim(ADOTblAbsenceForeground.AsString) <> ''
but this does not appear to work either.
The "" is not a valid integer value message occurs when you are trying to convert an empty string to an integer. There is nothing in your code sample that seems related. So either it must be in some code you are calling from here that you haven't shown us, or in some completely other code.
The StringToColor might be a candidate. If you have empty values in those columns, that's how that message gets triggered. So check your db values with a table browser or your database manager software.
Also, instead of checking all these various scenario's before doing a StrToInt, you could also use StrToIntDef which surrounds the StrToInt with a try except block and returns the default you supply when an exception occurs;
SomeInt := StrToIntDef(SomeString, 0);
I'd start by a test for the dataset being empty at the beginning of the procedure:
procedure TMainForm.DBGridAbsenceDrawColumnCell(Sender: TObject;
const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
begin
if not DBGridAbsensce.DataSet.IsEmpty then
begin
//Need to add code to detect if record exists
with DBGridAbsence.Canvas do
begin
RecordCountLabel.Caption.Text := 'Absence Records: ' +
IntToStr(ADOTblAbsence.RecordCount);
font.color:=clBlack;
brush.color:=clMoneyGreen;
If ( ADOTblAbsence.State <> dsInsert ) then
begin
Font.Color := StringToColor(ADOTblAbsenceForeground.AsString);
Brush.Color:= StringToColor(ADOTblAbsenceBackground.asstring);
end;
end;
end;
DBGridAbsence.DefaultDrawColumnCell(Rect, DataCol, Column, State);
end;