Cast to TObject - sql

I am struggling to get the ItemIndex of my a Item in my TComoBox...
Usually that doesn't seem to be a hard thing to do for me... but somehow when i try to cast my String(which I got from a SQL select) to a TObject. That doesn't seem to work. I tried to debug my code with writing the String manually into the Object like so:
TObject('U');
That somehow does work, i just cant explain that...
The way i fill the ComoBox:
for i := Low(_VerkaufTypenBez) to High(_VerkaufTypenBez) do
begin
CBBelegart.AddItem(VerkaufTypenBez(i), Tobject(VerkaufTypenShort[i]));
end;
The way i tried to set the Index:
CB.ItemIndex := CB.Items.IndexOfObject(TObject(SetIndexWithSQL('select top 1 * from KOMSAconfig_Allgemein', 'Belegart'))); //index = -1
helper := 'U';
CB.ItemIndex := CB.Items.IndexOfObject(TObject(helper)); //index = -1
CB.ItemIndex := CB.Items.IndexOfObject(TObject('U')); //index = 1
Any suggestions?

This is what happens when you try to use a visual control as if it were a general purpose container. A visual control is used purely for display and user interaction. Don't try to bend it to do more than that.
In your scenario it is pointless to attempt to add a second string into the combo box. Stop doing that. Change your loop that populates to be like so:
for i := Low(_VerkaufTypenBez) to High(_VerkaufTypenBez) do
begin
CBBelegart.Items.Add(VerkaufTypenBez(i));
end;
The other string is held in an array like structure and seemingly can be accessed like this: VerkaufTypenShort[i]. So, if you want to lookup the index of a particular value, that can be done like so:
function GetIndexFromShortString(const Value: string): Integer;
begin
for Result := Low(_VerkaufTypenBez) to High(_VerkaufTypenBez) do
if VerkaufTypenShort[Result]=Value then
exit;
Result := -1;
end;
Notice that this function is completely independent of the visual control. You should strive to use visual controls for the bare minimum, and write your business logic without reference to any visual controls.

Your problem here seems to be that you get two strings from your query, where one is used as display text and the other is needed for internal lookups (coming from a different query).
I see two ways to solve this:
change your second query, so it returns the string, that is used in the combobox
have a lookup in your form that connects both strings
For the second you could use a TDictionary. My approach here assumes that filling the combobox is done only in one loop and there aren't strings added or removed dynamically.
Add the Dictionary to your form:
TForm1 = class(TForm)
...
private
FLookUp: TDictionary<string, Integer>;
In the FormCreate and FormDestroy events create and destroy your Dictionary.
FLookUp := TDictionary<string, Integer>.Create;
FLookUp.Free;
In the loop you use to fill the Combobox, store the Indices and connect them to your lookup-strings (I just copied your loop, assuming that it works as is).
procedure TForm1.FillCombo;
var
Index, I: Integer;
begin
FLookUp.Clear;
for i := Low(_VerkaufTypenBez) to High(_VerkaufTypenBez) do
begin
Index := CBBelegart.Items.Add(VerkaufTypenBez(i));
FLookUp.Add(VerkaufTypenShort[i], Index);
end;
end;
Now you can look for the correct Index using that Dictionary
function TForm1.GetIndexOfString(const Value: string): Integer;
begin
if not FLookUp.TryGetValue(Value, Result) then
Result := -1;
end;
Use it like
CB.ItemIndex := GetIndexOfString(SetIndexWithSQL(...));

Related

Searching each element of array using like

I would like some professional advice regarding my problem.
You see our database is using an ETL to retrieve massive data. Some or rather most of these data are aggregated.
Problem is I need to retrieve a data, base on the selected Stations
For example. I selected MNL.
Now, in aggregated column we have arrays like:
{MNL-CEB,CEB-MNL,DVO-MNL,MNL-DVO,DVO-CEB,CEB-DVO}
Now, given with my selected code (MNL), I should only be able to pick the following elements from the array.
MNL-CEB,
CEB-MNL,
DVO-MNL,
MNL-DVO
I've been trying various where conditions with no success. Hope you guys can help me out. Thanks!
Here's a piece of code I've been using below -
select distinct
unnest(fpd.segment_agg) segments
from daylightreport.f_dailysales_agg fpd
The data is too big to unnest, causing the script to load more.
Edit - I'm also using more than 1 station code. For instance
Where fpd.segment_agg IN ('MNL','CEB') or something similar to this.
Thanks in advance!
One way that avoids unnesting is to convert the array to a string then do a regex match on that string:
select *
from daylightreport.f_dailysales_agg fpd
where array_to_string(fpd.segment_agg, ',') ~ '(-){0,1}(MNL)(-){0,1}';
The regex makes sure the search string doesn't appear as part of another string. If that can't happen anyway, a simple like would probably do as well. This also assumes that the data never contains a ,
This is not going to be fast though, but avoids the unnesting in multiple rows.
Another option (although probably not really that much faster) is to write a function that loops through the array elements and uses the LIKE operator:
create or replace function any_element_like(p_elements text[], p_pattern text)
returns boolean
as
$$
declare
l_word text;
begin
foreach l_word in array p_elements
loop
if l_word like p_pattern then
return true;
end if;
end loop;
return false;
end;
$$
language plpgsql;
That could be used like this:
select *
from daylightreport.f_dailysales_agg fpd
where any_element_like(fpd.segment_agg, '%MNL%');

