How to limit numeric column type to certain symbols before and after decimal separator in sqlite? - sqlite

I want to limit numeric column type to 10 symbols before decimal separator and 4 symbols after decimal separator. I executed the following command:
ALTER TABLE scustdisc ADD COLUMN spec_price numeric(10,4)
The command executed without errors but when I try to insert value in spec_price 10.123456 I am able to do it. It should give error and the value not to be inserted. Am I wrong in my alter command?

SQLite has a dynamic type system and the column types have a limited impact, but can be virtually any name. They are resolved to one of TEXT, NUMERIC, INTEGER, REAL or BLOB.
numeric(0,0) - numeric(99999999,99999999) and more resolve to NUMERIC.
As such 10,4 4,10 etc means nothing and makes no difference to SQLite.
With one exception bar constraints a column may hold any type of value. The column type only comes into play in determining the way the data is stored.
A must read is Datatypes In SQLite Version 3
You may also find How flexible/restricive are SQLite column types?
You may be able to resolve this by using a CHECK constraint CREATE TABLE or by using a TRIGGER or multiple TRIGGERs.
You could format the number(s) appropriately when they are displayed.
You could utilise the round(x,y) function Core Functions

Related

How to parse accounting number format in SQLite field?

I am a somewhat newbie to SQLite (and KMyMoney). KMyMoney (an open source personal finance manager) allows one-click exporting data into an SQLite database.
On browsing the SQLite database output, the dollar amount data is stored in a table called kmmSplits as several text fields in a strange format based on “value” and “valueFormatted” (see screen shot below). The “value” field is apparently written as a division equation (in a text format) which apparently yields the “valueFormatted” field (again in text format). The “valueFormatted is the correct number amount but the problem is that parenthesis are used to indicate a negative number instead of a simple minus in front of the value. This is apparently an accounting number format, but I don’t know how to parse this into a float value for running calculated SQL queries, etc. The positive values (without parenthesis) are no problem to convert to FLOATS.
I’ve tried using the CAST to FLOAT function but this does not do the division math, nor does it convert parenthesis into negative values (see screen shot).
The basic question is: how to parse a text value containing parenthesis in the “valueFormatted field (accounting money format) into a common number format OR, alternatively, how to convert a division equation in the “value” field to an actual calculation.
Use a CASE expression to check if valueFormatted is a numeric value inside parentheses and if it is multiply -1 with the substring starting from the 2nd char (the closing parenthesis will be discarded by SQLite during this implicit type casting):
SELECT *,
CASE
WHEN valueFormatted LIKE '(%)' THEN (-1) * SUBSTR(valueFormatted, 2)
ELSE valueFormatted
END AS value
FROM kmmSQLite;
Or, replace '(' with ''-'' and add 0 to covert the result to a number:
SELECT *,
REPLACE(valueFormatted, '(', '-') + 0 AS value
FROM kmmSQLite;

Can sqlite-utils convert function select two columns?

I'm using sqlite-utils to load a csv into sqlite which will later be served via Datasette. I have two columns, likes and dislikes. I would like to have a third column, quality-score, by adding likes and dislikes together then dividing likes by the total.
The sqlite-utils convert function should be my best bet, but all I see in the documentation is how to select a single column for conversion.
sqlite-utils convert content.db articles headline 'value.upper()'
From the example given, it looks like convert is followed by the db filename, the table name, then the col you want to operate on. Is it possible to simply add another col name or is there a flag for selecting more than one column to operate on? I would be really surprised if this wasn't possible, I just can't find any documentation to support it.
This isn't a perfect answer as it doesn't resolve whether sqlite-utils supports multiple column selection for transforms, but this is how I solved this particular problem.
Since my quality_score column would just be basic math, I was able to make use of sqlite's Generated Columns. I created a file called quality_score.sql that contained:
ALTER TABLE testtable
ADD COLUMN quality_score GENERATED ALWAYS AS (likes /(likes + dislikes));
and then implemented it by:
$ sqlite3 mydb.db < quality_score.sql
You do need to make sure you are using a compatible version of sqlite, as this only works with version 3.31 or later.
Another consideration is to make sure you are performing math on integers or floats and not text.
Also attempted to create the table with the virtual generated column first then fill it with my data later, but that didn't work in my case - it threw an error that said the number of items provided didn't match the number of columns available. So I just stuck with the ALTER operation after the fact.

Delphi/FireDAC/SQLite - force TEXT column affinity on a STRING type column (leading zeroes being lost)

I did a mistake and declared a SQLite table column in CREATE TABLE DDL statement as STRING. Inserting '00123' into that column results as numeric value 123 being written (so leading zeroes are lost). I now know I should have used TEXT.
Is there any way, using FireDAC, to somehow force TEXT affinity for that column without dropping and recreating the table? Or, in other words, force leading zeroes to be written in the STRING column type?
I've tried INSERT INTO table_name (string_column) VALUES (CAST('00123' AS TEXT)) with no luck.
If drop/create table is the only way forward, is there a way to know for a table whether a column was created as STRING (so, get its affinity)?
-žarko

sqlite3 cast REAL as NUMERIC(x,y)

I have a table with a numeric(6,4) column (x.xxxx)
In the select statement, I have to do math on this column which results in the value being typed as a REAL. I need to display the results using numeric(6,4) though and can't seem to see how to do this.
I tried "cast(column as numeric(6,4))" which works in other databases, but the displayed value is still REAL format with more than 4 digits to the right of the decimal. I need to get it back to the "x.xxxx" format from REAL.

SQLite: Numeric values in CSV treated as text?

I just imported a huge text file into a table, using the .import command. Everything is OK, except for the fact that it seems to treat clearly numeric values as text. For instance, conditions such as WHERE field > 4 are always met. I did not specify datatypes when I created the table, but this doesn't seem to matter when small tables are created.
Any advice would be welcome. Thanks!
Edit/conclusion: It turns out some of the values in my CSV file were blanks. I ended up solving this by being a bit less lazy and declaring the datatypes explicitly.
The way SQLite handles types is described on this page: http://www.sqlite.org/datatype3.html
In particular:
Under circumstances described below,
the database engine may convert values
between numeric storage classes
(INTEGER and REAL) and TEXT during
query execution.
Section 3.4 (Comparison Example) should give you concrete examples, which are likely to explain the problem you have. This is probably this example:
-- Because column "a" has text affinity, numeric values on the
-- right-hand side of the comparisons are converted to text before
-- the comparison occurs.
SELECT a < 40, a < 60, a < 600 FROM t1;
0|1|1
To avoid the affinity to be guessed, you can use CAST explicitly (see section 3.2 too):
SQLite may attempt to convert values
between the storage classes INTEGER,
REAL, and/or TEXT before performing a
comparison. Whether or not any
conversions are attempted before the
comparison takes place depends on the
affinity of the operands. Operand
affinity is determined by the
following rules:
An expression that is a simple reference to a column value has the
same affinity as the column. Note that
if X and Y.Z are column names, then +X
and +Y.Z are considered expressions
for the purpose of determining
affinity.
An expression of the form "CAST(expr AS type)" has an affinity
that is the same as a column with a
declared type of "type".
Otherwise, an expression has NONE affinity.
Here is another example:
CREATE TABLE test (value TEXT);
INSERT INTO test VALUES(2);
INSERT INTO test VALUES(123);
INSERT INTO test VALUES(500);
SELECT value, value < 4 FROM test;
2|1
123|1
500|0
It's likely that the CSV import create columns of affinity TEXT.

Resources