[linq2db bug] Исключение в запросе с OUTER APPLY
От: Петрухин Эдуард Россия  
Дата: 27.07.18 09:12
Оценка:
Версия 2.2, MS SQL Server 2016

Есть две таблицы:
[Table("Authors")]
public class Author
{
    [PrimaryKey, Identity]
    public int Id { get; set; }

    [Column(CanBeNull = false)]
    public string Name { get; set; }
}

[Table("Books")]
public class Book
{
    [PrimaryKey, Identity]
    public int Id { get; set; }

    [Column(CanBeNull = false)]
    public int AuthorId { get; set; }

    [Column(CanBeNull = false)]
    public string Title { get; set; }
}

Обращение к ним идёт через две переменные:
var authors = db.GetTable<Author>();
var books = db.GetTable<Book>();


Запрос
from author in authors
let booksCount =
(
    from book in books
    where book.AuthorId == author.Id
    select Sql.Ext.Count(book.Id).ToValue()
).Single()
where booksCount > 42
select new
{
    author.Name,
    BooksCount = booksCount
}

генерирует следующий SQL код:
SELECT
        [t3].[Name],
        [t2].[c1] as [c11],
        [t2].[c2] as [c21]
FROM
        [Authors] [t3]
                OUTER APPLY (
                        SELECT
                                COUNT([t1].[Id]) as [c1],
                                1 as [c2]
                        FROM
                                [Books] [t1]
                        WHERE
                                [t1].[AuthorId] = [t3].[Id]
                ) [t2]
WHERE
        (
                SELECT
                        COUNT([t4].[Id])
                FROM
                        [Books] [t4]
                WHERE
                        [t4].[AuthorId] = [t3].[Id]
        ) > 42

Запрос получается неестественный и, возможно, не оптимальный.

Запрос
from author in authors
let booksAgg =
(
    from book in books
    where book.AuthorId == author.Id
    select new
    {
        Count = Sql.Ext.Count(book.Id).ToValue()
    }
).Single()
where booksAgg.Count > 42
select new
{
    author.Name,
    BooksCount = booksAgg.Count
}

