Snowflake reopen a cursor with different parameters - stored-procedures

I'm trying to reuse a cursor in my Snowflake procedure but I get the error message that the cursor is already open.
CREATE OR REPLACE PROCEDURE SP_PAT_LIST()
returns varchar null
LANGUAGE SQL
STRICT
EXECUTE AS CALLER
AS
$$
declare
cPat CURSOR for select PAT_ID from values(1),(2),(3),(4) x (PAT_ID)
where rlike(PAT_ID,?,'i');
sPatId varchar;
sPatIdList varchar default '';
sDelimiter default '';
begin
begin
open cPat using ('[2,4]');
for cRow in cPat do
sPatIdList := sPatIdList||sDelimiter||cRow.Pat_Id;
sDelimiter := ',';
end for;
close cPat;
end;
begin
open cPat using ('[1,3]');
for cRow in cPat do
sPatIdList := sPatIdList||sDelimiter||cRow.Pat_Id;
sDelimiter := ',';
end for;
close cPat;
end;
return sPatIdList;
end;
$$;

I can confirm that this is a known issue which has been fixed, but not released yet.
For now, please create a different cursor to workaround the issue.
It should be released soon, but I don't know the exact date.

A workaround could be defining the cursor using let twice. They are inside two different(not overlapping) begin/end blocks so it will work:
declare
sPatId varchar;
sPatIdList varchar default '';
sDelimiter default '';
begin
begin
let cPat CURSOR for select PAT_ID from values(1),(2),(3),(4) x (PAT_ID)
where rlike(PAT_ID,?,'i');
open cPat using ('[2,4]');
for cRow in cPat do
sPatIdList := sPatIdList||sDelimiter||cRow.Pat_Id;
sDelimiter := ',';
end for;
close cPat;
end;
begin
let cPat CURSOR for select PAT_ID from values(1),(2),(3),(4) x (PAT_ID)
where rlike(PAT_ID,?,'i');
open cPat using ('[1,3]');
for cRow in cPat do
sPatIdList := sPatIdList||sDelimiter||cRow.Pat_Id;
sDelimiter := ',';
end for;
close cPat;
end;
return sPatIdList;
end;
Output:

Related

Am I able to use a logic function (and, or) between 2 comparisons

I am unsure whether it is generally impossible to use a logic function between to comparisons or if I've used my logic statement incorrectly because when I make all variables (NewUsername, NewUsername2, NewPass, NewPass2) to the characters "hi", it would continue to display the Application.MessageBox.
procedure TNewUserFrm.ApplyBtnClick(Sender: TObject);
begin
if (NewUsername <> NewUsername2) or (NewPass <> NewPass2) then
begin
Application.MessageBox('The usernames or passwords do not match. Try again', 'Error');
end
else
begin
if not modFile.UsersDataSet.Active then modFile.UsersDataSet.Open;
modFile.UsersDataSet.Append;
modFile.UsersDataSet.FieldByName('Username').AsString := NewUsername.Text;
modFile.UsersDataSet.FieldByName('Password').AsString := NewPass.Text;
modFile.UsersDataSet.Post;
NewUserFrm.Hide;
end;
NewUsername.Text := '';
NewUsername2.Text := '';
NewPass.Text := '';
NewPass2.Text := '';
ApplyBtn.SetFocus;
end;
I have tried using "and" statement, "or" statement and I've also tried using nested "if" statements instead but the same result occurs
You are comparing the TEdit controls addresses, not their content. You need to compare their contents.
if (NewUsername.Text <> NewUsername2.Text) or (NewPass.Text <> NewPass2.Text) then
Writing something like
NewUsername <> NewUsername2
will always have the value true in this case because these are two different TEdit controls, and their addresses will never be the same.

Search database table by record fields and display record on cxgrid

