Hi各位大老,下列問題我實際上已經用別的寫法解決,
但我還是想問一下關於下列程式碼是哪裡出問題?
我看我設置的reader都有關閉,但是卻一直跳錯誤。
都是在updateStageCommand.ExecuteNonQuery();這條出現(X)
訊息:
System.InvalidOperationException: 'There is already an open DataReader associated with this Connection which must be closed first.'
程式要達成的目標是:
當我刪除任一筆資料時,如果同個uid中有stage
大於要刪除的stage,那麼大於的部分都要-1
資料修正前(移除a2):
rid:a1、uid:ariel、stage:1
rid:a2、uid:ariel、stage:2
rid:a3、uid:ariel、stage:3
rid:a4、uid:ariel、stage:4
資料修正後:
rid:a1、uid:ariel、stage:1
rid:a3、uid:ariel、stage:2
rid:a4、uid:ariel、stage:3
我有兩種寫法,一開始只有用前端抓取rid。然後到資料庫操作。
public void DeletePoSignById(string rid)
{
SqlConnection sqlConnection = new SqlConnection(ConnStr);
SqlCommand sqlCommand = new SqlCommand("SELECT uid, stage FROM Po_sign WHERE rid = @rid");
sqlCommand.Connection = sqlConnection;
sqlCommand.Parameters.Add(new SqlParameter("@rid", rid));
sqlConnection.Open();
SqlDataReader reader = sqlCommand.ExecuteReader();
string uid = "";
int stage = 0;
if (reader.Read())
{
uid = Convert.ToString(reader["uid"]);
stage = Convert.ToInt32(reader["stage"]);
}
reader.Close();
sqlCommand = new SqlCommand("SELECT * FROM Po_sign WHERE uid = @uid");
sqlCommand.Connection = sqlConnection;
sqlCommand.Parameters.Add(new SqlParameter("@uid", uid));
SqlDataReader relatedReader = sqlCommand.ExecuteReader();
bool hasHigherStage = false;
while (relatedReader.Read())
{
int relatedStage = Convert.ToInt32(relatedReader["stage"]);
if (relatedStage > stage)
{
hasHigherStage = true;
int updatedStage = relatedStage - 1;
SqlCommand updateStageCommand = new SqlCommand("UPDATE Po_sign SET stage = @updatedStage WHERE uid = @uid AND stage = @relatedStage");
updateStageCommand.Connection = sqlConnection;
updateStageCommand.Parameters.Add(new SqlParameter("@updatedStage", updatedStage));
updateStageCommand.Parameters.Add(new SqlParameter("@uid", uid));
updateStageCommand.Parameters.Add(new SqlParameter("@relatedStage", relatedStage));
updateStageCommand.ExecuteNonQuery();
}
}
relatedReader.Dispose();
if (!hasHigherStage)
{
sqlCommand = new SqlCommand("DELETE FROM Po_sign WHERE rid = @rid");
sqlCommand.Connection = sqlConnection;
sqlCommand.Parameters.Add(new SqlParameter("@rid", rid));
sqlCommand.ExecuteNonQuery();
}
sqlConnection.Close();
}
第二種寫法是 再從前端多抓些資料過來操作,但還是一樣問題:
public void DeletePoSignById(string rid,string uid,int stage)
{
SqlConnection sqlConnection = new SqlConnection(ConnStr);
sqlConnection.Open();
SqlCommand sqlCommand = new SqlCommand("SELECT * FROM Po_sign WHERE uid = @uid");
sqlCommand.Connection = sqlConnection;
sqlCommand.Parameters.Add(new SqlParameter("@uid", uid));
SqlDataReader relatedReader = sqlCommand.ExecuteReader();
bool hasHigherStage = false;
while (relatedReader.Read())
{
int relatedStage = Convert.ToInt32(relatedReader["stage"]);
if (relatedStage > stage)
{
hasHigherStage = true;
int updatedStage = relatedStage - 1;
SqlCommand updateStageCommand = new SqlCommand("UPDATE Po_sign SET stage = @updatedStage WHERE uid = @uid AND stage = @relatedStage");
updateStageCommand.Connection = sqlConnection;
updateStageCommand.Parameters.Add(new SqlParameter("@updatedStage", updatedStage));
updateStageCommand.Parameters.Add(new SqlParameter("@uid", uid));
updateStageCommand.Parameters.Add(new SqlParameter("@relatedStage", relatedStage));
updateStageCommand.ExecuteNonQuery();
}
}
relatedReader.Dispose();
if (!hasHigherStage)
{
sqlCommand = new SqlCommand("DELETE FROM Po_sign WHERE rid = @rid");
sqlCommand.Connection = sqlConnection;
sqlCommand.Parameters.Add(new SqlParameter("@rid", rid));
sqlCommand.ExecuteNonQuery();
}
sqlConnection.Close();
}
最後我是用下列程式解決問題;
但因為是直接換寫法,所以上面的問題我還是不懂哪裡有誤!
public void DeletePoSignById(string rid, string uid, int stage)
{
SqlConnection sqlConnection = new SqlConnection(ConnStr);
sqlConnection.Open();
SqlCommand sqlCommand = new SqlCommand(
" DECLARE @tmp TABLE (rid varchar(50)) " +
" INSERT INTO @tmp " +
" SELECT rid FROM Po_sign WHERE uid = @uid And stage > @stage " +
" WHILE EXISTS(SELECT rid FROM @tmp) " +
" BEGIN " +
" UPDATE Po_sign SET stage = stage - 1 WHERE uid = @uid AND rid = (SELECT TOP 1 rid FROM @tmp) " +
" DELETE FROM @tmp WHERE rid = (SELECT TOP 1 rid FROM @tmp) " +
" END " +
" DELETE FROM Po_sign WHERE rid = @rid AND uid = @uid AND stage = @stage ");
sqlCommand.Connection = sqlConnection;
sqlCommand.Parameters.Add(new SqlParameter("@rid", rid));
sqlCommand.Parameters.Add(new SqlParameter("@uid", uid));
sqlCommand.Parameters.Add(new SqlParameter("@stage", stage));
sqlCommand.ExecuteNonQuery();
sqlConnection.Close();
}
第二種寫法效能比較好,建議用第二種,SQL 可以這樣串
SqlCommand sqlCommand = new SqlCommand(
@" DECLARE @tmp TABLE (rid varchar(50))
INSERT INTO @tmp
SELECT rid FROM Po_sign WHERE uid = @uid And stage > @stage
WHILE EXISTS(SELECT rid FROM @tmp)
BEGIN
UPDATE Po_sign SET stage = stage - 1 WHERE uid = @uid AND rid = (SELECT TOP 1 rid FROM @tmp)
DELETE FROM @tmp WHERE rid = (SELECT TOP 1 rid FROM @tmp)
END
DELETE FROM Po_sign WHERE rid = @rid AND uid = @uid AND stage = @stage ");
第一種改法:
SqlConnection sqlConnection1 = new SqlConnection(ConnStr);
sqlConnection1.Open();
while (relatedReader.Read())
{
int relatedStage = Convert.ToInt32(relatedReader["stage"]);
if (relatedStage > stage)
{
hasHigherStage = true;
int updatedStage = relatedStage - 1;
SqlCommand updateStageCommand = new SqlCommand("UPDATE Po_sign SET stage = @updatedStage WHERE uid = @uid AND stage = @relatedStage");
updateStageCommand.Connection = sqlConnection1;
updateStageCommand.Parameters.Add(new SqlParameter("@updatedStage", updatedStage));
updateStageCommand.Parameters.Add(new SqlParameter("@uid", uid));
updateStageCommand.Parameters.Add(new SqlParameter("@relatedStage", relatedStage));
updateStageCommand.ExecuteNonQuery();
}
}
new 一個 connection 就可以。
我記得同一條連線只能執行一次查詢,所以SqlConnection要先關閉在打開來使用。
就是在第一個 reader.Close(); 後面多加sqlConnection.Close();
後面要用在打開。
然後使用ADO.NET建議用using陳述式:
using (SqlConnection sqlConnection = new SqlConnection(ConnStr))
{
sqlConnection.Open();
// 執行 SQL 查詢
}