падает:
LinqToDB.Linq.LinqException: 'Table([Books]).Where(book => (book.AuthorId == <>h__TransparentIdentifier0.author.Id)).Select(book => new <>f__AnonymousType5`1(Count = Sql.Ext.Count(book.Id).ToValue())).Single().Count' cannot be converted to SQL.
   at LinqToDB.Linq.Builder.ExpressionBuilder.ConvertToSql(IBuildContext context, Expression expression, Boolean unwrap) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\ExpressionBuilder.SqlBuilder.cs:line 1029
   at LinqToDB.Linq.Builder.ExpressionBuilder.ConvertCompare(IBuildContext context, ExpressionType nodeType, Expression left, Expression right) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\ExpressionBuilder.SqlBuilder.cs:line 1588
   at LinqToDB.Linq.Builder.ExpressionBuilder.ConvertPredicate(IBuildContext context, Expression expression) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\ExpressionBuilder.SqlBuilder.cs:line 1370
   at LinqToDB.Linq.Builder.ExpressionBuilder.BuildSearchCondition(IBuildContext context, Expression expression, List`1 conditions) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\ExpressionBuilder.SqlBuilder.cs:line 2529
   at LinqToDB.Linq.Builder.ExpressionBuilder.BuildWhere(IBuildContext parent, IBuildContext sequence, LambdaExpression condition, Boolean checkForSubQuery, Boolean enforceHaving) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\ExpressionBuilder.SqlBuilder.cs:line 48
   at LinqToDB.Linq.Builder.WhereBuilder.BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\WhereBuilder.cs:line 23
   at LinqToDB.Linq.Builder.ExpressionBuilder.BuildSequence(BuildInfo buildInfo) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\ExpressionBuilder.cs:line 175
   at LinqToDB.Linq.Builder.SelectBuilder.BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\SelectBuilder.cs:line 36
   at LinqToDB.Linq.Builder.ExpressionBuilder.BuildSequence(BuildInfo buildInfo) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\ExpressionBuilder.cs:line 175
   at LinqToDB.Linq.Builder.ExpressionBuilder.Build[T]() in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\ExpressionBuilder.cs:line 146
   at LinqToDB.Linq.Query`1.CreateQuery(IDataContext dataContext, Expression expr) in C:\projects\linq2db\Source\LinqToDB\Linq\Query.cs:line 280
   at LinqToDB.Linq.Query`1.GetQuery(IDataContext dataContext, Expression& expr) in C:\projects\linq2db\Source\LinqToDB\Linq\Query.cs:line 233
   at LinqToDB.Linq.ExpressionQuery`1.GetQuery(Expression& expression, Boolean cache) in C:\projects\linq2db\Source\LinqToDB\Linq\ExpressionQuery.cs:line 84
   at LinqToDB.Linq.ExpressionQuery`1.get_SqlText() in C:\projects\linq2db\Source\LinqToDB\Linq\ExpressionQuery.cs:line 53
   at ConsoleApp.Program.WorkWithDb(DbMain db) in C:\Src\Personal\TestPolygon\ConsoleApp\Program.cs:line 68
   at ConsoleApp.Program.WorkWithDb(String dataSource, String initialCatalog) in C:\Src\Personal\TestPolygon\ConsoleApp\Program.Db.cs:line 57
   at ConsoleApp.Program.Main() in C:\Src\Personal\TestPolygon\ConsoleApp\Program.Core.cs:line 21


Если делать фильтрацию попозже:
var query =
    from author in authors
    let booksAgg =
    (
        from book in books
        where book.AuthorId == author.Id
        select new
        {
            Count = Sql.Ext.Count(book.Id).ToValue()
        }
    ).Single()
    select new
    {
        author.Name,
        BooksCount = booksAgg.Count
    };

query = query.Where(x => x.BooksCount > 42);

то падает с другим исключением:
System.NotImplementedException: The method or operation is not implemented.
   at LinqToDB.Linq.Builder.SelectContext.ProcessMemberAccess[T](Expression expression, MemberExpression levelExpression, Int32 level, Func`6 action) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\SelectContext.cs:line 974
   at LinqToDB.Linq.Builder.SelectContext.IsExpressionInternal(Expression expression, Int32 level, RequestFor requestFlag) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\SelectContext.cs:line 731
   at LinqToDB.Linq.Builder.SelectContext.IsExpression(Expression expression, Int32 level, RequestFor requestFlag) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\SelectContext.cs:line 627
   at LinqToDB.Linq.Builder.ExpressionContext.IsExpression(Expression expression, Int32 level, RequestFor requestFlag) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\ExpressionContext.cs:line 116
   at LinqToDB.Linq.Builder.ExpressionBuilder.<>c__DisplayClass97_0.<CheckSubQueryForWhere>b__0(Expression expr) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\ExpressionBuilder.SqlBuilder.cs:line 90
   at LinqToDB.Expressions.Extensions.Visit(Expression expr, Func`2 func) in C:\projects\linq2db\Source\LinqToDB\Expressions\Extensions.cs:line 373
   at LinqToDB.Expressions.Extensions.Visit(Expression expr, Func`2 func) in C:\projects\linq2db\Source\LinqToDB\Expressions\Extensions.cs:line 418
   at LinqToDB.Linq.Builder.ExpressionBuilder.CheckSubQueryForWhere(IBuildContext context, Expression expression, Boolean& makeHaving) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\ExpressionBuilder.SqlBuilder.cs:line 149
   at LinqToDB.Linq.Builder.ExpressionBuilder.BuildWhere(IBuildContext parent, IBuildContext sequence, LambdaExpression condition, Boolean checkForSubQuery, Boolean enforceHaving) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\ExpressionBuilder.SqlBuilder.cs:line 32
   at LinqToDB.Linq.Builder.WhereBuilder.BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\WhereBuilder.cs:line 23
   at LinqToDB.Linq.Builder.ExpressionBuilder.BuildSequence(BuildInfo buildInfo) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\ExpressionBuilder.cs:line 175
   at LinqToDB.Linq.Builder.ExpressionBuilder.Build[T]() in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\ExpressionBuilder.cs:line 146
   at LinqToDB.Linq.Query`1.CreateQuery(IDataContext dataContext, Expression expr) in C:\projects\linq2db\Source\LinqToDB\Linq\Query.cs:line 280
   at LinqToDB.Linq.Query`1.GetQuery(IDataContext dataContext, Expression& expr) in C:\projects\linq2db\Source\LinqToDB\Linq\Query.cs:line 233
   at LinqToDB.Linq.ExpressionQuery`1.GetQuery(Expression& expression, Boolean cache) in C:\projects\linq2db\Source\LinqToDB\Linq\ExpressionQuery.cs:line 84
   at LinqToDB.Linq.ExpressionQuery`1.get_SqlText() in C:\projects\linq2db\Source\LinqToDB\Linq\ExpressionQuery.cs:line 53
   at ConsoleApp.Program.WorkWithDb(DbMain db) in C:\Src\Personal\TestPolygon\ConsoleApp\Program.cs:line 22
   at ConsoleApp.Program.WorkWithDb(String dataSource, String initialCatalog) in C:\Src\Personal\TestPolygon\ConsoleApp\Program.Db.cs:line 56
   at ConsoleApp.Program.Main() in C:\Src\Personal\TestPolygon\ConsoleApp\Program.Core.cs:line 21


