I want to run this code from my query windows inside Excel but I get an error after it completes the first statement. If can run it fine in PCC. I'm assuming I need to create a stored procedure with a variable. Parent = 'B-8579-K' is the variable value.
How to I create the stored procedure?
How do I call the stored procedure and pass on the variable?
Will the call even work from within Excel without causing an error?
delete z_Expl_BOM_Temp;
insert into z_Expl_BOM_Temp
select
Parent, L1_Child_Seq, L1_Child, L1_Child_QTY,
L2_Child_Seq, L2_Child, L2_Child_QTY,
L3_Child_Seq, L3_Child, L3_Child_QTY,
L4_Child_Seq, L4_Child, L4_Child_QTY,
L5_Child_Seq, L5_Child, L5_Child_QTY
from
EGC_Expl_BOM_TT
where
Parent = 'B-8579-K' ;
Creating Stored Procedures in Pervasive are documented here.
An example of a stored procedure that takes a parameter is:
CREATE PROCEDURE Checkmax(in :classid integer);
BEGIN
DECLARE :numenrolled integer;
DECLARE :maxenrolled integer;
SELECT COUNT(*) INTO :numenrolled FROM Enrolls WHERE class_ID = :classid;
SELECT Max_size INTO :maxenrolled FROM Class WHERE id = :classid;
IF (:numenrolled >= :maxenrolled) THEN
PRINT 'Enrollment Failed. Number of students enrolled reached maximum allowed for this class' ;
ELSE
PRINT 'Enrollment Possible. Number of students enrolled has not reached maximum allowed for this class';
END IF;
END;
And then calling it:
CALL Checkmax(101)
I finally got the SP created without an error:
CREATE PROCEDURE EGC_Expl_BOM_TT(in :CParent varchar(20));
BEGIN
delete from z_Expl_BOM_Temp;
insert into z_Expl_BOM_Temp
select
Parent, L1_Child_Seq, L1_Child, L1_Child_QTY,
L2_Child_Seq, L2_Child, L2_Child_QTY,
L3_Child_Seq, L3_Child, L3_Child_QTY,
L4_Child_Seq, L4_Child, L4_Child_QTY,
L5_Child_Seq, L5_Child, L5_Child_QTY
from
EGC_Exploded_BOM
where
Parent = :CParent;
END;
I can call is just fine in PCC. However, when I run the CALL in Excel's query window is will execute the procedure but when it completes I get this error:
"The query did not run, or the database table could not be opened.
Check the database server or contact your database administrator. Make sure the external database is available and hasn't been moved or reorganized, then try the operation again."
This might now be turning into a VBA question. Can I run the CALL with VBA?
Related
I wrote a procedure and I need to return the last generated ID when doing an insert in the database using the node-oracledb library.
I'm using DBMS_OUTPUT to return the value of a variable declared in the procedure, I can return the value output in SQL Developer, but I can't return it in the node-oracledb library.
Procedure:
create or replace PROCEDURE TESTE.CREATE_PATIENT(
p_DT_NASCIMENTO IN TESTE.PESSOA_FISICA.DT_NASCIMENTO%TYPE,
p_IE_SEXO IN TESTE.PESSOA_FISICA.IE_SEXO%TYPE,
p_NR_CPF IN TESTE.PESSOA_FISICA.NR_CPF%TYPE,
p_NR_REG_GERAL_ESTRANG IN TESTE.PESSOA_FISICA.NR_REG_GERAL_ESTRANG%TYPE,
p_NR_PASSAPORTE IN TESTE.PESSOA_FISICA.NR_PASSAPORTE%TYPE,
p_NM_PESSOA_FISICA IN TESTE.PESSOA_FISICA.NM_PESSOA_FISICA%TYPE,
p_CD_ACOMPANHANTE IN TESTE.PESSOA_FISICA_ACOMPANHANTE.CD_ACOMPANHANTE%TYPE,
p_NR_TELEFONE_CELULAR IN TESTE.PESSOA_FISICA.NR_TELEFONE_CELULAR%TYPE
)
IS
p_CD_PESSOA_FISICA NUMBER;
BEGIN
INSERT INTO TESTE.PESSOA_FISICA (DT_NASCIMENTO,IE_SEXO,NM_PESSOA_FISICA,NR_CPF,NR_PASSAPORTE,NR_REG_GERAL_ESTRANG,NR_TELEFONE_CELULAR) VALUES(p_DT_NASCIMENTO,p_IE_SEXO,p_NM_PESSOA_FISICA,p_NR_CPF,p_NR_PASSAPORTE,p_NR_REG_GERAL_ESTRANG,p_NR_TELEFONE_CELULAR) RETURNING CD_PESSOA_FISICA INTO p_CD_PESSOA_FISICA;
INSERT INTO TESTE.PESSOA_FISICA_ACOMPANHANTE (CD_PESSOA_FISICA, CD_ACOMPANHANTE) VALUES(p_CD_PESSOA_FISICA, p_CD_ACOMPANHANTE);
DBMS_OUTPUT.PUT_LINE(p_CD_PESSOA_FISICA);
COMMIT;
END;
Call the procedure:
SET SERVEROUTPUT ON
BEGIN CREATE_PATIENT(TO_DATE('1981-12-10', 'YYYY-MM-DD'), 'F', '25845685236', '12345645', '65432145', 'Marina Santos', '3', '+5511999999999'); END;
See the return:
Return In SQL Developer
I need to return this value with node-oracledb, has anyone experienced this and could help me out?
Don't use DBMS_OUTPUT; use an OUT parameter:
create PROCEDURE TESTE.CREATE_PATIENT(
p_DT_NASCIMENTO IN TESTE.PESSOA_FISICA.DT_NASCIMENTO%TYPE,
p_IE_SEXO IN TESTE.PESSOA_FISICA.IE_SEXO%TYPE,
p_NR_CPF IN TESTE.PESSOA_FISICA.NR_CPF%TYPE,
p_NR_REG_GERAL_ESTRANG IN TESTE.PESSOA_FISICA.NR_REG_GERAL_ESTRANG%TYPE,
p_NR_PASSAPORTE IN TESTE.PESSOA_FISICA.NR_PASSAPORTE%TYPE,
p_NM_PESSOA_FISICA IN TESTE.PESSOA_FISICA.NM_PESSOA_FISICA%TYPE,
p_CD_ACOMPANHANTE IN TESTE.PESSOA_FISICA_ACOMPANHANTE.CD_ACOMPANHANTE%TYPE,
p_NR_TELEFONE_CELULAR IN TESTE.PESSOA_FISICA.NR_TELEFONE_CELULAR%TYPE,
o_CD_PESSOA_FISICA OUT TESTE.PESSOA_FISICA.CD_PESSOA_FISICA%TYPE
)
IS
BEGIN
INSERT INTO TESTE.PESSOA_FISICA (
DT_NASCIMENTO,
IE_SEXO,
NM_PESSOA_FISICA,
NR_CPF,
NR_PASSAPORTE,
NR_REG_GERAL_ESTRANG,
NR_TELEFONE_CELULAR
) VALUES(
p_DT_NASCIMENTO,
p_IE_SEXO,
p_NM_PESSOA_FISICA,
p_NR_CPF,
p_NR_PASSAPORTE,
p_NR_REG_GERAL_ESTRANG,
p_NR_TELEFONE_CELULAR
)
RETURNING CD_PESSOA_FISICA INTO o_CD_PESSOA_FISICA;
INSERT INTO TESTE.PESSOA_FISICA_ACOMPANHANTE (
CD_PESSOA_FISICA,
CD_ACOMPANHANTE
) VALUES(
o_CD_PESSOA_FISICA,
p_CD_ACOMPANHANTE
);
END;
/
Note: Using COMMIT in the procedure means that you cannot chain multiple procedures and/or DML statements together in a single transaction and then use ROLLBACK on them all if a later one fails. You should remove the COMMIT and call it outside the procedure when the transaction is complete.
Then if you want to call the procedure and output the value in SQL Developer then you can use DBMS_OUTPUT in the block you call the procedure from:
SET SERVEROUTPUT ON
DECLARE
v_CD_PESSOA_FISICA TESTE.PESSOA_FISICA.CD_PESSOA_FISICA%TYPE;
BEGIN
CREATE_PATIENT(
TO_DATE('1981-12-10', 'YYYY-MM-DD'),
'F',
'25845685236',
'12345645',
'65432145',
'Marina Santos',
'3',
'+5511999999999',
v_CD_PESSOA_FISICA
);
DBMS_OUTPUT.PUT_LINE(v_CD_PESSOA_FISICA);
COMMIT;
END;
/
You can call the same procedure in Node and read the out parameter into a variable.
Recently at my company we tried to use DUnitX with all it's blessings to test classes we wrote. Since those classes reflect entities in database all fields have to accept null values as well as specific type (e. g. Integer or string).
Since spring4d already have those we tried to use them:
INaleznosc = interface
['{D5D6C901-3DB9-4EC2-8070-EB0BEDBC7B06}']
function DajPodstawaVAT(): TNullableCurrency;
property PodstawaVAT: TNullableCurrency read DajPodstawaVAT;
end;
TNaleznosc = class(TInterfacedObject, INaleznosc)
strict private
FId: TNullableInt64;
FPodstawaVAT: Currency;
function TNaleznosc.DajPodstawaVAT(): TNullableCurrency;
published
property PodstawaVAT: TNullableCurrency read DajPodstawaVAT;
end;
INaleznoscFunkcje = interface
['{509288AB-110A-4A52-BE93-3723E5725F4B}']
function DajPodstawaVAT(pID: TNullableInt64): TNullableCurrency;
end;
function TNaleznosc.DajPodstawaVAT(): TNullableCurrency;
begin
FPodstawaVAT := FFunkcje.DajPodstawaVAT(FId);
end;
procedure TTestNaleznosc.PodstawaVATGetterNieWywolujefunkcji();
var
funkcjeNaleznosc: TMock<INaleznoscFunkcje>;
klasa: INaleznosc;
id: TNullableInteger;
begin
//initialize tested elements
funkcjeNaleznosc := TMock<INaleznoscFunkcje>.Create();
id := 15;
klasa := TNaleznosc.Create(funkcjeNaleznosc, id, zmienne);
//setup expected behaviour from mock
funkcjeNaleznosc.Setup.WillReturn(2).When.DajPodstawaVAT(id);
funkcjeNaleznosc.Setup.Expect.Once.When.DajPodstawaVAT(id);
//this triggers getter
klasa.PodstawaVAT;
end;
When this code is executed we get AV exception First chance exception at $00000000. Exception class $C0000005 with message 'access violation at 0x00000000: access of address 0x00000000'. Process Tests.exe (6556).
Eventually we narrowed this issue down to Move procedure in System.Rtti unit, TValueDataImpl.ExtractRawDataNoCopy function:
when Length(FData) is less or equal to 8 it works fine
when Length(FData) is between 9 and 32 at line 5905 of System unit (FISTP QWORD PTR [EDX+8] {Save Second 8}) whole call stack disappears beside two lines (we are not sure whether it's relevant or not, but it doesn't look like good sign) and after getting to topmost function (according to call stack) we get error.
Call stack before "saving second 8"
Call stack after "saving second 8"
Is it our fault or is it some issue with system/spring/dunitx units? How can we use nullable types and tests at the same time?
I am not sure if Delphi Mocks has a generic type parameter on its WillReturn method but if so then pass TNullableCurrency there - otherwise the compiler will infer the type from the parameter 2 you are passing and obviously internally it fails to put that into the TNullableCurrency it should return.
If it does not and only allows TValue then you need to pass one that contains a TNullableCurrency and not 2 which it would by using its implicit operator like so: TValue.From<TNullableCurrency>(2)
Furthermore I am not sure if they did fix the code in the SameValue routine in Delphi Mocks when the value to be compared is a record (as TNullableCurrency is)
Edit: no, they did not - see https://github.com/VSoftTechnologies/Delphi-Mocks/issues/39
You might want to consider giving Spring4D mocks a try which should be able to handle nullables.
Following on from my question Inno Setup disable component selection when a specific component is selected, I think there may be a way to get this to work without the problem of checked states set in code being permanent (though use of the Checked property) by instead using:
function CheckItem(const Index: Integer; const AOperation: TCheckItemOperation): Boolean;
in TNewCheckListBox, however I am having trouble getting the syntax correct. I am trying:
CheckItem(CompIndexSync, coUncheck) := Checked[CompIndexClient];
where the CompIndexes are constants assigned to the indexes for the component values. I get an Identifier Expected error at compile. Can someone advise how to correctly use this function and what I am doing wrong?
The CheckItem member of the TNewCheckListBox class is a method of type function which updates the checked state by the AOperation operation and returns True if any changes were made to the state of the item at Index, or any of its children. Here is its prototype (source):
function TNewCheckListBox.CheckItem(const Index: Integer;
const AOperation: TCheckItemOperation): Boolean;
The problem was that you were trying to assign a value to the function result. That's not what you can do in Pascal language in general.
What you want to do with the item(s) is passed by the AOperation parameter. In pseudocode e.g.:
var
CheckList: TNewCheckListBox;
Operation: TCheckItemOperation;
begin
...
if ShouldCheck then
Operation := coCheck
else
Operation := coUncheck;
if CheckList.CheckItem(ItemIndex, Operation) then
MsgBox('An item has changed its state.', mbInformation, MB_OK);
end;
A thread is looping through a list of 1000 objects every second.
The objects hold simple configuration data. When some conditons are met, a worker thread is given the configuration data and does some work based on that.
Now I want to bring up a settings dialog with the configuration data so I can change the data inside such an object. But then I have to access objects in the list while the thread is also continously accessing them. I know how to use a critical section, but if the thread enters a critical section each time it checks on an object, then the critical section will be entered and left 1000 times per second. Maybe there is a smarter way?
How to make threadsafe with the least overhead when:
a) loading the config data into the settings dialog form (which uses a TListView in virtual mode and needs to access the list of objects on demand)
b) and saving the form input back to the object?
EDIT: More detail was requested.
The objects are in a TList and basically look like this:
TConfigData = class
ID:Integer;
Name: String;
SwitchTime: TDateTime;
end;
The data of the ConfigData object needs to be loaded into the Settings Dialog form so it can be edited, and then, if the user clicks OK, the ConfigData object should be updated and the thread will happily use this new data next time the obkect is accessed. However, updating must not happen at the same time as the thread is reading the ConfigData object.
EDIT 2: Additional details:
The threads are reading ID, Name and SwitchTime but only SwitchTime is changed by threads. (When work is done, new time is calculated and thats what triggers next work event).
The settings dialog can change both Name and SwitchTime, but not ID.
After a bit of thought, you can get away without using critical sections at all just by using InterlockedExchangePointer:
You need to add a routine to update the config for an item:
procedure TMyThread.UpdateConfig(const aIndex: Integer; const aID:Integer;
const aName: String; const aSwitchTime: TDateTime);
var
newConfigToEdit: TConfigData;
oldValue: TConfigData;
begin
newConfigToEdit := TConfigData.Create;
newConfigToEdit.ID := aID;
newConfigToEdit.Name := aName;
newConfigToEdit.SwitchTime := aSwitchTime;
repeat
oldvalue := InterlockedExchangePointer(FConfigDataList.List[aIndex], newConfigToEdit);
until (oldvalue <> nil) and (oldValue <> newConfigToEdit);
oldvalue.Free; // Free the replaced one
end;
That routine would replace the config for the item with Index of aIndex. To get the config in your thread you will need to be a bit clever. We get a copy of it and replace the value in the list with nil while we are working on it. This prevents the other thread from replacing it. Once we have finished we put back the replaced value.
procedure TMyThread.Execute;
var
configToUse: TConfigData;
begin
repeat
// Get the config and replace it with nil so it won't be changed
configToUse := InterlockedExchangePointer(FConfigDataList.List[idx], nil);
// Do stuff with the config
// Put the config back
FConfigDataList.List[idx] := configToUse;
// You could also use the line below instead of the assignment
// InterlockedExchangePointer(FConfigDataList.List[idx], configToUse);
until Terminated;
end;
If you want to kick off a worker thread with the config then you should make a clone of it and then pass in the clone to the worker because it can be changed.
Main thread (pseudo)code (ObjList is the global variable):
if ConfigUpdated(i) then
ObjList[i].ConfigVersion := ObjList[i].ConfigVersion + 1;
Other thread(s) (pseudo)code (ObjConfVer is the local for thread)
for i := 0 to ObjCount - 1 do
if ObjConfVer[i] < ObjList[i].ConfigVersion then
begin
// Here you have to take care that main thread will can not change config while you handling it
// Do something about new config
ObjConfVer[i] := ObjList[i].ConfigVersion;
// Exit from critical section
end;
If you have n threads which works with the same ObjList it is allow to each thread doing something about changed config independently.
BTW if you are using FPC/Lazarus this link may be usefull: Parallel procedures
I have never used threads--never thought my code would benefit. However, I think threading might improve performance of the following pseudo code:
Loop through table of records containing security symbol field and a quote field
Load a web page (containing a security quote for a symbol) into a string variable
Parse the string for the quote
Save the quote in the table
Get next record
end loop
Loading each web page takes the most time. Parsing for the quote is quite fast. I guess I could take, say, half the records for one thread and work the other half in a second thread.
In OmniThreadLibrary it is very simple to solve this problem with a multistage pipeline - first stage runs on multiple tasks and downloads web pages and second stage runs in one instance and stores data into the database. I have written a blog post documenting this solution some time ago.
The solution can be summed up with the following code (you would have to fill in some places in HttpGet and Inserter methods).
uses
OtlCommon,
OtlCollections,
OtlParallel;
function HttpGet(url: string; var page: string): boolean;
begin
// retrieve page contents from the url; return False if page is not accessible
end;
procedure Retriever(const input: TOmniValue; var output: TOmniValue);
var
pageContents: string;
begin
if HttpGet(input.AsString, pageContents) then
output := TPage.Create(input.AsString, pageContents);
end;
procedure Inserter(const input, output: IOmniBlockingCollection);
var
page : TOmniValue;
pageObj: TPage;
begin
// connect to database
for page in input do begin
pageObj := TPage(page.AsObject);
// insert pageObj into database
FreeAndNil(pageObj);
end;
// close database connection
end;
procedure ParallelWebRetriever;
var
pipeline: IOmniPipeline;
s : string;
urlList : TStringList;
begin
// set up pipeline
pipeline := Parallel.Pipeline
.Stage(Retriever).NumTasks(Environment.Process.Affinity.Count * 2)
.Stage(Inserter)
.Run;
// insert URLs to be retrieved
for s in urlList do
pipeline.Input.Add(s);
pipeline.Input.CompleteAdding;
// wait for pipeline to complete
pipeline.WaitFor(INFINITE);
end;
If the number of records is relatively small, say 50 or less, you could just launch a separate thread for each record and let them all run in parallel, eg:
begin thread
Load a web page for symbol into a string variable
Parse the string for the quote
Save the quote in the table
end thread
.
Loop through table of records
Launch a thread for current security symbol
Get next record
end loop
If you have a larger number of records to process, consider using a pool of threads so you can handle records in smaller batches, eg:
Create X threads
Put threads in a list
Loop through table of records
Wait until a thread in pool is idle
Get idle thread from pool
Assign current security symbol to thread
Signal thread
Get next record
end loop
Wait for all threads to be idle
Terminate threads
.
begin thread
Loop until terminated
Mark idle
Wait for signal
If not Terminated
Load a web page for current symbol into a string variable
Parse the string for the quote
Save the quote in the table
end if
end loop
end thread
There are many different ways you could implement the above, which is why I left it in pseudocode. Look at the VCL's TThread, TList, and TEvent classes, or the Win32 API QueueUserWorkerItem() function, or any number of third party threading libraries.