SQL 데이터 리더-널 열 값 처리


297

SQLdatareader를 사용하여 데이터베이스에서 POCO를 작성하고 있습니다. 데이터베이스에서 null 값을 발견 한 경우를 제외하고 코드가 작동합니다. 예를 들어, 데이터베이스의 FirstName 열에 null 값이 포함되어 있으면 예외가 발생합니다.

employee.FirstName = sqlreader.GetString(indexFirstName);

이 상황에서 null 값을 처리하는 가장 좋은 방법은 무엇입니까?

답변:


470

다음을 확인해야합니다 IsDBNull.

if(!SqlReader.IsDBNull(indexFirstName))
{
  employee.FirstName = sqlreader.GetString(indexFirstName);
}

이것이이 상황을 감지하고 처리 할 수있는 유일한 방법입니다.

나는 그 것들을 확장 메소드에 싸서 열이 실제로 있다면 기본값을 반환하는 경향이 있습니다 null.

public static string SafeGetString(this SqlDataReader reader, int colIndex)
{
   if(!reader.IsDBNull(colIndex))
       return reader.GetString(colIndex);
   return string.Empty;
}

이제 다음과 같이 호출 할 수 있습니다.

employee.FirstName = SqlReader.SafeGetString(indexFirstName);

예외 또는 null값에 대해 다시 걱정할 필요가 없습니다 .


64
누군가 인덱스가 아닌 열 이름 이 필요한 경우 다음을 수행 할 수 있습니다 . int colIndex = reader.GetOrdinal(fieldname);@ marc_s의 SafeGetString함수를 쉽게 오버로드하십시오 .
ilans

2019 년에 VB로 왔습니다 믿을 수 없어요 .......... 감사합니다, 큰 도움
JimmyB

또한 다음과 같이 수행 할 수 있습니다. int ordinal = reader.GetOrdinal ( "col_name"); uint? val = reader.IsDBNull (ordinal)? (uint?) null : reader.GetUInt32 (ordinal);
ed22

여러분 안녕하세요! 복사하여 양식에 붙여 넣고 오류와 함께 반환했습니다. "확장 메소드는 제네릭이 아닌 정적 클래스에서 정의해야합니다."
Jansen Malaggay

SafeGetString 내부에서 reader.GetOrindal을 사용하고 루프 내에서 SafeGetString을 사용하려는 경우 성능 저하없이 어떻게이 작업을 수행 할 수 있습니까?
AspUser7724

223

기본값 as과 함께 ??연산자를 연산자 와 결합하여 사용해야 합니다. 값 유형은 널 입력 가능으로 읽고 기본값이 제공되어야합니다.

employee.FirstName = sqlreader[indexFirstName] as string;
employee.Age = sqlreader[indexAge] as int? ?? default(int);

as운영자는 DBNull이에 대한 검사를 포함하여 캐스팅을 처리합니다.


6
누군가 Age 열을 int에서 SQL bigint (c # long)로 변경하면 0을 반환하여 코드가 자동으로 실패합니다. ZXX의 대답은보다 안정적인 IMO입니다.
Martin Ørding-Thomsen

기본값 (int)을 0 대신 -1로 재정의 할 수 있는지 궁금합니다.
Chris

5
@Chris-default (int)를 -1로 바꾸면됩니다.
stevehipwell 8:17에

@ Stevo3000 맞습니다! 나는 그것을 시도하고 내가 게시 한 직후에 말한 것처럼 효과가 있었지만이 페이지로 돌아 오는 것을 잊었습니다 :)
Chris

5
여기서 "as"를 사용하면 색인 오류를 숨길 수 있습니다. 실수로를 사용 sqlreader[indexAge] as string ?? ""하면 항상을 (를)받을 수 ""있습니다. 실제로 원하는지 여부를 고려 (int?)sqlreader[indexAge] ?? defaultValue하여 SQL이 변경되면 잘못된 값 대신 예외가 발생합니다. @ Stevo3000 : default (int)는 0이 아니라 -1입니다. @Chris : 원하는 것을 사용하고 있는지 확인하십시오.
me22

30

문자열의 경우 간단히 객체 버전 (배열 연산자를 사용하여 액세스)을 캐스트하고 널 (null) 문자열에 널 (null)을 입력하면됩니다.

employee.FirstName = (string)sqlreader[indexFirstName];

또는

employee.FirstName = sqlreader[indexFirstName] as string;

정수의 경우 nullable int로 캐스팅하면 GetValueOrDefault ()를 사용할 수 있습니다