What is the best way to allow a user to search for a record in a database table by typing text into a tedit box clicking a button then the results will then display onto a tcxgrid.
I have a tadquery/datasource that is looking at a table which contains various fields. It would be nice if the user was able to search for more than one field in the table.
Thanks.
You can use TDataSet.Locate for this, passing a semicolon delimited list of field names and an array of constant field values to match. Typically, this is easy:
DataSet.Locate('Field1;Field2', ['Value1', 'Value2'], [loPartialKey]);
However, as you don't know ahead of time how many columns, you'll need to handle the array differently, using VarArrayCreate and setting each array value separately.
This example takes the list of fields from Edit1 (separated by semicolon), the list of values to match from Edit2 (again, separated by semicolons, with string values surrounded by ' characters so they're properly included in the array). It splits the content of Edit1 into an array to find out how many elements, allocates a variant array of the proper size, populates it, and then passes both the field list and the array of values to TDataSet.Locate. It was tested with Edit1.Text := 'Customer_No;Name'; and Edit2.Text := '1;''Smith''';.
procedure TForm5.Button1Click(Sender: TObject);
var
Temp: string;
Fields: TArray<string>;
Vals: TArray<string>;
FieldValues: Variant;
i: Integer;
begin
// Grab a copy so we can split it into separate values
Temp := Edit1.Text;
Fields := Temp.Split([';']);
// Create the array of variants to hold the field values
FieldValues := VarArrayCreate([0, High(Fields)], VarVariant);
// Temporary copy to allow splitting into individual values
Temp := Edit2.Text;
Vals := Temp.Split([';']);
for i := 0 to High(Fields) do
FieldValues[i] := Vals[i];
// Use original field list from Edit1 for the Locate operation
DataSet1.Locate(Edit1.Text, FieldValues, [loCaseInsensitive]);
end;
For versions of Delphi prior to the inclusion of TStringHelper (such as the XE2 you're using, just add Types and StrUtils to your uses clause and use SplitString and TStringDynArray instead:
procedure TForm5.Button1Click(Sender: TObject);
var
Temp: string;
Fields: TStringDynArray;
Vals: TStringDynArray;
FieldValues: Variant;
i: Integer;
begin
Temp := Edit1.Text;
Fields := SplitString(Temp, ';');
FieldValues := VarArrayCreate([0, Length(Fields)], VarVariant);
Temp := Edit2.Text;
Vals := SplitString(Temp, ';');
for i := 0 to High(Fields) do
FieldValues[i] := Vals[i];
DataSet1.Locate(Temp, FieldValues, [loCaseInsensitive]);
end;
I would use a query for the datasource and in the onclick event of the button, reload the query with a WHERE clause along the lines of
Query1.SQL.Add('WHERE Name LIKE :P1 OR Postcode LIKE :P2 OR Town LIKE :P3');
and add the parameters
Query1.SQL.Parameters.ParamByName('P1').Value := '%' + Edit1.Text + '%';
Query1.SQL.Parameters.ParamByName('P2').Value := '%' + Edit1.Text + '%';
Query1.SQL.Parameters.ParamByName('P3').Value := '%' + Edit1.Text + '%';
using '%' to allow searching anywhere in the string as an option.
Query1.Open;
I've used this technique many times.

FireDac query not reading large integers correctly

I am trying to query a database using FireDac. Here is my code;
procedure TfSMSViewer.LoadSMSFromDatabase(path: AnsiString);
var
con: TFDConnection;
query: TFDQuery;
LI: TListItem;
B: Int64;
begin
con := TFDConnection.Create(nil);
query := TFDQuery.Create(con);
con.LoginPrompt := False;
con.Open('DriverID=SQLite;Database=' + Path +' ;');
query.Connection := con;
query.SQL.Text := 'SELECT * FROM sms';
query.Open;
query.First;
While Not Query.EOF Do
Begin
LI := ListView1.Items.Add;
LI.Caption := inttostr(query.Fields[4].AsLargeInt); //This line
if query.FieldValues['type'] = 1 then
LI.SubItems.Add('Incoming')
else
LI.SubItems.Add('Outbound');
LI.SubItems.Add(query.FieldValues['address']);
LI.SubItems.Add(query.FieldValues['body']);
Query.Next;
End;
end;
However the line highlighted doesn't work correctly. In the database, an example value set in this column is 1418421520957 (a UNIX timestamp).
When that line of code is executed, the result is 1082313277.
The data type in the SQLite database is set to Integer. The freeware software I'm using to debug this shows the correct value. When debugging my code, the incorrect value is pulled from the database before any assignment is made.
Also some of the values populated in my TListView are negated.
Does TFDQuery not support large integers? How can I fix this?
Thanks
This is how I fixed it, with suggestions from TLama. Maybe this will be useful for someone, or my future self.
con := TFDConnection.Create(nil);
query := TFDQuery.Create(con);
with query.FormatOptions do begin
OwnMapRules := True;
with MapRules.Add do begin
SourceDataType := dtInt32;
TargetDataType := dtInt64;
end;
end;
'BIGINT' for your Database column can solve this problem

Removing duplicate from TStringList

I am parsing a dataset and assigning values to TStringList i want to avoid the duplicates. I use the following code but still duplicates are inserted.
channelList := TStringList.Create;
channelList.Duplicates := dupIgnore;
try
dataset.First;
while not dataset.EOF do
begin
channelList.Add(dataset.FieldByName('CHANNEL_INT').AsString) ;
dataset.Next;
end;
why does the duplicates added?
http://docwiki.embarcadero.com/Libraries/XE2/en/System.Classes.TStringList.Duplicates
You did read http://docwiki.embarcadero.com/Libraries/XE2/en/System.Classes.TStringList.Duplicates , didn't you ?
Then you missed the most repeated word there - "sorted"
channelList.Sorted := true
var F: TField;
channelList := TStringList.Create;
channelList.Sorted := True;
channelList.Duplicates := dupIgnore;
try
dataset.First;
F := dataset.FieldByName('CHANNEL_INT');
while not dataset.EOF do
begin
channelList.Add(F.AsString);
dataset.Next;
end;
Think out of the box and avoid the duplicates up front?
I don't know what DB you are using but for example on SQL server it is just a matter of querying:
'SELECT DISTINCT CHANNEL_INT FROM MYTABLE';
and then you can add the results to your TStringList without being worried about duplicates.

Indexes order in Delphi?

I'm basically coding some sort of table where for column tags I have some numbers and for row tags I have some strings which contain such numbers separated by commas.
I'm taking all the row tags from a TString named minterms_essentials and the column tags from one named minterms.
First I must tag the created 2 dimensions array. And then, if any string from the rows contains certain letter, I must place an 'x' in the proper column.
I've wrote this Delphi code but I'm getting access violation so far...
SetLength(tabla, minterms_essentials.Count+1,minterms.Count+1);
for i := 0 to minterms.Count-1 do
begin
tabla[0,i+1] := IntToStr(BinToInt(minterms[i]));
end;
for i := 0 to minterms_essentials.Count-1 do
begin
tabla[i+1,0] := minterms_essentials[i];
end;
for i := 1 to minterms_essentials.Count-1 do
begin
for g := 1 to minterms.Count-1 do
begin
ss := tabla[g,0].Split([',']);
for s in ss do
begin
if s = tabla[0,g] then
begin
tabla[g,i] := 'x';
end;
end;
end;
end;
Is there any better and correct way to do this?
Look at this:
first dimension is defined by minterms_essentials
SetLength(tabla, minterms_essentials.Count+1,minterms.Count+1);
but here you are using minterms to index first dimension of array:
for g := 1 to minterms.Count-1 do
begin
ss := tabla[g,0].Split([',']);
P.S. Have you still not turned on range check?

Resources