使用一系列整数左连接表?

问题描述 投票:0回答:1

我有一个包含两个表的层次结构:

CREATE TABLE A (
    Id nvarchar(50) NOT NULL,
    ChildrenCount int NOT NULL
)
CREATE TABLE B (
    ParentId nvarchar(50) NOT NULL,
    I int NOT NULL,
    OtherColumns text NULL,
    AFlag bit NOT NULL
)

在表 A 中,我有一个

ChildrenCount
属性,指示最终将出现在表 B 中的相关记录的数量,因此此时其中一些记录可能会丢失,请注意,这不是计算出有多少个孩子的值存在于数据库中。事实上,我们的目标是获取每位家长丢失的记录。 这是两个表中的数据示例:

身份证 儿童数
AA 1
BB 5
抄送 3
DD 1000
家长ID A专栏 AF标志
BB 0 文字 0
BB 2 文字 1
BB 4 0
抄送 0 1
抄送 1 0
抄送 2 文字 1

生成的 SQL 查询应该如下所示: |Id|I |ParentId |I |AColumn |AFlag| |- |- |- |- |- |-| |AA|0 |NULL |NULL |NULL |NULL| |BB|0 |BB |0 |文本 |0 | |BB|1 |NULL |NULL |NULL |NULL | |BB|2 |BB |2 |文本 |1 | |BB|3 |NULL |NULL |NULL |NULL | |BB|4 |BB |4 |NULL |0 | |CC|0 |CC |0 |NULL |1 | |CC|1 |CC |1 |NULL |0 | |CC|2 |CC |2 |文本 |1 | |DD|0 |NULL |NULL |NULL |NULL | |DD|1 |NULL |NULL |NULL |NULL | |~ |~ |~ |~ |~ |~ | |DD|999 |空 |空 |空 |空 |

我正在使用实体框架,我正在寻找一种更简洁的解决方案来维护此 C# 代码,并让我使用 LINQ 生成更复杂的查询。

var sequenceQuery = "<SOME RAW SQL>";
var query = dbc.Set<A>()
    .SelectMany(a => dbc.Set<Int32Record>()
                        .FromSqlRaw(sequenceQuery)
                        .Where(i => i.Value < a.ChildrenCount)
                        .Select(i => new { a, i }))
    .GroupJoin(dbc.Set<B>(),
        x => new { ParentId = x.a.Id, I = x.i.Value },
        x => new { x.ParentId, x.I },
        (x, bs) => new { x.a, I = x.i.Value, Bs = bs.ToArray() })
    .SelectMany(x => x.Bs.DefaultIfEmpty(), (x, b) => new { A = x.a, x.I, B = b });

问题是如何生成整数序列,无论是使用递归查询还是使用 T-SQL 函数

generate_series
,以及如何在 EF 中使用它:

SQL 中的第一种方法是递归查询:

var sequenceQuery = """
    WITH seq(i) AS (
        SELECT 0
        UNION ALL
        SELECT seq.i + 1 FROM seq WHERE (seq.i + 1) < 32767
    )
    SELECT seq.i FROM seq OPTION (MAXRECURSION 32767)
"""

问题是WITH子句要求它包装查询的其余部分,并且它不适合与

FromSqlRaw
一起使用。

另一种可能的解决方案是使用 T-SQL 函数

generate_series
:

var sequenceQuery = """
            SELECT value 
              FROM generate_series(0, (SELECT MAX(childrenCount) FROM A) - 1)
            """;

生成的 sql 查询将类似于以下内容:

SELECT A.Id, s.I, B.*
  FROM A
  JOIN (SELECT value AS I FROM generate_series(0, (SELECT MAX(ChildrenCount) FROM A))) s 
    ON s.I < ChildrenCount
  LEFT JOIN B ON B.ParentId = A.Id AND B.I = s.I
 ORDER BY A.Id, s.I

它确实获得了正确的数据,但我一直在寻找一种在数据库发生更改时不会出现问题的解决方案。

编辑:我对示例做了一些更改,因为前一个示例具有误导性。

sql-server linq entity-framework-core
1个回答
1
投票

您可以在模型中创建自定义表值函数映射

public IQueryable<int> GenerateSeries(int start, int stop, int step = 1)
    => FromExpression(() => GenerateSeries(start, stop, step));

在您的配置中

modelBuilder.HasDbFunction(typeof(MyDbContext).GetMethod(nameof(GenerateSeries), new[] { typeof(int), typeof(int), typeof(int) }));

现在你可以做

var results =
    from a in db.A
    from v in db.GenerateSeries(1, a.childrenCount)
    join b in db.B on new { a.id, v } equals { b.parentId, b.i } into g1
        from b in g1.DefaultIfEmpty()
    orderby a.id, v
    select new {
        a.id,
        value: v,
        b.i
    };
© www.soinside.com 2019 - 2024. All rights reserved.