Sqlite C/C++ API - Get timestamp value with select query - sqlite

I use sqlite3 C/C++ API to retrieve rows from a table using SELECT query. I don't see any sqlite3_column_timestamp() to retrieve a timestamp column value after sqlite3_step().. How to get timestamp values ?

SQLite does not have a special timestamp data type.
When you want to use any of SQLite's date and time functions, you have to store timestamps in one of the formats supported by them, i.e., a string like YYYY-MM-DD HH:MM:SS or HH:MM:SS, a julian date number, or a Unix timestamp number.
You can declare a table column type as DATETIME, but SQLite will just ignore that type; SQLite always allows to put values of any type in any column. Such a declaration would be useful only as documentation.

The column/value accessors will only have types corresponding to the data types they support directly (NULL, INTEGER, REAL, TEXT, BLOB).
You would use the TEXT access to get/set the column value of dates.
There are some helper functions within SQL that they provide that let you to handle them in your queries.
I am not familiar with SQLite Manager, but I would assume that it is only reporting the data type that the table was declared with.
When parsing CREATE statements, sqlite understands the intention of many well supported datatypes and automatically maps them to what is appropriate for its internal storage structure. VARCHAR would be mapped to TEXT, for instance. I assume the column was declared DATETIME and sqlite just internally mapped it to TEXT.

Related

Does SQLite actually support DATE type?

After reading https://sqlite.org/datatype3.html which states
"SQLite does not have a storage class set aside for storing dates
and/or times."
but able to run this
CREATE TABLE User (ID INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, BORN_ON DATE NULL)
and then see it in "DB Browser for SQL" like this:
I start to wonder if SQLite does support Date type of it is just "faking" the support using other types. And even if so why the DB Browser see it as a Date? Any meta info stored inside the DB?
SQLite does not fake Date with Numerics.
There is no Date data type in SQLite.
In Datatypes In SQLite Version 3 it is explained clearly that:
SQLite uses a more general dynamic type system
Instead of data types there are 5 Storage Classes: NULL, INTEGER, REAL, TEXT and BLOB.
Also:
Any column in an SQLite version 3 database, except an INTEGER PRIMARY
KEY column, may be used to store a value of any storage class.
So when you use Date as the data type of a column in the CREATE TABLE statement you are not restricted to store in it only date-like values. Actually you can store anything in that column.
Tools like "DB Browser for SQLite" and others may offer various data types to select from to define a column when you create the table.
The selection of the data type that you make is not restrictive, but it is rather indicative of what type of data you want to store in a column.
In fact, you can create a table without even declaring the data types of the columns:
CREATE TABLE tablename(col1, col2)
or use fictional data types:
CREATE TABLE tablename(col1 somedatatype, col2 otherdatatype)
and insert values of any data type:
INSERT INTO tablename(col1, col2) VALUES
(1, 'abc'),
('XYZ', '2021-01-06'),
(null, 3.5)
Based on what Colonel Thirty Two suggested (read more on the page) it seems that when you declare a field as Date its affinity will be numeric.
So SQLite "fakes" Date with Numerics.
And even if so why the DB Browser see it as a Date? Any meta info stored inside the DB?
Yes, it simply stores the type name used when the column was created. The linked page calls it "declared type". In this case you get NUMERIC affinity (DATE is even given as one of the examples in 3.1.1) and it behaves like any other column with this affinity:
A column with NUMERIC affinity may contain values using all five storage classes. When text data is inserted into a NUMERIC column, the storage class of the text is converted to INTEGER or REAL (in order of preference) if the text is a well-formed integer or real literal, respectively. If the TEXT value is a well-formed integer literal that is too large to fit in a 64-bit signed integer, it is converted to REAL. For conversions between TEXT and REAL storage classes, only the first 15 significant decimal digits of the number are preserved. If the TEXT value is not a well-formed integer or real literal, then the value is stored as TEXT. For the purposes of this paragraph, hexadecimal integer literals are not considered well-formed and are stored as TEXT. (This is done for historical compatibility with versions of SQLite prior to version 3.8.6 2014-08-15 where hexadecimal integer literals were first introduced into SQLite.) If a floating point value that can be represented exactly as an integer is inserted into a column with NUMERIC affinity, the value is converted into an integer. No attempt is made to convert NULL or BLOB values.
A string might look like a floating-point literal with a decimal point and/or exponent notation but as long as the value can be expressed as an integer, the NUMERIC affinity will convert it into an integer. Hence, the string '3.0e+5' is stored in a column with NUMERIC affinity as the integer 300000, not as the floating point value 300000.0.
So if you insert dates looking like e.g. "2021-01-05" they will be stored as strings. But
you can also insert strings which don't look like dates.
if you insert "20210105" it will be stored as the number 20210105.
You can use CHECK constraints to prevent inserting non-date strings.
See also https://sqlite.org/lang_datefunc.html which says what (string and number) formats date/time functions expect.