AsyncKeyDown pressed key value

Using Intraweb in Delphi,
Is there a way to find AsyncKeyDown pressed key value?
I know its somehow inside EventParams: TStringList but I only can see it through the Local variable list at runtime like this, string = 'char=i', and I cannot really get it out from there as a char unless I make a special function to pull it from there, so I'm thinking if there is a simple way to just get the pressed key as char from AsyncKeyDown ?
I know it's been a long time since the question was asked. But it will be good for those who still need it. I used it and working. You can get it as char as following:
http://www.devsuperpage.com/search/Articles.aspx?G=2&ArtID=52557
procedure TfrmLHasta.edSuzAsyncKeyDown(Sender: TObject;
EventParams: TStringList);
var
lKey: char;
begin
lKey := Chr(StrToInt(EventParams.Values['which']));
// WebApplication.ShowMessage(lKey);
if lKey = #13 then DoSomething;
end;

Passing Parameter from SQL-File to procedure

Sadly I don't know the exact names for each part, probably I can explain it:
We have a SQL-File, which is defined like this:
declare
l_exec boolean := true;
l_print boolean := false;
---
begin
dbms_output.enable;
for t in (
And so on. We use this to create some Triggers etc. in our Oracle-Database.
Now this File grew to much and I need to split it up.
So I created a Main-File, which is looking like this:
SET SERVEROUTPUT on
set feedback off
DEFINE l_exec = TRUE;
DEFINE l_print = FALSE;
ACCEPT ora_instance PROMPT "Zielinstanz angeben [123|...]: " DEFAULT somedefault
CONNECT somelogins
PROMPT -- GENERATING CODE --
#file1 l_print, l_exec
This would work fine, but I'd like to pass the l_exec and l_print to each file. I read in some other Threads passing the parameters like this is ok. But the problem is: I not only need to pass it from file to file, but I need to pass it to the Procedure-Block after begin.
I'm not sure if I made it understandable, but I'd like to use the variable I pass at #file1 in the begin block of the Files.
Is this kindahow possible? I googled alot about SQLPlus, but didn't find a solution so far.
Thanks as always and a nice week
Matthias
Ok, so if your main file has the call to your sub-file:
...
CONNECT somelogins
PROMPT -- GENERATING CODE --
#file1 l_print, l_exec
then your sub-file can use the parameters in a positional manner thus:
declare
l_print boolean := &1;
l_exec boolean := &2;
---
begin
dbms_output.enable;
for t in (

PL/SQL Validate/Insert/Update Status - retval vs. exception

I'm always looking to improve and applying best practices. I read quite a bit about refactoring in the last weeks. I have to work with a lot of awful code and I produced some not so nice stuff too but I'm trying to change that. Thats no problem for most languages but I'm pretty new to PL/SQL so I just copied the style of the already written code.
After reading some tutorials I realized that a lot of our code is pretty much more C style code using retval instead exceptions etc.
We have a lot of functions like open cursor, loop through it, validate the data, trim it or make some string manipulation and insert it into another table, update the status etc. I wonder what a best practice solution would look like on something like this. Atm most functions look like this:
LOOP
FETCH C_ABC INTO R_ABC;
EXIT WHEN C_ABC%NOTFOUND OR C_ABC%NOTFOUND IS NULL;
SAVEPOINT SAVE_LOOP;
retval := plausibilty_check(r_ABC);
IF (retval = STATUS_OK) THEN
retval := convert_ABC_TO_XYZ(r_ABC, r_XYZ);
END IF;
IF (retval = STATUS_OK) THEN
retval := insert_XYZ(r_XYZ);
END IF;
retval := update_ABC(r_ABC.PK_Id, retval);
END LOOP;
If i was using exceptions I guess I had to raise them inside the functions so I can handle them in the main function, if not everyone would have to crawl to every sub function to understand the program and where the updates happen etc. So I guess I would have to use another PL/SQL block inside the loop? Like:
LOOP
FETCH C_ABC INTO R_ABC;
EXIT WHEN C_ABC%NOTFOUND OR C_ABC%NOTFOUND IS NULL;
SAVEPOINT SAVE_LOOP;
BEGIN
plausibilty_check(r_ABC);
convert_ABC_TO_XYZ(r_ABC, r_XYZ);
insert_XYZ(r_XYZ);
update_ABC(r_ABC.PK_Id, STATUS_OK);
EXCEPTION
WHEN ERROR_CODE_XYZ THEN
update_ABC(r_ABC.PK_Id, ERROR_CODE_XYZ);
END
END LOOP;
I guess that function handles a pretty common problem but I still havn't found any tutorial covering something like this. Maybe someone more experienced with PL/SQL might gimme a hint what a best practice on a task like that would look like.
I like to use exceptions to jump out of a block of code if an exceptional event (e.g. an error) occurs.
The problem with the "retval" method of detecting error conditions is that it introduces a second layer of semantics on what a function is and for.
In principle, a function should be used to perform a calculation and return a result; in this sense, a function doesn't do anything, i.e. it makes no changes to any state - it merely returns a value.
If it cannot for some reason calculate that value, that would be an exceptional circumstance, so I'd want the function to raise an exception so that the calling program will not blindly continue on its merry way, thinking it got a valid value from the function.
On the other hand, a procedure is a method by which action is done - something is changed, something is validated, or something is sent. The normal expected path is that the procedure is executed, it does its thing, then it finishes. If an error occurs, I want it to raise an exception so that the calling program will not blindly continue thinking that the procedure has successfully done its thing.
Thus, the original intent of the fundamental difference between "procedures" and "functions" is preserved.
In languages like C, there are no procedures - everything is a function in a sense (even functions that return "void") - but in addition, there is no real concept of an "exception" - so these semantics don't apply. It's for this reason that the C style of returning an error/success flag don't translate well into languages like PL/SQL.
In your example, I'd consider doing it something like this:
BEGIN
LOOP
FETCH c_ABC INTO r_ABC;
EXIT WHEN c_ABC%NOTFOUND;
IF record_is_plausible(r_ABC) THEN
r_XYZ := convert_ABC_TO_XYZ(r_ABC);
insert_or_update_XYZ(r_XYZ);
ELSE
update_as_implausible(r_ABC);
END IF;
END LOOP;
EXCEPTION
WHEN OTHERS THEN
-- log the error or something, then:
RAISE;
END;
So where the semantics of the operation is doing some validation or something, and returning a result, I converted plausibilty_check into a function record_is_plausible that returns a boolean.
I'd pull the call to update_ABC out of the BEGIN block and make it common at the bottom of the loop, as in:
DECLARE
nFinal_status NUMBER;
BEGIN
LOOP
FETCH C_ABC INTO R_ABC;
EXIT WHEN C_ABC%NOTFOUND OR C_ABC%NOTFOUND IS NULL;
SAVEPOINT SAVE_LOOP;
nFinal_status := nSTATUS_OK;
BEGIN
plausibilty_check(r_ABC);
convert_ABC_TO_XYZ(r_ABC, r_XYZ);
insert_XYZ(r_XYZ);
EXCEPTION
WHEN excpERROR_CODE_XYZ THEN
nFinal_status := nERROR_CODE_XYZ;
END;
update_ABC(r_ABC.PK_Id, nFinal_status);
END LOOP;
END;
You might want to have each of the procedures throw its own exception so you could more easily identify where the issue(s) are coming from - or use different exceptions/error codes for each possible issue (for example, the plausibility check might raise different exceptions depending on what implausible condition it found). However, in my experience plausibility checks will often uncover multiple conditions (if the data's bad it's often really bad :-), so it might be nice to tabularize the errors and relate them to the base ABC data through a foreign key, thus allowing each individual error to be identified with just one pass through the data. Then the 'status' field on ABC becomes moot; you either have errors associated with the ABC row or you don't. If errors exist, do whatever is needed. If no errors, proceed with 'normal' processing.
Just a thought.
Share and enjoy.

Selecting large sequence value into global variable not working in PL/SQL code

I have a script where I would like to have a global variable to store a transaction number for later use. The code is working fine on one schema where the sequence value that I'm fetching is relatively low. It is not working on another schema with a higher sequence value where I get a "Numeric Overflow". If I change that sequence value to a lower number it is working as well but that is not an option.
VAR TRANSACTIONNR NUMBER;
BEGIN
--Works with NEXTVAL being around 946713241
--Doesn't work with NEXTVAL being around 2961725541
SELECT MY_SEQUENCE.NEXTVAL INTO :TRANSACTIONNR FROM DUAL;
MY_PACKAGE.STARTTRANSACTION(:TRANSACTIONNR);
END;
/
-- SQL Statements
BEGIN
MY_PACKAGE.ENDTRANSACTION;
MY_PACKAGE.DO_SOMETHING(:TRANSACTIONNR);
END;
/
What is also working is selecting the sequence into a variable declared in the DECLARE block:
DECLARE
TRANSACTIONNR NUMBER;
BEGIN
SELECT MY_SEQUENCE.NEXTVAL INTO TRANSACTIONNR FROM DUAL;
MY_PACKAGE.STARTTRANSACTION(TRANSACTIONNR);
END;
/
But that means I won't be able to reuse it in the block at the end. Setting the size of the number is not possible.
VAR TRANSACTIONNR NUMBER(15)
is not valid.
Any ideas what I could try or other ways to store global state?
On further investigation this looks like it might be a SQL Developer bug (making assumptions about what you're doing again, of course...). I can get the same error with:
VAR TRANSACTIONNR NUMBER;
BEGIN
SELECT 2961725541 INTO :TRANSACTIONNR FROM DUAL;
END;
/
It appears that SQL Developer's NUMBER is limited to 2^31, which isn't the case normally for Oracle.
A possibly workaround is to use BINARY_FLOAT to store the value, but you'll run into precision problems eventually (not sure where, but looks OK up to 2^53-ish), and you'll need to cast() it back to NUMBER when using it.
VAR TRANSACTIONNR BINARY_DOUBLE;
BEGIN
SELECT 2961725541 INTO :TRANSACTIONNR FROM DUAL;
-- dbms_output.put_line(cast(:TRANSACTIONNR as NUMBER)); -- null for some reason
END;
/
...
BEGIN
dbms_output.put_line(cast(:TRANSACTIONNR as NUMBER));
END;
/
For some reason I don't seem to be able to refer to the bind variable again in the anonymous block I set it in - it's null in the commented-out code - which seems to be another SQL Developer quirk, regardless of the var type; but as you were doing so in your code, I may again have assumed too much...
Original answer for posterity, as it may still be relevant in other circumstances...
Presumably you're doing something to end the current transaction, e.g. a commit in endtransaction; otherwise you could just refer to my_sequence.currval in the do_something call. A number variable is fine for this size of number though, it won't have a problem with a sequence that size, and it won't make any difference that it is from a sequence rather than manually assigned. I don't think the problem is with the storage or the sequence.
It seems rather more likely that the error is coming from one of the package procedures you're calling, though I can't quite imagine what you might be doing with it; something like this will cause the same error though:
create sequence my_sequence start with 2961725541;
create package my_package as
procedure starttransaction(v_num number);
procedure endtransaction;
procedure do_something(v_num number);
end my_package;
/
create package body my_package as
procedure starttransaction(v_num number) is
begin
dbms_output.put_line('starttransaction(): ' || v_num);
for i in 1..v_num loop
null;
end loop;
end starttransaction;
procedure endtransaction is
begin
dbms_output.put_line('endtransaction()');
end endtransaction;
procedure do_something(v_num number) is
begin
dbms_output.put_line('do_something(): ' || v_num);
end do_something;
end my_package;
/
When your code is run against that it throws your error:
BEGIN
*
ERROR at line 1:
ORA-01426: numeric overflow
ORA-06512: at "STACKOVERFLOW.MY_PACKAGE", line 6
ORA-06512: at line 5
endtransaction()
do_something():
Note the error is reported against line 6 in the package, which is the for ... loop line, not from the assignment in your anonymous block.
Looping like that would be an odd thing to do of course, but there are probably other ways to generate that error. The breakpoint for it working is if the nextval is above 2^31. If I start the sequence with 2147483647 it works, with 2147483648 it errors.
I'm assuming you are actually getting an ORA-01426 from the original question; if it's actually a ORA-1438 or ORA-06502 then it's easier to reproduce, by trying to assign the value to a number(9) column or variable. 'Numeric overflow' is pretty specific though.