안녕하세요. 지니입니다.
음.. 먼저 동상이몽님의 코드에서 발견된 문제점을 말씀드리겠습니다.
코드를 보면서 설명드리죠.
====> 에러가 발생한 소스 코드 <====================
if(DBConnect.DBConnection.State == ConnectionState.Open)
{
DBConnect.DBConnection.Close();
}
DBConnect.DBConnection.Open();
/*************************************************************************
여기까지의 코드는 이렇게 작성하실 필요가 없습니다. 다음처럼 단순화 할 수 있죠.
if (DBConnect.DBConnection.State != ConnectionState.Open)
DBConnect.DBConnection.Open();
즉, 열려있다면 굳이 닫아줄 필요가 없겠죠?
*************************************************************************/
string sql ="select id,product from userInfo,prduct where user_id = pro_id ";
Cmd = new OleDbCommand(sql,DBConnect.DBConnection);
OleDbDataReader dr = Cmd.ExecuteReader(CommandBehavior.CloseConnection);
/*************************************************************************
이 문장에 작성된 SQL 문장은 WHERE 절의 조건으로 보아 단 하나의 레코드만을
가져오게 될 것 같습니다.
대체로 회원 테이블에서 사용자의ID는 기본 키이죠? 따라서 회원 ID를 = 연산자로
검색하게 되면 당연히 레코드가 하나만 리턴 될 겁니다.
아래의 while 문이 한 번만 실행되는 이유가 바로 그거죠.
*************************************************************************/
while(dr.Read())
{
string id = dr["id"].ToString().Trim();
string product = dr["product"].ToString().Trim();
/*************************************************************************
리턴되는 레코드가 하나 뿐이므로 여기서는 굳이 while 문을 쓸 필요가 없습니다.
그런데 아래 쪽에 사용된 SQL 문장에서는 앞의 쿼리의 결과로 검색된
ID와 product 필드 값을 이용하여 쿼리를 작성하게 됩니다.
따라서 이 두 값을 별도의 변수에 저장해 두고 DataReader를 닫아버리는 쪽이
훨씬 낫겠죠?
코드는 다음과 같이 작성할 수 있을 것입니다.
string id = string.Empty; //사용자 ID
string product = string.Empty; //제품 이름
if (dr.Read()) {
id = dr["id"].ToString();
product = dr["product"].ToString();
dr.Close();
//CommandBehavior.CloseConnection 때문에 여기서 커넥션이 함께 닫히게 됩니다.
//따라서 CommandBehavior.CloseConnection 옵션은 생략하는게 좋겠죠?
왜냐하면 다음에 또 커넥션을 사용할 것이기 때문이죠.
}
*************************************************************************/
if(DBConnect.DBConnection.State == ConnectionState.Open)
{
DBConnect.DBConnection.Close();
}
DBConnect.DBConnection.Open();
/*************************************************************************
만약 ExecuteReader 메서드에서 CommandBehavior.CloseConnection 옵션을
제거하셨다면 위의 코드는 불필요한 코드가 될 것입니다.
*************************************************************************/
string sql3 = "Insert into user_product(id,product) Values (@id,@product)";
Cmd = new OleDbCommand(sql3,DBConnect.DBConnection);
Cmd.Parameters.Add("@id", OleDbType.Char, 10);
Cmd.Parameters.Add("@product", OleDbType.Char, 15);
Cmd.Parameters["@id"].Value = id;
Cmd.Parameters["@prduct"].Value =product;
Cmd.ExecuteNonQuery();
}
/*************************************************************************
여기까지의 별다른 문제가 없네요.
*************************************************************************/
dr.Close();
/*************************************************************************
이 친구는 이제 필요 없겠죠? 앞서서 이미 닫아주었으니 말이죠.
*************************************************************************/
DBConnect.DBConnection.Close();
}
이렇게 하면 코드가 조금더 간결해 질 것 같습니다.
다음으로 동상이몽님의 질문에 대한 답을 해보도록 하겠습니다.
=========================================================================
동상이몽님의 질문: select문으로 원하는 데이타를 선택한후에 나온 데이타 갯수만큼 다른 테이블에 저장을 하려고 합니다.
=========================================================================
두 가지 방법이 존재하겠네요. 하나는 지금처럼 DataReader 클래스로 읽어와 루프를 실행하는 방법이고 다른 하나는 그냥 SQL 레벨에서 처리하는 방법입니다.
방법 1:
편의상 SQL Data Provider 클래스들을 사용하여 예제를 보여드리겠습니다.
이 예제에서 주의할 점은 DataReader 클래스가 while 구문을 통해 레코드를 반복해서 읽는 동안에는 커넥션이 항상 열려있어야 한다는 것입니다.
따라서 while 구문 내에서 다른 명령을 실행하려면 별도의 커넥션이 또 필요합니다.
public void MoveRecord() {
using (SqlConnection cn = new SqlConnection(connectString)) {
//using 절을 이용하면 별도로 커넥션을 Close해 줄 필요가 없이
//using 절을 마치는 순간 자동으로 커넥션이 Dispose됩니다.
string query = "SELECT TOP 10 * FROM authors";
//pubs 데이터베이스의 authors 테이블에서
//10개의 레코드를 가져오는 쿼리를 실행합니다.
SqlCommand cmd = new SqlCommand(query, cn);
SqlDataReader reader = cmd.ExecuteReader();
if (reader.HasRows) {
//DataReaer에 레코드가 1개 이상 존재하면 새로운 커넥션을 생성합니다.
SqlConnection cn2 = new SqlConnection(connectString));
while (reader.Read()) {
string au_id = reader["au_id"].ToString();
//저자의 ID를 au_id 변수에 대입합니다.
string query2 = string.Format("INSERT table_temp VALUES ("{0}")",
au_id);
//임시 테이블에 저자의 ID를 저장하는 쿼리를 작성합니다.
SqlCommand cmd2 = new SqlCommand(cn2, query2);
cmd2.ExecuteNonQuery();
//쿼리를 실행합니다.
}
reader.Close();
//반복문이 끝나면 DataReader를 닫습니다.
}//end of if
}//end of using
}//end of method MoveRecord
이런 식으로 구현하면 됩니다.
===========================================================================
방법 2: 쿼리를 이용하여 한 방에!
===========================================================================
INSERT ~ SELECT 쿼리를 이용하면 이렇게 복잡한 과정을 거치지 않아도 됩니다.
다음 코드를 살펴보죠.
public void MoveRecord() {
using (SqlConnection cn = new SqlConnection(connectString)) {
string query = "INSERT table_temp (au_id) SELECT au_id FROM authors";
//authors 테이블에서 가져온 au_id 필드이 값을 table_temp 테이블의
//au_id 필드에 추가하는 쿼리입니다.
SqlCommand cmd = new SqlCommand(cn, query);
cmd.ExecuteNonQuery();
//쿼리를 실행합니다.
}//end of using
}//end of method MoveRecord
이게 끝입니다.
동상이몽님께서 편하신 방법대로 사용하시면 되겠죠. 참고로 전 두 번째 방법을 사용합니다.
도움이 되셨기를 바랍니다.
첫댓글 답변 고맙습니다. 알기 쉽게 설명을 잘 해주셔서 이해가 잘 되고 있습니다. 그런데 reader.HasRows ??? 이런 메소드도 있나요? reader. 찍어도 나타나지 않는군요 ㅠ.ㅠ reader.Read() 만 나오네요.
DataReader 클래스의 HasRows 속성은 .NET 1.1 버전에서만 지원됩니다. VS.NET의 경우 2003버전이죠. 만약 1.0 버전을 사용하신다면 그냥 DataReader 클래스의 Read 메서드를 호출해서 레코드가 존재하는지 여부를 검사하는 방법을 사용하시면 됩니다.