I have an Sqlitedb, with a column: amount text not null
In column amount, I store dollar values in this format: $1.01, $40.58, etc.
I'm trying to sum up the dollar values using
SELECT SUM(amount) FROM record WHERE date LIKE '"+date+"'"
However, it doesn't work, presumably because you can't add text. I know this isn't the ideal storage but how would I go about parsing the amounts so that I can add them up?
Well, as you have stated this is far from the ideal way to store the values (which should be in decimal / real format from the start), however you could do something like this:
SELECT SUM(SUBSTR(amount, 2)) FROM record WHERE date LIKE '"+date+"'"
The SUBSTR method is a text function that will take a section of the text (in this case, from a starting index). The problem here was the $ sign, which was confused the dynamic type conversion into believing you were trying to add strings together.
This should now work as SQLLite allows for dynamic typing (which in a nutshell, means that the value is used for comparison, not the container).
It's much better to store the amounts as unscaled integers, and just format them when needed for display. You get more compact storage, and easier and faster addition. Using integers avoids the risk of rounding errors in floating-point representations.
By unscaled, I mean $1.23 would be stored as the integer 123. (Use long in your Java code, and INTEGER in the SQL.)
You can format amounts for display by querying the locale for the relevant info:
// implement this any way you like!
long amount = getAmountFromDb();
// use default locale if you want
Locale locale = getLocaleFromSomewhere();
Currency currency = Currency.getInstance(locale);
NumberFormat format = NumberFormat.getCurrencyInstance(locale);
int scale = currency.getDefaultFractionDigits();
BigDecimal scaledAmount = new BigDecimal(unscaledBalance).movePointLeft(scale);
String textAmount = format.format(scaledAmount);
Related
I have a table called "sector" which ID is Long instead of Integer. When I send a request from postman where I say that i want the entities from certain table where "anotherEntity.idSector = 1" it will give me an error.
Anfter several try/error I just discovered that this "1" is an integer for Postman, so when it gets to Java it gives me an error because expects a Long value.
So, is there a way to tell JSON that the value is a Long number? If it was a double, i would replace the "1" by "1.0" and it'd do, but with Long numbers?
Thanks in advance!
No, you can't "tell JSON" anything, it's just an encoding format. Like JavaScript, JSON has no integers, no longs, no floats and no doubles.
It has numbers: https://datatracker.ietf.org/doc/html/rfc8259#section-6
If you need more integer precision than JSON's number type provides, you would typically encode the number as a string. This is generally a good policy for fields like record IDs which are incidentally numeric, rather than semantically numbers (ie, you'll never perform numeric operations on them like addition or division).
My question is actually my ultimate aim.
So far, I am having 2 issues.
How to save arabic date as a 'date' in mysql?
because, I have been converting Gregorian to Hijri and then, using preg_replace (php, for now, final is in Java) would change the numbers to arabic ascii hex... and then, save it in MySQL as varchar.
I know about collation cp1256_general_ci which allows us to store in arabic but, currently, for simplicity sake, I have put it aside. utf-8_general is doing fine too. So storing as varchar is not an issue, storing as 'date' is.
Performing queries on it.
I thought the requirements would end there but, now the task is to perform queries like date 'between' xyz and pqr... Also, the constraint is to 'store it in arabic only'.
Any inputs are much appreciated.
SQL dates
I'd think about it like this: the server actually stores a date as a reference to a given day. How it does that is no concern of yours. When storing data to or reading data from such a date column, the server represents that date using a specific calendar, which is gregorian by convention. What I'm trying to say is, I wouldn't consider the stored value to be gregorian, although it may well be. I would rather consider the transferred date to be gregorian.
So the best solution, in my opinion, is accepting that fact and converting between Gregorian and Hijri on the application side. That way, you could use normal between checks on that.
Strings made up from numbers
If this is not possible, due to the fact that the locale-dependent conversion is too complicated, or because the mapping betwen Hijri and Grogorian is not unique or not known in advance, then you will have to store the date in some other form. Possible forms that come to my mind are either a varchar containing strings of the form YYYY-MM-DD, with the letters signifying digits. This scheme ensures that strings would compare like the dates they represent, so you could still use between on them. Turning these strings back into spelled out dates would still be tricky, though.
One or more numeric columns
So I would actually suggest you use three columns., each containing a number signifying a date, You could then use 10000*year + 100*month + day_of_month to obtain a single number for each day, which you could use for comparisons and between. On the other hand, you could use the function ELT in your queries to turn the number for the month back into a name. If performance is an issue, you might be better of storing just a single number, and splitting it into parts upon selection. In a Gregorian calendar, this would look like this:
CREATE TABLE tableName (myDate DECIMAL(8));
SELECT myDate DIV 10000 AS year,
ELT((myDate DIV 100) MOD 100, "Jan", "Feb", …) AS month,
myDate MOD 100 AS day_of_month
FROM tableName
WHERE myDate BETWEN 20121021 AND 20121023;
Compatibility and convenience
If you have to maintain read-only compatibility with code that expects a single textual date column, you could use a VIEW to provide that. For example for a German Gregorian DD. MMMM YYYY format, you could use code like this:
CREATE VIEW compatibleName AS
SELECT CONCAT(myDate MOD 100, ". ",
ELT((myDate DIV 100) MOD 100, "Januar", "Februar", …), ". ",
myDate DIV 10000) as dateString,
* -- or explicitely name other columns needed for compatibility
FROM tableName
Decoding strings
If you need read-write access by another application using a string format, you'll have to parse those strings yourself. You can do that at the SQL level. Useful tools are SUBSTRING_INDEX to split the string into fields and FIELD to turn a month name into a number. You might want to add a trigger to the database which will ensure that your strings will always be in a valid format which you can decompose in this way. This question gives details on how to use triggers to enforce such checks.
you can store as date directly. I am usind normal date. my mysql functions are
DELIMITER $$
DROP FUNCTION IF EXISTS `kdmtest`.`IntPart` $$
CREATE FUNCTION `kdmtest`.`IntPart` (FloatNum float) RETURNS INT
BEGIN
if (floatNum< -0.0000001) then
return ceil(floatNum-0.0000001);
else
return floor(floatNum+0.0000001);
end if;
END $$
DELIMITER ;
DELIMITER $$
DROP FUNCTION IF EXISTS `kdmtest`.`Hicri` $$
CREATE DEFINER=`root`#`localhost` FUNCTION `Hicri`(MiladiTarih date) RETURNS date
BEGIN
declare d,m,y,jd,l,n,j int;
set d=day(MiladiTarih);
set m=month(MiladiTarih);
set y=year(MiladiTarih);
if ((y>1582) or((y=1582) and (m>10)) or ((y=1582) and (m=10) and (d>14))) then
set jd=intpart((1461*(y+4800+intpart((m-14)/12)))/4)+intpart((367*(m-2-12*(intpart((m-14)/12))))/12)- intpart( (3* (intpart( (y+4900+ intpart( (m-14)/12) )/100) ) ) /4)+d-32075;
else
set jd = 367*y-intpart((7*(y+5001+intpart((m-9)/7)))/4)+intpart((275*m)/9)+d+1729777;
end if;
set l=jd-1948440+10632;
set n=intpart((l-1)/10631);
set l=l-10631*n+354;
set j=(intpart((10985-l)/5316))*(intpart((50*l)/17719))+(intpart(l/5670))*(intpart((43*l)/15238));
set l=l-(intpart((30-j)/15))*(intpart((17719*j)/50))-(intpart(j/16))*(intpart((15238*j)/43))+29;
set m=intpart((24*l)/709);
set d=l-intpart((709*m)/24);
set y=30*n+j-30;
return concat(y,'-',m,'-',d);
END $$
DELIMITER ;
DELIMITER $$
DROP FUNCTION IF EXISTS `kdmtest`.`Miladi` $$
CREATE FUNCTION `kdmtest`.`Miladi` (HicriTarih date) RETURNS date
BEGIN
declare d,m,y,jd,l,n,j,i,k int;
set d=day(HicriTarih);
set m=month(HicriTarih);
set y=year(HicriTarih);
set jd=intPart((11*y+3)/30)+354*y+30*m-intPart((m-1)/2)+d+1948440-385;
if (jd> 2299160 ) then
set l=jd+68569;
set n=intPart((4*l)/146097);
set l=l-intPart((146097*n+3)/4);
set i=intPart((4000*(l+1))/1461001);
set l=l-intPart((1461*i)/4)+31;
set j=intPart((80*l)/2447);
set d=l-intPart((2447*j)/80);
set l=intPart(j/11);
set m=j+2-12*l;
set y=100*(n-49)+i+l;
else
set j=jd+1402;
set k=intPart((j-1)/1461);
set l=j-1461*k;
set n=intPart((l-1)/365)-intPart(l/1461);
set i=l-365*n+30;
set j=intPart((80*i)/2447);
set d=i-intPart((2447*j)/80);
set i=intPart(j/11);
set m=j+2-12*i;
set y=4*k+n+i-4716;
end if;
return concat(y,'-',m,'-',d);
END $$
DELIMITER ;
I was wondering how you can take just specific characters from a JTextField. For example if a JTextField has the date 20/12/2012 then how do you read only the "12" form the Field.
So is it possible and if so how or would it be easier to use multiple JTextFields?
One approach would be read full text, but split string based on "/" and take 0 index value in resulting array.
(or)
Parse String using SimpleDateFormatter and get Month from Date (assuming entered string will be always date)
If none of above works, then it would be easier to use multiple text fields
StringTokenizer can break up your string for you.
SimpleDateFormat can handle dates if you know the format in the field will be correct.
However having multiple fields for dates is common. You may just want different fields depending on your overall use of the data and user interaction with it.
I use the following statement to index a date:
luceneDoc.add(new NumericField(key).setLongValue(date.getTime()));
I also use statements as follows to add text properties:
luceneDoc.add(new Field(key, value, Field.Store.YES, Field.Index.ANALYZED));
Then I perform a text property query:
author:hans
This works perfect. But when I perform a range query, nothing gets returned:
my-date-property:[20100101 TO 20110101]
What am I missing here?
I had a look at the index with Luke, I see all my text property for a document but the date properties only appear in the overview page... maybe that is normal.
I actually DO SEE the date properties if I add it like this:
NumericField field = new NumericField(key, Field.Store.YES, true);
field.setLongValue(date.getTime());
luceneDoc.add(field);
But: the query still does not work! Maybe it only works from Java with the Query Builder? I have not tried out that. But it would be great if the text query would work too. ANY IDEA???
If you want a range query to work with dates in the form of YYYYMMDD, then index your date like this:
String dateString = DateTools.dateToString(date, Resolution.DAY);
luceneDoc.add(new Field(key, dateString, Store.YES, Index.NOT_ANALYZED));
Try to declare my-date-property as DateField.
Since you are using a NumbericField I suppose that the range you specified is interpreted as a numeric range instead of a date range. In this case the numbers 20100101 and 20110101 are far too low to get any reasonable results.
Numeric fields and numeric range queries are absolutely brilliant, but they really are tricky to use for the first time!
Currently, the standard query parser doesn't support numeric range queries. In order to make use of numeric fields, you'll need to derive your own query parser variants and construct numeric range queries where appropriate.
Clarifying my original answer a little (I've just seen it referred to in a comment...), I should note that range queries, where numbers are converted to (usually) zero prefixed text work fine (albeit relatively slowly) in the standard query parser. From the original information posted, the question is how to use numeric (trie encoded) fields in a query. For that you need to parse a query in such a way as to produce numeric range queries (which understand trie encodings). These work much faster than text encoded numeric fields.
Good luck
I am attempting to delete a row, whose primary key(column name=version) for example is 4.002000000000001. So I give say the statement:
DELETE FROM tbl WHERE version=4.002;
Now right now this wont work, because theres no row that has a version equal to 4.002, only a row with a version equal to 4.002000000000001. Please dont ask why I dont store the version as 4.002 in the DB because I am trying to do so by making the column type DECIMAL(5,3) but it still stores a double from Java as 4.002000000000001. And please dont ask why I dont say "...WHERE version=4.002000000000001;" because right now I have no control over how java formats it doubles(even using DecimalFormat class) when I create a statement using JDBC.
So is there a way to round down(floor function) in SQLite3? So then I can say "delete the row whose FLOOR(version) is equal to 4.002?
I am a C++ programmer (using Java to query an SQL database, please dont ask why :P) who is not proficient with complex SQL queries, so would this be correct for what I want to do:
DELETE FROM tbl WHERE ROUND(version, 0.5)=4.002;
And please dont ask why I dont say "...WHERE version=4.002000000000001;" because right now I have no control over how java formats it doubles(even using DecimalFormat class)
I won't.
But I will ask why you are using double to store a version number.
Machine floating point types (base 2) do not hold exact decimal numbers. But version number strings are exact. You would be better off treating version "numbers" as one of the following, depending on your version numbering scheme:
Scaled integers; e.g. 4.002 is 4002 divided by 1000. You probably don't need to store the scaling factor explicitly.
Tuples or arrays of integers; e.g. 4.002 is {4,2}. This allows version numbers like 4.003.005 to be represented as {4,3,5} etcetera.
Character strings constrained by a pattern; e.g. 4.002 is a string that matches the regex "\d+\.\d\d\d". This allows version strings with non-numeric parts.
Choose one of those and you won't have to worry about the fundamental inexact nature of floating point types, in memory or in the database.
Oh, and by the way the DecimalFormat class does let you control the number of digits after the decimal place when you format a number. You can specify this using a pattern or by calling setMaximumFractionDigits
Can you do?
DELETE FROM tbl WHERE version > 4.002 - 1e-4 AND version < 4.002 + 1e-4
or
DELETE FROM tbl WHERE version > 4.0015 AND version < 4.0025
DELETE FROM tbl WHERE abs(version-4.002)<.0001
if you want to remove all versions in an epsilon boundary of .0001