employee.Age = (sqlreader[indexAge] as int?).GetValueOrDefault();

또는 null 병합 연산자 (?? ).

employee.Age = (sqlreader[indexAge] as int?) ?? 0;

2
첫 번째 예에서와 같이 명시 적 캐스트는 작동하지 않습니다. 같은 오류가 발생합니다
musefan

@ musefan : 당신의 필드는 실제로 문자열입니까? 그렇지 않으면 다른 오류가 발생합니다. 이것은 작동하며 이것들은 예제 1과 2 사이의 실제 차이점이 아닙니다 (구문 제외).
사라진 코딩

1
@GoneCoding : 예, nullable 문자열이며 두 번째 문자열이 작동 할 때 첫 번째 문자열이 문제를 일으키는 경우입니다. null 값을 처리하는 방법으로 인해 문제가 발생한다고 생각합니다. 마찬가지로, 이들은 nullDBNull 객체 가 아닙니다 . 두 문의 차이점은 첫 번째 문자열이 문자열이 아닌 경우 실패하지만 두 번째 문은 문자열이 아닌 경우 null을 반환합니다.
musefan

23

IsDbNull(int)일반적으로와 같은 방법을 사용 GetSqlDateTime하고 비교하는 것보다 훨씬 느립니다 DBNull.Value. 에 대한 이러한 확장 방법을 사용해보십시오 SqlDataReader.

public static T Def<T>(this SqlDataReader r, int ord)
{
    var t = r.GetSqlValue(ord);
    if (t == DBNull.Value) return default(T);
    return ((INullable)t).IsNull ? default(T) : (T)t;
}

public static T? Val<T>(this SqlDataReader r, int ord) where T:struct
{
    var t = r.GetSqlValue(ord);
    if (t == DBNull.Value) return null;
    return ((INullable)t).IsNull ? (T?)null : (T)t;
}

public static T Ref<T>(this SqlDataReader r, int ord) where T : class
{
    var t = r.GetSqlValue(ord);
    if (t == DBNull.Value) return null;
    return ((INullable)t).IsNull ? null : (T)t;
}

다음과 같이 사용하십시오.

var dd = r.Val<DateTime>(ords[4]);
var ii = r.Def<int>(ords[0]);
int nn = r.Def<int>(ords[0]);

5
System.Data.SqlTypes 형식의 명시 적 연산자가이 코드를 사용하려는 모든 곳에서 오류를 발생시키는 것으로 나타났습니다 ...
Tetsujin no Oni

이것이 때때로 실패하는 이유에 대한 설명은 stackoverflow.com/a/21024873/1508467 을 참조하십시오 (Val <int>를 사용하여 SQL int 열을 읽으십시오).
Rhys Jones

12

이를 수행하는 한 가지 방법은 db null을 확인하는 것입니다.

employee.FirstName = (sqlreader.IsDBNull(indexFirstName) 
    ? ""
    : sqlreader.GetString(indexFirstName));

12

reader.IsDbNull(ColumnIndex) 많은 답변이 말한대로 작동합니다.

그리고 열 이름으로 작업하는 경우 유형을 비교하는 것이 더 편할 수 있습니다.

