Professional Documents
Culture Documents
KursovaBD E25ouob3.zp2
KursovaBD E25ouob3.zp2
ВСТУП..........................................................................................................................6
1 АНАЛІЗ ПРЕДМЕТНОЇ ОБЛАСТІ........................................................................7
1.1 Предметна область......................................................................................7
1.2 Поняття реляційної бази даних та СУБД..................................................8
1.3 Обґрунтування вибору СУБД..................................................................17
1.4 Обґрунтування вибору мови програмування..........................................20
1.5 Висновки....................................................................................................21
2 ПРОЕКТУВАННЯ БАЗИ ДАНИХ.......................................................................22
2.1 Проектування методом сутність-зв'язок.................................................22
2.2 Проектування ER-моделі..........................................................................26
2.3 Метод нормалізації відношень.................................................................29
2.4 Висновки....................................................................................................33
3 ПРАКТИЧНА РЕАЛІЗАЦІЯ БАЗИ ДАНИХ.......................................................34
3.1 Розробка таблиць.......................................................................................34
3.2 Розробка запитів........................................................................................38
3.3 Розробка форм...........................................................................................43
3.4 Розробка звітів...........................................................................................47
3.5 Висновки....................................................................................................54
ВИСНОВКИ...............................................................................................................55
ПЕРЕЛІК ПОСИЛАНЬ.............................................................................................56
ДОДАТКИ..................................................................................................................57
Додаток А Лістинг створення контексту бази даних...................................58
Додаток Б Форма для ведення журналу........................................................59
Додаток В Лістинг обробки форми для ведення журналу..........................60
Додаток Д Головне вікно програми...............................................................64
6
ВСТУП
3) SQLite:
а) відсутність системи користувачів - більшість СУБД включають в
свій склад системи управління правами доступу користувачів;
б) відсутність можливості збільшення продуктивності.
Беручи до уваги недоліки розглянутих СУБД для виконання завдання
курсової роботи, а саме створення бази даних для електронного журналу
користувача було обрано СУБД MS SQL через вбудовану підтримку .NET
Framework, платформи, що буде використана для написання клієнтського додатку
для роботи з базою даних, що буде розроблена. Завдяки цій підтримці, процедури
бази даних, що зберігаються, можуть бути написані на будь-якій мові
платформи .NET з використанням повного набору бібліотек, доступних для .NET
Framework. На відміну від інших процесів, .NET Framework виділяє додаткову
пам'ять і будує засоби керування SQL Server, не використовуючи вбудовані
засоби Windows. Це підвищує продуктивність порівняно із загальними
алгоритмами Windows, оскільки алгоритми розподілу ресурсів спеціально
налагоджені для використання у структурах SQL Server.
Microsoft SQL Server — комерційна система керування базами даних, що
розповсюджується корпорацією Microsoft. Мова, що використовується для запитів
— Transact-SQL, створена спільно Microsoft та Sybase. Transact-SQL є реалізацією
стандарту ANSI/ISO щодо структурованої мови запитів (SQL) із розширеннями.
Використовується як для невеликих і середніх за розміром баз даних, так і для
великих баз даних масштабу підприємства.
Microsoft SQL Server як мову запитів використовує версію SQL, що
отримала назву TRANSACT-SQL (скорочено T-SQL), яка є реалізацією SQL-92
(стандарт ISO для SQL) з багатьма розширеннями. T-SQL дозволяє
використовувати додатковий синтаксис процедур, що зберігаються і забезпечує
підтримку транзакцій (взаємодія бази даних з керуючим застосунком). Microsoft
SQL Server та Sybase ASE для взаємодії з мережею використовують протокол
рівня застосунка під назвою Tabular Data Stream (TDS, протокол передачі
табличних даних).
19
Оскільки для створення бази даних було обрано СУБД MS SQL, то для
створення клієнтського додатку найкращим рішенням є програмний каркас
ASP.NET MVC, який, як і MS SQL, є продуктом компанії Microsoft і має досить
20
1.5 Висновки
В даному розділі було описано основні поняття про бази даних та системи
управління базами даних. Наведено інформацію про моделі існуючих систем.
Проведено аналіз предметної області згідно індивідуального завдання. В
наступному розділі буде проведено проектування бази даних за допомогою
методів сутність-зв’язок та нормалізації.
В розділі здійснено обгрунтування вибору СУБД та мови програмування
для створення бази даних та клієнтського додатку.
22
2.4 Висновки
modelBuilder.Entity<User>()
.Property(e => e.Name)
.HasColumnName("Name")
.IsRequired()
.HasMaxLength(100)
.IsUnicode(true);
modelBuilder.Entity<User>()
.Property(e => e.Email)
.HasColumnName("Email")
.IsRequired()
.HasMaxLength(100)
.IsUnicode(true);
}
Створення контексту бази даних наведено у додатку А.
Для початкового заповнення таблиці осіб використано метод Import(), що
завповнює поля таблиці з файлу з даними, що попередньо підготовлений. Це
досить зручно, оскільки не потрібно здійснювати заповнення таблиці вручну.
public ActionResult Import()
{
var db = new Db();
var file = Server.MapPath("~/App_Data/start.csv");
var groups = db.Groups.ToList();
var students = db.Users.ToList();
var faculty = db.Faculties.Single(e => e.Name == "ФМ");
var studentRole = db.Roles.Single(e => e.Id == Roles.Student);
var teacherRole = db.Roles.Single(e => e.Id == Roles.Teacher);
var adminRole = db.Roles.Single(e => e.Id == Roles.Admin);
using (var fs = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.None))
{
using (var sr = new StreamReader(fs, Encoding.UTF8))
36
{
while (!sr.EndOfStream)
{
var info = sr.ReadLine().Split(',');
var user =
students.SingleOrDefault(e => e.Email == info[1]);
if (user == null)
{
user = new User
{
Id = Guid.Parse(info[0]),
Email = info[1],
Name = info[2]
};
students.Add(user);
db.Users.Add(user);
}
if (info[3] == "МБІС" || info[3] == "АІМ")
{
if (!user.Roles.Contains(teacherRole))
{
user.Roles.Add(teacherRole);
}
}
else
{
var group = groups.SingleOrDefault(e => e.Name == info[3]);
if (group == null)
{
group = new Group
{
Faculty = faculty,
Name = info[3],
IsDel = false
};
groups.Add(group);
}
if (!user.Groups.Contains(group))
user.Groups.Add(group);
if (!user.Roles.Contains(studentRole))
{
user.Roles.Add(studentRole);
}
}
if (info[1] == "Mariia.Chernetska@vntu.net" && !user.Roles.Contains(adminRole))
{
user.Roles.Add(adminRole);
}
}
37
}
db.SaveChanges();
}
return Content("Completed");
}
Після внесення даних таблиця матиме наступний вигляд (рис. 3.1):
.SelectMany(e => e.LessonPresences)
.Count(e =>
DbFunctions.TruncateTime(e.Lesson.Date) >= fromDate &&
DbFunctions.TruncateTime(e.Lesson.Date) <= toDate),
PerfectCount = g.Students
.SelectMany(e => e.LessonPresences)
.Where(e =>
DbFunctions.TruncateTime(e.Lesson.Date) >= fromDate &&
DbFunctions.TruncateTime(e.Lesson.Date) <= toDate)
.Count(e => e.Mark >= 10),
GoodCount = g.Students
.SelectMany(e => e.LessonPresences)
.Where(e =>
DbFunctions.TruncateTime(e.Lesson.Date) >= fromDate &&
DbFunctions.TruncateTime(e.Lesson.Date) <= toDate)
.Count(e => e.Mark >= 7),
NotBadCount = g.Students
.SelectMany(e => e.LessonPresences)
.Where(e =>
DbFunctions.TruncateTime(e.Lesson.Date) >= fromDate &&
DbFunctions.TruncateTime(e.Lesson.Date) <= toDate)
.Count(e => e.Mark >= 4),
BadCount = g.Students
.SelectMany(e => e.LessonPresences)
.Where(e =>
DbFunctions.TruncateTime(e.Lesson.Date) >= fromDate &&
DbFunctions.TruncateTime(e.Lesson.Date) <= toDate)
.Count(e => e.Mark > 0),
Absent = g.Students
.SelectMany(e => e.LessonPresences)
.Where(e =>
DbFunctions.TruncateTime(e.Lesson.Date) >= fromDate &&
DbFunctions.TruncateTime(e.Lesson.Date) <= toDate)
.Count(e =>
e.PresenceType == PresenceType.Absent ||
e.PresenceType == PresenceType.GoodReason),
GoodReason = g.Students
.SelectMany(e => e.LessonPresences)
.Where(e =>
DbFunctions.TruncateTime(e.Lesson.Date) >= fromDate &&
DbFunctions.TruncateTime(e.Lesson.Date) <= toDate)
.Count(e => e.PresenceType == PresenceType.GoodReason)
});
Для спрощення виконання цього запиту на стороні серверу керування
базами даних використаємо функції бібліотеки DbFunctions, які дозволять
транслювати виклики на отримання лише інформації за певну дату напряму до
СУБД.
40
});
У запиті відбувається сортування прізвищ студентів за алфавітом та
обчислення середнього балу студента.
Запит для отримання розкладу викладача. Формування запиту з розкладом
обраного викладача на поточний день відбувається у декілька етапів:
1) формування списку занять згідно основного розкладу;
2) формування списку занять згідно додаткового розкладу;
3) об’єднання цих списків та сортування їх.
Перший крок виконується наступним чином:
var regular = Context.BaseSchedules
.Include(e => e.Teacher)
.Include(e => e.Discipline)
.Include(e => e.LessonNumber)
.Include(e => e.AdditionalSchedule)
.Include(e => e.RegularSchedule)
.Where(e =>
e.TeacherId == CurrentUserId &&
e.RegularSchedule != null &&
e.RegularSchedule.DayOfWeek == today)
.ToList();
Директива Include дозволяє нам перезавантажити інформацію з залежних
сутностей (inner join залежної таблиці з вибіркою усіх полів з неї).
Другий крок виглядає майже так само, за виключенням отримання
інформації не про основний, а про додатковий розклад.
Текст запиту:
var additional = Context.BaseSchedules
.Include(e => e.Teacher)
.Include(e => e.Discipline)
.Include(e => e.LessonNumber)
.Include(e => e.AdditionalSchedule)
.Include(e => e.RegularSchedule)
.Where(e => e.TeacherId == CurrentUserId &&
e.AdditionalSchedule != null &&
DbFunctions.TruncateTime(
e.AdditionalSchedule.Date) ==
dateTime)
.ToList();
Третій крок об’єднує ці два запити і виконує сортування по номеру уроку:
var schedules = regular
.Union(additional)
42
.OrderBy(e => e.LessonNumber.StartTime)
.ToList();
Запит для відображення оцінок та відвідування студентів групи на певному
уроці. За допомогою даного запиту необхідно відобразити інформацію про
відвідування кожного студента групи на кожного з уроків даної дисципліни.
Так як кількість уроків з дисципліни не є сталою величиною і може
змінюватись залежно від багатьох факторів, одного запиту недостатньо аби
отримати всю необхідну інформацію. Тому необхідно виконувати отримання
інформації в декількох запитах, які будуть комбінуватись при відображенні.
Запит на отримання списку проведених уроків:
var lessons = await schedules
.SelectMany(e => e.Lessons)
.OrderBy(e => e.Date)
.ToListAsync()
.ConfigureAwait(false);
Запит на отримання інформації про групу, її студентів та їх відвідування з
оцінками:
var group = await Context.Groups
.SingleOrDefaultAsync(e => e.Id == groupId)
.ConfigureAwait(false);
var students = await group.Students
.OrderBy(e => e.Name)
.Select(e =>
new StudentPresenceVM
{
Id = e.Id,
Name = e.Name,
Presences =
lessons.SelectMany(l =>
l.LessonPresences
.Where(p => p.StudentId == e.Id))
.Select(
lp =>
new PresenceVM
{
Id = lp.Id,
Type = lp.PresenceType,
Mark = lp.Mark,
Comment = lp.Comment,
LessonId = lp.LessonId,
Date = lp.Date
})
43
.ToArray()
})
.ToListAsync()
.ConfigureAwait(false);
В результаті ці два запити об’єднуються в один об’єкт, який і буде
переданий для формування звіту.
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
@Html.ActionLink("Назад до списку", "Index", null, new { @class = "btn btn-default"
})
<input type="submit" value="Створити" class="btn btn-success" />
</div>
</div>
</div>
}
Код, що здійснює обробку форми:
public async Task<ActionResult> Create([Bind(Include="Id,Name,IsDel,FacultyId")] Group
group)
{
if (ModelState.IsValid)
{
group.Id = Guid.NewGuid();
db.Groups.Add(group);
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
В формі необхідно вказати всі деталі розкладу, наприклад, номер уроку, тип
заняття, номер тижня та інші. Для збереження введених даних необхідно
натиснути кнопку «Створити».
<td>Оцінок задовільно</td>
<td>Оцінок не задовільно</td>
<td>Пропусків</td>
<td>З них з поважної причини</td>
</tr>
</thead>
<tbody>
@foreach (var item in Model.Items)
{
<tr>
@Html.HiddenFor(e => item.Id)
<td>@item.Name</td>
<td>@item.MarksCount</td>
<td>@item.PerfectCount</td>
<td>@item.GoodCount</td>
<td>@item.NotBadCount</td>
<td>@item.BadCount</td>
<td>@item.Absent</td>
<td>@item.GoodReason</td>
</tr>
}
</tbody>
</table>
Звіт про успішність студентів групи. Звіт має більш деталізований характер,
ніж попередній, оскільки інформація про відівідування та успішінсть наводиться
для кодного студента за певний період по певному предмету. Даний звіт містить
фільтри, що дозволяють обрати групу, для студентів якої буде сформовано звіт,
предмет, рік навчання та семестр. Код, що здійснює формування фільтрів:
49
<h2>Звіт по групі</h2>
@using (Html.BeginForm())
{
<div class="form-horizontal">
<div class="form-group">
<label class="col-sm-2 control-label">Звіт по групі</label>
<div class="col-md-6">
@Html.BootstrapDropDownList("Id", (IEnumerable<SelectListItem>)ViewBag.Id)
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Звіт по предмету</label>
<div class="col-md-6">
@Html.BootstrapDropDownList("DisciplineId", (IEnumerable<SelectListItem>)ViewBa
g.DisciplineId)
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Рік навчання</label>
<div class="col-md-6">
@Html.BootstrapDropDownList("YearOfStudyId", (IEnumerable<SelectListItem>)Vie
wBag.YearOfStudyId)
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Семестр</label>
<div class="col-md-6">
@Html.EnumDropDownListFor(e => e.SemesterType, new { @class = "form-
control" })
</div>
</div>
</div>
<button type="submit" class="btn btn-info top10">Оновити</button>
}
Результат відображається у табличному вигляді (рис. 3.9). Код, що формує
таблицю:
<table class="table table-bordered">
<thead>
<tr>
<td>Студент</td>
<td>К-сть оцінок</td>
<td>Пропущено занять</td>
<td>Середній бал</td>
</tr>
</thead>
<tbody>
@foreach (var item in Model.Items)
{
50
<tr>
@Html.HiddenFor(e => item.Id)
<td>@item.Name</td>
<td>@item.Marks</td>
<td>@item.AbsentCount</td>
<td>@item.Average.ToString("F2")</td>
</tr>
}
</tbody>
</table>
@foreach (var item in ((IEnumerable<BaseSchedule>)ViewBag.TeacherSchedules).OrderB
y(e => e.LessonNumber.StartTime))
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.WeekNumber)
</td>
<td>
@if (item.RegularSchedule != null)
{
<text>Основний</text>
}
else
{
<text>Додатковий</text>
}
</td>
<td>
@Html.DisplayFor(modelItem => item.LessonNumber.DisplayName)
</td>
<td>
<a href="@Url.Action("Details", "Presence", new {id = item.Id, dateTime = DateTim
e.Now})">@Html.DisplayFor(modelItem => item.Discipline.Name)</a>
</td>
<td>
@item.LessonType.GetDisplayValue()
</td>
<td>
@Html.DisplayFor(modelItem => item.RoomNumber)
</td>
<td>
@foreach (var group in item.Groups)
{
@Html.ActionLink(group.Name, "Index","Presence",new { groupId = group.Id, dis
ciplineId = item.DisciplineId, lessonType = item.LessonType},null)
if (group != item.Groups.Last())
{
<text>, </text>
}
}
</td>
</tr>
}
</table>
}
52
var presence = student.Presences.FirstOrDefault(e => e.LessonId == lesson.ScheduleI
d && e.Date == lesson.Date);
if (presence != null)
{
<td comment="@presence.Comment">
@switch (presence.Type)
{
case PresenceType.Absent:
{
<text>н</text>
break;
}
case PresenceType.GoodReason:
{
<text>хв.</text>
break;
}
case PresenceType.Present:
case PresenceType.Late:
{
if (presence.Mark > 0)
{
<text>@presence.Mark.ToString("F0")</text>
}
else
{
<text>.</text>
}
break;
}
default:
throw new ArgumentOutOfRangeException();
}
</td>
}
else
{
<td>н</td>
}
}
</tr>
}
</tbody>
</table>
</div>
54
3.5 Висновки
ВИСНОВКИ
ПЕРЕЛІК ПОСИЛАНЬ
ДОДАТКИ
58
Додаток А
Лістинг створення контексту бази даних
public class Db : DbContext
{
public Db() : base("name=Db") { }
public virtual DbSet<AdditionalSchedule> AdditionalSchedules { get; set; }
public virtual DbSet<BaseSchedule> BaseSchedules { get; set; }
public virtual DbSet<Discipline> Disciplines { get; set; }
public virtual DbSet<Faculty> Faculties { get; set; }
public virtual DbSet<Lesson> Lessons { get; set; }
public virtual DbSet<LessonNumber> LessonNumbers { get; set; }
public virtual DbSet<LessonPresence> LessonPresences { get; set; }
public virtual DbSet<RegularSchedule> RegularSchedules { get; set; }
public virtual DbSet<Group> Groups { get; set; }
public virtual DbSet<Role> Roles { get; set; }
public virtual DbSet<Semester> Semesters { get; set; }
public virtual DbSet<User> Users { get; set; }
public virtual DbSet<YearOfStudy> YearOfStudies { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.AdditionalSchedule();
modelBuilder.BaseSchedule();
modelBuilder.Discipline();
modelBuilder.Group();
modelBuilder.Lesson();
modelBuilder.LessonNumber();
modelBuilder.LessonPresence();
modelBuilder.RegularSchedule();
modelBuilder.Role();
modelBuilder.Semester();
modelBuilder.User();
modelBuilder.YearOfStudy();
}
}
59
Додаток Б
Форма для ведення журналу
Додаток В
Лістинг обробки форми для ведення журналу
public class PresenceController : BaseController
{
public async Task<ActionResult> Index(Guid groupId, Guid disciplineId, LessonType
lessonType)
{
var discipline = await Context.Disciplines
.SingleOrDefaultAsync(e => e.Id == disciplineId)
.ConfigureAwait(false);
var schedules = await Context.BaseSchedules
.Where(e =>
e.Groups.Any(g => g.Id == groupId) &&
e.DisciplineId == disciplineId &&
e.LessonType == lessonType)
.ToListAsync()
.ConfigureAwait(false);
var lessons = await schedules
.SelectMany(e => e.Lessons)
.OrderBy(e => e.Date)
.ToListAsync()
.ConfigureAwait(false);
var group = await Context.Groups
.SingleOrDefaultAsync(e => e.Id == groupId)
.ConfigureAwait(false);
var students = await group.Students
.OrderBy(e => e.Name)
.Select(e =>
new StudentPresenceVM
{
Id = e.Id,
Name = e.Name,
Presences =
lessons.SelectMany(l =>
l.LessonPresences
.Where(p => p.StudentId == e.Id))
.Select(
lp =>
new PresenceVM
{
Id = lp.Id,
Type = lp.PresenceType,
Mark = lp.Mark,
Comment = lp.Comment,
LessonId = lp.LessonId,
Date = lp.Date
61
})
.ToArray()
})
.ToListAsync()
.ConfigureAwait(false);
var result = new PresenceReport
{
Group = group,
Students = students,
Lessons = lessons,
LessonType = lessonType,
Discipline = discipline
};
return View(result);
}
//
// GET: /Presence/
public async Task<ActionResult> Details(Guid id, DateTime? dateTime)
{
var baseSchedule = Context.BaseSchedules.Single(e => e.Id == id);
if (baseSchedule.RegularSchedule != null && dateTime == null)
return new HttpStatusCodeResult(400, "Date not provided");
var date = (baseSchedule.RegularSchedule == null
? baseSchedule.AdditionalSchedule.Date
: dateTime.Value).Date;
var lesson =
baseSchedule.Lessons.FirstOrDefault(
e => e.Date.Date == date);
var schedule = new LessonPresenceVM
{
Date = lesson?.Date??date,
Theme = lesson?.Theme ?? "",
Groups = baseSchedule.Groups.Select(g => new GroupVM
{
Id = g.Id,
Name = g.Name,
Students = g.Students.Select(s =>
{
var presence =
Context.LessonPresences.FirstOrDefault(
e => e.LessonId == id && e.StudentId == s.Id);
return new StudentVM
{
Id = s.Id,
Name = s.Name,
Comment = presence?.Comment ?? "",
PresenceType =
presence?.PresenceType ?? PresenceType.Absent,
62
Mark = presence?.Mark ?? 0
};
}).ToArray()
}).ToArray(),
Discipline = baseSchedule.Discipline.Name,
Teacher = baseSchedule.Teacher.Name,
LessonNumber = baseSchedule.LessonNumber.DisplayName,
WeekNumber = baseSchedule.WeekNumber,
Semestr = baseSchedule.Semester.Name
};
return View(schedule);
}
[HttpPost]
public async Task<ActionResult> Details(Guid id, LessonPresenceVM value)
{
var schedule = Context.BaseSchedules.SingleOrDefault(e => e.Id == id);
var lesson =
Context.Lessons.SingleOrDefault(e => e.ScheduleId == value.Id);
if (lesson == null)
{
lesson = new Lesson
{
ScheduleId = value.Id,
IsDel = false,
Theme = value.Theme,
Date = value.Date
};
Context.Lessons.Add(lesson);
}
else
{
lesson.Theme = value.Theme;
lesson.Date = value.Date;
}
foreach (var studentVm in value.Groups.SelectMany(e => e.Students))
{
var presence =
lesson.LessonPresences.SingleOrDefault(
e => e.StudentId == studentVm.Id);
if (presence == null)
{
presence = new LessonPresence
{
StudentId = studentVm.Id,
Comment = studentVm.Comment ?? "",
IsDel = false,
Lesson = lesson,
Mark = studentVm.Mark,
63
PresenceType = studentVm.PresenceType
};
lesson.LessonPresences.Add(presence);
}
else
{
presence.Comment = studentVm.Comment ?? "";
presence.Mark = studentVm.Mark;
presence.PresenceType = studentVm.PresenceType;
}
}
await Context.SaveChangesAsync().ConfigureAwait(false);
return View(value);
}
}
64
Додаток Д
Головне вікно програми