In Delphi XE, I am storing crc32 hash of a string in an SQlite database, in a column declared as INTEGER. My understanding is that SQlite does not distinguish among integer types: int, int64, signed and unsigned, they're all the same as far as the database is concerned. However, when I store a value declared as longword in Delphi, a WHERE clause fails to match on that value later.
My insert statement (trimmed down here) is:
INSERT INTO main VALUES (id, crc) (?, ?);
The longword value gets bound to the second parameter and all goes well. But when I do
SELECT id FROM main WHERE crc = ?;
the query returns no results.
Viewing the database in SQLiteSpy, the longword values are displayed as negative integers. If I execute the above SELECT with the negative value copy-and-pasted from that display, the query returns the expected record.
It would seem that when I bind a longword value to the INSERT statement, SQLIte does something else then when I bind the same longword value to the SELECT statement. Casting the value to integer (in Delphi code, not in SQL) fixes the problem, but it should not be necessary, and it'll be easy to forget to cast in other places. Is there a better solution? Is SQLite behaving correctly?
The problem is not in SQLite, but in the way you're binding your parameters to the SQLite engine.
Depending on the SQlite framework you're using, you must explicitely bound an Int64 to your statement:
INSERT INTO main VALUES (id, crc) (?, ?);
For example, using our framework, with a CRC declared as a Cardinal, you must use this code (and NOT any integer(CRC)):
sqlite3_check(RequestDB,sqlite3_bind_Int64(Request,2,Int64(CRC)));
If the above code doesn't work, this will always work:
var CRC64: Int64;
CRC64Rec: TInt64Rec absolute CRC64;
begin
CRC64Rec.Lo := CRC;
CRC64Rec.Hi := 0;
sqlite3_check(RequestDB,sqlite3_bind_Int64(Request,2,CRC64));
In all cases, without showing your own code about binding parameters, it's difficult to know what's wrong in your code.
Related
In SAP HANA i can create a procedure with in and output parameters. Even without output parameter the procedure can output a table.
I notice three versions of output in stored procedures:
a select at the end of the procedure - without declaring the structure.
an output parameter out parameter varhcar(100)
an implicit table definition returns table (var1 varchar(10)) after function parameter and before keyword LANGUAGE SQLSCRIPT
What is the difference of these and how can I reuse each of these output parameters in other stored procedures?
The only one I am aware of is
call procedure(input1, input1, outputVar)
Unfortunately I don't know how to bound an SQL result to output parameters without creating a physical table.
Reason for this question
Issue 1
Function callBuildJoinOn returns empty result. Due to that loop in SP_BUILD_JOIN_ON is not executed - but the list is build in split string (both tested)
Proceedure callBuildJoinOn
...
in colTable1 nvarchar(200)
out columnsTable1 "SCHEMA"."package::TT.STRING_LIST"
call "SCHEMA"."package::SP_SPLIT_STRING"(colTable1, columnsTable1);
call "SCHEMA"."package::SP_BUILD_JOIN_ON"(:columnsTable1, :columnsTable2, :joinOn);
SP_BUILD_JOIN_ON
columnsTable1 "SCHEMA"."package::TT.STRING_LIST"
declare cursor columnList for
select * from :columnsTable1;
for col as columnList do
joinOn := joinOn || 'a.' || col.item;
end for;
Why split in two functions?
Declare of cursor results in compiler error if after a call statement
There is even a fourth option to get to a result set from a HANA SQLScript procedure: the result view (not supported anymore as of HANA2).
But let's not stir up more confusion.
The different options can be used in different scenarios:
Procedure IN/OUT parameters are used to get data into and results out of procedures. The OUT parameters can either be bound to other SQLScript variables (when you call the procedure from another procedure), or a HANA client can read the result sets that get created for each OUT parameter of type TABLE). For example, a JDCB client would find multiple result sets after the procedure execution and would fetch those.
Table functions, that are functions that return a table, can be used just like tables or views:
SELECT * FROM <tablefunction>( in_param1, in_param2, ...);
This provides an easy option to read back whole tables from functions or to simulate parameterised views.
The remaining option you mentioned is the so-called "default result set". This really is only useful for consumption in the SQL editor or in a HANA client that fetches all result sets after procedure execution. The default result set cannot be bound to any SQLScript variable and cannot be referenced in SQL commands.
Problem was that joinOn was defined as output parameter: out joinOn nvarchar(1000)
this resulted that:
the variable was null
the variable was not populated: joinOn := joinOn || 'a.' || col.item; resulted in null !
Solution
Change out to inout
initialize parameter joinOn := ''
Using Delphi 10.2, SQLite and Teecharts. My SQLite database has two fields, created with:
CREATE TABLE HistoryRuntime ('DayTime' DateTime, Device1 INTEGER DEFAULT (0));
I access the table using a TFDQuery called qryGrpahRuntime with the following SQL:
SELECT DayTime AS TheDate, Sum(Device1) As DeviceTotal
FROM HistoryRuntime
WHERE (DayTime >= "2017-06-01") and (DayTime <= "2017-06-26")
Group by Date(DayTime)
Using the Field Editor in the Delphi IDE, I can add two persistent fields, getting TheDate as a TDateTimeField and DeviceTotal as a TLargeIntField.
I run this query in a program to create a TeeChart, which I created at design time. As long as the query returns some records, all this works. However, if there are no records for the requested dates, I get an EDatabaseError exception with the message:
qryGrpahRuntime: Type mismatch for field 'DeviceTotal', expecting: LargeInt actual: Widestring
I have done plenty of searching for solutions on the web on how to prevent this error on an empty query, but have had not luck with anything I found. From what I can tell, SQLite defaults to the wide string field when no data is returned. I have tried using CAST in the query and it did not seem to make any difference.
If I remove the persistent fields, the query will open without problems on an empty return set. However, in order to use the TeeChart editor in the IDE, it appears I need persistent fields.
Is there a way I can make this work with persistent fields, or am I going to have to throw out the persistent fields and then add the TeeChart Series at runtime?
This behavior is described in Adjusting FireDAC Mapping chapter of the FireDAC's SQLite manual:
For an expression in a SELECT list, SQLite avoids type name
information. When the result set is not empty, FireDAC uses the value
data types from the first record. When empty, FireDAC describes those
columns as dtWideString. To explicitly specify the column data type,
append ::<type name> to the column alias:
SELECT count(*) as "cnt::INT" FROM mytab
So modify your command e.g. this way (I used BIGINT, but you can use any pseudo data type that maps to a 64-bit signed integer data type and is not auto incrementing, which corresponds to your persistent TLargeIntField field):
SELECT
DayTime AS "TheDate",
Sum(Device1) AS "DeviceTotal::BIGINT"
FROM
HistoryRuntime
WHERE
DayTime BETWEEN {d 2017-06-01} AND {d 2017-06-26}
GROUP BY
Date(DayTime)
P.S. I did a small optimization by using BETWEEN operator (which evaluates the column value only once), and used an escape sequence for date constants (which, in real you replace by parameter, I guess; so just for curiosity).
This data type hinting is parsed by the FDSQLiteTypeName2ADDataType procedure that takes and parses column name in format <column name>::<type name> in its AColName parameter.
I have a table in my DB. In that table, I have a value stored (it's a date, in milliseconds): "1424386800000", in a column named "fechainicio".
I have downloaded SQLite browser in my Mac to actually check that the value is stored, and its correct, and it is.
Later, in my code, I retrieve that value using this:
var fechaInicio:Int64=Int64(results!.intForColumn("fechainicio"))
When I do this, I am receiving the value "-1542342272".
Why is the value retrieved incorrectly?
I have tried also
var fechaInicio:Int64=results!.intForColumn("fechainicio").toIntMax()
and
var fechaInicio=results!.intForColumn("fechainicio")
Isn't Int64 the correct type to retrieve a long value from a database?
The int type from (Objective-)C is a 32-bit integer type (and mapped to Int32 in Swift). This means that the
- (int)intForColumn:(NSString*)columnName;
method from FMDB can return only 32-but values. This method ultimately calls the SQLite function
int sqlite3_column_int(sqlite3_stmt*, int iCol);
which returns the lower 32-bit of the value, without any overflow check.
The proper solution is to use
- (long)longForColumn:(NSString*)columnName;
instead which returns Int64 in Swift.
I'm writing a delphi(7 ver) application and in some place I want to execute parameterized queries (for BDE and Paradox) which will be loaded at runtime into a TQuery by the user. These queries will be stored in text files (one text file for one query). The application then, will construct for any parameter of the query, one input control (Tedit) in order to be able to accept values by the user. Also there will be a button for the execution of query. My question is how can I recognize the datatype of the query's parameter? Is there a way to get this type without of cause to be included in some way in the text file containing the query?
Create a second query from the first, but modify its where clause to ensure no rows.
SELECT * FROM MYTABLE WHERE PKFIELD IS NULL
Name your parameters so that you can establish their datatypes from the fieldtypes of this second query.
I realise this only works for relatively simple cases, but it should get you some of the way.
the advantage of using a parameter is that you don't need to know its data type.
Use the string value from the tedit
"select * from mytable where myfield = :param1"
"parambyname('param1').asstring := edit1.text"
I've made this with MySQL database. you must define some parameters, Exemple:
SELECT * FROM MyTable WHERE MyField=[ANNEE];
in this case, i have an other table, called balise, that look like this
"ID" "BALISE" "CAPTION" "DEFAULT_VALUE" "CNDT" "COMPOSANT"
"1" "ANNEE" "Année" "2014" "Properties.MaxValue=2014||Properties.MinValue=2007" 1;
in runtime, this mean that:
Make in my Panel, a TLablel that have caption Année
Make in the same line an other component type 1 (That mean in my case TcxSpinEdit), this component have défault value 2014, have Two properties Max Value=2014 and Min Value=2007, (I use RTTI to modifie this value of parameters, in Delphi ver7, use TypeInfo).
An Other Button with function called Actualise, this function have Original query, must browse an array of TBalise that i have created, take the value (In my case, take TcxSpinEdit(MyObject).Value), and replace it in the copy of my query (AnsiReplaceStr(Requete, '[ANNEE]', MyValue)), so i have the final query to execute it.
I have module in complete projet, worked with this methode, and it workk fine.
I want to know the difference between following two statements related to datasets in delphi.
dsMyDataSet.ParamByName('ID').AsInteger := 1122; //If ID is integer
dsMyDataSet.ParamByName('ID').AsString := '1122'; //If ID is string
and
dsMyDataSet.ParamByName('ID').Value := 1122; //ID is string or integer
Do these statements carry same meaning? Does "value" implicitly converts integer into string?
The TParam.AsInteger property, for instance, set the value and the data type of the parameter. TParam.Value does the same, but TParam will decide which type will be mapped to the value inside the Variant and not always it´s the data type you would like.
I advise you to set values by using the AsXXX properties only, since you will be in control of the parameter's data type, what can save you from having parameter binding errors.
So, answering your final question: no, the values won´t be converted to the right data type, you have to set the data type by selecting the right property to assign the value.