Форум программистов, компьютерный форум, киберфорум
C#: Базы данных
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.56/9: Рейтинг темы: голосов - 9, средняя оценка - 4.56
15 / 13 / 3
Регистрация: 20.02.2018
Сообщений: 446

Обновление и сравнение записей БД из Excel

21.12.2020, 18:49. Показов 2074. Ответов 12

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

Написал програмку, которая считывает данные из Excel, потом выгружает из БД соответствующие записям в excel записи из БД, ищет их в БД по уникальному полю, значение которого берет из excel, далее сопоставляет данные из БД и Exce, выводит записи, которые в БД отличаются от соответствующих им записям в Excel и обновляет их из Excel.

Все работает кроме сравнения, оно работает не совсем корректно, возможно из-за формата данных.
После того как обновил записи в БД, выгружаю их в файл Excel и снова сравниваю С БД те же самые записи и находятся различия, хлтя их не должно быть потому что ведь сравниваю то что выгрузил с тем откуда выгрузил, по факту ИН И НЕТ, потому что если посмотреть, то в выгруженном файле то же самое значение, что и в БД.

Так себя ведут не все поля записей.

В БД все поля NVARCHAR, с Excel все считываю как string. БД - MSSQL.

Выгружаю, загружаю данные в лист словарей:
C#
1
2
3
List<Dictionary<string, string>> dataExcelFile; //Данные из Excel файла
List<Dictionary<string, string>> dataDB;  // Данные из БД
List<Dictionary<string, string>> changeDataInDbFromExcel; //Сопоставленные данные. Здесь хранятся только измененные записи
Сравнение данных:
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
public List<Dictionary<string, string>> MappingData(string nameParametr)
        {
            List<Dictionary<string, string>> changeData = new List<Dictionary<string, string>>(); //Измененные данные для загрузки
            changeDataInDbFromExcel= new List<Dictionary<string, string>>();
            for (int i = 0; i < dataExcelFile.Count; i++)
            {
                Dictionary<string, string>[] findDBKontainers = dataDB.Where(z => z[nameParametr].Equals(dataExcelFile[i][nameParametr])).ToArray();
 
                if (findDBKontainers.Length > 0)
                {
                    //Dictionary<string, string> findDBKontainer = findDBKontainers.First();
                    foreach (Dictionary<string, string> findDBKontainer in findDBKontainers)
                    {
                        Dictionary<string, string> dictchange = dataExcelFile[i].Where(entry => !findDBKontainer[entry.Key].Equals(entry.Value)).ToDictionary(entry => entry.Key, entry => "Нов. знач.:" + entry.Value);
                        
                        if (dictchange != null && dictchange.Count != 0)                        {
                            dictchange[nameParametr] = dataExcelFile[i][nameParametr];
                            Dictionary<string, string> dictchange2 = dataExcelFile[i].Where(entry => findDBKontainer[entry.Key].Equals(entry.Value) && !entry.Key.Equals(nameParametr)).ToDictionary(entry => entry.Key, entry => "");
                            Dictionary<string, string> unionDictChange = dictchange.Union(dictchange2).ToDictionary(key => key.Key, value => value.Value);
                            changeData.Add(unionDictChange);
                            changeDataInDbFromExcel.Add(dataExcelFile[i]);
                        }
                    }
                }
                else
                {
                    Dictionary<string, string> dictchange = new Dictionary<string, string>();
 
                    foreach (string key in dataExcelFile[i].Keys)
                    {
                        if (!key.Equals(nameParametr))
                        {
                            dictchange.Add(key, "Отсутствует в БД");
                        }
                        else
                        {
                            dictchange.Add(nameParametr, dataExcelFile[i][nameParametr]);
                        }
                    }
                    changeData.Add(dictchange);
                }
            }
            return changeData;
        }
