Creating Index ,PK, tables at runtime using FireDAC (no SQL script) - delphi-xe

I am trying do same with FireDac as following SQL:
CREATE TABLE [Franchises]([Franchise] TEXT(100) PRIMARY KEY ASC NOT NULL UNIQUE);
CREATE UNIQUE INDEX [IFran] ON [Franchises]([Franchise] ASC);
But i don't see index in SQLite Expert, property UNIQUE, PK. And why FireDac create table and column name in UPPERCASE?
I used code:
procedure TDataModule1.DataModuleCreate(Sender: TObject);
var
allTables:TStrings;
Table: TFDTable;
DBExists:Boolean;
begin
FDConnection1.Params.Database:= pathINI+baseTableDB;
DBExists:=FileExists(FDConnection1.Params.Database);
FDConnection1.Params.Values['CreateDatabase']:=BoolToStr(not DBExists,True);
FDConnection1.Params.DriverID:='SQLite';
FDConnection1.Connected:= True;
allTables := TStringList.Create;
FDConnection1.GetTableNames('', '', '', allTables, [osMy, osOther, osSystem],[tkView, tkTable, tkTempTable, tkLocalTable]);
if allTables.IndexOf('Franchises')=-1 then
begin
Table := TFDTable.Create(nil);
try
Table.Connection := FDConnection1;
Table.TableName := 'Franchises';
Table.FieldDefs.Add('Franchise', ftString, 100, True);
{ define primary key index }
// Table.IndexDefs.Add('IFran', 'Franchise',[ixPrimary]);
Table.AddIndex('IFran', 'Franchise', '', [soPrimary,soUnique,soNoCase]);
Table.IndexesActive:=True;
Table.IndexName:='IFran';
Table.CreateTable(False,[tpTable, tpPrimaryKey, tpIndexes]);
finally
Table.Free;
end;
end;
allTables.Free;
FDTable1.Connection:=FDConnection1;
FDTable1.TableName:='Franchises';
FDTable1.Open;
end;

Related

Oracle statement trigger cant access table values

When trying to access table values inside my statement trigger, I get the following error "ID XXX must be declared".
Table
CREATE TABLE testTable(
id INT,
store INT,
amount NUMBER
);
Trigger
CREATE OR REPLACE TRIGGER test_trigger
AFTER INSERT OR UPDATE ON testTable
DECLARE
myID INT;
myStore INT;
BEGIN
myID := id;
myStore := store;
IF amount < 3 THEN
DBMS_OUTPUT.PUT_LINE('DANGER');
END IF;
END;
Error
PLS-00201: ID 'id' must be declared
PLS-00201: ID 'store' must be declared
PLS-00201: ID 'amount' must be declared
I am new to oracle pl/sql and triggers and have zero clue how to solve this.
needs :new for new or update record
Try this.
CREATE OR REPLACE TRIGGER test_trigger
AFTER INSERT OR UPDATE
ON testTable
DECLARE
myID INT;
myStore INT;
BEGIN
myID := :new.id;
myStore := :new.store;
IF :new.amount < 3
THEN
DBMS_OUTPUT.PUT_LINE ('DANGER');
END IF;
END;

Exception after Locating a Detail TAdoQuery

