Форум программистов, компьютерный форум, киберфорум
C#: Базы данных
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 5.00/18: Рейтинг темы: голосов - 18, средняя оценка - 5.00
9 / 6 / 3
Регистрация: 10.01.2020
Сообщений: 330
.NET 4.x

Как опитмально поступить в передаче состояния у наследников (foreignkey)

03.06.2021, 08:59. Показов 3998. Ответов 55

Студворк — интернет-сервис помощи студентам
Всем доброе утро!!!

Представим вот такую задачу.

Есть некая онлайн школа допустим программирования.
В ней есть несколько курсов по разным ЯП (таблица Course).
Есть студенты (таблица Student), которые могут состоять ТОЛЬКО в одном курсе. То есть ТОЛЬКО один язык.
В кажом курсе может быть до 1000 студентов.

Есть структура базы
SQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
CREATE TABLE `Course` (
    `Id` INT(11) NOT NULL AUTO_INCREMENT,
    `Name` VARCHAR(50) NOT NULL,
    `Status` INT(11) NOT NULL
);
 
 
 
CREATE TABLE `Student` (
    `Id` INT NOT NULL AUTO_INCREMENT,
    `Name` VARCHAR(50) NOT NULL,
    `Status` INT NOT NULL,
    `CourseId` INT NOT NULL,
    CONSTRAINT `FK_course` FOREIGN KEY (`CourseId`) REFERENCES `Course` (`Id`)
);

У каждого студента есть свой статус/состояние учёбы в этом курсе.
Статусов ограниченное количество, поэтому они вынесены в код C# в enum, а в базу передаётся только число этого enum для оптимизации работы БД.
Допустим

Принят = 0,
Сдал = 1,
Должник = 2,
Не Сдал = 3,
Отсутствовал = 4,

Так вот задача.

В таблицу Course нужно передавать статус от всех студентов.
Вот что я имею в виду:
Есть курс "Программирование C#".
Есть 2 студента в этом курсе.
Вася со статусом "Не Сдал" = 3
Петя со статусом "Сдал" = 1

Нужно установить статус курсу, по самому безотвественному студенту, так как курс не может быть закрыт/закончен (со статусом "Сдал" = 1) до тех пор пока хоть один студент находится в другом состоянии.

Логика должна быть примерно такая:
Если в курсе есть хоть один студент (Student.Status) который в статусе Отсутствовал = 4, то и в Course.Status должно быть Отсутствовал = 4.
Если есть хоть один Student.Status Не Сдал = 3, то и в Course.Status должно быть Не Сдал = 3.
Если есть хоть один Student.Status Должник = 2, то и в Course.Status должно быть Должник = 2.
Если есть хоть один Student.Status Сдал = 1, то и в Course.Status должно быть Сдал = 1.
Если есть хоть один Student.Status Принят = 0, то и в Course.Status должно быть Принят = 0.

Данные в таблице по студентам обновляются каждый час.
То есть каждый час, после обновления таблицы Student нужно проверять статусы наследников, и устанавливать новые статусы в таблицу Course.

Данных в таблице (Student) будет более 10млн. И данных в таблице (Course) будет больше 1млн.

Как это сделать с минимальной выгрузкой всех данных в программу?

Или ещё лучше как сформировать запрос SQL чтобы это всё происходило на стороне СУБД, без получения данных в программу.
Или может SQL запрос который получает полуобработанные данные вида:
CourseId - Status(уже проверенный)

Или может уже существует подход к такой задаче.

p.s. Используется MySQL база и библиотека linq2db.
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
03.06.2021, 08:59
Ответы с готовыми решениями:

Фильтрация ForeignKey поля по другому ForeignKey полю в админке
Всем привет, форумчане и джангисты в частности. Нужна ваша помощь. Свой пример приводить не буду, т.к. слишком много лишнего, но на...

Как добавить ключ FOREIGNKEY в таблицу?
Установила денвер-3,на английском.В MySql создала таблицу.Не могу никакими судьбами,туплю,добавить ключ FOREIGKEY.кТО СКАЖЕТ,как его...

Как правильно указать default для ForeignKey?
class vedModel(models.Model): # прикрепит к базовому объекту профиля base_id = models.ForeignKey('account.profileModel',...

55
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16129 / 11253 / 2888
Регистрация: 21.04.2018
Сообщений: 33,086
Записей в блоге: 2
03.06.2021, 10:23
Цитата Сообщение от BeginnerCoderCS Посмотреть сообщение
Элд Хасп,... что может подскажете?
Цитата Сообщение от BeginnerCoderCS Посмотреть сообщение
Как это сделать с минимальной выгрузкой всех данных в программу?
Насколько я себе представляю - только SQL.
Но в этом я ничего вам подсказать не могу.
Изучал его лет тридцать назад - уже всё забыл.