if(reader["TeacherImage"].GetType() == typeof(DBNull)) { //logic }

또한 이전 버전의 System.Data 및 .NET FW
RaSor

11

NULL 이 없다고 생각 합니다.열 이름을 사용하여 데이터 리더 내에서 행이 반환되면 열 값 .

그렇게 datareader["columnName"].ToString();하면 항상 빈 문자열이 될 수있는 값을 제공합니다 (String.Empty 비교 해야하는 경우).

나는 다음을 사용하고 너무 걱정하지 않을 것입니다 :

employee.FirstName = sqlreader["columnNameForFirstName"].ToString();

4
NULL을 확인하기 위해 reader [FieldName] == DBNull.Value를 수행 할 수 있습니다.
Ralph Willgoss

11

이 솔루션은 공급 업체에 덜 의존적이며 SQL, OleDB 및 MySQL Reader와 함께 작동합니다.

public static string GetStringSafe(this IDataReader reader, int colIndex)
{
    return GetStringSafe(reader, colIndex, string.Empty);
}

public static string GetStringSafe(this IDataReader reader, int colIndex, string defaultValue)
{
    if (!reader.IsDBNull(colIndex))
        return reader.GetString(colIndex);
    else
        return defaultValue;
}

public static string GetStringSafe(this IDataReader reader, string indexName)
{
    return GetStringSafe(reader, reader.GetOrdinal(indexName));
}

public static string GetStringSafe(this IDataReader reader, string indexName, string defaultValue)
{
    return GetStringSafe(reader, reader.GetOrdinal(indexName), defaultValue);
}

1
이 코드를 바로 확장 클래스에 직접 복사하고 사용자 정의하십시오.
qxotk

8

내가하려는 경향은 SELECT 문의 null 값을 적절한 것으로 바꾸는 것입니다.

SELECT ISNULL(firstname, '') FROM people

여기서는 모든 null을 빈 문자열로 바꿉니다. 이 경우 코드에 오류가 발생하지 않습니다.


가능하면 널 사용을 피하십시오. 그렇지 않으면, 나는 도우미 방법에 대한 Sonny Boy의 답변을 좋아합니다.
환불 불가 환불 불가

3
왜 정적 인 별도의 도우미 메서드입니까? SqlDataReader의 확장 메서드가 더 강력하고 직관적으로 보이지 않습니까?
marc_s

7

널 (Null)을 점검하고 널 (NULL) 인 경우 기본값을 포함하는 일반 함수를 작성할 수 있습니다. Datareader를 읽을 때 이것을 호출

public T CheckNull<T>(object obj)
        {
            return (obj == DBNull.Value ? default(T) : (T)obj);
        }

Datareader 사용을 읽을 때

                        while (dr.Read())
                        {
                            tblBPN_InTrRecon Bpn = new tblBPN_InTrRecon();
                            Bpn.BPN_Date = CheckNull<DateTime?>(dr["BPN_Date"]);
                            Bpn.Cust_Backorder_Qty = CheckNull<int?>(dr["Cust_Backorder_Qty"]);
                            Bpn.Cust_Min = CheckNull<int?>(dr["Cust_Min"]);
                         }

6

sqlreader.IsDBNull(indexFirstName)읽으려고하기 전에 확인하십시오 .


4

헬퍼 메소드 작성 방법

문자열

private static string MyStringConverter(object o)
    {
        if (o == DBNull.Value || o == null)
            return "";

        return o.ToString();
    }

용법

MyStringConverter(read["indexStringValue"])

Int

 private static int MyIntonverter(object o)
    {
        if (o == DBNull.Value || o == null)
            return 0;

        return Convert.ToInt32(o);
    }

용법

MyIntonverter(read["indexIntValue"])

날짜

private static DateTime? MyDateConverter(object o)
    {
        return (o == DBNull.Value || o == null) ? (DateTime?)null : Convert.ToDateTime(o);
    }

용법

MyDateConverter(read["indexDateValue"])

참고 : DateTime의 경우 varialbe를 다음과 같이 선언하십시오.

DateTime? variable;

4

marc_s의 답변 외에도보다 일반적인 확장 방법을 사용하여 SqlDataReader에서 값을 가져올 수 있습니다.

public static T SafeGet<T>(this SqlDataReader reader, int col)
    {
        return reader.IsDBNull(col) ? default(T) : reader.GetFieldValue<T>(col);
    }

T가 구조체이면 null을 null이 아닌 기본값으로 변환하므로 실제로는 안전하지 않기 때문에이 메소드를 "SafeGet"이라고 부르지 않습니다. 아마도 "GetValueOrDefault"일 것입니다.
Rhys Jones

@RhysJones이 경우 왜 T를 구조체로 사용 하시겠습니까? 당신이 그렇게하더라도 구조체의 null이 아닌 기본값이 예상되는 동작이라고 주장합니다.
getpsyched

@RhysJones 그러나이 방법이 안전하지 않을 수 있다는 데 동의합니다. SqlDataReader의 InvalidCastException과 같은 예외를 처리해야합니다.
getpsyched

4

getpsyched 's answer에 영향을 주어 열 값을 이름으로 확인하는 일반적인 방법을 만들었습니다.

public static T SafeGet<T>(this System.Data.SqlClient.SqlDataReader reader, string nameOfColumn)
{
  var indexOfColumn = reader.GetOrdinal(nameOfColumn);
  return reader.IsDBNull(indexOfColumn) ? default(T) : reader.GetFieldValue<T>(indexOfColumn);
}

용법:

var myVariable = SafeGet<string>(reader, "NameOfColumn")

나는 당신이 누군지 모르지만 축복합니다. 이 기능은 3 시간 이상의 작업을 절약했습니다.
Ramneek Singh

3

나는 당신이 사용하고 싶다고 생각합니다.

SqlReader.IsDBNull(indexFirstName)

2

데이터 리더에서 모든 값을 가져 오기 위해 일련의 정적 메소드를 사용합니다. 따라서이 경우에는DBUtils.GetString(sqlreader(indexFirstName)) 입니다 정적 / 공유 메서드를 만들면 동일한 확인을 반복해서 반복 할 필요가 없다는 것입니다

정적 메서드에는 null을 확인하는 코드가 포함되어 있습니다 (이 페이지의 다른 답변 참조).


2

조건부 연산자를 사용할 수 있습니다.

employee.FirstName = sqlreader["indexFirstName"] != DBNull.Value ? sqlreader[indexFirstName].ToString() : "";

아래 답변 중 하나와 동일하지만 8 년이 지났습니다!
beercohol

2

유용한 정보 (및 잘못된 정보)가 널리 퍼져있는 많은 답변이 있습니다. 모두 통합하고 싶습니다.

질문에 대한 짧은 대답은 DBNull을 확인하는 것입니다. 거의 모든 사람들 이이 비트에 동의합니다 :)