Sqlite get the date part of a DateTime and return as a DateTime, not a string

In Sqlite I want to extract the date and time portions of a DateTime field separately in a view and return them also as a datetime, not strings. I've tried Cast, Date(), datetime(), but they all return strings.
I've read the SQLite documentation and understand how there is not an actual Date data type. Yet a Table field defined as DateTime is able to be parsed as a Date by an Excel query, but calculations on that field are not. I'm trying to do all data prep in the database view.
My data has the following field taken directly from the table definition:
LastModifiedDate datetime
I want the date (without time) to have the same DateTime data type as LastModifiedDate, not Text, because I use this view in many spreadsheets. I can apply Excel Date functions and formatting to LastModifiedDate field directly as returned from the ODBC query to Excel, and want to do the same to the Date-only part. I don't want to have to put a string-to-date conversion in every spreadsheet when I know it can get the date natively from Sqlite in LastModifiedDate.
SELECT LastModifiedDate,
date(LastModifiedDate) as Datepart,
cast(LastModifiedDate as numeric) as Date2
FROM Transactions
LastModifiedDate Datepart Date2
2019-07-28 18:22:38.9165394 2019-07-28 2019
LastModifiedDate in the above query is interpreted in Excel as a date to which date formats and date functions can be applied with no further processing required. Datepart above is returned as Text to Excel, and I can't apply date functions and formats without further pre-processing in Excel. I would like Datepart to be interpreted a date in Excel just as LastModifiedDate is.
I'm looking at the ch-werner.de sqliteodbc-0.9998. It will return an ODBC TIMESTAMP type only if the column decltype starts with timestamp or datetime. It returns ODBC TIME only for decltypes starting with time and ODBC DATE only for decltypes starting with date.
sqlite3 provides this decltype only for result table columns that are direct database column references. So if your SELECT statement has some expression that is more than a plain column reference, the decltype is lost. sqlite3 works like this at least up to version 3.39.0. It is documented.
The CAST expression converts the value of given expression to a storage classes by the determined affinity of the given declared type, but does not assign decltype to the result.
If you want to see the decltypes for query columns, you can use the sqlite3 cli and give it command .stats 2. Then it'll output the column declared types for each statement it executes.
If the decltype is found, the sqliteodbc-0.9998 will always parse string values into ODBC types. If DSN Option JDConv is enabled, it'll also parse floating point julianday values (whether provided as float or a string of a float) into ODBC types and when writing it'll write floating point into database.
If you can afford to change the schema, you can add a generated virtual column. This is cheap in storage, because data is not affected, but it costs when you query the column. This column can calculate other column into the values and decltypes you need for ODBC.
ALTER TABLE data ADD COLUMN
Datepart date AS (date(LastModifiedDate))
Then to get the Datepart, you simply query the column.
SELECT Datepart FROM data

Delphi - ClientDataSet SQL calculated field causing "Invalid field type" error at runtime [duplicate]

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.

DateTime Type Gets Converted into bigint in SQLite

I Am new to SQLite.I am trying to create table from my model class in which I have DATETIME field.when sqlite table creatd from This Class The DATETIME column creted of type bigint.I know the reason because SQLite doesn't support DATETIME
So what should I do to create Column Of type TEXT in SQLITE without Chnging DATETIME type in my model Class. I found some post regarding to this but don't get satisfactory solution.please help.Thanks
You can configure how DateTimes are stored in your connection string. Change the attribute DateTimeFormat to either "ISO8601" or "CurrentCulture".
But: I would not recommend to do that. If you ever want to sort by a datetime or you want to filter rows (give me all entries from the last 2 weeks) then the bigint approach is the most efficient one. While ISO8601 datetime strings are sortable, that is most likely not the case with localised CurrentCulture strings. So if you really want human-readable strings in your database then choose the ISO version.

Creating a DateType field on sqlite3

i want to create a table on sqlite with one field as DateTime (YYYY-MM-DD) , how i can create it?
i'm trying with:
create table test (_date datetime);
but i'm not sure if the datatype is correct 'cause i can do this:
create table test (_date nyanType);
and no error occours
SQLite is rather unique in that its columns are not statically typed. You can technically store a string in a column that was created as an integer column.
If you check out the SQlite Documentation for types, you'll see that SQLite dosn't have a date type, but it exposes date and time functions that are suitable for manipulating dates that are stored as TEXT, REAL or INTEGER. You should use those instead.

Resources