Добавлено через 14 минут
На Шарпе это будет без больших проблем.
Что-то в таком духе:
C#
1
2
3
4
5
6
7
8
9
using(var db = new ....)
{
    db.Courses.ToList()
        .ForEach(course => course.Status = db.Students.Where(st => course.Id == st.CourseId)
            .Select(st => st.Status)
            .Min()
        );
   db.SaveChanges();
}
Но такая реализация приведёт (насколько в этом я разбираюсь) к полной выгрузке всех записей БД.

В разделе, думаю, смогут подсказать как правильно это сделать на стороне сервера.
1
800 / 583 / 207
Регистрация: 21.02.2019
Сообщений: 2,095
03.06.2021, 10:28
BeginnerCoderCS,
.. ваша логика не совсем понятна ... если статус курса устанавливается по "самому безответственному", т.е. прогульщику со статусом 4, то тогда можно что-то типа
SQL
1
2
SELECT @courseId = Id FROM Course WHERE Name = 'Программирование C#'
UPDATE Course SET STATUS = (SELECT MAX(STATUS) FROM Student WHERE CourseId = @courseId) WHERE Id = @courseId
2
9 / 6 / 3
Регистрация: 10.01.2020
Сообщений: 330
03.06.2021, 10:39  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Но такая реализация приведёт (насколько в этом я разбираюсь) к полной выгрузке всех записей БД.
Да, будет полная выгрузка, а данных очень много.

Но в любом случае спасибо за отклик.

Добавлено через 8 минут
Цитата Сообщение от carrotik Посмотреть сообщение
.. ваша логика не совсем понятна ... если статус курса устанавливается по "самому безответственному", т.е. прогульщику со статусом 4, то тогда можно что-то типа
"самому безответственному" почти так, но не совсем.
В примере так получилось случайно.
Нельзя привязываться к возростанию порядкового номера.


Допустим важность статуса должна быть в таком порядке
Принят = 0
Отсутствовал = 4
Должник = 2
Не Сдал = 3
Сдал = 1

Если есть хоть один студент со статусом Принят = 0, то курсу установим Принят = 0,
Если есть хоть один студент со статусом Отсутствовал = 4 то курсу установим Отсутствовал = 4
и т.д.

Так как в будущем может измениться логика наследования статуса, и порядок важности статусов будет другим.

То есть без привязки возрастания числа.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16129 / 11253 / 2888
Регистрация: 21.04.2018
Сообщений: 33,086
Записей в блоге: 2
03.06.2021, 10:48
BeginnerCoderCS, дополнение к ответу от carrotik.
Наверное, имеет смысл в таблице Студентов задать дополнительную индексацию по сочетанию ключей CourseId и Status.
В таком случае необходимо для минимального/максимального Статуса выбрать только одну первую/последнюю запись по этому индексу.

Добавлено через 8 минут
Цитата Сообщение от BeginnerCoderCS Посмотреть сообщение
Так как в будущем может измениться логика наследования статуса, и порядок важности статусов будет другим.
То есть без привязки возрастания числа.
Это уже в принципе неверно.
Как запрос должен определить приоритеты статусов?
Он же не может "залезть в голову" и догадаться какой из них выше - какой ниже.
Приоритет должен быть либо неявно задан значением самого Статуса.
Либо необходима ещё одна дополнительная таблица, по которой определяется приоритетность Статусов.
1
9 / 6 / 3
Регистрация: 10.01.2020
Сообщений: 330
03.06.2021, 10:56  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Он же не может "залезть в голову" и догадаться какой из них выше - какой ниже.
Приоритет должен будет меняться в программе.
Это не частая задача, но это будет, так как будут добавляться новые статусы, и они могут быть как выше, так и ниже в приоритете.

