I am trying to make this query in OrmLite:
select b.* from blog b
join blog_to_blog_category btbc on b.id = btbc.blog_id
join blog_category bc on btbc.blog_category_id = bc.id
where b.url like '%.com' and bc.id in (100, 583)
group by b.id
having count(distinct bc.id ) = 1
I can't figure out how to get the Having() method structured. I can see there is a Sql.CountDistinct() method but I can't figure out how to use it with Having().
I figure I need to do something along the lines of:
var q = db
.From<Blog>()
.LeftJoin<BlogToBlogCategory>()
.Join<BlogToBlogCategory, BlogCategory>()
.Where<BlogCategory>(x => Sql.In(x.Id, 100, 583))
.GroupBy<Blog>(bc => bc.Id)
.Having(x => Sql.CountDistinct("blog_category.id") == "2")
This gives error:
42883: operator does not exist: bigint = text
I can't see how to type it to take a table column name and return a number for comparison.
Is this query possible?
EDIT
I got around it by setting having expression explicitly
q.HavingExpression = $"having count(distinct {q.Table<BlogCategory>()}.{q.Column<BlogCategory>(bc => bc.Id)}) = 2";
I am still curious though if it is possible to do this with fluent api.
I've just added multiple typed table overloads for Having() in the latest v5.11.1 that's now available on MyGet which will allow you to reference a joined table properties in a typed expression, e.g:
var q = db
.From<Blog>()
.LeftJoin<BlogToBlogCategory>()
.Join<BlogToBlogCategory, BlogCategory>()
.Where<BlogCategory>(x => Sql.In(x.Id, 100, 583))
.GroupBy<Blog>(bc => bc.Id)
.Having<BlogCategory>(x => Sql.CountDistinct(x.Id) == 2)
Related
TableAlias isn't working with multiple joins.
The query:
var q = Db.From<Blog>(Db.TableAlias("b"))
.LeftJoin<Blog, BlogToBlogCategory>((b,btb)=> b.Id == btb.BlogId, Db.TableAlias("btbc"))
.Join<BlogToBlogCategory, BlogCategory>((bt,bc)=>bt.BlogCategoryId == bc.Id, Db.TableAlias("cats"))
.GroupBy(x => x.Id);
.Select("b.*, json_agg(cats) as BlogCategoriesJson");
var results = Db.Select<BlogQueryResponse>(q);
Generates this SQL:
SELECT b.*, json_agg(cats) as BlogCategoriesJson
FROM "blog" "b" LEFT JOIN "blog_to_blog_category" "btbc" ON ("b"."id" = "btbc"."blog_id") INNER JOIN "blog_category" "cats" ON ("blog_to_blog_category"."blog_category_id" = "cats"."id")
GROUP BY "b"."id"
This causes error because it is referencing "blog_to_blog_category" instead of btbc
The Db.TableAlias() only provides an alias for the target join table, your inner join does not specify the alias to use for the source table so it references the full table name as expected.
You can use Sql.TableAlias() in your LINQ Expression to reference a table alias, e.g:
var q = Db.From<Blog>(Db.TableAlias("b"))
.LeftJoin<Blog, BlogToBlogCategory>((b,btb)=> b.Id == btb.BlogId, Db.TableAlias("btbc"))
.Join<BlogToBlogCategory, BlogCategory>((bt,bc)=>
Sql.TableAlias(bt.BlogCategoryId, "btbc") == bc.Id, Db.TableAlias("cats"))
.GroupBy(x => x.Id);
.Select("b.*, json_agg(cats) as BlogCategoriesJson");
I have this query
var q = Db
.From<Blog>()
.LeftJoin<BlogToBlogCategory>()
.Join<BlogToBlogCategory, BlogCategory>()
.Where( .. )
.SelectDistinct();
var results = Db.Select<BlogCategory>(q);
It generates this SQL:
SELECT DISTINCT "blog".*, 0 EOT, "blog_category".*, 0 EOT
FROM "blog" LEFT JOIN "blog_to_blog_category" ON
("blog"."id" = "blog_to_blog_category"."blog_id") INNER JOIN "blog_category" ON
("blog_category"."id" = "blog_to_blog_category"."blog_category_id")
WHERE ...
I want to select distinct blog_category but the select is adding all the blog fields also so I am getting duplicate blog_category entries.
How do I just select distint the joined table fields?
In OrmLite the SqlExpression is what builds the SQL query you want executed:
var q = Db
.From<Blog>()
.LeftJoin<BlogToBlogCategory>()
.Join<BlogToBlogCategory, BlogCategory>()
.Where( .. )
.SelectDistinct();
Whilst the API use to execute the query maps the result back to the specified model, in this case you're saying you want the results of the above query mapped to BlogCategory POCOs:
var results = Db.Select<BlogCategory>(q);
But you need to make sure that the query you create returns results that can map to the POCO you've specified.
If you only want to select distinct BlogCategory columns you'll need to do this in your SqlExpression:
var q = Db
.From<Blog>()
.LeftJoin<BlogToBlogCategory>()
.Join<BlogToBlogCategory, BlogCategory>()
.Where( .. )
.SelectDistinct<BlogCategory>(c => c);
Or if you want to select columns across different joined tables you'd use a standard anonymous type expression:
var q = Db
.From<Blog>()
.LeftJoin<BlogToBlogCategory>()
.Join<BlogToBlogCategory, BlogCategory>()
.Where( .. )
.SelectDistinct<Blog,BlogCategory>((b,c) => new { b.Id, c.Name, ... });
Then the custom model you map the results to should have properties that match the returned columns:
var results = db.Select<BlogCategoryResult>(q);
Alternatively you can access the custom result set with the Dynamic Result Set APIs.
I have a situation where records should be excluded depending on a value in a related table. When using joins with SqlExpressionSelectFilter the generated SQL will not use table alias for part of the query. I am running ServiceStack 5.5.
OrmLiteConfig.SqlExpressionSelectFilter = q => {
if (q.ModelDef.ModelType.HasInterface(typeof(IJoinFilter))) {
q.LeftJoin<IJoinFilter, FirstTable>((f, j) => f.FirstTableId == j.Id)
.Where<FirstTable>(j => j.Deleted != true);
}
};
And simply selecting from it with:
db.Select(db.From<SecondTable>().Where<SecondTable>(x => x.Id == 1));
This generates something like:
SELECT SecondTable.Id, SecondTable.FirstTableId, SecondTable.Deleted
FROM SecondTable
LEFT JOIN FirstTable ON (SecondTable.FirstTableId = FirstTable.Id)
WHERE (Id = 1) AND (FirstTable.Deleted <> 1)
Notice the Id in the where clause is not prepended with 'SecondTable'. There are also cases for which the select clause will not include aliases.
Are joins in select filters supported? Is there a recommended way I should be doing this kind of global filter instead?
I've created a quick repro here: https://github.com/TheScobey/OrmliteSelectFilterIssue
The SqlExpressionSelectFilter is applied after the expression is created so wont be able to impact previous expressions, you can try giving the Source From table an alias with:
db.Select(db.From<SecondTable>(db.TableAlias(nameof(SecondTable)))
.Where<SecondTable>(x => x.Id == 1));
An alternative is to force the query to include the table prefix in queries, e.g:
var q = db.From<SecondTable>();
q.PrefixFieldWithTableName = true;
q.Where<SecondTable>(x => x.Id == 1);
This is my sql Query:
SELECT ParkingPlaceName, NoOfParkingPlaces, COUNT(Place.ParkingAreaID) AS NoOfCarsParked, NoOfParkingPlaces-COUNT(Place.ParkingAreaID)
FROM ParkingArea
LEFT JOIN Place ON ParkingArea.ParkingAreaID = Place.ParkingAreaID
LEFT JOIN Car ON Car.CarID = Place.CarID
GROUP BY ParkingPlaceName, NoOfParkingPlaces, Place.ParkingAreaID
how to write in LINQ to SQL Query
I think you can use DefaultIfEmpty method to perform left outer join between 3 tables with let statements to deal with group results, hence the LINQ version from your SQL query should be similar (or same) like below:
var query = from pa in ParkingArea
from pl in Place.Where(x => pa.ParkingAreaID == x.ParkingAreaID).DefaultIfEmpty()
from ca in Car.Where(x => x.CarID == pl.CarID).DefaultIfEmpty()
group new { pa, pl.ParkingAreaID, ca.CarID } by new { pa.ParkingPlaceName, pa.NoOfParkingPlaces, pl.ParkingAreaID } into grp
let ParkingPlaceName = grp.Select(x => x.ParkingPlaceName)
let NoOfParkingPlaces = grp.Select(x => x.NoOfParkingPlaces)
let NoOfCarsParked = grp.Select(x => x.ParkingAreaID).Count()
select new
{
ParkingPlaceName,
NoOfParkingPlaces,
NoOfCarsParked,
AvailableParkingPlaces = NoOfParkingPlaces - NoOfCarsParked // new alias for holding substraction
};
I decided not to use join ... in ... on ... equals ... directly after first and third from clauses, since it may lead into confusion about which table instance should be handled when creating group elements after performing join.
Here is a SQL Query I want to convert to EF4.3
command = database.GetSqlStringCommand(#"
select
H.AUTHENTICATION_ID,
USERNAME,
PERMISSIONS,
ORGANIZATION_IDENTIFIER,
O.ORGANIZATION_ID
from
AUTHENTICATION H
left join [AUTHORIZATION] T on H.AUTHENTICATION_ID=T.AUTHENTICATION_ID
join ORGANIZATION O on O.ORGANIZATION_ID = T.ORGANIZATION_ID
order by H.AUTHENTICATION_ID");
Here is the best LINQ I could come up with:
var query = from h in context.Authentications
join t in context.Authorizations on h.AuthenticationId equals t.Authentications.AuthenticationId
join o in context.Organizations on t.Organizations.OrganizationId equals o.OrganizationId
orderby
h.AuthenticationId
select new
{ AUTHENTICATION_ID = (Int16?)h.AuthenticationId,
h.Username,
t.Permissions,
o.OrganizationIdentifier,
OrganizationID = (Int16?)o.OrganizationId
};
I know i need to merge my first join (between Authorizations & Authentications) into, lets say x and apply DefaultIfEmpty but can't make out the syntax.
EDIT: Image for clarification:
Any help will be highly appreciated. Regards.
The basic syntax for a "left join" in Linq is like this:
from x in table1
join y in table2 on x.id equals y.id into jointable
from z in jointable.DefaultIfEmpty()
select new
{
x.Field1,
x.Field2,
x.Field3,
Field4 = z == null ? 0 : z.Field4
};
In your case, I'm a little confused because the entity relations you seem to be using in your Linq don't match the ones implied by your SQL; are the relationships here zero-or-one, zero-or-many, one-to-one, etc? Specifically, you're doing this:
from h in context.Authentications
join t in context.Authorizations on h.AuthenticationId equals t.Authentications.AuthenticationId
but your SQL implies that "Authentication" is the parent here with zero-or-more "Authorization" children, not the other way around, which would be more like:
from h in context.Authentications
from t in h.Authorizations.DefaultIfEmpty()
If you can give us a better idea of the data model and what data you expect to get out of it we can more easily explain how that query would look in Linq. Assuming that your relationships match what is implied by the SQL, you should be able to get what you want using the following Linq queries:
var query = from h in context.Authentications
from t in h.Authorizations.DefaultIfEmpty()
select new
{
h.AuthenticationId,
h.Username,
Permissions = t == null ? null : t.Permissions,
Organizations = t == null ? new EntitySet<Organization>() : t.Organizations
};
var query2 = from x in query
from o in x.organizations.DefaultIfEmpty()
select new
{
AUTHENTICATION_ID = (short?)x.AuthenticationId,
x.Username,
x.Permissions,
OrganizationIdentifier = o == null ? null : o.OrganizationIdentifier,
OrganizationID = o == null ? (short?)null : o.OrganizationID
};
Given the foreign keys that exist in the question diagram, how about something like this?
var query = from a in context.Authentications
select new
{
a.AuthenticationID,
a.Username,
a.Authorisations.Permissions ?? false,
a.Authorisations.Organisations.OrganisationIdentifier ?? 0
a.Authorisations.Organisations.OrganisationID ?? 0
};
I went ahead and moved the entire query to a Stored Procedure on the database. This solves the problem by avoiding LINQ and ObjectBuilder in the first place.