Для меня больше важен вариант с
from book in books
where book.AuthorId == author.Id
select new
{
    Count = Sql.Ext.Count(book.Id).ToValue()
}

потому что на практике надо считать по подзапросу не один агрегат, а несколько.
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Re: [linq2db bug] Исключение в запросе с OUTER APPLY
От: IT Россия linq2db.com
Дата: 27.07.18 13:49
Оценка:
Здравствуйте, Петрухин Эдуард, Вы писали:

ПЭ>Запрос


Какая-то каша. Как должен выглядеть запрос на SQL?
Если нам не помогут, то мы тоже никого не пощадим.
Re[2]: [linq2db bug] Исключение в запросе с OUTER APPLY
От: Петрухин Эдуард Россия  
Дата: 31.07.18 06:18
Оценка:
Здравствуйте, IT, Вы писали:

IT>Какая-то каша. Как должен выглядеть запрос на SQL?


Прошу прощения за сумбур!

Итак, сначала:
Есть две таблицы, «Authors» (авторы) и «Books» (книги):
[Table("Authors")]
public class Author
{
    [PrimaryKey, Identity]
    public int Id { get; set; }

    [Column(CanBeNull = false)]
    public string Name { get; set; }
}

[Table("Books")]
public class Book
{
    [PrimaryKey, Identity]
    public int Id { get; set; }

    [Column(CanBeNull = false)]
    public int AuthorId { get; set; }

    [Column(CanBeNull = false)]
    public string Title { get; set; }
}

Пытаемся получить количество книг по каждому автору.

Первый вариант запроса

linq2db:
from author in authors
let booksCount =
(
    from book in books
    where book.AuthorId == author.Id
    select Sql.Ext.Count(book.Id).ToValue()
).Single()
where booksCount > 42
select new
{
    author.Name,
    BooksCount = booksCount
}

Работает, но генерирует неестественный и, возможно, не оптимальный SQL:
SELECT
        [t3].[Name],
        [t2].[c1] as [c11],
        [t2].[c2] as [c21]
FROM
        [Authors] [t3]
                OUTER APPLY (
                        SELECT
                                COUNT([t1].[Id]) as [c1],
                                1 as [c2]
                        FROM
                                [Books] [t1]
                        WHERE
                                [t1].[AuthorId] = [t3].[Id]
                ) [t2]
WHERE
        (
                SELECT
                        COUNT([t4].[Id])
                FROM
                        [Books] [t4]
                WHERE
                        [t4].[AuthorId] = [t3].[Id]
        ) > 42

Я ожидал, что будет как-то так:
SELECT
        Authors.Name,
        BooksAggregates.Count
FROM
        Authors
                OUTER APPLY (
                        SELECT
                                COUNT(Books.Id) as Count
                        FROM
                                Books
                        WHERE
                                Books.AuthorId = Authors.Id
                ) BooksAggregates
WHERE
        BooksAggregates.Count > 42

т.е. в WHERE не будет повторяться подзапрос из OUTER APPLY .

Второй вариант запроса

linq2db:
from author in authors
let booksAgg =
(
    from book in books
    where book.AuthorId == author.Id
    select new
    {
        Count = Sql.Ext.Count(book.Id).ToValue()
    }
).Single()
where booksAgg.Count > 42
select new
{
    author.Name,
    BooksCount = booksAgg.Count
}