Цитата Сообщение от Элд Хасп Посмотреть сообщение
Либо необходима ещё одна дополнительная таблица, по которой определяется приоритетность Статусов.
Но можно и так задать приоритет, если это облегчит базу.
0
800 / 583 / 207
Регистрация: 21.02.2019
Сообщений: 2,095
03.06.2021, 10:56
Цитата Сообщение от BeginnerCoderCS Посмотреть сообщение
Если есть хоть один студент со статусом Принят = 0, то курсу установим Принят = 0,
Если есть хоть один студент со статусом Отсутствовал = 4 то курсу установим Отсутствовал = 4
,. а если полмиллиона принято, а среди них сто тыщ прогульщиков? ... я до сих пор не понял приоритета ...
..но в любом случае можно использовать where status <> 3 or .....
1
9 / 6 / 3
Регистрация: 10.01.2020
Сообщений: 330
03.06.2021, 11:54  [ТС]
Цитата Сообщение от carrotik Посмотреть сообщение
,. а если полмиллиона принято, а среди них сто тыщ прогульщиков? ... я до сих пор не понял приоритета ...
..но в любом случае можно использовать where status <> 3 or .....
Пример с курсами - просто пример. На самом деле там всё гораздо сложнее, и я вряд ли бы смог это всё объяснить, чтобы было понято. Поэтому привёл пример с курсами.


Если учесть что приоритет вот такой.
Чем выше тем важнее.
Принят = 0
Отсутствовал = 4
Должник = 2
Не Сдал = 3
Сдал = 1

Пример 1:
В курсе "C#" 100 студентов.
Из них
50 студентов Сдал = 1
49 студентов Должник = 2
1 студент Отсутствовал = 4

Значит Курсу "C#" присвоить статус Отсутствовал = 4,
так как статус Отсутствовал = 4 из всех имеющихся в курсе выше всего в приоритете.


Пример 2:
В курсе "Java" 1000 студентов.
Из них
500 студентов Сдал = 1
450 студентов Принят = 0
49 студентов Не Сдал
1 студент Отсутствовал = 4


Значит Курсу "Java" присвоить статус Принят = 0,
так как статус Принят = 0 из всех имеющихся в курсе выше всего в приоритете.


Пример 3:
В курсе "SQL" 100 студентов.
Из них
99 студентов Сдал = 1
1 студент Не Сдал = 3

Значит Курсу "SQL" присвоить статус Не Сдал = 3,
так как статус Не Сдал = 3 из всех имеющихся в курсе выше всего в приоритете.


Желательно не смотрите на логику самого статуса (принят или сдал), а просто на последовательность важности. Это сделано для примера.

Добавлено через 29 минут
carrotik, накидал простенький пример, как это сделать на C#. Надеюсь будет понятнее.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class Course
{
    public int Id;
    public string Name;
    public int Status;
}
public class Student
{
    public int Id;
    public string Name;
    public int Status;
    public int CourseId;
}
 
public void UpdateCourseStatus()
{
    var course = new Course
    {
        Id = 777,
        Name = "C#",
        Status = -1, // нельзя null
    };
 
    List<Student> students = new List<Student>
    {
        new Student{ Id = 1, Name = "Иван", CourseId = 777, Status = 1}, // Сдал
        new Student{ Id = 2, Name = "Пётр", CourseId = 777, Status = 2 }, // Должник
        new Student{ Id = 3, Name = "Фёдор", CourseId = 777, Status = 3 }, // Не Сдал
    };
 
 
    // Список последовательности приоритетов.
    // Принят = 0
    // Отсутствовал = 4
    // Должник = 2
    // Не Сдал = 3
    // Сдал = 1
    // В нашем случае Статус Должник = 2 (который у Петра) должен стать статуом курса C#
    List<int> priority = new List<int> { 0, 4, 2, 3, 1 };
 
    for (int i = 0; i < priority.Count; i++)
    {
        var status = students
            .Where(x => x.Status == priority[i])
            .Select(x => x.Status)
            .FirstOrDefault();
 
        if (status != 0)
        {
            course.Status = status;
            break;
            // Как только нашли самый приоритетный, устанавливаем, и дальше не ищем.
        }
    }
}
0
785 / 616 / 273
Регистрация: 04.08.2015
Сообщений: 1,713
03.06.2021, 12:20
BeginnerCoderCS, добавьте в таблицу статусов приоритет и получайте не статус, а макс или мин приоритет.
1
9 / 6 / 3
Регистрация: 10.01.2020
Сообщений: 330
03.06.2021, 13:23  [ТС]
Цитата Сообщение от Igr_ok Посмотреть сообщение
добавьте в таблицу статусов приоритет и получайте не статус, а макс или мин приоритет.
Можно чуть подробнее?
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16129 / 11253 / 2888
Регистрация: 21.04.2018
Сообщений: 33,086
Записей в блоге: 2
03.06.2021, 13:31
Цитата Сообщение от BeginnerCoderCS Посмотреть сообщение
Но можно и так задать приоритет, если это облегчит базу.
Обязательно.
Поскольку по другому невозможно будет выполнить на стороне сервера.
У сервера должна быть вся необходимая информация.