I have a master and a detail TAdoQuery called FMaster and FDetail. They are connected via the DataSource FMasterSource. The detail query has a parameter, which is automatically filled and updated depending on the master query.
Now the problem is: After doing a FDetail.Locate (or in fact FDetail.Recordset.Clone), FMaster.Next throws an exception while trying to requery FDetail with the new parameter:
try
FMaster.Open;
FDetail.Open;
FDetail.Locate('Id', 2, []);
FMaster.Next; {<== throws Exception with ADO Errorcode 0x80040e05}
finally
FDetail.Close;
FMaster.Close;
end;
According to this List, Errorcode 0x80040e05 means "Object already open".
The problem seems to be, that FDetail.Locate creates a clone of the underlying Ado Recordset for later use, and saves it. We can therefore replace the Locate by
recordsetClone := FDetail.Recordset.Clone(adLockReadOnly); (which uses Winapi.ADOInt) and get the same error.
Inside FMaster.Next, the exception gets thrown by trying to requery FDetail after the new parameter for MasterId is set. This happens in TCustomADODataSet.RefreshParams.
When you disconnect Detail from Master, everything works just fine. Setting the parameter of FDetail by hand works just fine.
What did I miss?
Is it an error in the VCL?
Or is it yet another weird ADO bug?
Source Code
I am using Oracle 11g XE and Delphi 10.4 Sydney.
Oracle tables:
drop table Detail;
drop table Master;
create table Master (
Id NUMBER(2) not null,
constraint MasterPK primary key (Id)
);
create table Detail (
Id NUMBER(2) not null,
MasterId NUMBER(2),
constraint DetailPK primary key (Id),
constraint DetailFK foreign key (MasterId) references Master(Id)
);
insert into Master values (1);
insert into Master values (2);
insert into Detail values (1,1);
insert into Detail values (2,1);
insert into Detail values (3,2);
Delphi code:
procedure TForm1.Button1Click(Sender: TObject);
begin
//Initializing...
FConnection.Provider := 'OraOLEDB.Oracle.1';
FConnection.ConnectionString := 'Provider=OraOLEDB.Oracle.1;Password=xxxx;Persist Security Info=True;User ID=TEST;Data Source=XE';
FConnection.LoginPrompt := False;
FConnection.KeepConnection := False;
FMaster.Connection := FConnection;
FMaster.SQL.Text := 'select Id from Master';
FMaster.CursorLocation := clUseServer;
FMaster.CursorType := ctStatic;
FMasterSource.DataSet := FMaster;
FDetail.Connection := FConnection;
FDetail.DataSource := FMasterSource;
FDetail.SQL.Add('select Id, MasterId from Detail where MasterId = :Id');
FDetail.Parameters[0].DataType := ftInteger;
FDetail.CursorLocation := clUseServer;
//Do stuff
try
FMaster.Open;
FDetail.Open;
//FDetail.Locate('Id', 2, []);
recordsetClone := FDetail.Recordset.Clone(adLockReadOnly);
FMaster.Next; {<== throws Exception with ADO Errorcode 0x80040e05}
finally
FDetail.Close;
FMaster.Close;
end;
end;

Trigger oracle: whats wrong?

This is my code:
create trigger TriggerAdresse1
before INSERT
on Adresse
for each row
declare
enthaelt boolean;
begin
if ((Provinz in (select Name from Provinz2)) and (Laendercode in (select Laendercode from Provinz2))) then
enthaelt := true;
end if;
if(enthaelt:=false) then
rollback;
end if;
end;
I am trying to cancel the insert if the attribute Provinz or Laendercode isn´t in the table Provinz2.
Datagrip says it´s not valid...
Thanks for your help!
Best regards
A BOOLEAN value in Oracle can have three values: TRUE, FALSE and NULL, you did not initialize the variable.
You have to refer to new values (by default with :new)
By default a trigger cannot contain COMMIT or ROLLBACK
Your code must be this:
create trigger TriggerAdresse1
before INSERT
on Adresse
for each row
declare
enthaelt INTEGER;
begin
SELECT COUNT(*)
INTO enthaelt
FROM Provinz2
WHERE Name = :new.Provinz
AND Laendercode = :new.Laendercode;
if enthaelt = 0 then
RAISE_APPLICATION_ERROR(-20001, 'Provinz oder Ländercode ungültig');
end if;
end;
However, your requirement should be better implemented with FOREIGN KEY Constraint
alter table Adresse add constraint Provinz_FK FOREIGN KEY (Provinz)
references Provinz2 (Name);
alter table Adresse add constraint Laendercode_FK FOREIGN KEY (Laendercode)
references Provinz2(Laendercode);
Most likely Laendercode is not a UNIQUE key, but Name+Laendercode is. Then it would be this:
alter table Adresse add constraint Provinz_FK FOREIGN KEY (Provinz, Laendercode)
references Provinz2 (Name, Laendercode);
if(enthaelt:=false) then
Through this statement your intention is to perform a comparison however := does the assignment. So whenever you are comparing, you should use as below:
if(enthaelt=false) then
I think you intend to check the values being inserted so you really intend:
begin
enthaelt := false;
if ((:new.Provinz in (select Name from Provinz2)) and (:new.Laendercode in (select Laendercode from Provinz2))) then
enthaelt := true;
end if;
if(enthaelt:=false) then
rollback;
end if;
end;
You also need to initialize the variable.
That said, the correct way to implement this logic is with a foreign key constraint. It is sad that you are being taught to do this check with a trigger.