Не работает, падает с ошибкой. Разница с первым вариантом только в том что для агрегата Count используется анонимный тип. Ожидаемый SQL — такой же что и в первом варианте.
Ошибка:
LinqToDB.Linq.LinqException: 'Table([Books]).Where(book => (book.AuthorId == <>h__TransparentIdentifier0.author.Id)).Select(book => new <>f__AnonymousType5`1(Count = Sql.Ext.Count(book.Id).ToValue())).Single().Count' cannot be converted to SQL.
   at LinqToDB.Linq.Builder.ExpressionBuilder.ConvertToSql(IBuildContext context, Expression expression, Boolean unwrap) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\ExpressionBuilder.SqlBuilder.cs:line 1029
   at LinqToDB.Linq.Builder.ExpressionBuilder.ConvertCompare(IBuildContext context, ExpressionType nodeType, Expression left, Expression right) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\ExpressionBuilder.SqlBuilder.cs:line 1588
   at LinqToDB.Linq.Builder.ExpressionBuilder.ConvertPredicate(IBuildContext context, Expression expression) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\ExpressionBuilder.SqlBuilder.cs:line 1370
   at LinqToDB.Linq.Builder.ExpressionBuilder.BuildSearchCondition(IBuildContext context, Expression expression, List`1 conditions) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\ExpressionBuilder.SqlBuilder.cs:line 2529
   at LinqToDB.Linq.Builder.ExpressionBuilder.BuildWhere(IBuildContext parent, IBuildContext sequence, LambdaExpression condition, Boolean checkForSubQuery, Boolean enforceHaving) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\ExpressionBuilder.SqlBuilder.cs:line 48
   at LinqToDB.Linq.Builder.WhereBuilder.BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\WhereBuilder.cs:line 23
   at LinqToDB.Linq.Builder.ExpressionBuilder.BuildSequence(BuildInfo buildInfo) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\ExpressionBuilder.cs:line 175
   at LinqToDB.Linq.Builder.SelectBuilder.BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\SelectBuilder.cs:line 36
   at LinqToDB.Linq.Builder.ExpressionBuilder.BuildSequence(BuildInfo buildInfo) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\ExpressionBuilder.cs:line 175
   at LinqToDB.Linq.Builder.ExpressionBuilder.Build[T]() in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\ExpressionBuilder.cs:line 146
   at LinqToDB.Linq.Query`1.CreateQuery(IDataContext dataContext, Expression expr) in C:\projects\linq2db\Source\LinqToDB\Linq\Query.cs:line 280
   at LinqToDB.Linq.Query`1.GetQuery(IDataContext dataContext, Expression& expr) in C:\projects\linq2db\Source\LinqToDB\Linq\Query.cs:line 233
   at LinqToDB.Linq.ExpressionQuery`1.GetQuery(Expression& expression, Boolean cache) in C:\projects\linq2db\Source\LinqToDB\Linq\ExpressionQuery.cs:line 84
   at LinqToDB.Linq.ExpressionQuery`1.get_SqlText() in C:\projects\linq2db\Source\LinqToDB\Linq\ExpressionQuery.cs:line 53
   at ConsoleApp.Program.WorkWithDb(DbMain db) in C:\Src\Personal\TestPolygon\ConsoleApp\Program.cs:line 68
   at ConsoleApp.Program.WorkWithDb(String dataSource, String initialCatalog) in C:\Src\Personal\TestPolygon\ConsoleApp\Program.Db.cs:line 57
   at ConsoleApp.Program.Main() in C:\Src\Personal\TestPolygon\ConsoleApp\Program.Core.cs:line 21


Третий вариант запроса

linq2db:
var query =
    from author in authors
    let booksAgg =
    (
        from book in books
        where book.AuthorId == author.Id
        select new
        {
            Count = Sql.Ext.Count(book.Id).ToValue()
        }
    ).Single()
    select new
    {
        author.Name,
        BooksCount = booksAgg.Count
    };

query = query.Where(x => x.BooksCount > 42);

Не работает, падает с ошибкой. Тут, как и во втором варианте, для агрегата Count используется анонимный тип. Фильтрация выполняется позже. Ожидаемый SQL — такой же что и в первом варианте.
Ошибка:
System.NotImplementedException: The method or operation is not implemented.
   at LinqToDB.Linq.Builder.SelectContext.ProcessMemberAccess[T](Expression expression, MemberExpression levelExpression, Int32 level, Func`6 action) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\SelectContext.cs:line 974
   at LinqToDB.Linq.Builder.SelectContext.IsExpressionInternal(Expression expression, Int32 level, RequestFor requestFlag) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\SelectContext.cs:line 731
   at LinqToDB.Linq.Builder.SelectContext.IsExpression(Expression expression, Int32 level, RequestFor requestFlag) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\SelectContext.cs:line 627
   at LinqToDB.Linq.Builder.ExpressionContext.IsExpression(Expression expression, Int32 level, RequestFor requestFlag) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\ExpressionContext.cs:line 116
   at LinqToDB.Linq.Builder.ExpressionBuilder.<>c__DisplayClass97_0.<CheckSubQueryForWhere>b__0(Expression expr) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\ExpressionBuilder.SqlBuilder.cs:line 90
   at LinqToDB.Expressions.Extensions.Visit(Expression expr, Func`2 func) in C:\projects\linq2db\Source\LinqToDB\Expressions\Extensions.cs:line 373
   at LinqToDB.Expressions.Extensions.Visit(Expression expr, Func`2 func) in C:\projects\linq2db\Source\LinqToDB\Expressions\Extensions.cs:line 418
   at LinqToDB.Linq.Builder.ExpressionBuilder.CheckSubQueryForWhere(IBuildContext context, Expression expression, Boolean& makeHaving) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\ExpressionBuilder.SqlBuilder.cs:line 149
   at LinqToDB.Linq.Builder.ExpressionBuilder.BuildWhere(IBuildContext parent, IBuildContext sequence, LambdaExpression condition, Boolean checkForSubQuery, Boolean enforceHaving) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\ExpressionBuilder.SqlBuilder.cs:line 32
   at LinqToDB.Linq.Builder.WhereBuilder.BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\WhereBuilder.cs:line 23
   at LinqToDB.Linq.Builder.ExpressionBuilder.BuildSequence(BuildInfo buildInfo) in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\ExpressionBuilder.cs:line 175
   at LinqToDB.Linq.Builder.ExpressionBuilder.Build[T]() in C:\projects\linq2db\Source\LinqToDB\Linq\Builder\ExpressionBuilder.cs:line 146
   at LinqToDB.Linq.Query`1.CreateQuery(IDataContext dataContext, Expression expr) in C:\projects\linq2db\Source\LinqToDB\Linq\Query.cs:line 280
   at LinqToDB.Linq.Query`1.GetQuery(IDataContext dataContext, Expression& expr) in C:\projects\linq2db\Source\LinqToDB\Linq\Query.cs:line 233
   at LinqToDB.Linq.ExpressionQuery`1.GetQuery(Expression& expression, Boolean cache) in C:\projects\linq2db\Source\LinqToDB\Linq\ExpressionQuery.cs:line 84
   at LinqToDB.Linq.ExpressionQuery`1.get_SqlText() in C:\projects\linq2db\Source\LinqToDB\Linq\ExpressionQuery.cs:line 53
   at ConsoleApp.Program.WorkWithDb(DbMain db) in C:\Src\Personal\TestPolygon\ConsoleApp\Program.cs:line 22
   at ConsoleApp.Program.WorkWithDb(String dataSource, String initialCatalog) in C:\Src\Personal\TestPolygon\ConsoleApp\Program.Db.cs:line 56
   at ConsoleApp.Program.Main() in C:\Src\Personal\TestPolygon\ConsoleApp\Program.Core.cs:line 21
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Re[3]: [linq2db bug] Исключение в запросе с OUTER APPLY
От: Jack128  
Дата: 31.07.18 06:37
Оценка:
Здравствуйте, Петрухин Эдуард, Вы писали:

