move decimals sql or divide - mysql

I built an administrative system in Laravel for my office, using mysql, one of the main tables is for the price of the products which is a double. The system has been in production for over 2 years now, but the government here changed our currency and removed 5 zeroes, so I need to change all the price values while keeping them with some decimals.
Currently, the lowest price in the list is 500
While the highest is 14985010
I need to perform a query to change ALL the price values in that column on the production DB (backup is done, so should be fine)
So the 500 one should be: 0.005
And the 14985010 should be: 149.8501
I'm thinking that all that is needed is to divide the values by 100000, but i might be wrong.
My SQL skills are super rusty, and I've been searching for a while but couldn't find the right answer. Any pointer would be greatly appreciated. Thanks!

I actually would recommend against just dividing all your currency data by some large factor, e.g. 100K. Instead, I propose adding a new column to your prices table, which is key into another table giving the factor which should be used to determine the current price. So you might have a setup like this:
prices table
price | factorKey
500 | 2
14985010 | 2
factors table
factorKey | value
1 | 1
2 | 0.00001
Then, to generate the actual current prices, you could do a join:
SELECT
p.price * f.value AS price
FROM prices p
INNER JOIN factors f
ON p.factorKey = f.factorKey;
Under this scheme, you would only need to modify the current factor to be used with your price data. You could even maintain another table to keep track of the historical prices. One reason to suggest this is that perhaps your government will change prices again in the future. It is error prone to do such large manual divisions/multiplications on your original data.

You should first fix the column to be sure it can handle the appropriate decimal places. I would suggest something like:
alter table t modify column price decimal(38, 10);
Then just update the value:
update t
price = price / 100000;
Having said that, you might want to check if the values are exactly what you expect:
select cast(cast(price as decimal(38, 10)) / 100000 as decimal(38, 10))
There may be subtle rounding errors that are not to your liking.