Добавлено через 3 минуты
Цитата Сообщение от BeginnerCoderCS Посмотреть сообщение
Можно чуть подробнее?
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Status
{
    public int Id;
    public string Title;
    public int Priority;
}
 
public class Course
{
    public int Id;
    public string Name;
    public int StatusId;
}
public class Student
{
    public int Id;
    public string Name;
    public int StatusId;
    public int CourseId;
}
1
785 / 616 / 273
Регистрация: 04.08.2015
Сообщений: 1,713
03.06.2021, 14:39
Цитата Сообщение от BeginnerCoderCS Посмотреть сообщение
Можно чуть подробнее?
Создаете таблицу Statuses. Поля StatusID, StatusName, Priority.
Тогда запрос будет выглядеть так:
T-SQL
1
2
3
4
select CourseId, min(Statuses.Priority) 
from Student 
inner join Statuses on Student.Status=Statuses.StatusID
Group by CourseId
1
1497 / 1238 / 245
Регистрация: 04.04.2011
Сообщений: 4,363
03.06.2021, 15:36
На таблицу студентов вешается триггер AfterUpdate, в котором вызывается хранимка, в котрой
вычисляется минимальный статус по курсу, где студент. Этот минимальный статус записывается в поле статуса курса так, как было указано выше.
При любом изменении любого студента автоматом в курс пишется новый статус.
Никаких "почасовых" проверок-изменений не нужно, никакой таблицы статусов тоже.
Все делается на сервере, ничего никуда не тянется.

Все дела
2
9 / 6 / 3
Регистрация: 10.01.2020
Сообщений: 330
03.06.2021, 16:50  [ТС]
Цитата Сообщение от MsGuns Посмотреть сообщение
При любом изменении любого студента автоматом в курс пишется новый статус.
На сколько это будет затратная операция?
Частота изменения студентов планируется один раз в час, если были изменения.
То есть грубо в таблице студентов 10млн строк.
И каждый час будет меняться значения ~ от 10 000 до 500 000 студентов

Это не положит субд?
0
1497 / 1238 / 245
Регистрация: 04.04.2011
Сообщений: 4,363
03.06.2021, 17:35
Цитата Сообщение от BeginnerCoderCS Посмотреть сообщение
Это не положит субд?
MySQL ? Не положит
Мне трудно представить не веб систему, в которой одновременно работают десятки тысяч реальных юзеров.
1
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16129 / 11253 / 2888
Регистрация: 21.04.2018
Сообщений: 33,086
Записей в блоге: 2
03.06.2021, 17:38
Цитата Сообщение от BeginnerCoderCS Посмотреть сообщение
И каждый час будет меняться значения ~ от 10 000 до 500 000 студентов
Если update БД предусматривается только редкое пакетное, то оптимальнее будет внесение связанных изменений после update.
Если изменения частые и единичные, то вариант MsGuns предпочтительнее.

Добавлено через 1 минуту
BeginnerCoderCS, просто такое обновление (очень редкое и большими пакетами) довольно не типично и несклоько ставит в тупик, как уже заметил MsGuns.
1
1497 / 1238 / 245
Регистрация: 04.04.2011
Сообщений: 4,363
03.06.2021, 17:43
Не положит по двум причинам.
Во-первых триггеры сами по себе работают ну ооочень быстро. 1млн срабатываний займет несколько сек.
Во вторых, хранимка выполняет два действия: 1. вычисление min по единственной таблице, к тому же по "короткому" полю - это работает мгновенно даже на миллионе записей. 2. Update единственной записи (по Id) - это доли мс + проходит далеко не всегда - если новый статус ниже вычисленного. Итого, как правило, все ограничится лишь выборкой без апдейта.
Т.е. вся транзакция будет занимать макс. 1-2 милисекунды.
2
785 / 616 / 273
Регистрация: 04.08.2015
Сообщений: 1,713
03.06.2021, 17:53
Цитата Сообщение от BeginnerCoderCS Посмотреть сообщение
Это не положит субд?
Триггер будет срабатывать после обновления каждой записи. Т.е.
Цитата Сообщение от BeginnerCoderCS Посмотреть сообщение
от 10 000 до 500 000
раз в час. А извлечение данных запросом будет только тогда, когда эти данные кому-то понадобятся. Что вам подсказывает здравый смысл?)
Пара советов.
1.Используйте триггеры только тогда, когда без них не обойтись.
2.Храните в таблице вычисляемое поле только тогда, когда логика его вычисления затратна по времени.
1
1497 / 1238 / 245
Регистрация: 04.04.2011
Сообщений: 4,363
03.06.2021, 18:03
Если утверждение о сотнях тысяч в час изменений в БД (в чем я сильно сомневаюсь, разве что речь идет об автоматических процессах) верно и "тормоза" все же появятся, то с ними (тормозами) опять же можно успешно побороться, навесив на поле минимального статуса в таблице курсов вторичный индекс. При условии длинных текстов в остальных полях таблицы курсов это даст ускорение выборки в разы, а то и на порядок.