In PL/SQL can I set a variable dynamically?

In Oracle 12c R2, I have a function which receives a row type as a variable. In the function I want to read a table which contains a column name and a value, I want to then populate the row type variable passed in using the column name and the data from the table I read.
Here is a simplistic idea of what I want to do;
CREATE TABLE table_to_be_updated
(
key_value number,
cola varchar2(2),
colb varchar2(2),
colc varchar2(2),
cold varchar2(2),
cole varchar2(2),
colf varchar2(2)
);
CREATE TABLE table_default_value
(
default_stuff number,
column_name varchar(30),
column_default_value varchar2(2)
);
function do_defaults(in_table table_to_be_updated%rowtype, in_value number) return table_to_be_updated%rowtype
is
out_table table_to_be_updated%rowtype := in_table;
cursor my_curs
is
select * from table_default_value where default_stuff = in_value;
begin
for default_rec in my_curs
loop
out_table.[default_rec.column_name] := default_rec.column_default_value
end loop;
return out_table;
end;
insert into table_default_value (default_stuff,column_name,column_default_value) values (1,'cola','xx'));
insert into table_default_value (default_stuff,column_name,column_default_value) values (1,'colc','aa'));
insert into table_default_value (default_stuff,column_name,column_default_value) values (1,'cole','bb'));
In the line;
out_table.[default_rec.column_name] := [default_rec.column_default_value]
[default_rec.column_name] would be the column name, from the cursor, in out_table name I want to move data to.
and
[default_rec.column_default_value] is the value from the cursor I want to move into that column.
I suspect that what I want to do is impossible in PL/SQL, but I thought I'd ask.
There are other ways to accomplish updating the table directly, specifically using dynamic SQL with execute immediate, but I have a number of similar tables which all need to have the same things done to them, and I would prefer a single function to work on a record and then pass it back to have the calling routine update the proper table.
Here is the best I can come up with;
function do_defaults(in_table table_to_be_updated%rowtype, in_value number) return table_to_be_updated%rowtype
is
TYPE DEFAULT_TYPE IS TABLE OF VARCHAR2(2)
INDEX BY VARCHAR2(30);
DEFAULT_ARRAY DEFAULT_TYPE;
out_table table_to_be_updated%rowtype := in_table;
cursor my_curs
is
select * from table_default_value where default_stuff = in_value;
begin
DEFAULT_ARRAY('cola') := null;
DEFAULT_ARRAY('colb') := null;
DEFAULT_ARRAY('colc') := null;
DEFAULT_ARRAY('cold') := null;
DEFAULT_ARRAY('cole') := null;
DEFAULT_ARRAY('colf') := null;
for default_rec in my_curs
loop
DEFAULT_ARRAY(default_rec.column_name) := default_rec.column_default_value
end loop;
out_table.cola := DEFAULT_ARRAY('cola');
out_table.colb := DEFAULT_ARRAY('colb');
out_table.colc := DEFAULT_ARRAY('colc');
out_table.cold := DEFAULT_ARRAY('cold');
out_table.cole := DEFAULT_ARRAY('cole');
out_table.colf := DEFAULT_ARRAY('colf');
return out_table;
end;

check duplicate value before insert in oracle query without using trigger

i need in insert query check duplicates any fields like first name if not exist in this table make insert new row and return current inserted id
and else
if first name exist in table return zero as duplicated
not using trigger,function and proced
There is no such thing as an "insert query".
The normal way to implement such a requirement would be with a data integrity constraint:
alter table your_table
add constraint your_table_uk unique (first_name)
/
This would raise any exception if you attempt to insert a duplicate record.
To get the currently inserted ID:
insert into your_table (id, first_name)
values (your_seq.nextval, 'SAM-I-AM')
returning id
/
You say you don't want to use a function or a procedure but the only way to return 0 if the submitted first_name is a duplicate would be programmatic:
create or replace function new_record (p_name your_table.first_name%type)
return your_table.id%type
is
return_value your_table.id%type;
begin
begin
insert into your_table (id, first_name)
values (your_seq.nextval, p_first_name)
returning id into return_value;
exception
when dup_val_on_index then
return_value := 0;
end;
return return_value;
end;

Resources