Postgres 9.5.4 Window in update trigger function (lag and lead) - ruby-on-rails

Been banging away and would like some help
After running a lot of mathematical functions on rails and finding spurious results I decided to climb down to do it in the DB to find it's all very do-able for me however as soon as I get to the second function I have the following errors:
PG::UndefinedFunction: ERROR: function updown(integer) does not exist
LINE 3: SELECT "updown" (lag(updown) over (order by id)) table_nam...
^ HINT: No function matches the given name and argument types.
Migration:
class CreateSmoothings < ActiveRecord::Migration
def change
create_table :smoothings do |t|
t.decimal :firstsmprice, :precision => 8, :scale => 6
t.decimal :finalsmprice, :precision => 8, :scale => 6
t.decimal :firstsmdelta, :precision => 8, :scale => 6
t.decimal :finalsmdelta, :precision => 8, :scale => 6
t.integer :updown, :null => false, :default => 0
t.integer :posture
t.integer :step
t.references :usdzar, index: true, foreign_key: true
t.references :smoothval, index: true, foreign_key: true
t.timestamps null: false
end
add_index :smoothings, :finalsmprice
end
end
I have an update controller that sets the data in smoothings from another table and this is when the trigger is fired. The first trigger works like a charm. This is the controller action and then the trigger and function.
# POST /smoothings works and does the insert of all data to the smoothings table
def create
# Do the 1st and 2nd SMOOTHINGS
# Get the row with the smoothval_id parameter to use these
sv = Smoothval.find(smoothing_params[:smoothval_id])
fval = sv.fval
sval = sv.sval
svid = sv.id
toremove = Smoothing.where(:smoothval_id => sv.id)
toremove.destroy_all
# For every entry in the table do:
collection = Usdzar.all
collection.each_with_index do |usdzar, i|
# Declare the previous variables required for id = 1 as
# the calculation cannot be done as it has no previous id
# I am sure this can be refactored
if usdzar.id == 1
prevprice = usdzar.price
prevfirstsmprice = usdzar.price
prevfinalsmprice = usdzar.price
else
prevprice = collection[i-1].price
prevfirstsmprice = Smoothing.last.firstsmprice
prevfinalsmprice = Smoothing.last.finalsmprice
end
# Do the smoothing calcs for the first smoothing using fval (first value)
firstsmprice = (((fval * usdzar.delta)/100) + prevprice)
# Find the difference of the current and previous of the first smoothing
firstsmdelta = firstsmprice - prevfirstsmprice
# Do the final smooting with the fval
finalsmprice = ((sval * firstsmdelta)/100) + prevfirstsmprice
# Find the differance of the current and previous of the second smoothing
finalsmdelta = finalsmprice - prevfinalsmprice
# Create the rows at end of iteration
Smoothing.create!(
usdzar_id: usdzar.id,
smoothval_id: sv.id,
firstsmprice: firstsmprice,
firstsmdelta: firstsmdelta,
finalsmprice: finalsmprice,
finalsmdelta: finalsmdelta
)
end
redirect_to smoothings_url, notice: 'Old smoothings removed and new smoothings successfully created.'
The trigger function and trigger that works :-)
CREATE TRIGGER a_smoothings_trigger
AFTER INSERT
ON smoothings
FOR EACH ROW
EXECUTE PROCEDURE make_updowns_trg();
CREATE FUNCTION make_updowns_trg()
RETURNS trigger
LANGUAGE 'plpgsql'
AS $BODY$
BEGIN
update smoothings
set updown = case when (finalsmdelta < 0) then 1
when (finalsmdelta > 0 ) then 0
else 2 end;
RETURN NULL;
END
$BODY$;
This (the above) runs well.
What I am trying to do next is extend the function by another trigger and function (knowing now that they run in alphabetical order
The trigger/function is meant to take the "updown" (being 0, 1 or 2: up, down or same is the enum in rails) and:
If updown and next updown are the same then update as 2 (in between)
If the updown is greater than next "updown" update as 1 (high)
If the updown is less then next "updown" update as 0 (low)
CREATE FUNCTION public.make_high_lows_trg()
RETURNS trigger
LANGUAGE 'plpgsql'
COST 100.0
VOLATILE NOT LEAKPROOF
AS $BODY$
BEGIN
WITH next_updown AS
(SELECT updown (lag(updown) over (order by id)) table_name FROM smoothings
ORDER BY id DESC)
update smoothings
set posture = case when updown < next_updown then 1
when updown > next_updown then 0
else 2 end;
RETURN NULL;
END
$BODY$;
CREATE TRIGGER b_smoothings_trigger
AFTER INSERT
ON public.smoothings
FOR EACH ROW
EXECUTE PROCEDURE public.make_high_lows_trg();
Model for the controller:
class Smoothing < ActiveRecord::Base
enum updown: [:up, :down, :same]
enum posture: [:high, :low, :inbetween]
belongs_to :usdzar
belongs_to :smoothval
end
I had this all running in the controller as my model method coding really sucks and got it all running however after testing found the data to be very unreliable. Thanks in advance for the help. I am really astounded by the number of functions in Postgres and when I came across the window function thought it would be appropriate but can get over the hurdle. I know that (from the psql docs) you cannot put the lag/lead windowing into the update part of the function but have an issue obviously with how to do the sql block correctly
This query runs after running the first but how do I update the table on this:
with myUpdowns as (
Select id, updown, Lag(updown) over (order by id desc) next_updown from smoothings
)
select *
from myUpdowns
where updown < next_updown;
AND now I got one high to update correctly: I am now trying to do the full spread of high. low and inbetween (0, 1 and 2). If you have any suggestions I would love to hear them. The updated trigger that adds the high to the table looks as follows:
CREATE FUNCTION make_high_lows_trg()
RETURNS trigger
LANGUAGE 'plpgsql'
AS $BODY$
BEGIN
UPDATE smoothings
SET posture = 0
FROM (
Select id, updown, Lag(updown) over (order by id desc) next_updown from smoothings
) as highs
WHERE highs.updown < highs.next_updown
AND smoothings.id = highs.id;
RETURN NULL;
END
$BODY$;
The Trigger:
CREATE TRIGGER b_smoothings_trigger
AFTER INSERT
ON smoothings
FOR EACH ROW
EXECUTE PROCEDURE make_high_lows_trg();
Hmm, now how to do low and inbetween, actually just the high and low are good

Related

Create index for boolean columns

I have table that has 5 boolean columns.
on_stock | paid | received_payment | on_the_way | received
How to create an index for this table? Should I do it? I want to optimize a query like this:
SELECT "order".* FROM "order" INNER JOIN "seller_users" ON "order"."seller_foreign_id" = "seller_users"."google_id"
WHERE(((("order"."on_stock" <> true AND "order"."on_the_way" <> true ) AND "order"."paid" <> true ) AND "order"."received_payment" <> true ) AND "order"."received" <> true ) AND ("seller_users"."google_id" = 'lala#gmail.com' )
ORDER BY "order"."updated_at" DESC ;
When I try add this index - nothing happens. This index is not used.
add_index :order, [:on_stock, :on_the_way, :paid, :received_payment, :received], :name => "state_index"
If I add separate index for each columns - nothing happens too.
EXPLAIN ANALYZE output:
http://explain.depesz.com/s/FS2
Your table has a total of 8 rows, an index is not needed in this case. It is way faster to test each of this 8 rows against the where clause than to use an index here.

decimal value not getting updated from the rails app but gets updated when changed from console

Okay, so I am not able to update decimal value through the rails app (model class) but if changed from rails console it works perfectly fine.I am not able to save the updated record in to database
this is my function def below
def self.currentprice_cal(id)
totalstock = #stockname.stocksinmarket+#stockname.stocksinexchange
#stockname.currentprice = #Buy_id.price.to_f*#Buy_id.numofstock.to_f
#stockname.save
##stockname.update(currentprice: #stockname.currentprice.to_f)
#update_currentprice_files = Stock.update_current_price(id,#stockname.currentprice)
end
this my model class
class CreateStocks < ActiveRecord::Migration
def change
create_table :stocks do |t|
t.string :stockname
t.decimal :currentprice, precision: 4, scale: 2
t.decimal :dayhigh, precision: 4, scale: 2
t.decimal :daylow, precision: 4, scale: 2
t.decimal :alltimehigh, precision: 4, scale: 2
t.decimal :alltimelow, precision: 4, scale: 2
t.integer :stocksinexchange
t.integer :stocksinmarket
t.timestamps
end
end
end
in rails console it works fine
irb(main):015:0> u = Stock.first
Stock Load (0.5ms) SELECT "stocks".* FROM "stocks" ORDER BY "stocks"."id" ASC LIMIT 1
=> #<Stock id: 30677878, stockname: "Intel", currentprice: # <BigDecimal:5fbdbf0,'0.4552E2',18(45)>, dayhigh: #<BigDecimal:5fbd790,'0.552E2',18(45)>, daylow: #<BigDecimal:5fbd3d0,'0.2201E2',18(45)>, alltimehigh: #<BigDecimal:5fbd100,'0.457E2',18(45)>, alltimelow: #<BigDecimal:5fbca70,'0.2209E2',18(45)>, stocksinexchange: 47, stocksinmarket: 3, created_at: "2014-12-18 06:50:08", updated_at: "2014-12-19 06:04:18">
irb(main):016:0> u.currentprice
=> #<BigDecimal:5fbdbf0,'0.4552E2',18(45)>
irb(main):017:0> u.currentprice = 45.34
=> 45.34
irb(main):018:0> u.save
(0.2ms) begin transaction
SQL (0.5ms) UPDATE "stocks" SET "currentprice" = ?, "updated_at" = ? WHERE "stocks"."id" = 30677878 [["currentprice", 45.34], ["updated_at", "2014-12-19 07:18:34.214567"]]
(148.2ms) commit transaction
=> true
I dunno if I am doing something wrong here ,I am not able to figure it out
i am calling current price_cal from here
#user_buying = User.find(#Buy_id.user_id)
#user_buying.cash = #user_buying.cash - #Buy_id.price*#Buy_id.numofstock.to_f
logger.info #Buy_id.user_id
#user_buying.save
##user_selling = User.select('cash').where(:id => #Sell_id.user_id).first
#user_selling = User.find(#Sell_id.user_id)
#user_selling.cash = #user_selling.cash + #Sell_id.priceexpected*#Buy_id.numofstock.to_f
#user_selling.save
#stockused = StockUsed.create(:user_id => #Buy_id.user_id, :stock_id => #Buy_id.stock_id,:numofstock => #Buy_id.numofstock)
#stockused = StockUsed.create(:user_id => #Sell_id.user_id, :stock_id => #Sell_id.stock_id,:numofstock => -#Buy_id.numofstock)
#stockname = Stock.select('stockname,stocksinmarket,stocksinexchange,currentprice').where('id'=>id).first
User.currentprice_cal(id)
#notification = Notification.create(:user_id =>#Buy_id.user_id, :notification => "You bought #{#Buy_id.numofstock} stocks of #{#stockname.stockname} at the rate of $#{#Buy_id.price} per share", :seen => 1, :notice_type => 1)
#notification = Notification.create(:user_id =>#Sell_id.user_id, :notification => "You sold #{#Buy_id.numofstock} stocks of #{#stockname.stockname} at the rate of $#{#Sell_id.priceexpected} per share", :seen => 1, :notice_type => 1)
Unless absolutely required, don't use instance variables in the class method that you've defined. There's a good chance that at least one of those instance variables isn't properly set when you call the method. Subsequently, your database row isn't updated.
Either pass the object and new value to the method as arguments, or pass the ID and values as arguments and fetch the object from the database within the method.

Rename each entry (or row) of a column in a rails migration

I want to remap values in my status column based on a hash.
For example if the status is a, change it to b.
How do I do this in Rails and optimize it so that it executes one db call that changes all values in the column?
status_changes = { "a" => "b", "c" => "d" }
status_changes.each do |old, new|
# first arg is UPDATE, second arg is WHERE
SomeModel.update_all("status = #{new}", "status = #{old}")
end
If the hash is small compared to the table, you can do a SQL UPDATE ... WHERE ... for each entry in the hash, wrapped into a transaction.
I did this instead, using heredocs for better readability:
status_mapping = {"Open" => 1345, "Closed" => 1346, "Pending" => 1347}
query = ActiveRecord::Base.connection()
query.execute <<-SQL.strip_heredoc
UPDATE table_1
SET status = CASE
WHEN status = #{status_mapping["Open"]} THEN 0
WHEN status = #{status_mapping["Closed"]} THEN 1
WHEN status = #{status_mapping["Pending"]} THEN 2
ELSE NULL
END;
SQL

Rails: Postgresql where with multiple conditions with join (polymorphic)

Hi guys here is my code:
class Tailor < ActiveRecord::Base
has_many :tailor_items
has_many :order_items
[:collars, :sexes, :sleeves].each do |attribute|
has_many attribute, through: :tailor_items, source: :item, source_type: attribute.to_s.classify
end
end
class TailorItem < ActiveRecord::Base
belongs_to :tailor
belongs_to :item, polymorphic: true
end
class Collar < ActiveRecord::Base
end
What I need to do is this:
For a given shirt I need to select a tailor. A shirt can have a collar, male/female or a certain type of sleeve. Some tailors can make all collars but only a few sleeves, others can make only male stuff, etc.
The priority doesnt matter for this example. The idea is that I end up with 1 tailor.
I tried this:
tailors = Tailor.joins(:tailor_items).where("(item_id = ? and item_type = ?)",1,"Collar")
if tailors.count > 1
tailors.where("(item_id = ? and item_type = ?)",2,"Sleeve")
if tailors.count > 1
# and so forth.
end
end
But I never get a row back.
If I say:
Tailor.find(1).tailor_items
I get two results (sudo code for simplicity)
<id: 1, item_type: "Collar"><id:2, item_type:"Sleeve">
and for second tailor:
Tailor.find(2).tailor_items
I get two results (sudo code for simplicity)
<id: 1, item_type: "Collar"><id:3, item_type:"Sleeve">
but when I try to chain them in the query its no worky...
Not even if I put it all in one where:
Tailor.where("(item_id = 1 and item_type = 'Collar') and (item_id = 2 and item_type = 'Sleeve')")
I still get 0 results.
Tailor.where("item_id = 1 and item_type = 'Collar'") returns: Tailor #1
Tailor.where("item_id = 2 and item_type = 'Sleeve'") returns: Tailor #1
but together they return nothing.
Tailor Load (0.0ms) SELECT "tailors".* FROM "tailors" INNER
JOIN "tailor_items" ON "tailor_items"."tailor_id" = "tailors"."id" WHERE ((tailo
r_items.item_id = 1 and tailor_items.item_type = 'Collar') and (tailor_items.ite
m_id = 2 and tailor_items.item_type = 'Sleeve'))
I am confused..
Thanks for your help.
I run:
Win XP
Postgresql
Rails 3.2.2
PS: The only thing missing to make this complete after a polymorphic join is a bit of XML. :P Otherwise its just not enterprise-y enough..
EDIT:
Implementing Rob di Marcos scope, I get this SQL:
SELECT "tailors".* FROM "tailors" WHERE
(EXISTS(SELECT * FROM tailor_items WHERE tailor_items.item_id = 1 and tailor_items.item_type = 'Collar'))
AND (exists(select * from tailor_items where tailor_items.item_id = 2 and tailor_items.item_type = 'Sleeve'))
This returns
2 tailors instead of only tailor 1 who can do both (while tailor 2 cant do sleeve #2)
The problem is that the where needs to match on two rows. I generally will use sub-queries to test for this. So something like
Tailor.where("exists (select 'x' from tailor_items where
tailor_id = tailors.id and tailor_items.item_id = ? and
tailor_items.item_type=?)", 1, 'Collar').
where("exists (select 'x' from tailor_items where
tailor_id = tailors.id and tailor_items.item_id = ? and
tailor_items.item_type=?)", 2, 'Sleeve')
In this example, I have one sub-query for each tailor item I am looking for. I could easily make this a scope on Tailor like:
class Tailor
# ....
scope :with_item, lambda{ |item_id, item_type |
where("exists (select 'x' from tailor_items where
tailor_id = tailors.id and tailor_items.item_id = ? and
tailor_items.item_type=?)", item_id, item_type)
}
and then be able to chain my Tailor request
Tailor.with_item(1, 'Collar').with_item(2, 'Sleeve')

Searching and comparing ActiveRecord attributes to find largest value

I have a model that would look something like:
my_diet = Diet.new
my_diet.food_type_1 = "beef"
my_diet.food_type_1_percentage = 40
my_diet.food_type_2 = "carrots"
my_diet.food_type_2_percentage = 50
my_diet.food_type_3 = "beans"
my_diet.food_type_3_percentage = 5
my_diet.food_type_4 = "chicken"
my_diet.food_type_4_percentage = 5
I need to find which food_type has the highest percentage. So far I've tried creating a hash out of the attibutes and percentages then sorting the hash (see below) but it feels like there must be a cleaner way to do it.
food_type_percentages = { :food_type_1 => my_diet.foo_type_percentage_1_percentage.nil? ? 0 : my_dient.food_type_1_percentage,
:food_type_2 => my_diet.foo_type_percentage_2_percentage.nil? ? 0 : my_dient.food_type_2_percentage,
:food_type_3 => my_diet.foo_type_percentage_3_percentage.nil? ? 0 : my_dient.food_type_3_percentage,
:food_type_4 => my_diet.foo_type_percentage_4_percentage.nil? ? 0 : my_dient.food_type_4_percentage
}
food_type_percentages.sort {|a,b| a[1]<=>b[1]}.last
Any ideas?
Thanks!
To find the max value amongst columns of an existent row in the DB, do the following:
d = Diet.first(:select => "*, GREATEST(
food_type_1_percentage,
food_type_2_percentage,
food_type_3_percentage,
food_type_4_percentage) AS top_food_type_percentage,
CASE GREATEST(
food_type_1_percentage,
food_type_2_percentage,
food_type_3_percentage,
food_type_4_percentage)
WHEN food_type_1_percentage THEN food_type_1
WHEN food_type_2_percentage THEN food_type_2
WHEN food_type_3_percentage THEN food_type_3
WHEN food_type_4_percentage THEN food_type_4
END AS top_food_type")
d.top_food_type # carrots
d.top_food_type_percentage # 50
If you are trying to find the top food type in the current model instance then
class Diet < ActiveRecord::Base
def top_food_type
send(top_food_type_col)
end
def top_food_type_percentage
send("#{top_food_type_col}_percentage")
end
FOOD_TYPE_COL = %w(food_type_1 food_type_2 food_type_3 food_type_4)
def top_food_type_col
#top_food_type_col ||= FOOD_TYPE_COL.sort do |a, b|
send("#{a}_percentage") <=> send("#{b}_percentage")
end.last
end
end
Now you can do the following:
d = Diet.new
....
....
....
d.top_food_type # carrots
d.top_food_type_percentage # 50
I assume food_percentage is the column
if you just want to find out ref this
Diet.maximum('food_percentage') # gives 50
OR you want complete record use this
Diet.find(:first, :order=> 'food_percentage DESC', :limit=>1)

Resources