I would like to join two query result.
First one is
Gig.where(id:gigsInRadioIDS)
When I convert result to json I get this result
[{"id":1,"address":"test1"},{"id":2,"address":"test2"}
Second is.
Slot.where(status:"available").where(Sequel.lit('("from" > ?) AND ("to" < ?)', fromdate, todate))
when I convert result to json I get this result
[{"id":15,"status":"available","gig_id":1}]
What I want is to inner join to result
so I hope to get this result(I abbreviated some non related snippet)
[{"id":15,"status":"available","gig_id":1,"id":1,"address":"test1"}]
What I have tried is
Gig.where(id:gigsInRadioIDS).as(:tb_gig).join(Slot.where(status:"available").where(Sequel.lit('("from" > ?) AND ("to" < ?)', fromdate, todate)), gig_id: :id)
And I got this error
Sequel::DatabaseError - PG::AmbiguousColumn: ERROR: column reference "id" is ambiguous
LINE 1: ...) AS "t1" ON ("t1"."gig_id" = "gigs"."id") WHERE ("id" IN (2...
Thanks for reading this complex code.
Any news will be big help. Thanks.
There are a bunch of ways to approach it. It looks like you are querying Slots with some Gigs data mixed in, so you may want to start with your Slot model. The regular join syntax is a little easier to understand and makes it easier to select columns from both tables (i.e. instead of WHERE fk IN (..)). As a bonus, you can mix your gig_id filter into the JOIN clause. Finally, you don't need to use Sequel.lit to do greater than / less than in your WHERE clause, and you should be explicit about the columns you want to select.
Here's how I would write it:
Slot.join(:gigs, [[:id, :gig_id], [:id, gigsInRadioIDS]])
.where(status: 'available') { (from() > fromdate) & (to() < todate) }
.select(Sequel[:slots][:id].as(:slot_id), :status, :gig_id, :address)
Getting .all of the records and outputting JSON should yield something like:
[{"slot_id":15,"status":"available","gig_id":1,"address":"test1"}]
Note the Sequel[:table_name][:column_name] syntax. This is the new style of fully-qualifying identifiers in Sequel 4.49+. (In past versions of Sequel, it would have been written like :table_name__column_name, but that syntax is now deprecated.)
Your where(id:gigsInRadioIDS) translates into WHERE ("id" IN (...)), and since both gigs and slots have an "id" column, the database doesn't know which id you want.
You need to explicitly specify the table with where{{gigs[:id] => gigsInRadioIDS}}
Querying with Sequel
Related
I am trying to convert a "raw" PostGIS SQL query into a Rails ActiveRecord query. My goal is to convert two sequential ActiveRecord queries (each taking ~1ms) into a single ActiveRecord query taking (~1ms). Using the SQL below with ActiveRecord::Base.connection.execute I was able to validate the reduction in time.
Thus, my direct request is to help me to convert this query into an ActiveRecord query (and the best way to execute it).
SELECT COUNT(*)
FROM "users"
INNER JOIN (
SELECT "centroid"
FROM "zip_caches"
WHERE "zip_caches"."postalcode" = '<postalcode>'
) AS "sub" ON ST_Intersects("users"."vendor_coverage", "sub"."centroid")
WHERE "users"."active" = 1;
NOTE that the value <postalcode> is the only variable data in this query. Obviously, there are two models here User and ZipCache. User has no direct relation to ZipCache.
The current two step ActiveRecord query looks like this.
zip = ZipCache.select(:centroid).where(postalcode: '<postalcode>').limit(1).first
User.where{st_intersects(vendor_coverage, zip.centroid)}.count
Disclamer: I've never used PostGIS
First in your final request, it seems like you've missed the WHERE "users"."active" = 1; part.
Here is what I'd do:
First add a active scope on user (for reusability)
scope :active, -> { User.where(active: 1) }
Then for the actual query, You can have the sub query without executing it and use it in a joins on the User model, such as:
subquery = ZipCache.select(:centroid).where(postalcode: '<postalcode>')
User.active
.joins("INNER JOIN (#{subquery.to_sql}) sub ON ST_Intersects(users.vendor_coverage, sub.centroid)")
.count
This allow minimal raw SQL, while keeping only one query.
In any case, check the actual sql request in your console/log by setting the logger level to debug.
The amazing tool scuttle.io is perfect for converting these sorts of queries:
User.select(Arel.star.count).where(User.arel_table[:active].eq(1)).joins(
User.arel_table.join(ZipCach.arel_table).on(
Arel::Nodes::NamedFunction.new(
'ST_Intersects', [
User.arel_table[:vendor_coverage], Sub.arel_table[:centroid]
]
)
).join_sources
)
In SQL I would do a "select count(*), min(price) from products". What is the best way to do this with one query in active records?
Right now I have to do two queries, which doesn't feel right.
result_count = Product.where(filter_string).count
result_min = Product.where(filter_string).minimum(:price)
You can add the string in a select method like this:
result_stats = Product.select("count(*) as product_count, min(price) as price_min").where(filter_string)[0]
result_stats["product_count"] # => 123
result_stats["price_min"] # => 12.35
The limitation here is that this will initialize Product objects with only those fields accessible (in this case only 1 object since there's no group by clause). In this case, it's not really an issue, but something worth knowing if you get an error when you try to access relations.
http://guides.rubyonrails.org/active_record_querying.html#selecting-specific-fields
Ok, I feel really stupid for asking this, but it's driving me nuts and I can't figure it out. The docs say I should be able to use select AS in a Rails/ActiveRecord query. So:
d = Dvd.where(id: 1).select("title AS my_title")
Is a valid query and if I do a to_sql on it, it produces the expected SQL:
SELECT title AS my_title FROM `dvd` WHERE `dvd`.`id` = 1
However, d.my_title will give an error:
NoMethodError: undefined method `my_title' for #<ActiveRecord::Relation
I need to be able to use AS since the columns I want to retrieve from different joins have the same name so I can't access them the "regular" way and have to resort to using AS.
I also don't want to resort to using find_by_sql for future compatibility and a possible switch form Mysql to PostGresql.
Just to clarify, what I'm really trying to do is write this SQL in a Railsy way:
SELECT tracks.name AS track_name, artists.name AS artist_name, composers.name AS composer_name, duration
FROM `tracks_cds`
INNER JOIN `tracks` ON `tracks`.`id` = `tracks_cds`.`track_id`
INNER JOIN `artists` ON `artists`.`id` = `tracks_cds`.`artist_id`
INNER JOIN `composers` ON `composers`.`id` = `tracks_cds`.`composer_id`
WHERE cd_id = cd.id
The top example was just a simplification of the fact that SELECT AS will not give you an easy way to refer to custom fields which I find hard to believe.
ActiveRecord automatically creates getter and setter methods for attributes based on the column names in the database, so there will be none defined for my_title.
Regarding the same common names, why not just do this:
d = Dvd.where(id: 1).select("dvds.title")
You can write your sql query and then just pass into ActiveRecord's execute method
query = "SELECT title AS my_title FROM `dvd` WHERE `dvd`.`id` = 1"
result = ActiveRecord::Base.connection.execute(query)
I need some help constructing a LINQ expression. I tend to use Lambda syntax.
I have 2 tables
OrderItem >- LibraryItem
OrderItem has a number of columns:
Id
FkLibraryItemId
Text
FkOrderId
LibraryItem has a number of Columns:
Id
Text
Type
Usually when selecting an "OrderItem", one picks a "Library Item". The "Id" and "Text" value are placed into the item record.
Sometimes a user may add a one off "OrderItem" which does not need storing in the "LibraryItem" table. It is simply stored in "OrderItem", but without a "FkLibraryItemId". So I have records in "OrderItem" that do not exist in "LibraryItem".
I need the LINQ to pull out all the relevant "LibraryItem" records of "Type=X" in addition to the "OrderItem" records for the relevant Order Id.
Many thanks in advance.
UPDATE:
I think I am talking about something like:
LibraryItem.Select(new{Id,Text}).Union(Order.Select(new{Id, Text})
context.OrderItems.Include("LibraryItems")
.Where(o => o.OrderId == orderId
&&(o.LibraryItem != null ? o.LibraryItem.Type == "X" : true))
Linq-to-SQL has a different syntax for Include, which basically eager-loads the reference objects (probably LoadWith, I don't remember at the moment).
Actually you can also do it with left inner join.
Why do I get the error:
Unable to create a constant value of type 'Closure type'. Only
primitive types (for instance Int32, String and Guid) are supported in
this context.
When I try to enumerate the following Linq query?
IEnumerable<string> searchList = GetSearchList();
using (HREntities entities = new HREntities())
{
var myList = from person in entities.vSearchPeople
where upperSearchList.All( (person.FirstName + person.LastName) .Contains).ToList();
}
Update:
If I try the following just to try to isolate the problem, I get the same error:
where upperSearchList.All(arg => arg == arg)
So it looks like the problem is with the All method, right? Any suggestions?
It looks like you're trying to do the equivalent of a "WHERE...IN" condition. Check out How to write 'WHERE IN' style queries using LINQ to Entities for an example of how to do that type of query with LINQ to Entities.
Also, I think the error message is particularly unhelpful in this case because .Contains is not followed by parentheses, which causes the compiler to recognize the whole predicate as a lambda expression.
I've spent the last 6 months battling this limitation with EF 3.5 and while I'm not the smartest person in the world, I'm pretty sure I have something useful to offer on this topic.
The SQL generated by growing a 50 mile high tree of "OR style" expressions will result in a poor query execution plan. I'm dealing with a few million rows and the impact is substantial.
There is a little hack I found to do a SQL 'in' that helps if you are just looking for a bunch of entities by id:
private IEnumerable<Entity1> getByIds(IEnumerable<int> ids)
{
string idList = string.Join(",", ids.ToList().ConvertAll<string>(id => id.ToString()).ToArray());
return dbContext.Entity1.Where("it.pkIDColumn IN {" + idList + "}");
}
where pkIDColumn is your primary key id column name of your Entity1 table.
BUT KEEP READING!
This is fine, but it requires that I already have the ids of what I need to find. Sometimes I just want my expressions to reach into other relations and what I do have is criteria for those connected relations.
If I had more time I would try to represent this visually, but I don't so just study this sentence a moment: Consider a schema with a Person, GovernmentId, and GovernmentIdType tables. Andrew Tappert (Person) has two id cards (GovernmentId), one from Oregon (GovernmentIdType) and one from Washington (GovernmentIdType).
Now generate an edmx from it.
Now imagine you want to find all the people having a certain ID value, say 1234567.
This can be accomplished with a single database hit with this:
dbContext context = new dbContext();
string idValue = "1234567";
Expression<Func<Person,bool>> expr =
person => person.GovernmentID.Any(gid => gid.gi_value.Contains(idValue));
IEnumerable<Person> people = context.Person.AsQueryable().Where(expr);
Do you see the subquery here? The generated sql will use 'joins' instead of sub-queries, but the effect is the same. These days SQL server optimizes subqueries into joins under the covers anyway, but anyway...
The key to this working is the .Any inside the expression.
I have found the cause of the error (I am using Framework 4.5). The problem is, that EF a complex type, that is passed in the "Contains"-parameter, can not translate into an SQL query. EF can use in a SQL query only simple types such as int, string...
this.GetAll().Where(p => !assignedFunctions.Contains(p))
GetAll provides a list of objects with a complex type (for example: "Function"). So therefore, I would try here to receive an instance of this complex type in my SQL query, which naturally can not work!
If I can extract from my list, parameters which are suited to my search, I can use:
var idList = assignedFunctions.Select(f => f.FunctionId);
this.GetAll().Where(p => !idList.Contains(p.FunktionId))
Now EF no longer has the complex type "Function" to work, but eg with a simple type (long). And that works fine!
I got this error message when my array object used in the .All function is null
After I initialized the array object, (upperSearchList in your case), the error is gone
The error message was misleading in this case
where upperSearchList.All(arg => person.someproperty.StartsWith(arg)))