Практическое занятие № 12

 

Тема: Обработка БД с помощью запросов

Цель работы: получить практические навыки по использованию запросов на обновление, выполнению расчетов с помощью запросов

 

Ход работы

 

Пусть в СУБД Access спроектирована база данных (скачать). База защищена паролем "rpo" и имеет две таблицы.

 

Таблица "students", состоит из полей:

 

Имя поля Тип Размер
id_stud Счетчик Ключевое
fam Текстовый 15
imya Текстовый 15
otch Текстовый 15
finance Текстовый 10 (Госзаказ, Контракт)
datar Дата/время  
srbal Числовой Одинарной точности с плавающей точкой. Значение по умолчанию - 0
foto Поле OLE  
id_grup Числовой Внешний ключ для связи с таблицей groups

 

Таблица "groups", состоит из полей:

 

Имя поля

Тип

Размер

id_grup Счетчик Ключевое
grupa

Текстовый

30

 

В программе на C# выполните подключение базы данных

 

Указания: на панели "Источники данных" щелкните на кнопке "Добавить новый источник данных" (если панель не отображается, то выбери команду меню "Данные - Показать источники данных"). Запуститься мастер создания подключения к БД. В окне выберите значение "База данных", затем значение "Набор данных". На следующем шаге щелкните на кнопке "Создать подключение". В новом окне Вам будет предложено подключить файлы БД SQL Server. Щелкните на кнопке "Изменить" и выберите формат БД "Microsoft Access". После этого укажите нужный файл БД (путь к файлу не удаляйте, среда C# самостоятельно скопирует указанный файл в папку проекта и скорректирует путь к нему). Так как база защищена паролем, то щелкните на кнопке "Дополнительно" и в строке "Jet OLEDB: DataBase Password" укажите пароль "rpo". Для проверки соединения щелкните на кнопке "Проверить подключение". После задания всех параметров мастер предложит выбрать способ сохранения пароля к БД: не включать в строку подключения или включать в строку подключения. Выберите первый вариант для безопасности.

После задания параметров БД при переходе на следующий шаг мастера появится запрос на копирование файла БД в папку с проектом, подтвердите эту операцию.

На следующем шаге мастер предложит сохранить строку подключения в файл конфигурации приложения. Так как пароль доступа не включен в строку подключения, то можете согласиться на сохранение в файл.

На следующем шаге выберите обе таблицы students и groups, с которыми планируете работать в проекте, и щелкните "Готово".

Сразу нужно настроить поведение полей-счетчиков в таблицахВ окне "Источники данных" щелкните на кнопке "Изменить набор данных в конструкторе". Отобразится структура таблиц. Выделите поле id_stud в таблице students и измените свойства: AutoIncrementSeed = 1AutoIncrementStep = 1Выделите поле id_grup в таблице groups и измените свойства: AutoIncrementSeed = 1, AutoIncrementStep = 1.

 

Настройка корректного удаления новых записей

Если добавить новую запись в таблицу и сразу попытаться ее удалить, то часто появляется сообщение об ошибке, связанное с тем, что мастер подключения БД неправильно формирует команды на добавление, обновление и удаление данных. Для решения этой проблемы в окне со структурой таблиц щелкните правой кнопкой на строке "studentsTableAdapter" таблицы studentsвыберите команду "Настроить". В новом окне щелкните на кнопке "Дополнительно", в следующем окне выключите флажок "Использовать оптимистическую блокировку", щелкните ОК и Готово.

Повторите эти действия для таблицы groups.

 

Создайте программу для работы с данными таблицы. При этом форма должна иметь вид:

 

 

Указания: для формы задайте свойства: Caption=Работа с запросами, Position=DesktopCenter, Borderstyle=bsSingle (нерастягиваемая форма), MiximizeBox = false (нет кнопки разворачивания окна).

 

Для подключения таблицы groups нанесите на форму компонент DataRowView. Появится окно, в котором нужно указать подключаемую таблицу groups. На форме появятся дополнительные компоненты для работы с таблицей: bazaDataSet - для доступа к подключению, groupsBindingSource - для работы с набором данных, groupsTableAdapter - для работы с копией таблицы БД. Названия групп будет отображать в алфавитном порядке. Для этого для компонента groupsBindingSource задайте свойство Sort = grupa (имя поля для сортировки). Для сетки задайте свойства: AllowUserToAddRows = false (запрет добавления новых строк в сетке), AllowUserToDeleteRows = false (запрет на удаление данных в сетке без подтверждения), AlternatingRowsDefaultCellStyle - BackColor = серый (цвет фона четных строк сетки), AutoSizeColumnsMode=Fill (ширина колонок подстраивается под размер сетки). Для настройки параметров колонок сетки откройте ее свойство Columns. Вы увидите список всех ее колонок. Колонку id_grup сделайте невидимой (Visible=false), для остальных колонок в свойстве HeaderText укажите названия как на рисунке.

 

Для подключения подчиненной таблицы students нанесите на форму компонент DataRowView. Появится окно, в котором нужно раскрыть компонент groupsBindingSource и указать имя соединения groupsstudents. На форме появятся дополнительные компоненты для работы с таблицей:  groupsstudentsBindingSource - для работы со связанным набором данных, studentsTableAdapter - для работы с копией таблицы БД. Для удобства чтения кода рекомендуется переименовать компонент groupsstudentsBindingSource по имени подчиненной таблицы, а не по имени соединения. Задайте для компонента свойство Name = studentsBindingSource.

Для сетки задайте свойства: AllowUserToAddRows = false (запрет добавления новых строк в сетке), ReadOnly=true, AllowUserToDeleteRows = false (запрет на удаление данных в сетке без подтверждения), AlternatingRowsDefaultCellStyle - BackColor = серый (цвет фона четных строк сетки), AutoSizeColumnsMode=Fill (ширина колонок подстраивается под размер сетки).

Для настройки параметров колонок сетки откройте ее свойство Columns. Вы увидите список всех ее колонок. Колони id_stud и id_grup сделайте невидимыми (Visible=false), для остальных колонок в свойстве HeaderText укажите названия как на рисунке.

 

Если запустить программу на выполнение, то сетка будет пустой, потому что в строке соединения мы не сохранили пароль к БД и подключение не установлено.

Для формы откройте событие Load. В этом событии будут находится две команды Fill(), загружающие копии таблиц в компоненты TableAdapter. Над этими командами добавьте команду подстановки пароля в строку соединения:

 

Properties.Settings.Default["BazaConnectionString"] += ";Jet OLEDB:Database Password=rpo";

 

Обычно в программах для работы с БД при переходе между строками сеток автоматически происходит сохранение изменений, если они имели место. В сетке dataGridView это возможность не реализована. Нужно обеспечить такое сохранение данных.

 

Указания: для набора данных studentsBindingSource в событии CurrentChanged введите код:

 

private void groupsBindingSource_CurrentChanged(object sender, EventArgs e)

{

    //если в наборе данных groups имеются изменения

    if (bazaDataSet.groups.GetChanges() != null)

          {

        //сохраняем их в таблице БД

        Validate();

        groupsBindingSource.EndEdit();   

        groupsDataAdapter.Update(bazaDataSet.grups);

     }

}

 

Для для набора данных studentsBindingSource в событии CurrentChanged введите код:

 

private void studentsBindingSource_CurrentChanged(object sender, EventArgs e)

{

    //если в наборе данных students имеются изменения

    if (bazaDataSet.students.GetChanges() != null)

         {

        Validate();

        studentsBindingSource.EndEdit(); 

        studentsDataAdapter.Update(bazaDataSet.students);

    }

}

 

Реализуйте возможность изменения среднего балла всех студентов на процент, введенный пользователем в поле.

 

Указания: перейдите в окно "Источники данных" и щелкните на кнопке "Изменить набор данных в конструкторе". Так как будем менять данные в таблице students, то щелкните правой кнопкой на этой таблице и выберите команду "Добавить - Запрос". В окне укажите тип запроса UPDATE и введите текст запроса:

 

update students set srbal = srbal+srbal*?/100

 

В следующем окне введите имя метода для вызова запроса. Например, введите имя "UpdateSrbal".  В результате будет создан метод UpdateSrbal(параметр), при вызове которого нужно будет в скобках указать значение процента изменения.

 

Для ввода значения процента нанесите на форму компонент textBox. Также нанесите на форму кнопку и для нее напишите код

 

Для кнопки напишите код:

 

void button1_Click(object sender, EventArgs e)

{

    //вызываем метод для обновления поля srbal

    studentsTableAdapter.UpdateSrbal(Convert.ToDecimal(textBox1.Text));

    //обновляем данные в сетке

    studentsTableAdapter.Fill(bazaDataSet.students);

}

 

Выполните расчет количества студентов, среднего, максимального и среднего баллов по всем студентам и по студентам выбранной группы.

 

Указания: нанесите на панель 8 компонентов label.

Для выполнения расчетов будем использовать запрос Select по таблице students. Перейдите в окно "Источники данных" и щелкните на кнопке "Изменить набор данных в конструкторе". Так как будем делать выбор по таблице students, то щелкните правой кнопкой на этой таблице и выберите команду "Добавить - Запрос". В окне укажите тип запроса "Инструкция SELECT, возвращающая строки" и введите текст запроса для расчета показателей по всей таблице:

 

select count(inn) as kolvo,

round(max(srbal),2) as maxbal,

round(min(srbal),2) as minbal,

round(avg(srbal),2) as sredbal

from students

 

В следующем окне после флажка "Вернуть таблицу данных" введите имя метода для вызова запроса. Например, введите имя "CalcAll".  В результате будет создан метод CalcAll() для вызова этого запроса.

 

Для расчета показателей по конкретной группе аналогично создайте новый запрос с текстом:

 

select count(inn) as kolvo,

round(max(srbal),2) as maxbal,

round(min(srbal),2) as minbal,

round(avg(srbal),2) as sredbal

from students

where id_grup=?

 

В качестве имени метода введите текст CalcGroup. В результате будет создан метод CalcGroup(параметр) для вызова этого запроса. При этом в качестве параметра нужно будет указать номер нужной группы.

 

Создадим процедуру, которую будем вызывать в разных событиях.

 

void schet()

{

    //описываем переменную для сохранения запроса  

    DataTable rez = new DataTable();

 

    //в переменную записываем запрос по всей таблице

    rez = studentsTableAdapter.CalcAll();

    //в метках на форме отображаем результат запроса

    label1.Text = "Всего студентов: " + rez.Rows[0]["kolvo"].ToString();

    label2.Text = "Максимальный балл: " + rez.Rows[0]["maxbal"].ToString();

    label3.Text = "Минимальный балл: " + rez.Rows[0]["minbal"].ToString();

    label4.Text = "Средний балл: " + rez.Rows[0]["sredbal"].ToString();

 

    //в переменную записываем запрос по конкретной группе

    //в качестве параметра метода указываем

    //текущее значение поля id_grup из главной таблицы (bindingSource1)

    rez = studentsTableAdapter.CalcGroup(Convert.ToInt16((bindingSource1.Current as DataRowView)["id_grup"]));

    //в метках на форме отображаем результат запроса

    label5.Text = "Всего студентов: " + rez.Rows[0]["kolvo"].ToString();

    label6.Text = "Максимальный балл: " + rez.Rows[0]["maxbal"].ToString();

    label7.Text = "Минимальный балл: " + rez.Rows[0]["minbal"].ToString();

    label8.Text = "Средний балл: " + rez.Rows[0]["sredbal"].ToString();

 

    //удаляем переменную из памяти   

    rez.Dispose();

}

 

Созданная процедура вызывается с помощью команды

 

schet();

 

Данную процедуру нужно вызвать в нескольких событиях:

- при перемещении по таблицам в событии CurrentChanged компонентов groupsBindingSource (за пределами операторных скобок if) и studentsBindingSource (внутри операторных скобок if);

- в конце кода кнопки для обновления среднего балла студентов.

 

Обратите внимание, что несмотря на использования функции round() в запросе, полученные расчетные значения могут иметь большое количество знаков после запятой. Это происходит потому, что СУБД Access не поддерживает эту функцию и данные не округляются. Если программа работает с другой СУБД, то проблемы с округлением не будет и рассмотренный ниже код можно не использовать.

Для решения проблемы округления нужно переписать процедуру schet() с использование округления полученных значений. Сначала значение конвертируем в double, затем методом Math,Round() округляем, а затем конвертируем в текст для отображения на форме

 

void schet()

{

    //описываем переменную для сохранения запроса  

    DataTable rez = new DataTable();

 

    //в переменную записываем запрос по всей таблице

    rez = studentsTableAdapter.CalcAll();

    //в метках на форме отображаем результат запроса

    //с помощью функции округления

    label1.Text = Math.Round(Convert.ToDouble("Всего студентов: ", rez.Rows[0]["kolvo"]),2).ToString();

    label2.Text = Math.Round(Convert.ToDouble("Максимальный балл: ", rez.Rows[0]["maxbal"),2).ToString();

    label3.Text = Math.Round(Convert.ToDouble("Минимальный балл: ", rez.Rows[0]["minbal"]),2).ToString();

    label4.Text = Math.Round(Convert.ToDouble("Средний балл: ", rez.Rows[0]["sredbal"]),2).ToString();

 

    //в переменную записываем запрос по конкретной группе

    //в качестве параметра метода указываем

    //текущее значение поля id_grup из главной таблицы (gouprBindingSource)

    rez = studentsTableAdapter.CalcGroup(Convert.ToInt32((groupsBindingSource.Current as DataRowView)["id_grup"]));

   

    //в метках на форме отображаем результат запроса

    //с помощью функции округления

   

    label5.Text = Math.Round(Convert.ToDouble("Всего студентов: ", rez.Rows[0]["kolvo"]),2).ToString();

    label6.Text = Math.Round(Convert.ToDouble("Максимальный балл: ", rez.Rows[0]["maxbal"),2).ToString();

    label7.Text = Math.Round(Convert.ToDouble("Минимальный балл: ", rez.Rows[0]["minbal"]),2).ToString();

    label8.Text = Math.Round(Convert.ToDouble("Средний балл: ", rez.Rows[0]["sredbal"]),2).ToString();

}

 

Разместите на форме два переключателя для отображения студентов по группам или общи списком.

 

Указания: нанесите на форму компонент RadioButton и задайте свойства: Text = Просмотр по группам, Checked = true. Нанесите второй RadioButton и задайте свойство Text = Просмотр общим списком.

 

Для первого переключателя в событии CheckedChanged напишем код:

 

private void radioButton1_CheckedChanged(object sender, EventArgs e)

{

    //включаем просмотр по группам

    //ссылаемся на BindingSource главной таблицы

    studentsBindingSource.DataSource = groupsBindingSource;

    //ссылаемся на имя соединения

    studentsBindingSource.DataMember = "groupsstudents";

}

 

Для второго переключателя в событии CheckedChanged напишем код:

 

private void radioButton2_CheckedChanged(object sender, EventArgs e)

{

    //включаем просмотр всех записей

    //ссылаемся на подключение к БД

    studentsBindingSource.DataSource = bazaDataSet;

    //ссылаемся на подчиненную таблицу

    studentsBindingSource.DataMember = "students";

}