Обновление измененных данных из Excel в БД:
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
public void UpdateDataBase(List<Dictionary<string, string>> updateDictionaryList,string nameParametr,string[] nameParametrs)
        {
            Console.WriteLine("Getting Connection ...");
            SqlConnection sqlConnection = SqlUtils.GetSqlConnection();
 
            int count = 0;
 
            try
            {
                Console.WriteLine("Openning Connection ...");
 
                sqlConnection.Open();
 
                label_status_connection.Text = "Подключение успешно выполнено!";
 
                foreach (Dictionary<string, string> updateDiction in updateDictionaryList)
                {
                    SqlCommand sqlCommand=CreateSqlCommand(updateDiction, nameParametr, nameParametrs,sqlConnection);
 
                    try
                    {
                        int n = sqlCommand.ExecuteNonQuery();
                        count++;
                    }catch(Exception e)
                    {
                        Console.WriteLine("Error: " + e.Message);
                        label_errorUpdateRecords.Text = String.Format("Ошибка обновления записи № {0}", count+1);
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("Error: " + e.Message);
                label_status_connection.Text =String.Format("Ошибка обновления записи");
            }
            label_count_update_records.Text =Convert.ToString("Записей обновлено: "+count);
        }
Выгрузка данных из БД:
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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
public void GetDBData (string nameParametr,List<String> valueParametrs)
        {
            Console.WriteLine("Getting Connection ...");
            SqlConnection sqlConnection = SqlUtils.GetSqlConnection();
 
            try
            {
                Console.WriteLine("Openning Connection ...");
 
                sqlConnection.Open();
 
                label_status_connection.Text = "Подключение успешно выполнено!";
 
                //List<string> nameColumnsDB = Enumerable.Range(0, datareader.FieldCount).Select(datareader.GetName).ToList();
                dataDB = new List<Dictionary<string, string>>();
 
                if (nameParametr != null && valueParametrs != null)
                {
                    foreach (string valueParametr in valueParametrs)
                    {
                        // запрос
                        string sqlExpression = String.Format("SELECT * FROM dbo.KONTEYNERNYE_MESTA WHERE {0} LIKE @valueParametrs", nameParametr);
                        // объект для выполнения SQL-запроса
                        SqlCommand command = new SqlCommand(sqlExpression, sqlConnection);
                        // создаем параметр для имени
                        SqlParameter nameParam = new SqlParameter("@valueParametrs", valueParametr);
                        // добавляем параметр к команде
                        command.Parameters.Add(nameParam);
 
                        SqlDataReader datareader = command.ExecuteReader();
 
                        if (datareader.HasRows)
                        {
                            while (datareader.Read())
                            {
                                Dictionary<string, string> diction = new Dictionary<string, string>();
 
                                for (int i = 0; i < datareader.FieldCount; i++)
                                {
                                    diction.Add(datareader.GetName(i), datareader.GetValue(i).ToString());
                                }
                                dataDB.Add(diction);
                            }
                        }
                        datareader.Close();
                    }
                }
                else
                {
                    // запрос
                    string sqlExpression = String.Format("SELECT * FROM dbo.KONTEYNERNYE_MESTA");
                    // объект для выполнения SQL-запроса
                    SqlCommand command = new SqlCommand(sqlExpression, sqlConnection);
                    SqlDataReader datareader = command.ExecuteReader();
 
                    if (datareader.HasRows)
                    {
                        while (datareader.Read())
                        {
                            Dictionary<string, string> diction = new Dictionary<string, string>();
 
                            for (int i = 0; i < datareader.FieldCount; i++)
                            {
                                diction.Add(datareader.GetName(i), datareader.GetValue(i).ToString());
                            }
                            dataDB.Add(diction);
                        }
                    }
                    datareader.Close();
                }
                sqlConnection.Close();
            }
            catch (Exception e)
            {
                Console.WriteLine("Error: " + e.Message);
                label_status_connection.Text = "Ошибка запроса к БД!";
            }
        }
Есть ощущение, что это все-таки связано с форматом данных, но в этом не уверен и как это проверить не знаю.
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
21.12.2020, 18:49
Ответы с готовыми решениями:

Excel и группы записей. Импорт записей, согласно категории(которая указана в экселе)
Задаю вопрос, т.к. не нашёл ни в яндексе, ни тут ответ. Просьба ткнуть носом, т.к. считаю что вопрос на уровне новичка и тема должна была...

сравнение записей
Не знаю как можно сравнить вводимую запись с записью у базе. как заменить if(isp==col.surnane)? #include &lt;iostream&gt; ...

Сравнение записей
Всем привет, снова я(знаю что я вас уже замучил :-| ). С ListCout я разобрался. Но понял после этого что мне придётся менять всю форму....

12
800 / 583 / 207
Регистрация: 21.02.2019
Сообщений: 2,095
21.12.2020, 19:14
...Volodya_,
.. в принципе, при сохранении/обновлении могут в БД (или Excele) добавляться трейлерные пробелы к значению, т.е. возможно есть смысл использовать TRIM() и в SQL-запросе .... Но вообще говоря схема у вас сложная .. У вас и БД, и Excel редактируются из разных источников (кроме вашей программы) ???
0
15 / 13 / 3
Регистрация: 20.02.2018
Сообщений: 446
21.12.2020, 20:05  [ТС]
Цитата Сообщение от carrotik Посмотреть сообщение
У вас и БД, и Excel редактируются из разных источников (кроме вашей программы) ???
БД нет - загружается только из Excel, а Excel выгружается из БД и редактируется пользователями

Добавлено через 3 минуты
Цитата Сообщение от carrotik Посмотреть сообщение
в принципе, при сохранении/обновлении могут в БД (или Excele) добавляться трейлерные пробелы к значению, т.е. возможно есть смысл использовать TRIM()
Перед сравнением поудалять все пробелы вначале и конце строки?
Попробую
0
1497 / 1238 / 245
Регистрация: 04.04.2011
Сообщений: 4,363
22.12.2020, 00:10
Цитата Сообщение от Volodya_ Посмотреть сообщение
БД нет - загружается только из Excel, а Excel выгружается из БД и редактируется пользователями
Т.е. Вы используете автомобиль просто вместо лошади, впрягая в него обыкновенную телегу ?
А написать простейшее веб-приложение с возможностью работы непосредственно с БД ? Тогда и никакой Эксель не нужен, и будет возможность работы с мобильных устройств, и данные будут сразу актуализироваться для всех пользователей.

Добавлено через 17 минут
Хотя в Вашей проблеме может быть один нюанс.
НЮАНСИЩЕ !
И называется он "Система".
Административная.

Это когда просто ставят перед фактом: есть "конечные" пользователи (ответственные лица), которые работают в Excel.
И точка !
Они должны видеть текущее состояние информации и иметь возможность вносить в нее изменения.
Которые "где-то" фиксируются и в следующий раз приходят к ним же в виде "свежего" Excel-файла.
Обычно по почте

В этом случае проблема вовсе не так проста, как может показаться с первого взгляда.
"Грабли" подстерегают разработчика на каждом углу.

Просто перечислю некоторые:
1) Конкурентные изменения. Когда в БД приходит два или более изменений одной и той же сущности (записи таблицы).
Какой отдать предпочтение ?
2) Ошибки набора. Настройка Excel-шаблона на справочники далеко не всегда помогает. Да и не все можно решить справочниками. Например одного и того же человека можно ввести как Иванов Иван Петрович, так и Иван Петрович Иванов. А можно набрать Иванова на разных регистрах: первая "в" на русской раскладке, вторая "в" - на латинской. Визуально хрен различишь. Но в БД будет ошибка, которая приведет например к не выборке или неудачному поиску этого Иванова.
3) Некомплектность данных. Когда пользователь не укажет обязательную информацию. Сервер не "примет" такую запись. Что с ней делать ?
Возвращать с пометкой об ошибке ? Но как ? И что ? Только эту строку или весь последний "пакет" изменений. Или полностью весь Excel-файл с пометками об ошибках. Тогда что, не писать в БД все изменения ?

Добавлено через 12 минут
Задача не проста, но решается

Если концептуально.

1. Валидация при вводе.
Макросами обеспечить проверку всех данных с выдачей ошибок. Желательно "на лету". Для этого нужно писать обработчики событий ухода из ячейки, из строки, потерю фокуса и т.д. Т.е. при ошибке сразу "бить по рукам" Это реально помогает
Для общей валидации предусмотреть кнопку "Проверить", после которой автоматически выставлять где-то "в кишках" флажок ошибок в документе.
Пополнение справочников - отбельной страницей (листом) Книги. На каждый справочник - свой лист.
Кнопка "отправить". При нажатии проверяется флажок (который, кстати, устанавливается автоматически после любых изменений в файле) и если не 0 (при условии, что 0 - нет ошибок), то ничего не посылать. А если 1, то автоматически отсылать файл по почте.

2. Валидация на сервере.
После того, как пользователь все ввел, надо слать файл не на прямую в БД, а в буфер для серверной валидации. При нахождении хотя бы одной ошибки изменений не делать вообще - список ошибок с указанием "лист-строка-столбец" отсылается автору "произведения". Из буфера книга удаляется.

Добавлено через 13 минут
3. Идентификация изменений.

3.1. На клиенте (VB)

Все "исходные" строчки документа (т.е. записи таблиц БД) должны иметь на листе Id. Показывать его юзеру, конечно, не следует. Кроме того, флажок модификации. (U - изменен, D - удален). Их можно показывать, но нельзя давать править.
Удобнее всего это отображать цветом фона или фонта.

Если юзер удаляет запись, то она помечается "D", если изменяет - "U", если вводит новую, то можно не помечать - ведь у нее не будет Id.

Таким образом при отправке файла на серверную валидацию все строчки будут иметь признак изменений.

3.2. На сервере (C#, SQL)

Как валидация, так и внесение изменений в БД существенно ускорится и упростится, т.к. обрабатываться будут не все 10000 строк документа, а только 2 удаленных, 50 измененных и 10 добавленных.

Добавлено через 7 минут
Когда-то работал в крупной госконторе, где из филиалов в центр шли данные. В Excel.
Валидация на сервере была там сделана просто, но надежно - через сетевой файл-сервер.
Клерк отсылал в специальную папку на сервере (для филиала) готовый файл и ждал, пока там произойдет валидация.
Через время (обычно до часа) смотрел эту папку и если файл там лежал (обычно с приставкой err), то он его забирал назад и смотрел ошибки, исправлял их и посылал по новой.
0
15 / 13 / 3
Регистрация: 20.02.2018
Сообщений: 446
22.12.2020, 07:33  [ТС]
[
Цитата Сообщение от MsGuns Посмотреть сообщение
Т.е. Вы используете автомобиль просто вместо лошади, впрягая в него обыкновенную телегу ?
А написать простейшее веб-приложение с возможностью работы непосредственно с БД ? Тогда и никакой Эксель не нужен, и будет возможность работы с мобильных устройств, и данные будут сразу актуализироваться для всех пользователей.
Данные собираются со множества пользователей и обновляются большими партиями. Давать всем пользователям доступ к БД нельзя. По 1-ой записи вбивать неудобно.

Добавлено через 9 минут
Цитата Сообщение от MsGuns Посмотреть сообщение
1. Валидация при вводе.
Валидацию при вводе какую возможно сделал, но данные имеют такое содержание, что их особо не провалидируешь на вводе
0
Эксперт .NET
 Аватар для Usaga
14297 / 9382 / 1353
Регистрация: 21.01.2016
Сообщений: 35,370
22.12.2020, 10:57
Volodya_, я вообще проблемы не понимаю. Посмотрите под отладкой на работу своего кода и всё. Сразу станет видно, где идёт неправильное сравнение.
0
800 / 583 / 207
Регистрация: 21.02.2019
Сообщений: 2,095
22.12.2020, 11:06
Volodya_,
.. для подобных ситуаций (куча диких пользователей в Экселе или еще где) есть один хак, опробованный мною давно, и работавший ... А именно: не пытаться сравнивать вносимые записи с существующими, делать грубо говоря "вали кулём" - т.е. аналог bulk insert в БД из Экселя (а не update), а потом хранимкой или внешним скриптом удалять продублированные записи из базы ... Это совершается проще и однозначно быстрее ... пример из скрипта :
T-SQL
1
delete t1 from Ledger t1, Ledger t2 where t1.Invoice = t2.Invoice and t1.TransID < t2.TransID
.. здесь Ledger - имя таблицы, поле Invoice - составное поле, являющееся уникальным для данной коммерческой (не sql-ной) транзакции, а TransID - автоинкрементное поле ... Т.е. удаляются все дублирующие записи, кроме последней по времени (т.е. с масимальным Id) ...
0
Эксперт .NET
 Аватар для Usaga
14297 / 9382 / 1353
Регистрация: 21.01.2016
Сообщений: 35,370
22.12.2020, 11:21
carrotik, э, не) Такое себе решение) Получается, что между массовым импортом данных и JOB'ой по удалению дубликатов, данные в базе будут в не консистентном состоянии. Не надо так делать)
1
800 / 583 / 207
Регистрация: 21.02.2019
Сообщений: 2,095
22.12.2020, 11:33
Цитата Сообщение от Usaga Посмотреть сообщение
carrotik, э, не) Такое себе решение) Получается, что между массовым импортом данных и JOB'ой по удалению дубликатов, данные в базе будут в не консистентном состоянии. Не надо так делать)
.. ИМХО, подобная с позволения сказать "архитектура", где консистентность данных отдается на откуп пользователю в Экселе (а он очень творчески бывает настроен в части ввода данных в нужном формате) все равно подразумевает костыли ... Мой костыль как минимум работает быстрее поиска/сравнения/апдейта записи ... и скрипт удаления может идти сразу же после инсерта ... Но я назвал это "хаком", соответственно, на усмотрение разработчика ...
0
Эксперт .NET
 Аватар для Usaga
14297 / 9382 / 1353
Регистрация: 21.01.2016
Сообщений: 35,370
22.12.2020, 11:37
carrotik, как он будет быстрее работать, если ему точно так же придётся перетряхивать всю базу на предмет поиска дубликатов? И экономии на трафике я тоже не вижу, ибо в базу всё так же все данные пачкой вначале полетят. Только впустую первычиные ключи расходовать.
0
1497 / 1238 / 245
Регистрация: 04.04.2011
Сообщений: 4,363
22.12.2020, 14:17
carrotik,
Ваше решение подходит только для небольших баз и не слишком "ответственной" информации в ней.
Если база > 1млн записей, то гарантированы жуткие тормоза сервера. Ну и куча "мусора", которое будет вводить в заблуждение пользователей.

В общем, как правильно сказал уважаемый Usaga, решение "так себе"
0
800 / 583 / 207
Регистрация: 21.02.2019
Сообщений: 2,095
22.12.2020, 14:43
Цитата Сообщение от MsGuns Посмотреть сообщение
Если база > 1млн записей, то гарантированы жуткие тормоза сервера. Ну и куча "мусора", которое будет вводить в заблуждение пользователей
.. базу в мильон записей редактировать Экселем? ... безумству храбрых ... (с)
0
1497 / 1238 / 245
Регистрация: 04.04.2011
Сообщений: 4,363
22.12.2020, 15:11
Цитата Сообщение от carrotik Посмотреть сообщение
базу в мильон записей редактировать Экселем?
Я уже писал выше про госконтору. Там записей не миллион. На пару порядков больше
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
22.12.2020, 15:11
Помогаю со студенческими работами здесь

Сравнение записей
Приветствую всех и прошу помощи! Возникла такая проблема: при добавление записи в таблицу нужно сравнить ее с имеющимися. При совпадении...

Сравнение и обновление данных
Необходимо создать временную таблицу, в которую будут загружаться .csv файл и по уникальному будет сравниваться с таблицей бд для избежания...

Обновление записей в БД
Как сделать SQL запрос к БД, чтобы изменить у определенной записи из Combo1 значение других её записей. К примеру: мы берем номер...

Обновление записей в БД
У меня есть компонент DataGrid, именно DataGrid, а не DataGridView. Потому что все материалы что я находил они были именно для DataGridView...

Обновление записей в БД
Добрый день, помогите пожалуйста реализовать сохранение\обновление БД после редактирования записей в DGV. Подойдет либо при нажатии на...


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

Или воспользуйтесь поиском по форуму:
13
Ответ Создать тему
Новые блоги и статьи
Модульный подход на примере F#
DevAlt 06.03.2026
В блоге дяди Боба наткнулся на такое определение: В этой книге («Подход, основанный на вариантах использования») Ивар утверждает, что архитектура программного обеспечения — это структуры,. . .
Управление камерой с помощью скрипта OrbitControls.js на Three.js: Вращение, зум и панорамирование
8Observer8 05.03.2026
Содержание блога Финальная демка в браузере работает на Desktop и мобильных браузерах. Итоговый код: orbit-controls-threejs-js. zip. Сканируйте QR-код на мобильном. Вращайте камеру одним пальцем,. . .
SDL3 для Web (WebAssembly): Синхронизация спрайтов SDL3 и тел Box2D
8Observer8 04.03.2026
Содержание блога Финальная демка в браузере. Итоговый код: finish-sync-physics-sprites-sdl3-c. zip На первой гифке отладочные линии отключены, а на второй включены:. . .
SDL3 для Web (WebAssembly): Идентификация объектов на Box2D v3 - использование userData и событий коллизий
8Observer8 02.03.2026
Содержание блога Финальная демка в браузере. Итоговый код: finish-collision-events-sdl3-c. zip Сканируйте QR-код на мобильном и вы увидите, что появится джойстик для управления главным героем. . . .
Реалии
Hrethgir 01.03.2026
Нет, я не закончил до сих пор симулятор. Эта задача сложнее. Не получилось уйти в плавсостав, но оно и к лучшему, возможно. Точнее получалось - но сварщиком в палубную команду, а это значит, в моём. . .
Ритм жизни
kumehtar 27.02.2026
Иногда приходится жить в ритме, где дел становится всё больше, а вовлечения в происходящее — всё меньше. Плотный график не даёт вниманию закрепиться ни на одном событии. Утро начинается с быстрых,. . .
SDL3 для Web (WebAssembly): Сборка библиотек: SDL3, Box2D, FreeType, SDL3_ttf, SDL3_mixer и SDL3_image из исходников с помощью CMake и Emscripten
8Observer8 27.02.2026
Недавно вышла версия 3. 4. 2 библиотеки SDL3. На странице официальной релиза доступны исходники, готовые DLL (для x86, x64, arm64), а также библиотеки для разработки под Android, MinGW и Visual Studio. . . .
SDL3 для Web (WebAssembly): Реализация движения на Box2D v3 - трение и коллизии с повёрнутыми стенами
8Observer8 20.02.2026
Содержание блога Box2D позволяет легко создать главного героя, который не проходит сквозь стены и перемещается с заданным трением о препятствия, которые можно располагать под углом, как верхнее. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru