Форум программистов, компьютерный форум, киберфорум
C#: Базы данных, ADO.NET
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.94/35: Рейтинг темы: голосов - 35, средняя оценка - 4.94
54 / 6 / 5
Регистрация: 24.01.2019
Сообщений: 171
1
MS SQL

ID связанных таблиц 2

17.05.2020, 11:06. Показов 7170. Ответов 88
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Доброго времени суток, нужен совет(подсказка, пример) по следующей задаче:
Имеются в БД(MSSQL) две таблицы, головная "Покупки" и дополнительная "ПокупкаИнфо".
У первой есть первичный уникальный ключ, по полю ID(со свойством IDENTITY).
У второй таблицы есть составной "PRIMARY KEY(ПокупкаID,LineItem)".

Получается связка такая, на каждый уникальный "Покупки.ID" приходится по нескольку "ПокупкаИнфо.ПокупкаID".
С "Покупки.ID" автоматически(в SQL) создаётся новый номер.
А вот с "ПокупкаИнфо.ПокупкаID" возникает вопрос:
Как в С# сделать так , что бы для "ПокупкаИнфо.ПокупкаID" присваивался тот же номер что и у "Покупки.ID" ?

Эта же тема была тут: ID связанных таблиц
Но там сказали пойти сюда)
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
17.05.2020, 11:06
Ответы с готовыми решениями:

Сохранение связанных таблиц
Доброго времени суток! Проблема заключается в том что у меня есть три таблицы в БД,...

Отображение связанных таблиц
Здравствуйте Не подскажите как такое можно реализовать? Есть 2 таблицы на форме. Они связаны 2...

Работа с Id из связанных таблиц
Всем доброго времени суток!!! Работаю на языке с# c средой разработки баз данных ms sql server...

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

88
2298 / 1662 / 325
Регистрация: 14.08.2018
Сообщений: 5,463
Записей в блоге: 4
30.09.2020, 08:36 21
Author24 — интернет-сервис помощи студентам
7-2-3, а причем тут вот это???
C#
1
myCommand.Parameters["@ID"].Value =intIDLast ;
Это же ВЫХОДНОЙ параметр от сервера, зачем ему присваивать непонятное значение, у него есть свое и берется оно из команды отсюда:
T-SQL
1
set @ID = SCOPE_IDENTITY()
Значение этого параметра можно только присвоить чему нибудь, например переменной и использовать ее где надо.
Ну и про object уже сказали.
1
1643 / 1144 / 171
Регистрация: 23.07.2010
Сообщений: 6,794
30.09.2020, 09:47 22
не пойму, зачем
set @ID = SCOPE_IDENTITY()
что за императивщина?
T-SQL
1
output inserted.id
и юзать
C#
1
.ExecuteScalar()
чем не угодил?
0
54 / 6 / 5
Регистрация: 24.01.2019
Сообщений: 171
30.09.2020, 13:52  [ТС] 23
Я пытался вникнуть в [9], но мне просто-напросто знаний не хватает, что бы осмыслить написанное, соответственно и проблемы в самом понимании кода)
Получается каша в голове.

На счёт транзакции, я думаю имеется введу едино-разовый обмен данными ? Вроде как, запросы должны выполнится зараз, при одном обмене ?

Я уже пытался писать в ветку для новичков, с другим вопросом, но там после решения , модератор меня перекинул в ветку "C# Windows Forms", соответственно по аналогии , я сюда и написал.

Добавлено через 17 минут
А вот так правильно:
C#
1
intIDLast =myCommand.Parameters["@ID"].Value;
?

Добавлено через 14 минут
C#
1
object
А какой там тип должен быть ?
0
2298 / 1662 / 325
Регистрация: 14.08.2018
Сообщений: 5,463
Записей в блоге: 4
30.09.2020, 13:58 24
7-2-3, Параметры в ADO.NET - https://metanit.com/sharp/adonet/2.9.php
И там еще много чего интересного
1
54 / 6 / 5
Регистрация: 24.01.2019
Сообщений: 171
30.09.2020, 22:28  [ТС] 25
Прочитал первые 2 страницы, получается они там значение этого параметра idParam(в котором лежит полученный id) выводят на консоль:
C#
1
2
3
4
5
6
7
8
9
10
SqlParameter idParam = new SqlParameter
    {
        ParameterName = "@id",
        SqlDbType = SqlDbType.Int,
        Direction = ParameterDirection.Output // параметр выходной
    };
    command.Parameters.Add(idParam);     
    command.ExecuteNonQuery();    
   // получим значения выходного параметра
    Console.WriteLine("Id нового объекта: {0}", idParam.Value);
А у меня получается оно передаётся в переменную intIDLast
C#
1
intIDLast = cmd.Parameters["@ID"].Value
Из которой я должен героическими усилиями передать(ну хотя бы) в textBox , пока всё так ?

Добавлено через 58 минут
Поменял на вот эту абру-кадабру:
C#
1
2
3
 object intIDLast;
                intIDLast = myCommand.Parameters["@ID"].Value;
               textBox5.Text = Convert.ToString(intIDLast);
И получаю вместо ID, ноль.
Понятно почему ноль:
C#
1
myCommand.Parameters.AddWithValue("@ID", 0).Direction = ParameterDirection.Output;
А для чего он там нужен тогда ?



И на счёт транзакций, что бы мои операции выполнялись в "виде одного целостного пакета ", мне нужно напихать вот эти строки:
C#
1
2
3
command.Transaction = transaction;
transaction.Commit();
transaction.Rollback();
В определённых местах кода, я всё правильно понял ?
0
1496 / 1238 / 244
Регистрация: 04.04.2011
Сообщений: 4,362
01.10.2020, 12:39 26
Цитата Сообщение от 7-2-3 Посмотреть сообщение
В определённых местах кода, я всё правильно понял ?
Неправильно.

Есть "Клиент". Есть "сервер".
Между ними "разговор".
"Фразами".

"Клиент" объясняет "серверу" что нужно сделать. Если эти объяснения идут в одной "фразе", то "Сервер" их выполнит все до последнего пункта. Или не выполнит ничего. Из-за ошибки. Любой.

Но "Клиент" может свои объяснения оформить в несколько "фраз". И чтобы "Сервер" понял, что все эти "фразы" являются не чем-то самостоятельным, а единым целым, "Клиент" должен перед первой "фразой" сказать что-то типа "Начинаю передавать команды. Получай первую". А после последней "Все, я закончил".

Если все это перевести на язык SQL-сервера, то получится : клиент открывает соединение и указывает, что явно запускает транзакцию. Сервер получает такую посылочку, создает у себя соединение и пишет в свой журнал, что открывается новая транзакция, завершение которой отложено и будет выполняться по ожидаемой то клиента этого соединения специальной интструкции.

В Вашем случае Вы готовите пакет запросов (скрипт), который содержит все необходимые операции от первой до последней, а именно: 1) Добавление новой записи в Master, 2) Получение UID только что вставленной записи, 3) Добавление в Detail новой записи с использованием UID Master-записи. Все это заворачиваете в единую "фразу" (sqlcommand) и отправляете на сервер одним махом. Никакого дополнительного управления никакой транзакцией не требуется - сервер лучше Вас знает что и как делать.

А вот если Вам надо последовательно послать несколько "фраз" (sqlcommand) одну за другой, то для гарантии правильности нужно использовать транзакцию. При этом надо помнить, что слишком большие интервалы между этими "фразами" могут привести к тому, что сервер прервет свое ожидание и "сбросит" транзакцию не дождавшись "финального свистка".

Добавлено через 10 минут
У Вас в первой "фразе" - sqlcommand выполняется вставка в Master и извлечение нового UID в некую переменную, которую, кстати, надо объявить инструкцией DECLARE перед всеми командами INSERT и SELEST. На этом "фраза" завершается.
После этого идет посылка другой "фразы" - вставки в Detail. Но:
- Во-первых, Вам надо получить этот самый UID, чтобы использовать далее и который Вы зачем-то возжелали сунуть в некий TextBox.
- Во-вторых, не гарантия, что этот самый UID будет актуальным при второй фразе. Запись из Мастера с этим UID вполне может быть уже удалена в другом или даже этом же соединении.

Для правильного решения этой проблемы Вам и привели корректный и гарантированный способ в [9]
1
2298 / 1662 / 325
Регистрация: 14.08.2018
Сообщений: 5,463
Записей в блоге: 4
01.10.2020, 12:42 27
Цитата Сообщение от MsGuns Посмотреть сообщение
В Вашем случае Вы готовите пакет запросов (скрипт), который содержит все необходимые операции от первой до последней, а именно: 1) Добавление новой записи в Master, 2) Получение UID только что вставленной записи, 3) Добавление в Detail новой записи с использованием UID Master-записи. Все это заворачиваете в единую "фразу" (sqlcommand) и отправляете на сервер одним махом. Никакого дополнительного управления никакой транзакцией не требуется - сервер лучше Вас знает что и как делать.
Череду этих действий проще всего оформить хранимой процедурой на сервере. И в ней уже сделать BEGIN TRANSACTION, TRY, INSERT, SCOPE_IDENTITY(), INSERT, COMMIT TRAN, CATCH, RAISERROR, ROLLBACK TRAN. Так будет правильнее, надежнее и быстрее, чем гнать три запроса из приложения.
0
1496 / 1238 / 244
Регистрация: 04.04.2011
Сообщений: 4,362
01.10.2020, 13:03 28
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
Череду этих действий проще всего оформить хранимой процедурой на сервере. И в ней уже сделать BEGIN TRANSACTION, TRY, INSERT, SCOPE_IDENTITY(), INSERT, COMMIT TRAN, CATCH, RAISERROR, ROLLBACK TRAN. Так будет правильнее, надежнее и быстрее, чем гнать три запроса из приложения
Это не всегда возможно
0
Эксперт .NET
12073 / 8383 / 1280
Регистрация: 21.01.2016
Сообщений: 31,578
01.10.2020, 13:09 29
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
Так будет правильнее, надежнее и быстрее, чем гнать три запроса из приложения.
Так человек говорит о ОДНОМ запросе с последовательным набором действий. Тело этого запроса будет полностью соответствовать телу такой хранимой процедуры. Хранимка тут не даёт ровным счётом никаких преимуществ ни в чём.
0
54 / 6 / 5
Регистрация: 24.01.2019
Сообщений: 171
01.10.2020, 21:18  [ТС] 30
C#
1
2
3
4
DECLARE @NewMasterId INT \\ вот тут мы объявляем переменную @NewMasterId, в которую извлечём новый UID
INSERT INTO MASTER () ...  \\ Тут происходит вставка в табличку Master
SET @NewMasterID = (SELECT @@IDENTITY) \\  возвращаем последнее значение идентификатора 
INSERT INTO DETAIL (.., PID, ..) VALUES (..,@NewMasterID,..) \\ Тут вставляем этот идентификатор из @NewMasterID в таблицу Detail в PID
Всё верно ?
0
1496 / 1238 / 244
Регистрация: 04.04.2011
Сообщений: 4,362
01.10.2020, 21:42 31
Цитата Сообщение от 7-2-3 Посмотреть сообщение
Всё верно ?
Что "верно" ? Копипаста ?
А что вместо точек ? И таблицы у Вас называются MASTER и DETAIL ?

Добавлено через 1 минуту
Попробуйте запрос в MSSMS
0
54 / 6 / 5
Регистрация: 24.01.2019
Сообщений: 171
01.10.2020, 22:27  [ТС] 32
T-SQL
1
2
3
4
5
6
7
8
DECLARE @Newid int 
INSERT INTO Покупки(ПоставщикID, PrihodDate) VALUES (4, getdate())
SET @Newid = (SELECT @@IDENTITY) 
INSERT INTO ПокупкаИнфо (ПокупкаID, LineItem, ProductID, Qty , Price) 
VALUES                              
                                          (@Newid, 1, 1, 21, 450),  
                                          (@Newid, 2, 3, 11, 550)
GO
Вот так ?
А как всё это привязывать к приложению ?
0
Эксперт .NET
12073 / 8383 / 1280
Регистрация: 21.01.2016
Сообщений: 31,578
02.10.2020, 05:55 33
Цитата Сообщение от 7-2-3 Посмотреть сообщение
Вот так ?
Нет. Вот так:

SQL
1
2
3
4
5
6
7
8
DECLARE @Newid INT 
INSERT INTO Покупки(ПоставщикID, PrihodDate) VALUES (4, getdate())
SET @Newid = SCOPE_IDENTITY()
INSERT INTO ПокупкаИнфо (ПокупкаID, LineItem, ProductID, Qty , Price) 
VALUES                              
(@Newid, 1, 1, 21, 450),  
(@Newid, 2, 3, 11, 550)
GO
Это при условии, что вы откроете транзакцию в коде (IDbConnection.BeginTransaction или TransactionScope) иначе это всё надо обернуть в BEGIN TRANSACTION и COMMIT TRANSACTION.

Ну и русский язык использовать в именовании сущностей - малое фу. А уж смешивать с английским - фу в квадрате.
1
1643 / 1144 / 171
Регистрация: 23.07.2010
Сообщений: 6,794
02.10.2020, 09:27 34
Цитата Сообщение от Usaga Посмотреть сообщение
Ну и русский язык использовать в именовании сущностей - малое фу. А уж смешивать с английским - фу в квадрате.
0
1496 / 1238 / 244
Регистрация: 04.04.2011
Сообщений: 4,362
02.10.2020, 11:09 35
Цитата Сообщение от 7-2-3 Посмотреть сообщение
А как всё это привязывать к приложению ?
Одним из способов:

1. На Клиенте.
Вот этот скрипт всунуть как строку в sqlcommand, далее проинициализировать параметры и Execute

2. На Сервере
Оформить код как хранимку (StoredProc) с параметрами непосредственно в БД (MSSMS)
В коде вызывать эту SP опять же с подстановкой параметров - всех, кроме естественно NewId, он определится "внутри".

Кстати, нормальной практикой вторым способом является возвращение хранимкой после вставки нового (новых) ID.
Они очень могут понадобиться при локализации новой записи после перечитки грида. Ну это для того, чтобы после добавления новой записи в видимую таблицу (грид) именно эта запись становилась текущей, что весьма удобно при изменениях модальными формами.
1
54 / 6 / 5
Регистрация: 24.01.2019
Сообщений: 171
02.10.2020, 23:26  [ТС] 36
Получилась вот такая процедура:
T-SQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
CREATE PROC Newid 
@ПоставщикID int,
 @PrihodDate Date,
 @LineItem int,
 @ProductID int,
 @Qty int,
 @Price money
 AS
    --SET NOCOUNT ON    
    DECLARE @Newid int
    INSERT INTO Покупки
    VALUES 
    (@PrihodDate,@ПоставщикID ) 
 
    SET @Newid = SCOPE_IDENTITY()
 
    INSERT  ПокупкаИнфо
    VALUES
    (@Newid, @LineItem, @ProductID, @Qty, @Price)