헬퍼 메소드를 사용하여 SQL 데이터 유형 당 널 입력 가능 값을 읽는 대신 일반 메소드를 사용하면 훨씬 적은 코드로이를 해결할 수 있습니다. 그러나 nullable 값 형식과 참조 형식 모두에 대해 단일 제네릭 메서드를 사용할 수 없습니다. 가능한 일반적인 매개 변수로 Nullable 형식으로 길게 설명 합니까? 그리고 모든 NULLABLE가 C #을 제네릭 형식 제약 조건 .

따라서 @ZXX 및 @getpsyched의 답변에 이어 nullable 값을 얻는 두 가지 방법과 null이 아닌 값에 대한 세 번째 방법을 추가했습니다 (메서드 명명을 기반으로 설정을 완료 함).

public static T? GetNullableValueType<T>(this SqlDataReader sqlDataReader, string columnName) where T : struct
{
    int columnOrdinal = sqlDataReader.GetOrdinal(columnName);
    return sqlDataReader.IsDBNull(columnOrdinal) ? (T?)null : sqlDataReader.GetFieldValue<T>(columnOrdinal);
}

public static T GetNullableReferenceType<T>(this SqlDataReader sqlDataReader, string columnName) where T : class
{
    int columnOrdinal = sqlDataReader.GetOrdinal(columnName);
    return sqlDataReader.IsDBNull(columnOrdinal) ? null : sqlDataReader.GetFieldValue<T>(columnOrdinal);
}

public static T GetNonNullValue<T>(this SqlDataReader sqlDataReader, string columnName)
{
    int columnOrdinal = sqlDataReader.GetOrdinal(columnName);
    return sqlDataReader.GetFieldValue<T>(columnOrdinal);
}

나는 일반적으로 열 이름을 사용하고 열 인덱스를 사용하는 경우 이름을 변경하십시오. 이러한 메서드 이름을 기반으로 데이터가 nullable이 될지 여부를 알 수 있으며 오래 전에 작성된 코드를 볼 때 매우 유용합니다.

팁;

  • 데이터베이스에 널 입력 가능 열이 없으면이 문제를 피할 수 있습니다. 데이터베이스를 제어 할 수있는 경우 기본적으로 열은 널이 아니어야하며 필요한 경우에만 널 입력 가능해야합니다.
  • 캐스트가 잘못되면 자동으로 null을 반환하므로 C # 'as'연산자로 데이터베이스 값을 캐스트하지 마십시오.
  • 기본값 표현식을 사용하면 int, datetime, bit 등과 같은 값 유형에 대해 데이터베이스 널을 널이 아닌 값으로 변경합니다.

마지막으로, 모든 SQL Server 데이터 형식에서 위의 메서드를 테스트하는 동안 SqlDataReader에서 char []를 직접 가져올 수는 없습니다. char []를 원하면 문자열을 가져와 ToCharArray ()를 사용해야합니다.


1

아래 나열된 코드를 사용하여 데이터 테이블로 읽히는 Excel 시트의 null 셀을 처리하고 있습니다.

if (!reader.IsDBNull(2))
{
   row["Oracle"] = (string)reader[2];
}

1
private static void Render(IList<ListData> list, IDataReader reader)
        {
            while (reader.Read())
            {

                listData.DownUrl = (reader.GetSchemaTable().Columns["DownUrl"] != null) ? Convert.ToString(reader["DownUrl"]) : null;
                //没有这一列时,让其等于null
                list.Add(listData);
            }
            reader.Close();
        }

1

및 / 또는 할당과 함께 삼항 연산자를 사용하십시오.

employee.FirstName = rdr.IsDBNull(indexFirstName))? 
                     String.Empty: rdr.GetString(indexFirstName);

각 속성 유형에 따라 기본 값 (널인 경우)을 바꿉니다.


1

이 메소드는 0부터 시작하는 열 서수 여야하는 indexFirstName에 종속됩니다.

if(!sqlReader.IsDBNull(indexFirstName))
{
  employee.FirstName = sqlreader.GetString(indexFirstName);
}

열 인덱스를 모르지만 이름을 확인하지 않으려는 경우이 확장 방법을 대신 사용할 수 있습니다.

public static class DataRecordExtensions
{
    public static bool HasColumn(this IDataRecord dr, string columnName)
    {
        for (int i=0; i < dr.FieldCount; i++)
        {
            if (dr.GetName(i).Equals(columnName, StringComparison.InvariantCultureIgnoreCase))
                return true;
        }
        return false;
    }
}

그리고 다음과 같은 방법을 사용하십시오.

if(sqlReader.HasColumn("FirstName"))
{
  employee.FirstName = sqlreader["FirstName"];
}

1

오래된 질문이지만 어쩌면 누군가 대답이 필요할 수도 있습니다.

실제로 나는이 문제를 해결했다.

int :

public static object GatDataInt(string Query, string Column)
    {
        SqlConnection DBConn = new SqlConnection(ConnectionString);
        if (DBConn.State == ConnectionState.Closed)
            DBConn.Open();
        SqlCommand CMD = new SqlCommand(Query, DBConn);
        SqlDataReader RDR = CMD.ExecuteReader();
        if (RDR.Read())
        {
            var Result = RDR[Column];
            RDR.Close();
            DBConn.Close();
            return Result;
        }
        return 0;
    }

""는 빈 문자열이므로 문자열과 동일하게 0 대신 ""를 반환합니다.

그래서 당신은 그것을 사용할 수 있습니다

int TotalPoints = GatDataInt(QueryToGetTotalPoints, TotalPointColumn) as int?;

string Email = GatDatastring(QueryToGetEmail, EmailColumn) as string;

매우 유연하므로 모든 쿼리를 삽입하여 열을 읽을 수 있으며 오류와 함께 반환되지 않습니다.


0

@marc_s 답변을 기반으로 필요한 경우 다른 사람들이 사용할 수있는 도우미 클래스는 다음과 같습니다.

public static class SQLDataReaderExtensions
    {
        public static int SafeGetInt(this SqlDataReader dataReader, string fieldName)
        {
            int fieldIndex = dataReader.GetOrdinal(fieldName);
            return dataReader.IsDBNull(fieldIndex) ? 0 : dataReader.GetInt32(fieldIndex);
        }

        public static int? SafeGetNullableInt(this SqlDataReader dataReader, string fieldName)
        {
            int fieldIndex = dataReader.GetOrdinal(fieldName);
            return dataReader.GetValue(fieldIndex) as int?;
        }

        public static string SafeGetString(this SqlDataReader dataReader, string fieldName)
        {
            int fieldIndex = dataReader.GetOrdinal(fieldName);
            return dataReader.IsDBNull(fieldIndex) ? string.Empty : dataReader.GetString(fieldIndex);
        }

        public static DateTime? SafeGetNullableDateTime(this SqlDataReader dataReader, string fieldName)
        {
            int fieldIndex = dataReader.GetOrdinal(fieldName);
            return dataReader.GetValue(fieldIndex) as DateTime?;
        }

        public static bool SafeGetBoolean(this SqlDataReader dataReader, string fieldName)
        {
            return SafeGetBoolean(dataReader, fieldName, false);
        }

        public static bool SafeGetBoolean(this SqlDataReader dataReader, string fieldName, bool defaultValue)
        {
            int fieldIndex = dataReader.GetOrdinal(fieldName);
            return dataReader.IsDBNull(fieldIndex) ? defaultValue : dataReader.GetBoolean(fieldIndex);
        }
    }

0

핸들 DbNull을 현명하게 변환합니다.

employee.FirstName = Convert.ToString(sqlreader.GetValue(indexFirstName));

DBNull은 널값이 아닌 빈 문자열로 변환됩니다.
Rhys Jones

-2

당신은 이것도 확인할 수 있습니다

if(null !=x && x.HasRows)
{ ....}

-1이 시점되지 않습니다 : 우리가하지 그 null 또는 공의, 널 열 값의 사건을 처리하는SqlDataReader
푸른 빛이 도는
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.