I have a more complex query (does not work)
query.select_all(:table_1).select_more(:col_1)
.left_outer_join(:table_2, table_1_id: :id, col_1: 1,
Seqel.lit('col_2 > ?', 1.week.ago)
I need to compare col_2 like this:
"col_2 > '2018-01-01 10:10:10'"
it is easy to write whole query as custom sql, but I would like to chain methods and so far I couldn't succeed in adding the above.
expected result ~:
"SELECT `table_1`.*, `table_2`.`col_1` FROM `table_1`
LEFT OUTER JOIN `table_2`
ON (
`table_2`.`table_1_id` = `table_1`.`id`
AND
`table_2`.`col_1` = 1
AND
`table_2`.`col_2` > '2018-01-01 10:10:10')"
I used with_sql but it ignores other scopes.
Also I tried:
.left_outer_join("`table_2` ON (`table_2`.`table_1_id` =
`table_1`.`id` AND `table_2`.`col_1` = 1 AND `table_2`.`col_2` >
'2017-09-28 06:49:53 UTC')")
Problem in last one is that it wraps that whole string with extra ``
what worked for me:
.left_outer_join(:table_2,
Sequel.lit('table_2.table_1_id = table_1.id AND table_2.col_1 = ?
AND table_2.col_2 > ?', id, date))
but is there a way to accomplish it without using string queries?
.left_outer_join(:table_2, :table_1_id=>:id, :col_1=>id){Sequel[:table_2][:col_2] > date}
I will post my own answer, but will not accept it because I think it could be done better.
.left_outer_join(:table_2,
Sequel.lit('table_2.table_1_id = table_1.id AND table_2.col_1 = ?
AND table_2.col_2 > ?', id, date))
So waiting for other suggestions.
This is might be a bit cleaner:
.left_outer_join(:table_2,
table_2.where(table_1_id: 'table_1.id')
.where(col_1: id)
.where('col 2 > ?', date)
)
Related
In ActiveRecord, I can do partial matches by using little tidbits of SQL like so:
Foo.where("name LIKE ?", "%matchthis%")
However, I'm looking to do something a little more complicated: use string concatenation between two tables and do a partial matches on the results. In PostgreSQL, I'd do it like this:
select
foos.id
from
foos
join
bars on
bars.id = foos.bar_id
where
foos.name || '.' || bars.name like '%match.this%'
;
How can I do the above using ActiveRecord? I would greatly prefer to use only ActiveRecord syntax only, and if I can't, it would be very beneficial to have the SQL blurbs SQL-agnostic.
A bit more ActiveRecord:
Foo.joins(:bars).where("foos.name || '.' || bars.name LIKE ?", '%match.this%')
This produces a query in AR but not sure if it is exactly what you are looking for. The query produced is:
SELECT "foos".* FROM "foos"
INNER JOIN "bars" ON "bars"."foo_id" = "foos"."id"
WHERE (foos.name || '.' || bars.name LIKE '%match.this%')
You might want to approach it separately like:
Foo.where('foos.name LIKE ?', '%match%').joins(:bars).where('bars.name LIKE ?', '%this%')
SELECT "foos".* FROM "foos"
INNER JOIN "bars" ON "bars"."foos_id" = "foos"."id"
WHERE (foos.name LIKE '%match%') AND (bars.name LIKE '%this%')
Hi I have a table where there is a RespondBy property which is of DateTime and is Nullable. Here is the linq I'm trying to run over EF6:
IEnumerable<Enquiry> ASAPEnquiries = db.Enquiries
.Where(enq => enq.RespondBy == null && enq.JobCostings.Count == 0)
.OrderBy(enq => enq.FlReference);
However, when I run if (ASAPEnquiries.Count() > 0) I get an error stating Nullable object must have a value. How would one query the database using linq if you want to check null DateTime columns?
Thank you.
EDIT:
The SQL that is produced by EF when tested in MSSMS brings back the desired result FYI. SQL Produced:
SELECT
[Project1].[Id] AS [Id],
[Project1].[FlReference] AS [FlReference],
[Project1].[EnquiryDate] AS [EnquiryDate],
[Project1].[ContactName] AS [ContactName],
[Project1].[ProjectReference] AS [ProjectReference],
[Project1].[EnquiryDetails] AS [EnquiryDetails],
[Project1].[RespondBy] AS [RespondBy],
[Project1].[CreatedBy] AS [CreatedBy],
[Project1].[Created] AS [Created],
[Project1].[ModifiedBy] AS [ModifiedBy],
[Project1].[Modified] AS [Modified],
[Project1].[RowVersion] AS [RowVersion],
[Project1].[Enquiry_Customer] AS [Enquiry_Customer],
[Project1].[Enquiry_EnquiryStatus] AS [Enquiry_EnquiryStatus]
FROM ( SELECT
[Extent1].[Id] AS [Id],
[Extent1].[FlReference] AS [FlReference],
[Extent1].[EnquiryDate] AS [EnquiryDate],
[Extent1].[ContactName] AS [ContactName],
[Extent1].[ProjectReference] AS [ProjectReference],
[Extent1].[EnquiryDetails] AS [EnquiryDetails],
[Extent1].[RespondBy] AS [RespondBy],
[Extent1].[CreatedBy] AS [CreatedBy],
[Extent1].[Created] AS [Created],
[Extent1].[ModifiedBy] AS [ModifiedBy],
[Extent1].[Modified] AS [Modified],
[Extent1].[RowVersion] AS [RowVersion],
[Extent1].[Enquiry_Customer] AS [Enquiry_Customer],
[Extent1].[Enquiry_EnquiryStatus] AS [Enquiry_EnquiryStatus],
(SELECT
COUNT(1) AS [A1]
FROM [dbo].[JobCostings] AS [Extent2]
WHERE [Extent1].[Id] = [Extent2].[JobCosting_Enquiry]) AS [C1]
FROM [dbo].[Enquiries] AS [Extent1]
) AS [Project1]
WHERE ([Project1].[RespondBy] IS NULL) AND (0 = [Project1].[C1])
ORDER BY [Project1].[FluidReference] ASC
Also, if I iterate over the collection, I get no error so it seems to be something to do with calling .Count() on the collection.
Use the HasValue property to check if a Nullable has value.
IEnumerable<Enquiry> ASAPEnquiries = db.Enquiries
.Where(enq => enq.RespondBy.HasValue && enq.JobCostings.Count == 0)
.OrderBy(enq => enq.FlReference);
Please make sure that your Enquiry class assign value to the JobCostings property in it's constructor
e.g.
public Enquiry()
{
this.JobCostings = new HashSet<JobCosting>();
//.......
}
and try this one :
IEnumerable<Enquiry> ASAPEnquiries = db.Enquiries
.Where(enq => enq.RespondBy == null && !enq.JobCostings.Any())
.OrderBy(enq => enq.FlReference);
It would seem I have egg on my face. The if statement was calling a void where I was then using item.RespondBy.Value.ToShortDateTime(). How embarresing. Thank you all for your help though, I appreciate your time.
Can anyone help?
I have 1 class, basically it holds Members and within that class is a List.
The members i have in a List also... So basically it goes like this,
I have 2 members and each member has a number of sessions.
I wish to only return each member with 1 Session.
I have done a LINQ query, but of course it doesn't work...
I think i need to do a self join, any ideas?
Basically my error is m doesn't exist in my subquery self join.
var sessions =
from m in this.members
join s in
(
from se in m.Sessions
group se by se.Name into g
select new {Name = g.Key, SessioEndTime = g.Max(a=>a.SessioEndTime)}
)
on m.Name equals s.Name
select new { MemberName = m.Name, SessionTime = s.SessioEndTime}
I would appreciate any feedback anyone has.
Thanks in advance.
EDIT
Ok i managed to do it like the following, but is this the best way?
var sessions =
from m in this.members
let sn = m.Sessions.OrderByDescending(a => a.SessionEndTime).FirstOrDefault()
select new { MemberName = m.Name, SessionTime = sn.SessioEndTime}
This way sn contains 1 record, but i have access to all the properties...
But is this the best way to do using a LET?
Thanks.
Unless I am missing something you need this, no?
var sessions =
from m in members
select new {
MemberName = m.Name,
SessionTime = m.Sessions.Max(s => s.SessioEndTime)
};
You have to change the way you think about LINQ queries, think more from object point rather than from SQL implementation point. What is it that I need? I need all members, each with its latest session end time, then act on that.
EDIT:
The let option you used is ok, just keep something in mind FirstOrDefault will return null if member has an empty list of Sessions, and then sn.SessionEndTime hits null reference. If on the other hand you are certain that every member has at least one session use First instead or aggregate.
Also don't use FirstOrDefault() in the let, it kind of messes up the LINQ and prevents it from tying it to the master (causing a separate SQL query for each master to detect missing subsets), so usable queries with let are:
from m in Members
let sn = m.Sessions.Max(s => s.SessioEndTime)
select new { MemberName = m.Name, SessionTime = sn};
from m in Members
let sn = m.Sessions.OrderByDescending(a => a.SessioEndTime).First()
select new { MemberName = m.Name, SessionTime = sn.SessioEndTime};
As for ordering vs Max aggregation, both queries will generate a subquery:
-- MAX
SELECT [t0].[Name] AS [MemberName], (
SELECT MAX([t1].[SessioEndTime])
FROM [Session] AS [t1]
WHERE [t1].[memberId] = [t0].[id]
) AS [SessionTime]
FROM [Member] AS [t0]
GO
-- ordering
SELECT [t0].[Name] AS [MemberName], (
SELECT [t2].[SessioEndTime]
FROM (
SELECT TOP (1) [t1].[SessioEndTime]
FROM [Session] AS [t1]
WHERE [t1].[memberId] = [t0].[id]
ORDER BY [t1].[SessioEndTime] DESC
) AS [t2]
) AS [SessionTime]
FROM [Member] AS [t0]
With a descending index on SessioEndTime the ordering script is about twice slower (you can get execution plans for these to check for yourself), without the index its about 5times slower.
I tried to use the suggestion provided here for using In operator in linq but, i am not able to convert my requirement into LINQ statement.
Below is the SQL query which i need to convert to Linq
select *
from navigator_user_field_property
where user_id = 'albert'
and field_id in (
select field_id
from navigator_entity_field_master
where entity_id = 1
and use_type = 0)
order by field_id
I want this to be converted to a Efficient Linq.
Most of the answers deal with the predetermined list of string array which is not working in my case.
Thanks
Looks like a join to me:
var query = from navigator in db.NavigatorUserFieldProperties
where navigator.UserId == "albert"
join field in db.NavigatorEntityFieldMasters
.Where(f => f.EntityId == 1 && f.UseType == 0)
on navigator.FieldId equals field.FieldId
select navigator;
Note that this will return the same value multiple times if there are multiple fields with the same ID - but I suspect that's not the case.
You could do a more literal translation like this:
var query = from navigator in db.NavigatorUserFieldProperties
where navigator.UserId == "albert" &&
db.NavigatorEntityFieldMasters
.Where(f => f.EntityId == 1 && f.UseType == 0)
.select(f => f.FieldId)
.Contains(navigator.FieldId)
select navigator;
... and that may end up translating to the same SQL... but I'd personally go with the join.
Here is an efficient and readable LINQ query:
var fields =
from field in db.navigator_entity_field_masters
where field.entity_id == 1 && field.user_type == 0
select field;
var properties =
from property in db.navigator_user_field_properties
where property.user_id == "albert"
where fields.Contains(property.field)
select property;
Look mama!! Without joins ;-)
I have the following query to start with:
var query = from p in db.Products
from pc in p.NpProductCategories
where pc.CategoryId == categoryId
select p;
I'm applying some more filtering on it and in the end I want to sort the results:
if (orderBy == ProductSortingEnum.Name)
query = query.OrderBy(x => x.Name);
else
query = query.OrderBy(............);
My big problem (coming from not knowing linq too good) is the ELSE here. How can I sort results by a column that is not in the current result set? I would like to somehow link to another linq query in the orderby. The sorting I'm trying to achive is to link to NpProductVariants query using the ProductId to match between NpProductVariant and Products
and sort by the Price of the NpProductVariant
Assuming you have the relationship set up in the dbml...
For one to one (and many to one):
query = query.OrderBy(p => p.NpProductVariant.Price);
For one to many:
query = query.OrderBy(p => p.NpProductVariants.Select(v => v.Price).Max());
Also:
var query =
from p in db.Products
where p.NpProductCategories.Any(pc => pc.CategoryId == categoryId)
select p;
I think you can hook your Join to your query as long as it is returning the same thing. So maybe something like (I'm not 100 % sure since I haven't tried it):
query = from i1 in query
join i2 in query2 on i1.PropertyToJoin equals i2.PropertyToJoin
orderby i1.OrderProp1, i2.OrderProp2
select i1;
But I think it might be a good idea to check the generated sql so it is still effective.