I have Merchant Table which has the following fields
Merchant(id: integer, name: string, logo: string, description: text, categories: string, created_at: datetime, updated_at: datetime)
I want to run first_or_create query with name attribute. The problem is the name column contains single quotes, for example Brandy's Boy.
mname = "brandy's boy"
conds = "lower(name) = #{mname}"
Merchant.where(conds)
Merchant Load (1.5ms) SELECT "merchants".* FROM "merchants" WHERE "merchants"."deleted_at" IS NULL AND (lower(name) = 'brandy's boy ') LIMIT $1 [["LIMIT", 11]]
OutPut:
#<Merchant::ActiveRecord_Relation:0x000055d0f72a2ce0>
But this gives me the output when I run static query.
Merchant.where("lower(name) = ?", 'Brandy\'s Boy'.downcase)
Merchant Load (3.7ms) SELECT "merchants".* FROM "merchants" WHERE "merchants"."deleted_at" IS NULL AND (lower(name) = 'brandy''s boy') LIMIT $1 [["LIMIT", 11]]
#<ActiveRecord::Relation [#<Merchant id: 1413, name: "Brandy's Boy", logo: nil, description: "ABC", categories: [], created_at: "2020-07-17 07:32:29", updated_at: "2020-07-17 07:32:29">]>
But the variable mname is populated in a loop and it is dynamic. So in that case I need to escape the single quotes and get the desired result (existing Brandy's Boy Merchant Object)
Kindly help.
You don't need to interpolate to create the arguments for where, just bind it/them:
Merchant.where("lower(name) = ?", mname)
Active Record will take care of that and your query will most likely look like this:
SELECT "merchants".* FROM "merchants" WHERE (lower(name) = 'brandy''s boy')
Escaping is doing in the internals of the framework, from the docs:
If an array is passed, then the first element of the array is treated
as a template, and the remaining elements are inserted into the
template to generate the condition. Active Record takes care of
building the query to avoid injection attacks, and will convert from
the ruby type to the database type where needed. Elements are inserted
into the string in the order in which they appear.
User.where(["name = ? and email = ?", "Joe", "joe#example.com"])
# SELECT * FROM users WHERE name = 'Joe' AND email = 'joe#example.com';
Doing:
Merchant.where(["lower(name) = ?", mname])
Is pretty much the same as doing:
Merchant.where("lower(name) = ?", mname)
So your query is handled as stated above.
Related
This is my test query and result:
>Pack.first.pages
Pack Load (0.3ms) SELECT "packs".* FROM "packs" ORDER BY "packs"."id" ASC LIMIT 1
Page Load (0.1ms) SELECT "pages".* FROM "pages" WHERE "pages"."pack_id" = $1 [["pack_id", 1]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Page id: 53, created_at: "2015-02-27 21:59:12", updated_at: "2015-03-23 16:41:05", pack_id: 1, name: "test - pack12?", client_name: "", thumbnail: nil, printable_page: nil, preview: nil, vuforia_archive: "dinosaur.zip", unity_bundle: "girl.unity3d", vuforia_identifier: nil, vuforia_archive_updated_at: "2015-02-27 21:59:12", unity_bundle_updated_at: "2015-03-23 16:41:05">]>
The fields I am concerned with are nil, so why doesn't this work...
> Pack.first.pages.where('thumbnail=? OR printable_page=? OR preview=?',nil,nil,nil)
Pack Load (0.3ms) SELECT "packs".* FROM "packs" ORDER BY "packs"."id" ASC LIMIT 1
Page Load (0.1ms) SELECT "pages".* FROM "pages" WHERE "pages"."pack_id" = $1 AND (thumbnail=NULL OR printable_page=NULL OR preview=NULL) [["pack_id", 1]]
=> #<ActiveRecord::AssociationRelation []>
Depending on your version of SQL, you'll need to format the query like so:
Pack.first.pages.where("thumbnail is null or printable_page is null or preview is null")
field = null doesn't work.
Databases implement three-value logic, in which null is neither true nor false. Null cannot be stated to be equal to, not equal to, greater then, or less than any other value, including null, so comparisons with null will always be null, and therefore not true. There is a separate test for nullness: "thumbnail is null".
As a side note, remember that a predicate such as "name = 'jim'" in an RDBMS is a statement of truth which is tested against rows, which are included or not based on whether that statement is true.
As another side note, this means that "age in (1,null)" might be true is age equals 1, but will not be true is age has a value of null. Similarly, "age not in (1, null)" is false for age = 2 because "2 = null" is false.
Active record is generally pretty good at handling this as it can respond to a condition such as where(:age => my_age) by writing different predicates based on whether my_age is null or not. It will even handle my_age being an array of [1, 2, nil] correctly by writing a predicate such as "where age in (1,2) or age is null".
Long story short, use:
Pack.first.pages.where('thumbnail is null OR printable_page is null OR preview is null')
I am trying to get a "Select As" query statement to work, but keep getting an error and am not sure why it is not working. Per the API docs, the format is correct.
User.select("firstname as fname")
Results in:
User Load (1.7ms) SELECT firstname as fname FROM "users"
=> #<ActiveRecord::Relation [#<User id: nil>, #<User id: nil>]
However if i use:
User.select(:firstname)
I get:
User Load (2.8ms) SELECT "users"."firstname" FROM "users"
=> #<ActiveRecord::Relation [#<User id: nil, firstname: "John">, #<User id: nil, firstname: "Brian">,
So I can see from the query why it's not returning the results, but I don't understand why its creating the incorrect query. (The actual query I need to use the select as on is more complicated then this query, but I was trying to use the simpler query to try to figure out why it wasn't working properly.
The reason I need to use a select as query is because I have two separate objects from two very different tables that i need to join together and change one of the column names so I can sort by that column. I'm not sure if there is an easier way to change the name prior to combining the objects.
Thanks!
You can use alias_attribute :firstname, :fname in the model and then use User.select(:fname) in the controller as well.
I have a small problem with my ActiveRecord query.
I have a Product model, which looks like:
#<Product id: nil, name: nil, price: nil, order_id: nil, created_at: nil, updated_at: nil, restaurant_id: nil>
Now I want to select DISTINCT on all names, and get all Product's attributes back.
I tried:
#products = Product.where(restaurant_id: !nil).group("products.name").order("name")
but I got this error:
PG::GroupingError: ERROR: column "products.id" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: SELECT "products".* FROM "products" WHERE "products"."rest...
^
: SELECT "products".* FROM "products" WHERE "products"."restaurant_id" = 1 GROUP BY products.name
Ok, then I added product_id to my query:
#products = Product.where(restaurant_id: !nil).group("products.name, products.id").order("name")
but this query returns Products with duplicated names.
So I tried this too:
#products = Product.where(restaurant_id: !nil).select("DISTINCT(NAME)").order("name")
but in return I got only Product record with id and name only (it's obvious), so If this query returned correct set, I added attributes which I need later:
#products = Product.where(restaurant_id: !nil).select("DISTINCT(NAME), restaurant_id, price").order("name")
And it returns also duplicated names.
Do you have any solution or idea, how to fix this query for PostgreSQL?
I'm used to writting query like this (on MySQL) and it's correct:
#products = Product.where(restaurant_id: !nil).select("DISTINCT(NAME), restaurant_id, price").order("name")
Why does PostreSQL not accept that query?
You should write the query as :-
Product.where
.not(restaurant_id: nil)
.select("DISTINCT ON(name) name, restaurant_id, price, updated_at")
.order("updated_at, name")
As per the official documentation SELECT DISTINCT ON ( expression [, ...] )
keeps only the first row of each set of rows where the given expressions evaluate to equal. The DISTINCT ON expressions are interpreted using the same rules as for ORDER BY (see above). Note that the "first row" of each set is unpredictable unless ORDER BY is used to ensure that the desired row appears first. For example:
SELECT DISTINCT ON (location) location, time, report
FROM weather_reports
ORDER BY location, time DESC;
retrieves the most recent weather report for each location. But if we had not used ORDER BY to force descending order of time values for each location, we'd have gotten a report from an unpredictable time for each location.
If I submit
Role.select("roles.character, actors.lname AS actors_lname").joins(:actor)
It returns:
Role Load (0.0ms) SELECT roles.character, actors.lname AS actors_lname
FROM "roles" INNER JOIN "actors" ON "actors"."id" = "roles"."actor_id"
#<ActiveRecord::Relation [#<Role id: nil, character: "Ellis Boyd 'Red' Redding">,
#<Role id: nil, character: "Andy Dufresne">, #<Role id: nil, character: "Warden Norton">]>
Why doesn't the actors.lname column get displayed?
Use select.
Order.select("orders.id, customers.name").joins(:customers)
You can fetch the associated values if you alias them
orders = Order.select("orders.id, customers.name AS customer_name").joins(:customers)
# you must call the method implicitly, or use .attributes
orders.first.customer_name
Please note that the value of customer_name will not show up in the inspection of the record. Therefore the following code
orders.first
in IRB will not print out the attribute.
Can anyone please explain me, why my db queries return empty, when I have data in my table?
Event.all
returns
...
Event Load (0.3ms) SELECT "events".* FROM "events"
=> #<ActiveRecord::Relation [#<Event id: 1, created_at: "2013-08-01 12:27:36", updated_at: "2013-08-01 12:27:36">
...
etc,
While
Event.where(created_at: Date.today)
gives me
Event Load (0.3ms) SELECT "events".* FROM "events" WHERE "events"."created_at" = '2013-08-01'
=> #<ActiveRecord::Relation []>
Where is everything?
The field created_at is a DateTime, but you are comparing it to a Date (no time).
You need to Cast the field as a Date to get all Events created today:
Event.where('CAST(events.created_at as DATE) = ?', Date.today)
Attention: The syntax may change depending on your Data-base system (PostGreSQL / MySQL, etc).
Hope this helps!
Useful link:
http://sqlserverplanet.com/tsql/cast-date
If you look at your actual query -
SELECT "events".* FROM "events" WHERE "events"."created_at" = '2013-08-01'
You are looking for a record with created_at equals 2013-08-01, but in actuality, the record you are trying to search for - the created_at field equals 2013-08-01 12:27:36.
Change your statement to do a search for created_at that contains 2013-08-01
The problem is that the created_at: attribute (assuming that it was created in the migration timestamps) also stores the time. So it will never equal a simple date. You're best option is to parse the date and then compare it.