ПЭ>Здравствуйте, IT, Вы писали:


IT>>Какая-то каша. Как должен выглядеть запрос на SQL?


ПЭ>Прошу прощения за сумбур!


ПЭ>Итак, сначала:

ПЭ>Есть две таблицы, «Authors» (авторы) и «Books» (книги):
ПЭ>
ПЭ>[Table("Authors")]
ПЭ>public class Author
ПЭ>{
ПЭ>    [PrimaryKey, Identity]
ПЭ>    public int Id { get; set; }

ПЭ>    [Column(CanBeNull = false)]
ПЭ>    public string Name { get; set; }
ПЭ>}

ПЭ>[Table("Books")]
ПЭ>public class Book
ПЭ>{
ПЭ>    [PrimaryKey, Identity]
ПЭ>    public int Id { get; set; }

ПЭ>    [Column(CanBeNull = false)]
ПЭ>    public int AuthorId { get; set; }

ПЭ>    [Column(CanBeNull = false)]
ПЭ>    public string Title { get; set; }
ПЭ>}
ПЭ>

ПЭ>Пытаемся получить количество книг по каждому автору.


Э-э-э, а такие сложности точно нужны ??

var result = 
    from author in db.GetTable<Author>()
    from book in db.GetTable<Book>().InnerJoin(b => b.AuthorId == author.Id)
    group book by new {author.Id, author.Name}
    into gr
    select new {AuthorId = gr.Key.Id, AuthorName = gr.Key.Name, BookCount = gr.Count()}