First of all, your currency data type should support decimal numbers, not just integers. I would suggest DECIMAL(12,4) for your column.
After you are sure the column data type can hold decimal numbers, you can update all rows in the table.
Here's a demonstration:
mysql> create table MyTable ( MyCurrencyColumn decimal(12,4));
Query OK, 0 rows affected (0.03 sec)
mysql> insert into MyTable values (500), (14985010);
Query OK, 2 rows affected (0.01 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> select * from MyTable;
+------------------+
| MyCurrencyColumn |
+------------------+
| 500.0000 |
| 14985010.0000 |
+------------------+
2 rows in set (0.00 sec)
mysql> UPDATE MyTable SET MyCurrencyColumn = MyCurrencyColumn / 100000;
Query OK, 2 rows affected (0.02 sec)
Rows matched: 2 Changed: 2 Warnings: 0
mysql> select * from MyTable;
+------------------+
| MyCurrencyColumn |
+------------------+
| 0.0050 |
| 149.8501 |
+------------------+

Related

Left Join Overrides Non-Null Value with Null value when record is empty on second table

I have two tables:
Table 1: qtrade
qtrade columns
qtrade values
Table 2: qsale
qsale columns
qsale values
These two table have common "tid" which is unique trade id. I need to get tid's with their qsale values if it is available. So, i tried to LEFT JOIN method like this:
'SELECT *
FROM `qtrade`
LEFT JOIN `qsale` ON qtrade.tid = qsale.tid'
The query retrieves joined data, but for tid=11 there is no qsale record, so it retrieves NULL valeus as expected, but also overrides tid with NULL value as not expected. It gets tid NULL.
I have serached that and found COALESCE trick. It might work, but i would write down all column names in qtrade and qsale, these are around 32 columns. Too long. If there any trick to overcome this issue. I think 'SELECT *, COALESCE(qsale.tid, qtrade.tid) tid' will not work. Meaning only coalesce tid, and get all column data. Is there any other way ?
What you describe does work.
Demo:
mysql> create table qtrade (tid int);
Query OK, 0 rows affected (0.01 sec)
mysql> create table qsale (tid int);
Query OK, 0 rows affected (0.01 sec)
mysql> insert into qtrade set tid=42;
Query OK, 1 row affected (0.01 sec)
mysql> SELECT *
-> FROM `qtrade`
-> LEFT JOIN `qsale` ON qtrade.tid = qsale.tid;
+------+------+
| tid | tid |
+------+------+
| 42 | NULL |
+------+------+
1 row in set (0.00 sec)
mysql> SELECT *, COALESCE(qsale.tid, qtrade.tid) AS tid
FROM `qtrade` LEFT JOIN `qsale` ON qtrade.tid = qsale.tid;
+------+------+------+
| tid | tid | tid |
+------+------+------+
| 42 | NULL | 42 |
+------+------+------+
1 row in set (0.00 sec)
MySQL query result sets allow multiple columns to have the same name.
But the problem arises when you have a client that fetches the results into an associative array or hashmap, which only allows one entry per name.
In that case, the only alternative is to change the client code to fetch results into an ordinal array instead of an associative array, and then reference the columns of the result by position instead of by name.
I never use SELECT * in production code anyway. Just write out the columns. If typing 32 column names is the bottleneck in your programming productivity, then you're doing it wrong.

Nested Loop Calculation

Okay so maybe this is way deeper than I will ever need to go however I want to be able to analyze this nested loop so that I can understand it.
Given:
mysql> describe t1;
+-------+----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+----------+------+-----+---------+-------+
| dt | datetime | NO | MUL | NULL | |
+-------+----------+------+-----+---------+-------+
1 row in set (0.00 sec)
And:
mysql> insert t1 values(101),(102),(103),(104),(105),(106),(107),(#c:=now());
Query OK, 8 rows affected (0.03 sec)
Records: 8 Duplicates: 0 Warnings: 0
And:
mysql> insert t1 select #c:=#c+interval 1 second from t1,t1 b,t1 c,t1 d,t1 e,t1 f;
Query OK, 262144 rows affected (1.94 sec)
Records: 262144 Duplicates: 0 Warnings: 0
So far I have the understanding that (#ofrows)^(#oftables)=(# of rows Added)
My question is why this is the case. I cannot find out exactly how MySQL is handling the rows and other system variables in order to create the equation that I have provided here. My equation is clearly a simplified version of the resulting action performed by the server as using 2 rows of data and 6 tables similarly gives an output of 64.
Does anyone know exactly how this is manipulated? I have been working on this for 2 days and I cannot get my mind off of it...
Also why is it inserting more than 6... maybe 36? rows into the table in the first place?? it is only specifying one possible select-able row from the tables and that is the previously inserted now() and then adding 1 second to that row and resetting the #c based on the final change so shouldn't it by logic only insert a few rows?
I guess to put it simply I understand what is happening specifically in the select #c:=#c+interval 1 second portion of the statement, but after that I am not quite sure...
I guess to put it simply how does:
select #c:=#c+interval 1 second;
+--------------------------+
| #c:=#c+interval 1 second |
+--------------------------+
| 2014-07-20 18:17:50 |
+--------------------------+
1 row in set (0.00 sec)
turn into this:
...
Query OK, 262144 rows affected (1.94 sec)
Records: 262144 Duplicates: 0 Warnings: 0
The answer that was satisfactory for me on this issue was so simple I can't believe that I didn't realize it before now.
Today I needed to quickly fill a table with values so I decided to use another table that I had to do this. For example take table i which has the column i with values 1-50. I executed the following command to populate my table c column i.
insert into c select i.i from i,i b;
Based on my knowledge above I know that this would fill table c with exactly 2500 values. being that it executed the 50^2.
What I actually found that this was doing was examining each row from table i and multiplying it by the count of rows found in the first buffered instance i b and then inserting the resulting rows into the table. If you have more buffered instances it will get the results from one and then proceed to multiply those results further by the original 50 rows that would be found in the instance (for example i c).
I ended up with 50 rows of 1-50 within table c.
I didn't realize that this match based multiplication was being performed in this way before because I was using an incrementing operator and it would incrementally create new rows based on this multiplication, however it did not leave any indication that this is what it did due to the fact that it was not matching rows to duplicate rather it matched the number of rows to insert.

Mysql-If I insert multiple values in a column of a table simultaneously ,is it possible that the inserting orders of values get change?

I am doing these :
insert into table_name(maxdate) values
((select max(date1) from table1)), -- goes in row1
((select max(date2) from table2)), -- goes in row2
.
.
.
((select max(date500) from table500));--goes in row500
is it possible that while insertion , order of inserting might get change ?.Eg when i will do
select maxdate from table_name limit 500;
i will get these
date1 date2 . . date253 date191 ...date500
Short answer:
No, not possible.
If you want to double check :
mysql> create table letest (f1 varchar(50), f2 varchar(50));
Query OK, 0 rows affected (0.00 sec)
mysql> insert into letest (f1,f2) values
( (SELECT SLEEP(5)), 'first'),
( (SELECT SLEEP(1)), 'second');
Query OK, 2 rows affected, 1 warning (6.00 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> select * from letest;
+------+--------+
| f1 | f2 |
+------+--------+
| 0 | first |
| 0 | second |
+------+--------+
2 rows in set (0.00 sec)
mysql>
SLEEP(5) is the first row to be inserted after 5 seconds,
SLEEP(1) is the second row to be inserted after 5+1 seconds
that is why query takes 6 seconds.
The warning that you see is
mysql> show warnings;
+-------+------+-------------------------------------------------------+
| Level | Code | Message |
+-------+------+-------------------------------------------------------+
| Note | 1592 | Statement may not be safe to log in statement format. |
+-------+------+-------------------------------------------------------+
1 row in set (0.00 sec)
This can affect you only if you are using a master-slave setup, because the replication binlog will not be safe. For more info on this http://dev.mysql.com/doc/refman/5.1/en/replication-rbr-safe-unsafe.html
Later edit: Please consider a comment if you find this answer not usefull.
Yes, very possible.
You should consider a database table unordered, and a SELECT statement without ORDER clause as well. Every DBMS can choose how to implement tables (often even depending on Storage Engine) and return the rows. Sure, many DBMS's happen to return your data in the order you inserted, but never rely on it.
The order of the retrieved data my depend on the execution plan, and may even be different when running the same query multiple times. Especially when only retrieving part of the data (TOP/LIMIT).
If you want to impose an order, add a field which orders your data. Yes, an autoincrement primary key will be enough in many cases. If you think you'll be wanting to change the order someday, add another field.

Storing 0.5 in MySQL

Using a decimal (I have tried variations of it) 0.5 is always converted to 5.
I can store 1.5, etc, no problem... Just curious on how I need to set up my data type to correctly store a '0.5' number.
Thanks
EDIT:
The current data type is Decimal (3,1). I have tried float as well.
I simply retreive the number through $_POST from a Form text box which is restricted to numbers only through Java. I never considered that maybe the browser is simply sending it as a 5, I don't know.
EDIT 2: I have confirmed through echo that the 0.5 is sent fine, it's once it gets into the database that it becomes 5
You know, I might get voted down for this, but I've always had success (as a sort of hack) in storing values like this as a VARCHAR and then using format() function in PHP when I need to make calculations. MySQL still seems to parse it well enough for SQL functions as well. Just my 2 cents :)
I used a decimal value in a database with various inputs:
I have tried to recreate you issue, but I get the following:
mysql> create table test(myDec decimal(2,2));
Query OK, 0 rows affected (0.02 sec)
mysql> insert into test values(0.5);
Query OK, 1 row affected (0.01 sec)
mysql> insert into test values(.5);
Query OK, 1 row affected (0.00 sec)
mysql> select * from test;
+-------+
| myDec |
+-------+
| 0.50 |
| 0.50 |
+-------+
2 rows in set (0.00 sec)
Given your edit, I think that you might be getting values from international visitors, where the decimal point might be formatted using a comma instead of a dot?
mysql> insert into test values('0,5');
Query OK, 1 row affected, 1 warning (0.00 sec)
mysql> select * from test;
+-------+
| myDec |
+-------+
| 0.50 |
| 0.50 |
| 0.00 |
+-------+
3 rows in set (0.00 sec)

mysql prevent negative numbers

Is their an int field in mysql where negative numbers are not allowed? or more specifically if a negative number is inserted into the field it will insert a zero. I ask this because we have a scoring system and we don't allow people to have negative scores. So if their score does reach bellow zero, it will just insert a zero instead. I'm trying to do this without having to query the user's score to check if it will fall bellow zero.
In addition to the DDL change (INT UNSIGNED) that others have recommended, I'd also change your application logic. You say:
I'm trying to do this without having to query the user's score to check if it will fall bellow zero.
You don't have to explicitly check in a separate query:
UPDATE your_table
SET score = GREATEST(score + ?, 0) -- This '?' is the adjustment to the score
WHERE user_id = ?
Now your application cannot UPDATE score to fall below zero, nor will it generate errors or warnings depending on the SQL mode.
Yes. You can create an int field and mark it as UNSIGNED.
From MySQL 5.0 Reference Manual:
INT[(M)] [UNSIGNED] [ZEROFILL]
A normal-size integer. The signed range is -2147483648 to 2147483647.
The unsigned range is 0 to 4294967295.
MySQL has an UNSIGNED qualifier for integer types.
Negative values will be clamped to zero, but will generate a warning:
mysql> create table test ( id int(5) unsigned not null );
Query OK, 0 rows affected (0.05 sec)
mysql> insert into test values (-1), (5), (10);
Query OK, 3 rows affected, 1 warning (0.01 sec)
Records: 3 Duplicates: 0 Warnings: 1
mysql> select * from test;
+----+
| id |
+----+
| 0 |
| 5 |
| 10 |
+----+
3 rows in set (0.01 sec)
If you are running in strict sql mode this would throw an error and an insert/update would fail.
I usally create a user-defined function for this sort of thing. (In this case a very trivial "if (expr1, expr2, expr3)" will do the trick