I just need the record with the longest string in the product_description
column.
A record could have nil
in the product_description
column
Which is why this won't work:
Product.where(parent_product_id: 22033).pluck(:product_description).max_by(&:length)
Then I try SQL and get:
ActiveRecord::UnknownAttributeReference (Query method called with non-attribute argument(s): "max(length(product_description))")
From this query:
Product.where(parent_product_id: 22033).pluck("max(length(product_description))")
This returns the same:
Product.where(parent_product_id: 22033).order("MAX(CHAR_LENGTH(product_description)) desc").limit(1)
But product_description
is definitely a column on the Products
table.. that's not the issue
CodePudding user response:
You can order by length and take first like this
Product
.where(parent_product_id: 22033)
.where.not(product_description: nil)
.order("LENGTH(product_description) DESC")
.first
LENGTH
is RDBMS function and depends on specific system and may differ therefore
CodePudding user response:
You can use the length
function of your RDBMS to calculate the length, then order by it.
Ties
There might be many products with the same description length. In order to have consistent results, you will need a tie breaker as otherwise the order within the products with same description length is not defined. You could ass an order by id
clause.
NULL
Be aware that
select length(null)
will return null
and not 0
.
null
might be sorted before actual values or after (depending on your RDBMS and its config).
If you always need a numeric value you can do
select length(coalesce(null, ''))
which will return 0
coalescereturns the first
non-nullargument and therefore ensures that we always pass at least an empty string to
length`.
You can also use the null last
option for the order clause.
You can also exclude records with a null
value for the description:
products = Product.where.not(product_description: nil)
to avoid dealing with null
values alltogether.
If the collumn is not nullable, then there is no problem either.
Now if you just use this:
products = Product.all # or whatever conditions you need
products
.order("length(coalesce(product_description, '')) desc")
.order(id: :asc)
.first
then Rails might complain (depends on the Version you are using) for security reasons with something like ActiveRecord::UnknownAttributeReference: Dangerous query method
which means you need to wrap the whole thing in Arel.sql
products = Product.all # or whatever conditions you need
products
.order(Arel.sql("length(coalesce(product_description, '')) desc"))
.order(id: :asc)
.first
Index
If you have many records, you might want to add an index on the column öength. See https://sqlfordevs.com/function-based-index for how to create a funciton based index.