How do i display all customers who have purchased an item on a certain day, my code doesn't seem to work, ive tried implementing the code within the displayByDayPurchased procedure. Sorry if this is a simple question, i'm still new to programming.
type
Tday = (monday, tuesday);
Tcustomer = record
itemPurchased:string;
dayPurchased: Tday;
end;
Tcustomers = array of Tcustomer;
function readDay(prompt:string): Tday;
var
selection:Integer;
begin
writeln('1. Monday');
writeln('2. Tuesday');
selection := ReadIntegerRange('Select day purcased (1 - 3): ', 1, 3);
result := Tday(selection-1);
end;
function readCustomers(prompt:string):TCustomers;
var
numOfCustomers:integer;
i:integer;
begin
numOfCustomers := ReadInteger('Enter number of customers: ');
setLength(result, numOfCustomers);
for i := 0 to high(result) do
begin
writeln(i);
result[i].itemPurchased := ReadString('Item Purchased: ');
result[i].dayPurchased := readDay(prompt);
end;
end;
procedure displayByDayPurchased(customers:TCustomers);
var
specific_day:integer;
begin
specific_day := ReadInteger('Enter day to see items purchased');
if (specific_day = customers.dayPurchased[specific_day])then
begin
end;
end;
procedure Main();
var
customer: Tcustomers;
begin
customer := readCustomers('Read in customers');
end;
begin
Main();
end.
my code doesn't seem to work, ive tried implementing the code within the displayByDayPurchased procedure.
Well, in the code you've posted, your displayByDayPurchased doesn't actually implement anything which would result in matching records being displayed, all you have is an empty begin ... end block:
if (specific_day = customers.dayPurchased[specific_day])then
begin
end;
and a) that doesn't correctly specify the match condition anyway and b) it will not compile because a Tcustomers array does not have a dayPurchased field ( a Tcustomer record does, but not the array).
Your code depends of quite a lot of things whose definitions you have not provide (ReadString, ReadInteger, ReadIntegerRange, etc) so it is difficult to give you a tested solution.
However an implementation of your displayByDayPurchased should probably look something like this:
procedure displayByDayPurchased(customers:TCustomers);
var
specific_day:integer;
i : integer;
ADay : Tday;
begin
specific_day := ReadInteger('Enter day to see items purchased');
ADay := Tday(specific_day); // converts integer to TDay value
for i := Low(customers) to High(Customers) do begin
if customers[i].dayPurchased = ADay then begin
writenln(customers[i].itemPurchased);
end;
end;
end;
I assume your Tcustomer record actually includes the customer's name, andx your code needs modifying to handle that.
Btw, your function readDay(prompt:string): Tday is wrong; Because of your definition of Tday, the allowed values in readDay should be 0 and 1, because the lowest value of the Tday enumeration actually corresponds to zero, not 1.
Also, you did not say which Pascal compiler you are using, but most modern versions allow a call like Tday(integerValue) to convert an integer to an instance value of the enumeration.
Related
for read data from database server im using mysql query like this:
FDQuery1.SQL.Text := 'select * from `table` WHERE dcid=3;';
FDQuery1.Open;
memo1.Lines.Add( FDQuery1.FieldByName('value').AsString );
but when i have to use a loop for read data from mysql
i need a array to read and store data on it
i know in php its like this
$arr = array();
while($obj = $q->fetch_object() ){
$arr[] = $obj;
}
but how can i do this syntax in Delphi ?
You don't need to do FDQuery1.First as that is implicit in the FDQuery1.Open. However if you want to know how many records you have it's important to do a FDQuery1.Last; before using FDQuery1.RecordCount to get the true record count otherwise you might get strange results.
All you need to do get your data into a memo is this
FDQuery1.SQL.Text := 'select * from table WHERE dcid=3;';
memo1.Lines.clear;
FDQuery1.Open;
While not FDQuery1.Eof do
begin
memo1.Lines.Add( FDQuery1.FieldByName('value').AsString );
FDQuery1.next;
end;
end;
FDQuery1.Close;
although a better solution with minimal exception handling is
FDQuery1.SQL.Text := 'select * from table WHERE dcid=3;';
memo1.Lines.clear;
try
try
begin
FDQuery1.Open;
While not FDQuery1.Eof do
begin
memo1.Lines.Add( FDQuery1.FieldByName('value').AsString );
FDQuery1.next;
end;
end;
end;
except
on E : Exception do
begin
showmessage ('Exception class name = '+E.ClassName+ slinebreak
+ 'Exception message = '+E.Message);
end //on E
end; //try-except
finally
FDQuery1.Close;
end; //try-finally
You mention an array. This is not needed if you only want to put the data in a memo but if you did want to put the data in an array (a dynamic array of variants as you don't know at design time how many record elements you need, how many field elements you need or what type each field is) then you would use the following code.
(Note this is deliberately not optimised code as I was trying to make the process clear)
Const
FirstRecordIndex = 0;
FirstFieldIndex = 0;
Var
DataArray : Variant;
TheRecordCount, TheFieldCount,
RecordNumber, FieldNumber : integer;
Data : variant;
begin
FDQuery1.SQL.Text := 'select * from table WHERE dcid=3;';
FDQuery1.Open;
FDQuery1.Last; //to get correct recordcount
TheRecordCount := FDQuery1.RecordCount;
TheFieldCount := FDQuery1.FieldCount;
FDQuery1.First; //go back to the beginning of the dataset
//set the dimensions of the 2D array of variants to hold data
DataArray := VarArrayCreate([FirstRecordIndex, TheRecordCount, FirstFieldIndex, TheFieldCount], varVariant ); //element can be of any type
//fill it
RecordNumber := -1; //initialise record indexe to just before the start
While not FDQuery1.Eof do
begin
inc(RecordNumber); //point to next record element in the array
for FieldNumber := FirstFieldIndex to TheFieldCount -1 do //load all the fields of this record
begin
Data := FDQuery1.Fields[FieldNumber].asVariant; //get the data
DataArray[RecordNumber, FieldNumber] := Data; //put into array
end;
FDQuery1.next; //get next record
end; //while
end;
FDQuery1.Close;
end;
To get the data back again use
For RecordNumber := FirstRecordIndex to TheRecordCount -1 do
For FieldNumber := FirstFieldIndex to TheFieldCount -1 do
begin
Data := DataArray[RecordNumber, FieldNumber] ;
//do something with the data ie put into a memo
end;
You can use this, i hope that help u:
FDQuery1.SQL.Text := 'select * from `table` WHERE dcid=3;';
FDQuery1.Open;
if FDQuery1.Active and (FDQuery1.RecordCount > 0) then
begin
FDQuery1.First;
While not FDQuery1.Eof do
begin
memo1.Lines.Add( FDQuery1.FieldByName('value').AsString );
FDQuery1.next;
end;
end;
you can do this with array of array consept
for this first you must create your new type
type
myArray = array [1..10] of integer;
myData= array of myArray;
and then you must define your variable
var
Records: myData;
i:integer;
on,now we have aarray of array to save database records on it so
begin
FDQuery1.SQL.Text := 'select * from table WHERE type=3;';
FDQuery1.Open;
FDQuery1.First;
SetLength(Records ,FDQuery1.recordcount);//or maybe you need a do a select count(*) query first
i:=0;
While not FDQuery1.Eof do
begin
Records[i][1]:= FDQuery1.FieldByName('value1').AsString;
Records[i][2]:= FDQuery1.FieldByName('value2').AsString;
Records[i][3]:= FDQuery1.FieldByName('value3').AsString;
Records[i][4]:= FDQuery1.FieldByName('value4').AsString;
Records[i][5]:= FDQuery1.FieldByName('value5').AsString;
Records[i][6]:= FDQuery1.FieldByName('value6').AsString;
i:=i+1;
FDQuery1.next;
end;
FDQuery1.Close;
end;
now all of your selected database records are in your local variable
you can go over the row and make new queries with FDQuery1...
I'm migrating the database engine an application from MySql to SAP HANA.
I found a little trouble. I have a query like this:
Select SUBSTRING_INDEX(id, "-", -2) as prod_ref From products;
I don't know how to "translate" the function substring_index, because the initial part of the id has a variable length.
Thanks.
This can be done using a regex:
select substr_regexpr( '.*-([^-]*-[^-]*)$' in 'varia-ble---part-part1-part2' group 1) from dummy;
select substr_regexpr( '.*-([^-]*-[^-]*)$' in 'variable-part-part1-part2' group 1) from dummy;
According to the HANA 2.0 SP0 doc you could use locate with a negative offset (and then using right()), but this does not work on my system ("...feature isn't supported...")
If you execute such queries on a regular basis on lots of records I would recommend extracting the part you are interested in during ETL into a separate field. Or, alternatively fill a separate field using " GENERATED ALWAYS AS...".
I have seen it more than once, that people calculate a field like this in complex SQL queries or complex CalcViews, and then wonder why performance is bad when selecting 100 million records and filtering on the calculated field etc... Performance is usually no problem when you have aggregated your intermediate result set to a reasonable size and then apply "expensive" functions.
I don't think there is any direct function like SUBSTRING_INDEX in SAP HANA. But you have a work around alternative by creating a function to pass the input string and delimiter.
And I am assuming that -2 in SUBSTRING_INDEX and providing the solution
Reverse the string and get the position of the second delimiter, '-' in your case, into "obtainedPosition"
Now subtract that "obtainedPosition" from the length of the string.
obtainedPosition = LENGTH(id) - obtainedPosition
Using that value in the inbuilt substring function you can get the required string and return it from the function.
SELECT SCHEMA.FN_SUBSTRING_INDEX(id,obtainedPosition) INTO ReturnValue FROM DUMMY;
CREATE FUNCTION FN_SUBSTRING_INDEX
(
id VARCHAR(500),
delim VARCHAR(2)
)
RETURNS SplitString VARCHAR(500)
LANGUAGE SQLSCRIPT AS
BEGIN
DECLARE reversedString VARCHAR(500);
DECLARE charString VARCHAR(2);
DECLARE i INT := LENGTH(:id);
DECLARE len INT := LENGTH(:id);
DECLARE obtainedPosition INT := 0;
DECLARE flag INT := 0;
reversedString := '';
--loop to reverse the inputstring
WHILE :i > 0
DO
reversedString = CONCAT(:reversedString, SUBSTRING(:id,:i,1));
i := :i - 1;
END WHILE;
--loop to get the second delimiter position
i := 1;
WHILE :i <= :leng
DO
charString := '';
charString := SUBSTRING(:reversedString,i,1);
IF((:charString = :delim ) AND (:flag < 2)) THEN
BEGIN
obtainedPosition := :i;
flag := :flag + 1;
END;
END IF;
i := :i + 1;
END WHILE;
--IF condition to check if at least 2 delimiters are available, else print complete string
IF(flag = 2) THEN
obtainedPosition := :len - :obtainedPosition + 2; --2 is added to avoid the character at that position and '-' from printing
ELSE
obtainedPosition := 1;
END IF;
--SplitString contains the string's splitted return value
SELECT SUBSTRING(:id,:obtainedPosition) INTO SplitString FROM DUMMY;
END;
The above function is modified from http://www.kodyaz.com/sap-abap/sqlscript-reverse-string-function-in-sap-hana.aspx
For string functions in SAP HANA refer to this: http://www.sapstudent.com/hana/sql-string-functions-in-sap-hana/3
You can use anonymous block in SAP HANA to call and check the function
DO
BEGIN
DECLARE id VARCHAR(500) := 'Test-sam-ple-func';
DECLARE delim VARCHAR(2) := '-';
SELECT SCHEMA.FN_SUBSTRING_INDEX(id,delim) AS "SplitStringIndex" FROM DUMMY;
END;
I would be glad to know reason for a downvote. :)
can anybody help me with my sorting function - seriously I don't know how can I make it work as supposed to. :( Database is in MariaDB in Xampp. I use phpMyAdmin to execute the query.
DELIMITER $$
DROP FUNCTION IF EXISTS convRomanNumeral$$
CREATE FUNCTION convRomanNumeral (numeral CHAR(4))
RETURNS INT
BEGIN
DECLARE intnum INT;
CASE numeral
WHEN "I" THEN intnum = 1;
WHEN "II" THEN intnum = 2;
END CASE;
RETURN intnum;
END;
$$
SET #iteration = -1;
UPDATE `st0gk_docman_documents`
SET created_on = DATE('2016-06-14') + INTERVAL(#iteration := #iteration + 1) SECOND
WHERE `docman_category_id` = 141 ORDER BY convRomanNumeral(SUBSTRING(SUBSTRING_INDEX(title,'/',1),' ',-2) ASC, SUBSTRING_INDEX(title,'/',-2)+0 ASC;
So what I want to achieve is to sort documents by title. Example titles are:
Document Nr I/36/2006
Document Nr II/36/2006
Document Nr I/32/2006
Document Nr II/19/2006
After sorting them by first Roman number and then by second Arabic number I want to update the date. Code below for updating by only second Arabic number works properly:
SET #iteration = -1;
UPDATE `st0gk_docman_documents`
SET created_on = DATE('2016-06-14') + INTERVAL(#iteration := #iteration + 1) SECOND
WHERE `docman_category_id` = 141 ORDER BY SUBSTRING_INDEX(title,'/',-2)+0 ASC;
I would like to use CASE to return proper variable for Roman values. I know it's not perfect but I can't even make the CASE and FUNCTION work. What I am doing wrong? All suggestions are welcome.
The best way to do this is to add another column that has a sortable equivalent of that string. And use non-SQL code to do the parsing and building of that column before inserting into the table.
First mistake that I was making it was trying to execute the whole query at once... After taking the first lodge out of the way the debugging seemed way simpler. :D
So I created my case function to convert Roman numerals:
DELIMITER $$
DROP FUNCTION IF EXISTS convRomanNumeralSubFunction$$
CREATE FUNCTION convRomanNumeralSubFunction (numeral CHAR(1))
RETURNS INT
BEGIN
DECLARE intnum INT;
CASE numeral
WHEN "I" THEN SELECT 1 INTO intnum;
WHEN "X" THEN SELECT 10 INTO intnum;
WHEN "C" THEN SELECT 100 INTO intnum;
WHEN "M" THEN SELECT 1000 INTO intnum;
WHEN "V" THEN SELECT 5 INTO intnum;
WHEN "L" THEN SELECT 50 INTO intnum;
WHEN "D" THEN SELECT 500 INTO intnum;
END CASE;
RETURN intnum;
END;
$$
After that I declared the second function needed for conversion. I don't know if You can declare function inside function... and I didn't want to waste more time on this. For sure You can declare Function inside Procedure. Anyhow. WARNING: This function is not proof of BAD numerals like IIX. Numerals like that or will be badly counted. Also AXI will not count.
DELIMITER $$
DROP FUNCTION IF EXISTS convRomanNumeral$$
CREATE FUNCTION convRomanNumeral (numeral CHAR(10))
RETURNS INT
BEGIN
DECLARE currentintnum, previntnum, intnum, counter, numerallength INT;
SET numerallength = LENGTH(numeral);
SET counter = numerallength;
SET intnum = 0;
SET previntnum = 0;
WHILE counter > 0 DO
SET currentintnum = CAST(convRomanNumeralSubFunction(SUBSTRING(numeral,counter, 1)) as integer);
IF currentintnum < previntnum THEN
SET intnum = intnum - currentintnum;
ELSE
SET intnum = intnum + currentintnum;
END IF;
SET previntnum = currentintnum;
SET counter = counter - 1;
END WHILE;
RETURN intnum;
END;
$$
So that's it. Now You can convert all kind of Roman numerals and sort them up.
Use this to test the conversion:
SELECT convRomanNumeral("XIX");
This is example sorting code that I in the end used:
SET #iteration = -1;
UPDATE `st0gk_docman_documents`
SET created_on = DATE('2016-06-07') + INTERVAL(#iteration := #iteration + 1) SECOND
WHERE `docman_category_id` = 67 ORDER BY convRomanNumeralBreak(SUBSTRING_INDEX(SUBSTRING_INDEX(title,'/',1),' ',-1)) ASC, SUBSTRING_INDEX(title,'/',-2)+0 ASC;
Also one more thing - if You'll try to excecute this on mySQL then You have to fix this line:
SET currentintnum = CAST(convRomanNumeralSubFunction(SUBSTRING(numeral,counter, 1)) as integer);
into this:
SET currentintnum = CAST(convRomanNumeralSubFunction(SUBSTRING(numeral,counter, 1)) as SIGNED);
This code could be improved but as the #Rick James stated this should be done differently - not in as db update but in different table structure and sorting mechanism.
is there a way to compare the content of a cell in a grid against the content of a variable.
I need to compare cell 0,1 and if it's value is lower (or higher) than variable x then something happens.
I'm using Lazarus and a StringGrid.
You can try something like this (for integer values, but can easily be modified for another ordinal types). You can follow the commented version of this post as well:
uses
Math;
function CompareValueEx(StringGrid: TStringGrid; const Column, Row: Integer;
const Value: Integer; out Relationship: TValueRelationship): Boolean;
var
Output: Integer;
begin
Result := TryStrToInt(StringGrid.Cells[Column, Row], Output);
if Result then
Relationship := CompareValue(Value, Output);
end;
procedure TForm1.Button1Click(Sender: TObject);
var
I: Integer;
Relationship: TValueRelationship;
begin
I := 111;
StringGrid1.Cells[1, 2] := '112';
if CompareValueEx(StringGrid1, 1, 2, I, Relationship) then
begin
case Relationship of
EqualsValue: ShowMessage('The values are the same');
LessThanValue: ShowMessage('The I value is less than in cell [1;2]');
GreaterThanValue: ShowMessage('The I value is greater than in cell [1;2]');
end;
end
else
ShowMessage('The value in cell [1;2] is not a valid integer value!');
end;
I'm working with a large set of legacy data (converted from a flat-file db), where a field is formatted as the last 2 digits of the year the record was entered, followed by a 4 digit increment...
e.g., the third record created in 1998 would be "980003", and the eleventh record created in 2004 would be "040011".
i can not change these values - they exist through their company, are registered with the state, clients, etc. I know it'd be great to separate out the year and the rest of it into separate columns, but that's not possible. i can't even really do it "internally" since each row has about 300 fields that are all sortable, and they're very used to working with this field as a record identifier.
so i'm trying to implement a MySQL UDF (for the first time) to sort. The query executes successfully, and it allows me to "select whatever from table order by custom_sort(whatever)", but the order is not what i'd expect.
Here's what I'm using:
DELIMITER //
CREATE FUNCTION custom_sort(id VARCHAR(8))
RETURNS INT
READS SQL DATA
DETERMINISTIC
BEGIN
DECLARE year VARCHAR(2);
DECLARE balance VARCHAR(6);
DECLARE stringValue VARCHAR(8);
SET year = SUBSTRING(0, 2, id);
SET balance = SUBSTRING(2, 6, id);
IF(year <= 96) THEN
SET stringValue = CONCAT('20', year, balance);
ELSE
SET stringValue = CONCAT('19', year, balance);
END IF;
RETURN CAST(stringValue as UNSIGNED);
END//
The records only go back to 96 (thus the arbitrary "if first 2 characters are less than 96, prepend '20' otherwise prepend '19'). I'm not thrilled with this bit, but don't believe that's where the core problem is.
To throw another wrench in the works, it turns out that 1996 and 1997 are both 5 digits, following the same pattern described above but instead of a 4 digit increment, it's a 3 digit increment. Again, I suspect this will be a problem, but is not the core problem.
An example of the returns I'm getting with this custom_sort:
001471
051047
080628
040285
110877
020867
090744
001537
051111
080692
040349
110941
020931
090808
001603
051175
I really have no idea what I'm doing here and have never used MySQL for a UDF like this - any help would be appreciated.
TYIA
/EDIT typo
/EDIT 2 concat needed "year" value added - still getting same results
You have some problems with your substrings, and the cast to int at the end makes it sort values with more digits at the end, not by year. This should work better;
DELIMITER //
CREATE FUNCTION custom_sort(id VARCHAR(8))
RETURNS VARCHAR(10)
READS SQL DATA
DETERMINISTIC
BEGIN
DECLARE year VARCHAR(2);
DECLARE balance VARCHAR(6);
DECLARE stringValue VARCHAR(10);
SET year = SUBSTRING(id, 1, 2);
SET balance = SUBSTRING(id, 3, 6);
IF(year <= 96) THEN
SET stringValue = CONCAT('20', year, balance);
ELSE
SET stringValue = CONCAT('19', year, balance);
END IF;
RETURN stringValue;
END//
DELIMITER ;
This can be simplified a bit to;
DELIMITER //
CREATE FUNCTION custom_sort(id VARCHAR(8))
RETURNS varchar(10)
DETERMINISTIC
BEGIN
IF(SUBSTRING(id, 1, 2) <= '96') THEN
RETURN CONCAT('20', id);
ELSE
RETURN CONCAT('19', id);
END IF;
END//
DELIMITER ;