Как подсчитать сумму по условию (SQL запрос) в Winforms C#
Пишу приложение на WinForms C# (.NET Framework 4.7.2), БД в MS SQL SERVER 2019.
Моё приложение очень простое, у меня нет привязки Binding, ни DataTable.
Создал Главную форму с DataGridView, которая берёт данные из таблицы: Plan_db в БД и соответственно в таком же формате таблица выводится в datagridview. (все колонки datagridview, как на прикреплённой картинке). (Например, Преподаватель Иванов ведёт два предмета в 3 семестре , один предмет 126 часов , а второй допустим 80 часов).
Далее создаю следующую форму с textbox' ами (как на прикреплённой картинке) ,
в textbox "Преподаватель" хочу ввести фамилию и инициалы преподавателя Иванова, который уже есть в БД и datagridview, в textbox "Семестр" хочу ввести цифру семестра в котором Преподаватель ведёт несколько предметов и нажимаю кнопку "Подсчитать" и хочу что бы в textbox' е "Итоговая часовая нагрузка" был произведён подсчёт и вывод часов.
По итогу эта форма должна подсчитать общее количество часов выбранного преподавателя в выбранном семестре по двум критериям - textbox' am, если он ведёт в выбранном семестре несколько предметов (Например: Иванов А.А., 3 семестр, Итого часов : 126 +80 = 206 часов).
Я понимаю , что нужен SQL запрос с условием на выборку из таблицы Plan_db по двум критериям (Преподаватель и Семестр) , но что то пока никак не могу свести это воедино и реализовать в коде.
Нужен простой, работающий код, который считает количество часов выбранного Преподавателя , если он ведёт в выбранном семестре , больше чем 1 предмет. Потому что мой код не работает.
Может кто-то пожалуйста помочь с примерной реализацией всего этого в коде?
Как это по простому реализовать? Без Binding и DataTable?
Может какие похожие на мою ситуацию примеры есть?
P.S. Возникла сложность в реализации. Как видите на картинке , форму я создал и textbox-ксы в ней тоже , а также кнопку. Но это всё не работает. Я только примерно знаю что нужно написать , что бы это всё заработало, а именно: нужен SQL запрос на выборку по условию (Преподаватель и Семестр) . Эти данные (Преподаватель и Семестр) берутся из одной и той же таблицы , колонки которой соответствуют картинке , что я прикрепил выше, в предыдущем посте. Таблица Plan_db и в ней колонки: id, Преподаватель, Количество часов, Дисциплина, Семестр, Группа, Форма контроля.
Вот код , который я написал, но он не работает:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Data.SqlClient;
using Excel = Microsoft.Office.Interop.Excel;
using System.IO;
namespace Teachers_Plan2
{
public partial class Total_Time : Form
{
DataBase dataBase = new DataBase();
public Total_Time()
{
InitializeComponent();
StartPosition = FormStartPosition.CenterScreen;
}
private void Total_Time_Load(object sender, EventArgs e)
{
textBox_fio.MaxLength = 50;
textBox_semestr.MaxLength = 50;
}
private void textBox_ttime_TextChanged(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
var logFio = textBox_fio.Text;
var logSem = textBox_semestr.Text;
SqlDataAdapter adapter = new SqlDataAdapter();
DataTable table = new DataTable();
string querystring = $"select SUM(time_of) from Plan_db where fio = '{logFio}' and semestr = '{logSem}'";
SqlCommand command = new SqlCommand(querystring, dataBase.getConnection());
adapter.SelectCommand = command;
//adapter.Fill(table);
if (table.Rows.Count == 1)
{
//var user = new checkUser(table.Rows[0].ItemArray[1].ToString(), Convert.ToBoolean(table.Rows[0].ItemArray[3]));
//MessageBox.Show("Часовая нагрузка подсчитана!", "Успешно!", MessageBoxButtons.OK, MessageBoxIcon.Information);
//Form1 frm1 = new Form1(user);
//this.Hide();
//frm1.ShowDialog();
//this.Show();
}
else
MessageBox.Show("Часовая нагрузка не подсчитана!", "Ошибка!", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
}
Ответы (1 шт):
Судя по Вашему коду, Вам нужна просто работа с SQL, и получение результатов выполнения некотрых запросов.
При этом схема вложенности разных SQL-сущностей довольно простА:
SqlConnection, в него вложена SqlCommand, в комманд пихаем параметры и получаем данные последством DataReader (в случае множественных данных) или при помощи ExecuteScalar в случае, если надо получить одну цифИрь.
Кроме того, логично не заставлять пользователя писать данные в текстбокс, а дать ему возможнсоть выбора.
Но во многих примерах по работе с SQL сразу приводят пример с адаптером - фактически, "прослойкой", которая наполняется данными из базы, а потом с ней же связывются "потребители данных" на стороне клиента. И у Вас в коде следы работы с адаптером - есть. А он здесь не нужен.
Из своего примера я это исключил полностью, как лишнюю сущность.
Учтя это, я немного переписал Ваш код, фактически - сделал небольшой новый проект.
Он доступен здесь, или Вы можете загрузить репку командой git clone [email protected]:junecat/VariousExamples.git и посмотреть в папочку SqlLearnDemo
Проект работает с одной таблицей, все запросы - синхронные, никакой лишней оптимизации, только простотА, только хардкор :-)
Вот как выглядит окно приложения:

Таблица БД:
И - вот он, основной код:
using System;
using System.Data;
using System.Windows.Forms;
using System.Data.SqlClient;
namespace SqlForLearn
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Shown += Form1_Shown;
button1.Click += Button1_Click;
}
private void Button1_Click(object sender, EventArgs e)
{
using (SqlConnection cn = new SqlConnection(sqlConnectionString))
{
cn.Open();
const string sqlCalc = "SELECT SUM(time_of) FROM Plan_db WHERE fio=@fio AND semestr=@semestr";
using (SqlCommand cmd = new SqlCommand(sqlCalc, cn))
{
cmd.Parameters.Add("@fio", DbType.String).Value = comboBox1.SelectedItem;
cmd.Parameters.Add("@semestr", DbType.Int32).Value = Convert.ToInt32(comboBox2.SelectedItem);
var rez = cmd.ExecuteScalar();
textBox1.Text = rez.ToString();
}
}
}
const string sqlConnectionString = "Password=rem;Persist Security Info=False;User ID=davydov;Initial Catalog=ExperimentsDB;Data Source=172.19.110.215";
private void Form1_Shown(object sender, EventArgs e)
{
// заполняем выпадающие списки - преподавателей и семестров
using ( SqlConnection cn = new SqlConnection(sqlConnectionString))
{
cn.Open();
const string sqlSelPeoples = "SELECT DISTINCT fio FROM Plan_db ORDER BY fio";
using (SqlCommand cmd = new SqlCommand(sqlSelPeoples, cn))
{
comboBox1.Items.Clear();
using (SqlDataReader reader = cmd.ExecuteReader())
{
while(reader.Read())
{
comboBox1.Items.Add(reader.GetString(0));
}
}
if (comboBox1.Items.Count > 0)
comboBox1.SelectedIndex = 0;
}
}
using (SqlConnection cn = new SqlConnection(sqlConnectionString))
{
cn.Open();
const string sqlSelSemestr = "SELECT DISTINCT semestr FROM Plan_db ORDER BY semestr";
using (SqlCommand cmd = new SqlCommand(sqlSelSemestr, cn))
{
comboBox2.Items.Clear();
using (SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
comboBox2.Items.Add(reader.GetInt32(0));
}
}
if (comboBox2.Items.Count > 0)
comboBox2.SelectedIndex = 0;
}
}
}
}
}
Теперь пара замечаний.
В Form1_Shown() происходит заполнение "выпадушек" - комбо-боксов, содержащих имена преподавателй и номера семестров.
В SQL запросах используются формальные параметры, вместо вставки текста в запрос. Вставка текста из поля ввода чревата sql injection, а у меня в примерах все sql запросы - это const. Добавление параметров сделано через Add(), а не через AddWithValue()
Что не очень хорошо.
У меня прописан пароль к БД прямо в програме.Вы его видите :-)
Таблица Plan_db - не нормализована. Логично хранить не fio, а идентификатор преподавателя, взятый из таблицы преподавателей.
Дополнение
После того, как в вопрос добавилась картинка с классом Database - можно сделать еще одно замечание:
Почему не работает код, еслине исправить строку sqlConnectionString? Потому, что эта строка - это "путь к базе данных". И у меня в строке прописан мой путь - к моей базе данных, а Вам нужно прописать путь к своей.
Класс Database работает, суда по всему, так: у него есть публичный метод getConnection(), который как раз возвращает объект типа SqlConnection.
Вам нужно просто вместо
using (SqlConnection cn = new SqlConnection(sqlConnectionString))
{
cn.Open();
писать что то вроде
var databse = new Database();
databse.openConnecton();
using ( var cn = databse.getConnection() )
{
...
То есть, смысл в том, что у Вас создаётся то же самое - SqlConnection - но внутри отдельного класса.