генерирует вполне ожидаемый запрос


SELECT
    t2.Id,
    t2.Name,
    Count(*) as c1
FROM
    Authors t2
        INNER JOIN Books t1 ON t1.AuthorId = t2.Id
GROUP BY
    t2.Id,
    t2.Name
Re[4]: [linq2db bug] Исключение в запросе с OUTER APPLY
От: Петрухин Эдуард Россия  
Дата: 31.07.18 14:53
Оценка:
Здравствуйте, Jack128, Вы писали:

J>Э-э-э, а такие сложности точно нужны ??


В данном примере конечно не нужны, но это только сокращённый пример кода, на котором ошибка воспроизводится. В реальности там не одна таблица «Authors», а соединение нескольких таблиц; и агрегатов несколько считается (не один); и данные для построения агрегатов через EXISTS отбираются; и фильтрация по агрегату выполняется, только если на UI соответсвующий фильтр задан. При таком подходе, как в моём примере, проще запрос по кускам строить.
К сожалению, не могу сюда добавить реальный пример, из рабочего кода.

Как я понимаю, проблема в том что не может linq2db построить запрос, в котором идёт фильтрация по данным из OUTER APPLY.

Разработчики linq2db, могу надеятся, что повяится поддержка таких запросов?
Может, какие-то неясности ещё остались?
Может, issue на GitHub завести?
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Re[5]: [linq2db bug] Исключение в запросе с OUTER APPLY
От: Danchik Украина  
Дата: 05.08.18 17:25
Оценка:
Здравствуйте, Петрухин Эдуард, Вы писали:

ПЭ>Здравствуйте, Jack128, Вы писали:


J>>Э-э-э, а такие сложности точно нужны ??


ПЭ>В данном примере конечно не нужны, но это только сокращённый пример кода, на котором ошибка воспроизводится. В реальности там не одна таблица «Authors», а соединение нескольких таблиц; и агрегатов несколько считается (не один); и данные для построения агрегатов через EXISTS отбираются; и фильтрация по агрегату выполняется, только если на UI соответсвующий фильтр задан. При таком подходе, как в моём примере, проще запрос по кускам строить.

ПЭ>К сожалению, не могу сюда добавить реальный пример, из рабочего кода.

ПЭ>Как я понимаю, проблема в том что не может linq2db построить запрос, в котором идёт фильтрация по данным из OUTER APPLY.


ПЭ>Разработчики linq2db, могу надеятся, что повяится поддержка таких запросов?

ПЭ>Может, какие-то неясности ещё остались?
ПЭ>Может, issue на GitHub завести?

Да все можно, только времени свободного на это нет. К примеру такие запросы как вы пишете, я иногда сам не понимаю как оттранслировать в SQL.
Начинайте писать терминами SQL и все будет нормально. let — это вам кажется что его легко оттранслировать, но когда дальше начинается с этой переменной возня, это кошмар.
Re[5]: [linq2db bug] Исключение в запросе с OUTER APPLY
От: Danchik Украина  
Дата: 05.08.18 17:53
Оценка:
Здравствуйте, Петрухин Эдуард, Вы писали:

ПЭ>Здравствуйте, Jack128, Вы писали:


J>>Э-э-э, а такие сложности точно нужны ??


ПЭ>В данном примере конечно не нужны, но это только сокращённый пример кода, на котором ошибка воспроизводится. В реальности там не одна таблица «Authors», а соединение нескольких таблиц; и агрегатов несколько считается (не один); и данные для построения агрегатов через EXISTS отбираются; и фильтрация по агрегату выполняется, только если на UI соответсвующий фильтр задан. При таком подходе, как в моём примере, проще запрос по кускам строить.

