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.