Добавлено через 4 минуты
Цитата Сообщение от Igr_ok Посмотреть сообщение
раз в час. А извлечение данных запросом будет только тогда, когда эти данные кому-то понадобятся
А вот тут не понял. Триггер будет срабатывать раз в час ?

Цитата Сообщение от Igr_ok Посмотреть сообщение
1.Используйте триггеры только тогда, когда без них не обойтись.
Да что Вы говорите.. Серьезно ? Тогда приведите пример, "когда без них не обойтись".
Цитата Сообщение от Igr_ok Посмотреть сообщение
2.Храните в таблице вычисляемое поле только тогда, когда логика его вычисления затратна по времени.
О каком вычисляемом поле идет речь ? Min по всем студентам данного курса ?

Добавлено через 1 минуту
Поправка: вторичный индекс вешается на таблицу студентов, конечно же,- ведь именно из нее определяется минимальный статус
1
785 / 616 / 273
Регистрация: 04.08.2015
Сообщений: 1,713
03.06.2021, 18:26
MsGuns, в 6 ТС объяснил, почему одного поля статус недостаточно. А на троллинг я не ведусь
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
03.06.2021, 18:26
Помогаю со студенческими работами здесь

Как правильно отлавливать Constraint ForeignKey в DAO?
Введение: как известно, В БД при соответствующие настроенном ForeignKey constraint при попытке удалить запись (если на неё будут ссылаться...

Как изменить отображение текста ForeignKey в моделе
Здравствуйте, Возможно я не правильно гуглю, но не смог найти нужной мне информации, а опыта допереть до этого самому не хватает...

Как шифровать файлы при передаче на сервер и дешефровать при передаче с сервера на клиент
Есть программа клиент/сервер, файлы со стороны клиента передаются на сервер, если были внесены изменения. Как правильно зашифровать файлы,...

Как сделать каскадный вызов элементов ForeignKey в одной view в Django?
Задача: Вывести статью, в которой будет 3 вопроса (из относящейся модели), в каждом вопросе будет по 3 ответа (уже относящиеся к самому...

Как посмотреть наследников класса
Возможно вопрос задавался на форуме уже, я не нашел. Собственно, есть класс в каком-то пространстве имен. Как посмотреть...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
Конвертировать закладки radiotray-ng в m3u-плейлист
damix 19.02.2026
Это можно сделать скриптом для PowerShell. Использование . \СonvertRadiotrayToM3U. ps1 <path_to_bookmarks. json> Рядом с файлом bookmarks. json появится файл bookmarks. m3u с результатом. # Check if. . .
Семь CDC на одном интерфейсе: 5 U[S]ARTов, 1 CAN и 1 SSI
Eddy_Em 18.02.2026
Постепенно допиливаю свою "многоинтерфейсную плату". Выглядит вот так: https:/ / www. cyberforum. ru/ blog_attachment. php?attachmentid=11617&stc=1&d=1771445347 Основана на STM32F303RBT6. На борту пять. . .
Символьное дифференцирование
igorrr37 13.02.2026
/ * Программа принимает математическое выражение в виде строки и выдаёт его производную в виде строки и вычисляет значение производной при заданном х Логарифм записывается как: (x-2)log(x^2+2) -. . .
Камера Toupcam IUA500KMA
Eddy_Em 12.02.2026
Т. к. у всяких "хикроботов" слишком уж мелкий пиксель, для подсмотра в ESPriF они вообще плохо годятся: уже 14 величину можно рассмотреть еле-еле лишь на экспозициях под 3 секунды (а то и больше),. . .
И ясному Солнцу
zbw 12.02.2026
И ясному Солнцу, и светлой Луне. В мире покоя нет и люди не могут жить в тишине. А жить им немного лет.
«Знание-Сила»
zbw 12.02.2026
«Знание-Сила» «Время-Деньги» «Деньги -Пуля»
SDL3 для Web (WebAssembly): Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 12.02.2026
Содержание блога Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами и вызывать обработчики событий столкновения. . . .
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 11.02.2026
Содержание блога Библиотека SDL3 содержит встроенные инструменты для базовой работы с изображениями - без использования библиотеки SDL3_image. Пошагово создадим проект для загрузки изображения. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru