Хранимая процедура для вставки двух таблиц со связью?

#sql #stored-procedures

#sql #хранимые процедуры

Вопрос:

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

 ALTER PROCEDURE InsertUserProfile
(
    @UserID varchar(10),
    @Pass varchar(50),
    @Enabled int,
    @Permission int,
    @Rank int,
    @FName varchar(50),
    @LName varchar(50),
    @Phone varchar(50),
    @Email1 varchar(50),
    @Email2 varchar(50)
)
AS

BEGIN TRANSACTION
INSERT INTO tbl_user_login VALUES (@UserID, @Pass, @Enabled, @Permission, @Rank)
IF @@ERROR <> 0
BEGIN 
    ROLLBACK
    RETURN
END


INSERT INTO tbl_user_profile VALUES (@FName, @LName, @Phone, @Email1, @Email2)
IF @@ERROR <> 0
BEGIN
    ROLLBACK
    RETURN
END

COMMIT
  

Из этого следует ASP.NET Код

 SqlConnection sqlConn = new SqlConnection(ConfigurationManager.ConnectionStrings["DBConnString"].ConnectionString);
        SqlCommand cmd = new SqlCommand("dbo.InsertUserProfile", sqlConn);
        cmd.CommandType = CommandType.StoredProcedure;

        cmd.Parameters.Add("@UserID", DbType.String).Value = txtUserID.Text;
        cmd.Parameters.Add("@Pass", DbType.String).Value = txtPass.Text;
        cmd.Parameters.Add("@Enabled", DbType.Int32).Value = 1;
        cmd.Parameters.Add("@Permission", DbType.Int32).Value = Convert.ToInt32(ddlPermission.SelectedValue);
        cmd.Parameters.Add("@Rank", DbType.Int32).Value = Convert.ToInt32(ddlRank.SelectedValue);
        cmd.Parameters.Add("@FName", DbType.String).Value = txtFName.Text;
        cmd.Parameters.Add("@LName", DbType.String).Value = txtLName.Text;
        cmd.Parameters.Add("@Phone", DbType.String).Value = txtPhone.Text;
        cmd.Parameters.Add("@Email1", DbType.String).Value = txtEmail1.Text;
        cmd.Parameters.Add("@Email2", DbType.String).Value = txtEmail2.Text;

        sqlConn.Open();
        int rows = cmd.ExecuteNonQuery();
        sqlConn.Close();
  

Но я получаю следующую ошибку.

Оператор INSERT конфликтовал с ограничением ВНЕШНЕГО КЛЮЧА «FK_tbl_user_profile_tbl_user_login». Конфликт произошел в базе данных «My DB Location», таблица «dbo.tbl_user_login», столбец «ID». Выполнение инструкции было завершено.

Я новичок в хранимых процедурах, поэтому есть предложения, как мне это исправить, чтобы я мог вставлять данные в две таблицы?

СХЕМА ТАБЛИЦЫ

 tbl_user_login

ID (int) 
UserID (varchar10) 
Pass (varchar50) 
Enabled (int) 
Permission (int) 
Rank (int)

tbl_user_profile

ID (int)
FName (varchar50)
LName (varchar50)
Phone (varchar50)
Email1 (varchar50)
Email2 (varchar50)
  

Комментарии:

1. Какова взаимосвязь между двумя таблицами? Я не вижу никакого общего столбца между ними . . .

2. @Richard Его «ИДЕНТИФИКАТОР», который является автоматическим увеличением в обеих таблицах.

3. Можете ли вы совместно использовать схему обеих таблиц?

4. @TechGiant, Схема добавлена к вопросу.

5. Вы можете посмотреть мой последний пост, о котором я упоминал. Вам нужно установить столбец id в качестве идентификатора, а затем вставить значения.

Ответ №1:

@Richard Его «ИДЕНТИФИКАТОР», который является автоматическим увеличением в обеих таблицах.

Использование автоинкремента ( IDENTITY ) в качестве первичного ключа — это нормально, но использовать его в качестве внешнего ключа опасно, поскольку вы не можете гарантировать, что они всегда будут синхронизированы; любой откат может привести к их нарушению (откат не отменяет приращения идентификаторов, поскольку это повлияло бы на другие SPID). Кроме того, любая гонка потоков между двумя параллельными INSERT потоками будет под угрозой.

Правильный подход здесь заключается в том, чтобы запросить SCOPE_IDENTITY() после первой вставки и использовать это в INSERT ко второй таблице; т.е. во второй таблице вы сообщаете ей значение. Обратите внимание, что, поскольку @@ERROR и SCOPE_IDENTITY() являются плавающими значениями, вы должны запросить их обе непосредственно после первой INSERT :

 SELECT @Error = @@ERROR, @NewId = SCOPE_IDENTITY()
  

Комментарии:

1. Я согласен с Марком, это не только опасно, в большинстве случаев это не имеет особого смысла. Связь обычно означает, что они связаны каким-то значимым образом, и чаще всего значение indentity на самом деле не имеет «значения», это просто первичный ключ, способ гарантировать уникальную запись. Попробуйте найти какую-нибудь значимую связь между таблицами. При этом иногда уместно, чтобы идентификатором был внешний ключ. (нет жестких правил).

2. Должен ли я использовать идентификатор пользователя из tbl_user_login в качестве FK в tbl_user_profile?

Ответ №2:

Вам нужно зафиксировать автоматически увеличиваемое значение, которое вы получаете при вставке в первую таблицу, tbl_user_login. После того, как вы ее захватите, вам нужно использовать ее для вставки во вторую таблицу.

 DECLARE @ID int

BEGIN TRANSACTION 
INSERT INTO tbl_user_login VALUES (@UserID, @Pass, @Enabled, @Permission, @Rank)

SET @ID = SCOPE_IDENTITY()    
IF @@ERROR <> 0
BEGIN      
    ROLLBACK     
    RETURN 
END   

INSERT INTO tbl_user_profile VALUES (@ID, @FName, @LName, @Phone, @Email1, @Email2) 

IF @@ERROR <> 0 
BEGIN     
    ROLLBACK     
    RETURN 
END  

COMMIT 
  

Комментарии:

1. Во-первых, @@IDENTITY это опасно (на него влияют триггеры и т.д.); SCOPE_IDENTITY() следует всегда использовать вместо этого. Во-вторых; когда вы читали, @@IDENTITY вы, вероятно, выбросили @@ERROR ту, которую проверяли.

2. Чтобы добавить к этому, столбец ID в tbl_user_profile не должен быть автоматически увеличивающимся столбцом IDENTITY.

Ответ №3:

 Create Table tbl_user_login
(
**ID INT Identity(1,1),**
UserID varchar10,
Pass varchar50 ,
Enabled int,
Permission int ,
Rank int
);
  

Создайте столбец ID как Identity (1,1), который будет автоматически увеличиваться, и это решит вашу проблему. Сделайте это в обеих таблицах.

Комментарии:

1. Вероятно, в итоге у вас будут разные идентификаторы пользователей в обеих таблицах

2. Нет, если вы одновременно вставляете значения через хранимую процедуру, чтобы идентификатор был одинаковым. Значения столбца Identity автоматически увеличиваются и будут одинаковыми в обеих таблицах.

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

4. Да, вы правы, дело в том, что он ищет решение, почему он не вставляет значения, поэтому я думаю, что если бы он попробовал это, тогда он смог бы вставлять значения, и тогда он мог бы лучше понять проблему, с которой он столкнулся?