ПЭ>К сожалению, не могу сюда добавить реальный пример, из рабочего кода.

ПЭ>Как я понимаю, проблема в том что не может linq2db построить запрос, в котором идёт фильтрация по данным из OUTER APPLY.


ПЭ>Разработчики linq2db, могу надеятся, что повяится поддержка таких запросов?

ПЭ>Может, какие-то неясности ещё остались?
ПЭ>Может, issue на GitHub завести?

Желательно сюда кинуть запрос который строите с максимальной сложностью. Дабы понять что творится.
Re[3]: [linq2db bug] Исключение в запросе с OUTER APPLY
От: IT Россия linq2db.com
Дата: 05.08.18 20:56
Оценка:
Здравствуйте, Петрухин Эдуард, Вы писали:

ПЭ>Пытаемся получить количество книг по каждому автору.


from a in authors
select new
{
    a.Name,
    Count = (from b in books where b.AuthorId == a.Id select b).Count()
} into a
where a.Count > 42
select a

SELECT
    [a].[Name] as [Name1],
    [a].[c1] as [c11]
FROM
    (
        SELECT
            (
                SELECT
                    Count(*)
                FROM
                    [Books] [t1]
                WHERE
                    [t1].[AuthorId] = [t2].[Id]
            ) as [c1],
            [t2].[Name]
        FROM
            [Authors] [t2]
    ) [a]
WHERE
    [a].[c1] > 42
Если нам не помогут, то мы тоже никого не пощадим.
Re: [linq2db bug] Исключение в запросе с OUTER APPLY
От: Danchik Украина  
Дата: 07.08.18 15:27
Оценка:
Здравствуйте, Петрухин Эдуард, Вы писали:

[Skip]

ПЭ>Для меня больше важен вариант с

ПЭ>
ПЭ>from book in books
ПЭ>where book.AuthorId == author.Id
ПЭ>select new
ПЭ>{
ПЭ>    Count = Sql.Ext.Count(book.Id).ToValue()
ПЭ>}
ПЭ>

ПЭ>потому что на практике надо считать по подзапросу не один агрегат, а несколько.

Проанализировал я — дело непростое. То как вы записали выражение, это еще парсать и парсать.
Я записал его вот так, и все корректно. И блоками тоже можно достраивать.

var aggregates = from book in db.GetTable<Book>()
    select new
    {
        book.AuthorId,
        RN = Sql.Ext.RowNumber().Over().OrderBy(book.AuthorId).ToValue(),

        BooksCount = Sql.Ext.Count(book.Id).Over().PartitionBy(book.AuthorId).ToValue(),
        Count2 = Sql.Ext.Count(book.Title).Over().PartitionBy(book.AuthorId).ToValue(),
    };

aggregates = aggregates.Where(a => a.RN == 1);

var query = from author in db.GetTable<Author>()
    from a in aggregates.InnerJoin(a => a.AuthorId == author.Id)
    where a.BooksCount > 42
    select new
    {
        author.Name,
        BooksCount = a.BooksCount
    };


Если дадите сценарий более четко с типами запросов что будут, можно и солюшин придумать.
Клеить, напрмер можно так

int? minBookCount = 42;
int? minBookCount2 = null;

var query = from author in db.GetTable<Author>()
    select new
    {
        Author = author,

        BookCount = (from book in db.GetTable<Book>()
            where book.AuthorId == author.Id
            select book).CountExt(b => b.Id, Sql.AggregateModifier.None),

        BookCount2 = (from book in db.GetTable<Book>()
            where book.AuthorId == author.Id
            select book).CountExt(b => b.Id, Sql.AggregateModifier.None)
    };

if (minBookCount != null)
{
    query = query.Where(b => b.BookCount > minBookCount);
}

if (minBookCount2 != null)
{
    query = query.Where(b => b.BookCount2 > minBookCount2);
}

var resultQuery = from author in query
    select new
    {
        author.Author.Name,
        author.BookCount
    };


Тоесть задаем все возможные поля фильтра в первом запросе, а потом используем только части. linq2db лишнее откинет.
Отредактировано 07.08.2018 15:43 Danchik . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.