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");
Related
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 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)
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);
I want to write this raw query using the TypeORM createQueryBuilder. How can I achieve this?
SELECT *
FROM Location l
INNER JOIN LocationType t ON l.TypeId = t.Id
LEFT JOIN Location p ON l.ParentId = p.Id
LEFT JOIN (SELECT locationId, SUM(Units) TotalUnits FROM ItemInventory GROUP BY LocationId) qty ON l.Id = qty.LocationId`
I want to get the list of all Branch even if they have no accounts with user role
Query query = em.createQuery("SELECT NEW com.package.BranchInstructors(b,a) FROM Branch b LEFT JOIN b.accounts a WHERE b.dFlg = 0 AND a.userRole = :role ORDER BY b.name ASC");
query.setParameter("role", "user");
return query.getResultList();
Unfortunately it's returning only Branches with user role, it's like doing INNER JOIN instead.
Any idea what's going on?
just add the a.userRole is null condition to your query to avoid filtering the null userRole that you got from the left join
SELECT NEW com.package.BranchInstructors(b,a)
FROM Branch b
LEFT JOIN b.accounts a
WHERE b.dFlg = 0
AND (a.userRole = :role OR a.userRole IS NULL)
ORDER BY b.name ASC"
The problem is in your WHERE vs LEFT JOIN clause.
If you use LEFT JOIN table Accounts and use this table in the WHERE with AND condition, it behaves like a JOIN.
So, you can use WITH in the LEFT JOIN:
Query query = em.createQuery("SELECT NEW com.package.BranchInstructors(b,a) FROM Branch b
LEFT JOIN b.accounts a WITH a.userRole = :role
WHERE b.dFlg = 0 ORDER BY b.name ASC");