From 522fed818f22224e2e33ffc68b5863ac21c3acd4 Mon Sep 17 00:00:00 2001 From: Lorenz Stechauner Date: Sat, 14 Oct 2023 14:20:44 +0200 Subject: [PATCH] Billing: Make bin calculate way more efficient --- Elwig/Helpers/AppDbContext.cs | 2 +- Elwig/Helpers/Billing/Billing.cs | 172 ++++++++++++++++--------------- 2 files changed, 91 insertions(+), 83 deletions(-) diff --git a/Elwig/Helpers/AppDbContext.cs b/Elwig/Helpers/AppDbContext.cs index 5d1ca11..de2ccd4 100644 --- a/Elwig/Helpers/AppDbContext.cs +++ b/Elwig/Helpers/AppDbContext.cs @@ -208,7 +208,7 @@ namespace Elwig.Helpers { public async Task> GetMemberBins(Member m, int year) { using var cnx = await ConnectAsync(); - var (rights, obligations) = await Billing.Billing.GetMemberRightsObligations(m.MgNr, year, cnx); + var (rights, obligations) = await Billing.Billing.GetMemberRightsObligations(cnx, year, m.MgNr); var bins = await Billing.Billing.GetMemberBinWeights(m.MgNr, year, cnx); var list = new List<(string, string, int, int, int)>(); diff --git a/Elwig/Helpers/Billing/Billing.cs b/Elwig/Helpers/Billing/Billing.cs index c584d60..087a587 100644 --- a/Elwig/Helpers/Billing/Billing.cs +++ b/Elwig/Helpers/Billing/Billing.cs @@ -39,100 +39,37 @@ namespace Elwig.Helpers.Billing { } public async Task CalculateBins() { + using var cnx = await AppDbContext.ConnectAsync(); + var memberOblRig = await GetMemberRightsObligations(cnx, Year); var inserts = new List<(int, int, int, int, int, int, int)>(); - foreach (var mgnr in Context.Members.Where(m => m.IsActive).OrderBy(m => m.MgNr).Select(m => m.MgNr)) { - inserts.AddRange(await CalculateMemberBins(mgnr)); - } - using var cnx = await AppDbContext.ConnectAsync(); - using var cmd = cnx.CreateCommand(); - cmd.CommandText = $""" - INSERT INTO delivery_part_bin (year, did, dpnr, bin_1, bin_2, bin_3, bin_4, bin_5) - VALUES {string.Join(",\n ", inserts.Select(i => $"({Year}, {i.Item1}, {i.Item2}, {i.Item3}, {i.Item4}, {i.Item5}, {i.Item6}, {i.Item7})"))} - ON CONFLICT DO UPDATE - SET bin_1 = excluded.bin_1, - bin_2 = excluded.bin_2, - bin_3 = excluded.bin_3, - bin_4 = excluded.bin_4, - bin_5 = excluded.bin_5, - bin_6 = NULL, - bin_7 = NULL, - bin_8 = NULL, - bin_9 = NULL; - """; - await cmd.ExecuteNonQueryAsync(); - } - public static async Task<(Dictionary, Dictionary)> GetMemberRightsObligations(int mgnr, int year, SqliteConnection cnx) { - var rights = new Dictionary(); - var obligations = new Dictionary(); - - using var cmd = cnx.CreateCommand(); - cmd.CommandText = $""" - SELECT t.vtrgid, - SUM(COALESCE(area * min_kg_per_ha, 0)) / 10000 AS min_kg, - SUM(COALESCE(area * max_kg_per_ha, 0)) / 10000 AS max_kg - FROM area_commitment c - JOIN area_commitment_type t ON t.vtrgid = c.vtrgid - WHERE mgnr = {mgnr} AND (year_from IS NULL OR year_from <= {year}) AND (year_to IS NULL OR year_to >= {year}) - GROUP BY t.vtrgid - ORDER BY LENGTH(t.vtrgid) DESC, t.vtrgid - """; - - var reader = await cmd.ExecuteReaderAsync(); - while (await reader.ReadAsync()) { - var vtrgid = reader.GetString(0); - obligations[vtrgid] = reader.GetInt32(1); - rights[vtrgid] = reader.GetInt32(2); - } - - return (rights, obligations); - } - - public static async Task> GetMemberBinWeights(int mgnr, int year, SqliteConnection cnx) { - var bins = new Dictionary(); - - using var cmd = cnx.CreateCommand(); - cmd.CommandText = $""" - SELECT bin, weight - FROM v_bin - WHERE (year, mgnr) = ({year}, {mgnr}) - """; - - var reader = await cmd.ExecuteReaderAsync(); - while (await reader.ReadAsync()) { - var bin = reader.GetString(0); - bins[bin] = reader.GetInt32(1); - } - - return bins; - } - - protected async Task> CalculateMemberBins(int mgnr) { - using var cnx = await AppDbContext.ConnectAsync(); - var (rights, obligations) = await GetMemberRightsObligations(mgnr, Year, cnx); - - var deliveries = new List<(int, int, string, int, double, string, string[], string[])>(); + var deliveries = new List<(int, int, int, string, int, double, string, string[], string[])>(); using (var cmd = cnx.CreateCommand()) { cmd.CommandText = $""" - SELECT did, dpnr, sortid, weight, kmw, qualid, attributes, modifiers + SELECT mgnr, did, dpnr, sortid, weight, kmw, qualid, attributes, modifiers FROM v_delivery - WHERE (year, mgnr) = ({Year}, {mgnr}) - ORDER BY sortid, abgewertet ASC, LENGTH(attributes) DESC, COALESCE(attributes, '~'), kmw DESC, lsnr, dpnr + WHERE year = {Year} + ORDER BY mgnr, sortid, abgewertet ASC, LENGTH(attributes) DESC, COALESCE(attributes, '~'), kmw DESC, lsnr, dpnr """; var reader = await cmd.ExecuteReaderAsync(); while (await reader.ReadAsync()) { deliveries.Add(( - reader.GetInt32(0), reader.GetInt32(1), reader.GetString(2), reader.GetInt32(3), - reader.GetDouble(4), reader.GetString(5), - reader.IsDBNull(6) ? Array.Empty() : reader.GetString(6).Split(",").Order().ToArray(), - reader.IsDBNull(7) ? Array.Empty() : reader.GetString(7).Split(",").Order().ToArray() + reader.GetInt32(0), reader.GetInt32(1), reader.GetInt32(2), reader.GetString(3), reader.GetInt32(4), + reader.GetDouble(5), reader.GetString(6), + reader.IsDBNull(7) ? Array.Empty() : reader.GetString(7).Split(",").Order().ToArray(), + reader.IsDBNull(8) ? Array.Empty() : reader.GetString(8).Split(",").Order().ToArray() )); } } - List<(int, int, int, int, int, int, int)> inserts = new(); - foreach (var (did, dpnr, sortid, weight, kmw, qualid, attributes, modifiers) in deliveries) { - if (qualid == "WEI" || qualid == "RSW" || qualid == "LDW") { + int lastMgNr = 0; + Dictionary? rights = null; + foreach (var (mgnr, did, dpnr, sortid, weight, kmw, qualid, attributes, modifiers) in deliveries) { + if (lastMgNr != mgnr) { + rights = memberOblRig.GetValueOrDefault(mgnr, (new(), new())).Item2; + } + if (rights == null || rights.Count == 0 || qualid == "WEI" || qualid == "RSW" || qualid == "LDW") { + // Mitglied hat keine Flächenbindungen, oder // Nicht mindestens Qualitätswein (QUW) -> ungebunden inserts.Add((did, dpnr, 0, 0, 0, 0, weight)); continue; @@ -160,8 +97,79 @@ namespace Elwig.Helpers.Billing { } } inserts.Add((did, dpnr, b[0], b[1], b[2], b[3], weight - b[0] - b[1] - b[2] - b[3])); + lastMgNr = mgnr; } - return inserts; + + using (var cmd = cnx.CreateCommand()) { + cmd.CommandText = $""" + INSERT INTO delivery_part_bin (year, did, dpnr, bin_1, bin_2, bin_3, bin_4, bin_5) + VALUES {string.Join(",\n ", inserts.Select(i => $"({Year}, {i.Item1}, {i.Item2}, {i.Item3}, {i.Item4}, {i.Item5}, {i.Item6}, {i.Item7})"))} + ON CONFLICT DO UPDATE + SET bin_1 = excluded.bin_1, + bin_2 = excluded.bin_2, + bin_3 = excluded.bin_3, + bin_4 = excluded.bin_4, + bin_5 = excluded.bin_5, + bin_6 = NULL, + bin_7 = NULL, + bin_8 = NULL, + bin_9 = NULL; + """; + await cmd.ExecuteNonQueryAsync(); + } + } + + public static async Task, Dictionary)>> GetMemberRightsObligations(SqliteConnection cnx, int year, int? mgnr = null) { + var members = new Dictionary, Dictionary)>(); + + using var cmd = cnx.CreateCommand(); + cmd.CommandText = $""" + SELECT mgnr, t.vtrgid, + SUM(COALESCE(area * min_kg_per_ha, 0)) / 10000 AS min_kg, + SUM(COALESCE(area * max_kg_per_ha, 0)) / 10000 AS max_kg + FROM area_commitment c + JOIN area_commitment_type t ON t.vtrgid = c.vtrgid + WHERE ({(mgnr == null ? "NULL" : mgnr)} IS NULL OR mgnr = {(mgnr == null ? "NULL" : mgnr)}) AND + (year_from IS NULL OR year_from <= {year}) AND + (year_to IS NULL OR year_to >= {year}) + GROUP BY mgnr, t.vtrgid + ORDER BY LENGTH(t.vtrgid) DESC, t.vtrgid + """; + + var reader = await cmd.ExecuteReaderAsync(); + while (await reader.ReadAsync()) { + var m = reader.GetInt32(0); + var vtrgid = reader.GetString(1); + if (!members.ContainsKey(m)) members[m] = (new(), new()); + members[m].Item1[vtrgid] = reader.GetInt32(2); + members[m].Item2[vtrgid] = reader.GetInt32(3); + } + + return members; + } + + public static async Task<(Dictionary, Dictionary)> GetMemberRightsObligations(SqliteConnection cnx, int year, int mgnr) { + var members = await GetMemberRightsObligations(cnx, year, (int?)mgnr); + return members[mgnr]; + } + + public static async Task> GetMemberBinWeights(int mgnr, int year, SqliteConnection cnx) { + var bins = new Dictionary(); + + using var cmd = cnx.CreateCommand(); + cmd.CommandText = $""" + SELECT bin, weight + FROM v_bin + WHERE (year, mgnr) = ({year}, {mgnr}) + """; + + var reader = await cmd.ExecuteReaderAsync(); + while (await reader.ReadAsync()) { + var bin = reader.GetString(0); + bins[bin] = reader.GetInt32(1); + } + + return bins; } } }