Transact-SQL(T-SQL) 문이나 저장 프로시저를 실행하는 데 사용됩니다 SqlCommand . 명령어는 열린 SqlConnection가 필요합니다. 사용 후에는 명령 개체를 폐기하고, 연결을 재사용하기 전에 DataReader를 폐기하세요.
이 문서의 예제에서는 connection라는 이름의 열린 SqlConnection이 AdventureWorks 샘플 데이터베이스를 대상으로 한다고 가정합니다.
C# 예제는 using Microsoft.Data.SqlClient;를 요구합니다.
CommandType, CommandBehavior, 또는 SqlDbType를 사용하는 예시에도 using System.Data;가 필요합니다.
연결 설정, 인증, 암호화에 대해서는 Connection strings 및 암호화 및 인증서 유효성 검사을 참조하세요.
실행 방법을 선택하세요
기대 결과에 맞는 방법을 선택하세요:
| 메서드 | 사용 시기 | 결과 |
|---|---|---|
| ExecuteNonQuery | 명령어는 데이터나 스키마를 변경합니다. | 영향을 받는 행의 수입니다. 카운트를 사용할 수 없을 때, SET NOCOUNT ON가 카운트를 억제하는 경우를 포함하여 -1는 발생할 수 있습니다. |
| ExecuteScalar | 한 가지 값만 있으면 됩니다. | 첫 번째 행의 첫 번째 열 또는 결과에 행이 없으면 null입니다. 데이터베이스 NULL는 Value로 반환됩니다. |
| ExecuteReader | 행, 여러 결과 세트, 또는 스키마가 필요합니다. | SqlDataReader. |
| ExecuteXmlReader | 명령어는 ExecuteXmlReader에서 요구하는 형태의 XML을 반환합니다. |
XmlReader입니다. |
각 메서드는 작업 기반 비동기 대응 메서드를 가지고 있습니다. 호출자가 기다리는 것을 중지해야 할 때 CancellationToken를 전달하세요. 취소 및 타임아웃 동작에 대해서는 비동기 프로그래밍을 참조하세요.
텍스트 명령어 실행
CommandType.Text 기본값입니다. 문장을 CommandText에 넣고 값을 매개변수에 전달합니다:
const string sql = """
SELECT ProductID, Name, ListPrice
FROM Production.Product
WHERE ProductSubcategoryID = @subcategoryId
ORDER BY ListPrice DESC;
""";
using var command = new SqlCommand(sql, connection);
command.Parameters.Add("@subcategoryId", SqlDbType.Int).Value = 2;
using SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
Console.WriteLine(
$"{reader.GetInt32(0)} | {reader.GetString(1)} | {reader.GetDecimal(2):C}");
}
매개변수 유형, 크기, null 값 및 방향에 대해서는 매개변수 구성 항목을 참조하세요. 신뢰할 수 없는 값을 CommandText에 연결하지 마세요.
저장 프로시저 실행
CommandType.StoredProcedure를 설정하고 스키마로 한정된 프로시저 이름을 사용하세요:
using var command = new SqlCommand(
"dbo.uspGetEmployeeManagers",
connection);
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add(
"@BusinessEntityID",
SqlDbType.Int).Value = 6;
using SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
Console.WriteLine(
$"{reader.GetString(2)} {reader.GetString(3)}");
}
Microsoft.Data.SqlClient는 CommandType.StoredProcedure 및 CommandType.Text를 지원합니다.
CommandType.TableDirect 설정 시 ArgumentOutOfRangeException이 발생합니다.
각 메서드가 무엇을 반환하는지 확인해 보세요
한 가지 값이 필요할 때는 ExecuteScalar을 사용하세요:
using var command = new SqlCommand(
"SELECT COUNT(*) FROM Production.Product WHERE SellEndDate IS NULL;",
connection);
int currentProductCount = (int)command.ExecuteScalar()!;
Console.WriteLine($"Current products: {currentProductCount}");
ExecuteScalar는 결과에 행이 없으면 DBNull.Value, 첫 번째 값이 데이터베이스 null이면 NULL를 반환합니다.
데이터를 변경하는 문에 ExecuteNonQuery를 사용합니다. 이 예시는 AdventureWorks가 변경되지 않은 상태로 유지되도록 변경을 되돌립니다:
using SqlTransaction transaction = connection.BeginTransaction();
using var command = new SqlCommand("""
UPDATE Sales.SalesOrderHeader
SET Status = @status
WHERE SalesOrderID = @salesOrderId;
""", connection, transaction);
command.Parameters.Add("@status", SqlDbType.TinyInt).Value = 2;
command.Parameters.Add("@salesOrderId", SqlDbType.Int).Value = 43659;
int rowsAffected = command.ExecuteNonQuery();
Console.WriteLine($"Rows affected: {rowsAffected}");
transaction.Rollback();
SET NOCOUNT ON 영향을 받은 행 수를 억제하여 문장이 데이터를 변경해도 ExecuteNonQuery가 -1를 반환할 수 있습니다.
ExecuteXmlReader 결과를 위해 FOR XML 활용:
using var command = new SqlCommand("""
SELECT TOP (3) ProductID, Name
FROM Production.Product
ORDER BY ProductID
FOR XML PATH('Product'), ROOT('Products');
""", connection);
using System.Xml.XmlReader reader = command.ExecuteXmlReader();
reader.MoveToContent();
Console.WriteLine(reader.ReadOuterXml());
로컬 트랜잭션 사용
연결에 활성 로컬 SqlTransaction가 있을 때는 해당 트랜잭션을 해당 연결에서 실행되는 모든 명령어에 할당합니다. 이 예시는 AdventureWorks가 변경되지 않은 상태로 유지되도록 변경을 되돌립니다:
using SqlTransaction transaction = connection.BeginTransaction();
using var command = new SqlCommand("""
UPDATE Sales.SalesOrderHeader
SET Status = @status
WHERE SalesOrderID = @salesOrderId;
""", connection, transaction);
command.Parameters.Add("@status", SqlDbType.TinyInt).Value = 2;
command.Parameters.Add("@salesOrderId", SqlDbType.Int).Value = 43659;
command.ExecuteNonQuery();
transaction.Rollback();
애플리케이션이 전체 거래를 수락하는 경우에만 Commit 대신 Rollback를 호출하세요. 활성 트랜잭션을 참조하지 않는 명령어는 InvalidOperationException을 throw합니다. 트랜잭션이 다른 연결에 속해 있을 경우에도 실행이 실패합니다. 트랜잭션 경계와 롤백에 대해서는 로컬 트랜잭션을 참조하세요.
명령 타임아웃을 설정하세요
CommandTimeout 명령 실행과 누적 네트워크 읽기 대기를 몇 초 단위로 제한합니다. 명령어는 구성된 경우 연결의 Command Timeout 설정을 사용합니다. 그렇지 않으면 기본값은 30초입니다.
0 값에는 시간 제한이 없습니다. 음수 값은 ArgumentException를 발생시킵니다.
타임아웃이 만료되면 명령은 Number이 SqlException로 설정된 -2을(를) 발생시킵니다:
using var command = new SqlCommand(
"WAITFOR DELAY '00:00:05';",
connection)
{
CommandTimeout = 1
};
try
{
command.ExecuteNonQuery();
}
catch (SqlException exception) when (exception.Number == -2)
{
Console.WriteLine("The command timed out.");
}
Connect Timeout 연결 설정과 풀 대기 시간을 제한합니다.
CommandTimeout은(는) 명령 실행이 시작된 후에 실행됩니다. 자세한 내용은 연결 옵션 참조하세요.
유한한 타임아웃과 취소 토큰을 함께 사용하세요. 타임아웃은 데이터베이스 연산에 한도를 설정합니다. 토큰을 취소하면 타임아웃이 만료되기 전에 호출자에게 제어권을 돌려줄 수 있습니다. 취소와 타임아웃이 같은 예외를 만든다고 가정하지 마세요.
명령어 재사용 및 준비
매개변수를 한 번 추가한 후, 각 실행이 끝난 후 값을 변경합니다:
using var command = new SqlCommand("""
SELECT TOP (1) Name, ListPrice
FROM Production.Product
WHERE ProductSubcategoryID = @subcategoryId
ORDER BY ListPrice DESC;
""", connection);
SqlParameter subcategoryId = command.Parameters.Add(
"@subcategoryId",
SqlDbType.Int);
foreach (int id in new[] { 1, 2, 3 })
{
subcategoryId.Value = id;
using SqlDataReader reader = command.ExecuteReader();
if (reader.Read())
{
Console.WriteLine($"{id}: {reader.GetString(0)}");
}
}
측정값이 이를 정당화할 때만 Prepare를 호출하세요.
Prepare 매개변수 없이 저장 프로시저나 텍스트 명령을 준비하지 않습니다. 매개변수화된 텍스트의 경우, Prepare를 호출하기 전에 각 매개변수에 Scale, Precision, Size와 같은 완전한 메타데이터를 설정하세요.
명령어 문제 해결하기
서버가 여러 오류를 반환할 때 데이터베이스 작업을 SqlException로 감싸고 그 Errors 컬렉션을 점검하세요. 명령어 오류, 경고, 정보 메시지에 대해서는 오류 및 메시지 처리하기를 참조하세요.
오류를 이해하기 위해 더 많은 정보가 필요할 때는 진단 카운터, 이벤트 소스 추적 및 SqlClient 문제 해결 가이드를 활용하세요. 명령어를 반복하는 것이 안전할 때만 설정 가능한 재시도 로직 을 적용하세요.