А вот как её вызвать в приложении пока не разобрался.
0
54 / 6 / 5
Регистрация: 24.01.2019
Сообщений: 171
03.10.2020, 22:24  [ТС] 37
Нацарапал вот такую кнопочку:
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
  private void button6_Click(object sender, EventArgs e)
        {            
            int ПоставщикID = int.Parse(label22.Text);
            DateTime PrihodDate = Convert.ToDateTime(dateTimePicker1.Text);
          
            int LineItem = int.Parse(dataGridView4.Rows[dataGridView4.CurrentCell.RowIndex].Cells["LineItemDataGridViewTextBoxColumn"].Value.ToString());
            int ProductID = int.Parse(dataGridView4.Rows[dataGridView4.CurrentCell.RowIndex].Cells["ProductIDDataGridViewTextBoxColumn"].Value.ToString());
            int Qty = int.Parse(dataGridView4.Rows[dataGridView4.CurrentCell.RowIndex].Cells["QtyDataGridViewTextBoxColumn"].Value.ToString());
            int Price = int.Parse(dataGridView4.Rows[dataGridView4.CurrentCell.RowIndex].Cells["PriceDataGridViewTextBoxColumn"].Value.ToString());
 
            using (
                 conn = new SqlConnection()) ;
                 conn.ConnectionString = @"Data Source=IGOR-PC\SQL150916;Initial Catalog=Base5;Integrated Security=True";
                 conn.Open();
 
            {
                using (SqlCommand myCommand = new SqlCommand("Newid", conn))
                {
                    myCommand.CommandType = CommandType.StoredProcedure;
 
                    myCommand.Parameters.Add("@PrihodDate", SqlDbType.DateTime, 8);
                    myCommand.Parameters["@PrihodDate"].Value = PrihodDate;
 
                    myCommand.Parameters.Add("@ПоставщикID", SqlDbType.Int, 4);
                    myCommand.Parameters["@ПоставщикID"].Value = ПоставщикID;
        
                    myCommand.Parameters.Add("@LineItem", SqlDbType.Int, 3);
                    myCommand.Parameters["@LineItem"].Value = LineItem;
 
                    myCommand.Parameters.Add("@ProductID", SqlDbType.Int, 3);
                    myCommand.Parameters["@ProductID"].Value = ProductID;
 
                    myCommand.Parameters.Add("@Qty", SqlDbType.Int, 1000);
                    myCommand.Parameters["@Qty"].Value = Qty;
 
                    myCommand.Parameters.Add("@Price", SqlDbType.Int, 1000);
                    myCommand.Parameters["@Price"].Value = Price;
           
                    myCommand.ExecuteNonQuery();
                }                
            }        
        }
Добавлено через 8 минут
Правда пока только для одной строки, возникает вопрос, тут лучше процедуру доработать или цикл какой-нибудь в кнопочку прилепить ? Что бы из dataGridView4, несколько строк вставлять в базу.
0
Эксперт .NET
12073 / 8383 / 1280
Регистрация: 21.01.2016
Сообщений: 31,578
05.10.2020, 10:44 38
Лучший ответ Сообщение было отмечено 7-2-3 как решение

Решение

7-2-3, мой вам совет: возьмите Dapper. Прост в освоении и позволяет не писать руками эти девятиэтажные портянки с заполнением параметров.
0
54 / 6 / 5
Регистрация: 24.01.2019
Сообщений: 171
05.10.2020, 19:25  [ТС] 39
В другой раз я возможно воспользуюсь Вашим советом, но в данном случае хотелось бы как-нибудь "домучить" эту кнопку(пусть и с девятиэтажным кодом).
0
Эксперт .NET
12073 / 8383 / 1280
Регистрация: 21.01.2016
Сообщений: 31,578
05.10.2020, 19:40 40
Цитата Сообщение от 7-2-3 Посмотреть сообщение
Правда пока только для одной строки, возникает вопрос, тут лучше процедуру доработать или цикл какой-нибудь в кнопочку прилепить ? Что бы из dataGridView4, несколько строк вставлять в базу.
Так-то запрос вам может N строк вернуть. Вы их сразу все можете вставить в свой грид.
0
05.10.2020, 19:40
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
05.10.2020, 19:40
Помогаю со студенческими работами здесь

Вывод связанных таблиц в DataGridView
Здравствуйте. Имеются три таблицы: Books (со столбцами ID, Name, Author, Genre), Authors (со...

Обновление связанных таблиц в DataGridView
Всем привет. Возник такой вопрос: как можно обновить связанную таблицу при программной сортировке...

Обновление связанных таблиц в datagridview
Есть кнопка на вывод информации: SqlConnection con; SqlDataAdapter sda; ...

Отображение полей связанных таблиц
Здравствуйте. Существуют таблицы: Товары, тип товара, категория. "Тип товара" связанны с...

Удалить записи из связанных таблиц
Есть три связанных таблицы (один ко многим).Нужно удалить запись из таблицы "Customers" с...

Вывод записей из связанных таблиц
Здравствуйте все обитатели форума) У меня есть БД, в ней таблица книги и таблица авторы, связь...


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

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru