How to CAST one attribute to INTEGER then group and sum - ruby-on-rails

I would like to do the following with a query/SQL in Rails:
Collect a batch of Orders, selecting :buyer_id and :weight_lb.
Convert every weight_lb from a string (like "12.3lb" to an integer 12).
Sum all the weight_lb and group by buyer_id.
The output should look like: {buyer_id_1: 65, buyer_id_2: 190}, etc., where each number is the sum of each buyer's order weights.
This is what I've tried:
Order.find_by_sql("SELECT \"orders\".\"id\", \"orders\".\"buyer_id\", CAST(\"orders\".\"weight_lb\" AS DECIMAL) FROM \"orders\" LIMIT 500 OFFSET 1000")
=> [
#<Order:0x0000000118054830 id: 15076494, buyer_id: 22918, weight_lb: "315.0">,
#<Order:0x0000000118054918 id: 15076495, buyer_id: 22918, weight_lb: "110.0">,
...]
Despite CAST() as DECIMAL, the weight is still output as a string.
When I try to CAST() as INTEGER, it fails entirely with PG::InvalidTextRepresentation: ERROR: invalid input syntax for type integer: "315.0" (ActiveRecord::StatementInvalid)
What I would ideally like to have is:
{
15076494: 425, # Sum of all weights for the ID 15076494
15076495: 0,
15076496: 95, ...
}
I'm just not sure how to get there efficiently using Postgres.

We can use a combination of REPLACE, CAST and SUM operations
Order
.select("buyer_id, SUM(CAST(REPLACE(weight_lb, 'lb', '') AS DECIMAL)) AS weight_lb")
.group("buyer_id")
.limit(500)
.offset(1000)
The generated SQL will be:
SELECT "orders"."buyer_id", SUM(CAST(REPLACE("orders"."weight_lb", 'lb', '') AS DECIMAL)) AS weight_lb
FROM "orders"
GROUP BY "orders"."buyer_id"
LIMIT 500
OFFSET 1000
Let me know if it helps. :)

Related

Rails ActiveRecord: Missing column in grouping query

Key.select('products.name as product, product_groups.name as product_group, AVG(keys.cost) as cost')
.group('products.id, product_groups.id')
.left_joins(:product,:product_group)
the result:
=> #<ActiveRecord::Relation [#<Key id: nil, cost: 0.6e1>, #<Key id: nil, cost: 0.4e1>]>
Expected return 3 field, but returnig value: 2 field.
I found the solution. The detail areas in the console did not appear as HASH.
In my understanding the grouping statement would only return Aggregated columns and columns that are used to group the data set. In your case you have not used the grouping columns in the select list but, some other fields. As a result you don't receive the other two columns.

Neo4j - ORDER BY syntax error

This cipher query is throwing the syntax error when I am sorting the result on relationship property using ORDER BY clause.
My query:
$MATCH(a{word:"review"})-[co:coocr]->(b) RETURN co LIMIT 20 ORDER BY co.val DESC
Syntax Error report:
Invalid input 'R': expected 'p/P' (line 4, column 2 (offset: 60))
"ORDER BY co.val DESC"
^
where the relationship co exists a property like "val:0.66" where value is a double valued number.
Please some one suggest where I am missing out.
Just reverse the order of the LIMIT and ORDER BY clauses:
MATCH (a{word:"review"})-[co:coocr]->(b)RETURN co ORDER BY co.val DESC LIMIT 20;

Whats the best way to get the nth to last record?

I saw here: How to get last N records with activerecord? that the best way to get the last 5 records is SomeModel.last(5).
Is the best way to get the 5th last record only SomeModel.last(5).first or is it something else?
Thanks in advance!
What you're looking for is a combination of LIMIT, OFFSET, and ensuring that the query is using ORDER BY as a constraint. This is described on the PostgreSQL - Queries: Limit and Offset documentation page.
An example is:
irb> SomeModel.count
=> 35
irb> SomeModel.order(:id).reverse_order.limit(1).offset(4)
# SomeModel Load (0.7ms) SELECT "some_models".* FROM "some_models" ORDER BY "some_models".id DESC LIMIT 1 OFFSET 4
=> #<ActiveRecord::Relation [#<SomeModel id: 31, name: "hello world", created_at: "2014-03-24 21:52:46", updated_at: "2014-03-24 21:52:46">]>
Which yields the same result (albeit as an AR::Relation) as:
irb> SomeModels.last(5).first
# SELECT "some_models".* FROM "some_models" ORDER BY "some_models"."id" DESC LIMIT 5
=> #<SomeModel id: 31, name: "hello world", created_at: "2014-03-24 21:52:46", updated_at: "2014-03-24 21:52:46">
The difference is that in the former query, PostgreSQL will treat the .reverse_order.limit.offset query in memory, and restrict the values appropriately before it returns the result. In the latter, PostgreSQL will return 5 results, and you restrict the values in Ruby by using #first.
You can use offset with a query in the reverse order. For example, the fifth last created record can be retrieved with:
SomeModel.order("created_at DESC").offset(4).first
The simplest way to get the 5th last record is with combination of descending sort, and fifth:
SomeModel.order("created_at DESC").fifth

Rails: Calling .limit(5) changes order of results

I have a search function that basically runs an ordered list of model records. The problem is whenever I called .search.limit(5), the results are in a different order from when I call .search
Here is some of my method
def self.search(server_name, pvp_type)
if server_name.nil?
result = Rom::Leaderboard.order('pvp_vs desc, win_percent desc').limit(200)
end
end
When I call
Rom::Leaderboard.search(nil, 2).pluck(:actor_name)
SQL Translation:
SELECT "rom_leaderboards"."actor_name" FROM "rom_leaderboards" WHERE "rom_leaderboards"."pvp_type" = 2 ORDER BY pvp_vs desc, win_percent desc LIMIT 200
I get the following results:
[Zarglon, Lirav, adf, sdfa, Nonad, ...]
Zarglon and Lirav have the same pvp_vs & win_percent attribute values; afd, sdfa, and Nonad also have the same relationship.
Now when I call
Rom::Leaderboard.search(nil, 2).limit(5).pluck(:actor_name)
SQL Translation:
SELECT "rom_leaderboards"."actor_name" FROM "rom_leaderboards" WHERE "rom_leaderboards"."pvp_type" = 2 ORDER BY pvp_vs desc, win_percent desc LIMIT 5
I get the following results:
[Lirav, Zarglon, sfda, Nonad, adf]
These queries are both correct (since search returns a ordered list based on pvp_vs & win_percent and both list are ordered correctly). But I want them to be the same. For some reason limit changes this order. Is there anyway to keep them the same?
Suppose you try to order this array-of-arrays by the first element:
[
[ 1, 1 ],
[ 1, 2 ],
[ 1, 3 ]
]
Both of these (and several others) are valid results because you have duplicate sort keys:
[ [1,1], [1,2], [1,3] ]
[ [1,3], [1,1], [1,2] ]
You're encountering the same problem inside the database. You say that:
Zarglon and Lirav have the same pvp_vs & win_percent attribute values; afd, sdfa, and Nonad also have the same relationship.
So those five values can appear in any order and still satisfy your specified ORDER BY condition. They don't even have to come out of the database in the same order in two executions of the same query.
If you want consistent ordering, you need to ensure that each row in your result set has a unique sort key so that ties will be broken consistently. This is ActiveRecord so you'll have a unique id available so you can use that to break your ordering ties:
result = Rom::Leaderboard.order('pvp_vs desc, win_percent desc, id').limit(200)
# --------------------------------------------------------------^^
That will give you a well defined and unique ordering.

Why the 'BINARY' in a SELECT statement?

In a Vote model, there is a validation:
validates_uniqueness_of :topic_id, :scope => [:user_id]
It is translated to the following SQL in development log:
SELECT 1 AS one FROM `votes` WHERE (`votes`.`topic_id` = BINARY 2 AND `votes`.`user_id` = 1) LIMIT 1
Where there is a BINARY in front of 2 (topic_id)? And, what does it mean?
It is an efficient way of comparing byte to byte instead of character to character
example
Suppose if your have a database table called products record which has vin_number (some column name) with record with value of vin_number say 123456
Now If you ran the this
select * from products where vin= '123456'
and
select * from products where vin = '123456 '
Both will result the same result
Notice the space in the second select
But with binary the comparison
select * from products where vin= BINARY '123456'
or
select * from producst where vin = BINARY '123456 '
A byte by byte match is done as against character to character
so the first one would result in valid result
and
the second one would no result
Here the link that will further help you on this

Resources