I have an Rails application that defines a migration that contains a decimal with precision 8 and scale 2. The database I have set up is PostgreSQL 9.1 database.
class CreateMyModels < ActiveRecord::Migration
def change
create_table :my_models do |t|
t.decimal :multiplier, precison: 8, scale: 2
t.timestamps
end
end
end
When I run rake db:migrate, the migration happens successfully, but I noticed an error when I was trying to run a MyModel.find_or_create_by_multiplier. If I ran the following command twice, the object would get created twice:
MyModel.find_or_create_by_multiplier(multiplier: 0.07)
I am assuming this should create the object during the first call and then find the object during the second call. Unfortunately, this does not seem to be happening with the multiplier set to 0.07.
This DOES work as expected for every other number I have thrown at the above command. The following commands work as expected (creating the object during the first call and then finding the object during the second call).
MyModel.find_or_create_by_multiplier(multiplier: 1.0)
MyModel.find_or_create_by_multiplier(multiplier: 0.05)
MyModel.find_or_create_by_multiplier(multiplier: 0.071)
When I view the PostgreSQL database description of the MyModel table, I notice that the table does not have a restriction on the numeric column.
Column | Type | Modifiers
-------------+-----------------------------+-------------------------------------------------------
id | integer | not null default nextval('my_models_id_seq'::regclass)
multiplier | numeric |
created_at | timestamp without time zone | not null
updated_at | timestamp without time zone | not null
My db/schema.rb file also does not state the precision and scale:
ActiveRecord::Schema.define(:version => 20121206202800) do
...
create_table "my_models", :force => true do |t|
t.decimal "multiplier"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
...
So my first question is, why do I not see precision and scale pushed down to PostgreSQL when I migrate? Documentation states that it should be supported.
My second question is, why is 0.07 not correctly comparing using the MyModel.find_or_create_by_multiplier(multiplier: 0.07) command? (If I need to open another question for this, I will).
This is embarrassing...
I have precision misspelled.
Changing the migration to:
t.decimal :multiplier, precision: 8, scale: 2
fixed everything.
PostgreSQL 9.1 will let you declare a column in any of these ways.
column_name decimal
column_name numeric
column_name decimal(8, 2)
column_name numeric(8, 2)
If you look at that column using, say, pgAdminIII, it will show you exactly how it was created. If you (or Rails) created the column as numeric, it will say "numeric". If you (or Rails) created the column as decimal(8, 2), it will say "decimal(8, 2)".
So it looks to me like Rails is not passing precision and scale to PostgreSQL. Instead, it's simply telling PostgreSQL to create that column with type "numeric". Rails docs suggest it should not be doing that.
Example syntax in that link is different from yours.
td.column(:bill_gates_money, :decimal, :precision => 15, :scale => 2)
I was using :numeric at first. Although ActiveRecord changed it to :decimal for me, both :precision and :scale were ignored.
# 202001010000000_add_my_col_to_my_table.rb
add_column :my_table, :my_col :numeric, percision: 3, scale: 2, comment: 'Foobar'
# schema.rb
t.decimal "my_col", comment: 'Foobar'
Simply change to :decimal in migration file fixed it for me:
# 202001010000000_add_my_col_to_my_table.rb
add_column :my_table, :my_col :decimal, percision: 3, scale: 2, comment: 'Foobar'
# schema.rb
t.decimal "my_col", precision: 3, scale: 2, comment: 'Foobar'
Related
I just realized my column type needs to be decimal instead of integer
Can i make the change directly in my seeds.rb or there's something else i should do?
Also how do i do this if i intend to add a letter after the decimal?
Example: 2.0L, 2.5L, 5.7L, etc.
If you want to store a letter in your DB, that column type can't be a decimal or integer. A string field would be more appropriate for your case. I'll assume your main question is regarding decimals though.
If you do want to change a column type in the DB, you're supposed to do it using Migrations. From terminal, in your app directory, run: bin/rails g migration changeFieldNameFromIntegerToDecimal.
This will generate a migration file with a timestamp in the filename, in which there is a change method telling rails how you want to change the database. In that change method, put:
def change
change_column :table_name, :column_name, :decimal, precision: :8, scale: :2
end
the precision and scale options do the follwing (from the above link):
precision: Defines the precision for the decimal fields, representing the total number of digits in the number.
scale: Defines the scale for the decimal fields, representing the number of digits after the decimal point.
If you want to convert it to a string, then the migration file would contain:
def change
change_column :table_name, :column_name, :string
end
Finally run bin/rails db:migrate from the terminal in your app directory to execute the migration.
This will change the column type to decimal. To know about the scale and precision check out this Stackoverflow question https://stackoverflow.com/a/2377176/12297707 or the docs https://edgeguides.rubyonrails.org/active_record_migrations.html#column-modifiers
change_column :table_name, :column_name, :decimal, :precision => 8, :scale => 2, :default => 0
I am creating some table data in a Rails-React application.
I had creating this piece of data here in console:
2.3.3 :024 > Crop.create date: Date.today, cropname: 'Radishes', ismetric: false, bagspackaged: '20', unitweight: '0.5', totalweight: '10'
Today I realized that Rails did not accept the 0.5 decimal for unitweight and no matter how I try to update it in console, it does not save.
This is my schema.rb file:
ActiveRecord::Schema.define(version: 20171004224716) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "crops", force: :cascade do |t|
t.date "date"
t.string "cropname"
t.boolean "ismetric"
t.integer "bagspackaged"
t.integer "unitweight"
t.integer "totalweight"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
Two issues here
First you have given the data type integer to unitweight and totalweight, while you should have given it decimal or float in order to accept and store fractions. decimal data type with precision is better as it will give you more accurate result as stated below in the comments' section.
when you use decimal you can control it by The precision which is total number of digits in a number, whereas scale is number of digits following the decimal point.
here is an example
add_column :tickets, :price, :decimal, precision: 5, scale: 2
this will allow you to store decimal numbers like these 60.00, 80.99 and 100.00
Second you are passing string to integer, it is not a problem because rails will convert it to integer as long as it is a valid integer otherwise it will be 0. But generally it is not a good practice.
I would avoid rolling back your crops table, it would just be more work. It is up to you.
I would just do:
rails g migration ChangeUnitweightToFloat
Inside that file I would configure like so:
class ChangeUnitweightToFloat < ActiveRecord::Migration
def change
change_column :crops, :unitweight, :float
end
end
With these two steps, you should be go to go.
For future reference, please keep in mind that if you want to work with decimals, it will either be a t.decimal or t.float.
That isn't a decimal, it's a string. Don't put quotes around your numeric literals.
Crop.create(
date: Date.today,
cropname: 'Radishes',
ismetric: false,
bagspackaged: 20,
unitweight: 0.5,
totalweight: 10
)
You could use a decimal (or float) type field instead of an integer:
create_table "crops", force: :cascade do |t|
t.decimal "unitweight"
end
and then don't use quotes around the value:
2.3.3 :024 > Crop.create date: Date.today, cropname: 'Radishes', ismetric: false, bagspackaged: '20', unitweight: 0.5, totalweight: '10'
I created my migration with limit 8 in the chat_id column:
class CreateChat < ActiveRecord::Migration[5.0]
def change
create_table :bots do |t|
t.integer :user_chat_id, null: false, limit: 8, unique: true
...
t.timestamps
end
end
end
The migration is created perfectly and I can insert data into it.
But if I do:
class Chat < ApplicationRecord
self.primary_key = 'user_chat_id'
end
The following error occurs, 5187762395178250 is out of range for ActiveModel :: Type :: Integer with limit 4.
I researched and looked in the documentation, but I did not find anything about it.
Looks like you need to suppress the creation of the primary key, which by default is the id. So, rollback the migration and modify the migration file to include this line first:
create_table :bots, id: false do |t|
t.integer :user_chat_id, null: false, limit: 8, unique: true
...
t.timestamps
end ...
The options you applied to user_chat_id will make it the primary key.
We also deal with this error, here is our case:
Change DB type from int to bigint (MySQL table, column id)
When id column exceeded maximum int value (2.1 billion - unsigned integer), our app returned this error in some API
Restart puma server resolved it.
Just this simple command, it took us around 4 hours. :((
If you are using Unicorn or Passenger 5 or any others for your web server, try to restart it, I think it can resolve your issue.
Hope this helpful, thank you.
I’m using Rails 4.2.3 with a PostGre database. I want a column in my database to store a number of milliseconds — note, NOT a timestamp, but rather a duration in milliseconds. So I created my column like so
time_in_ms | bigint
However, when I go to store a value in Rails, I get the below error
ActiveRecord::StatementInvalid (PG::NumericValueOutOfRange: ERROR: value "3000002000" is out of range for type integer
: INSERT INTO "my_object_times" ("time_in_ms", "my_object_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"):
app/controllers/my_objects_controller.rb:31:in `update'
It would seem the number, “3000002000” is smaller than the maximum value for the column (which I’m reading is “9223372036854775807”), so I’m wondering what else is going wrong and how I can fix it.
Edit: To provide additional information, in my db/schema.rb file, the column in question is described thusly ...
create_table "my_object_times", force: :cascade do |t|
...
t.integer "time_in_ms", limit: 8
Edit 2: Here is the output of create table in PSQL
CREATE TABLE my_object_times (
id integer NOT NULL,
first_name character varying,
last_name character varying,
time_in_ms bigint,
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL,
name character varying,
age integer,
city character varying,
state_id integer,
country_id integer,
overall_rank integer,
age_group_rank integer,
gender_rank integer
);
I have had it happen to me before where when I initially try to create a bigint field in the db, for some reason the Model thinks it is an integer instead, even when the schema and migration file specify it as a bigint.
For example: I had this migration file
class CreateSecureUserTokens < ActiveRecord::Migration
def change
create_table :secure_user_tokens do |t|
t.integer :sso_id, null: false, length: 8
t.string :token, null: false
t.timestamps null: false
end
end
end
Note, it has the included length: 8 requirement to make an integer a bigint. However, after I ran the migration, I was having the same issue as you. Eventually I just created another migration to try and fix the issue, and it worked. Here's the migration I used to fix the issue:
class ModifySecureTokensForLargerSsoIdSizes < ActiveRecord::Migration
def change
change_column :secure_user_tokens, :sso_id, :integer, limit: 8
end
end
So if we changed that to fit your needs, it would be:
class ObjectTimesBigInt < ActiveRecord::Migration
def change
change_column :my_object_times, :time_in_ms, :integer, limit: 8
end
end
Hope that helps!
-Charlie
I guess, the table my_object_times might not be created from the schema.rb file or it might be overwritten in other migration file. Because in the migration file integer column with limit 8 is itself a bigint. So you should cross-check the table definition from the PG-admin. If the column is not bigInt then run the following migration
class ChangeTimeInMsToBigint < ActiveRecord::Migration
def change
execute <<-SQL
ALTER TABLE my_object_times
ALTER COLUMN time_in_ms TYPE bigint USING time_in_ms::bigint
SQL
end
end
edit: I just re-read this and my original answer actually does not make sense in your case. I do believe you need to look outside that column for an answer, and confirm every bit of what you think you know about the state of it, manually. Any addition of detail would help us find a correct answer.
Set breakpoints to step through the request and see if you can spot the integer
create_table "my_object_times", force: :cascade do |t|
...
t.integer "time_in_ms", limit: 8
t.integer
- this looks like your culprit to me.
...
Well, I tried, my last thought is that it has to be related to some kind of Rails request middleware, but I'm ignorant of what the specifics might be. Something in the request path thinks that column is an integer. I didn't understand how Rails migration datatypes worked until now, so I learned something. (And I went fishing all day, so I'll count this day a win.) Good luck!
For anyone on Rails 5/6 using the paper_trail gem, check what the polymorphic foreign_key id field item_id is set as in versions. I had it set as an integer, and got this bug.
Changing versions.item_id to a bigint fixed the error.
bigint is 64-bit, while Rails is 32-bit.
3000002000 is greater than 2^32. That's why converting it into a 32-bit integer fails with NumericValueOutOfRange.
I have an existing postgresql database that I want to use in a new rails app, so I first want to dump the existing schema into schema.rb using rake db:schema:dump. However, when I do this, the schema.rb has a strange precision value for the numeric columns.
create_table "order", :id => false, :force => true do |t|
....
t.decimal "Quantity", :precision => 131089, :scale => 0
....
In my PostgreSQL db, the numeric type column does not have an explicit precision or scale set.
Is there a reason why precision is showing such a huge value?
I've also tried changing and removing the precision modifier in schema.rb, but everytime I do a migration, it regenerates the schema.rb file with these huge values. I've looked at the ActiveRecord table definition, but that wasn't very helpful.
I suspect that this is being picked as the maximum precision of a numeric value in PostgreSQL. See http://www.postgresql.org/docs/9.2/static/datatype-numeric.html.