Проверьте на имя столбца в объекте SqlDataReader

Примечание: использование неопределенной константы XXX - предполагается, что «XXX»

, или в PHP 7.2 или новее:

Предупреждение: использование неопределенной константы XXX - предполагаемый «XXX» ( это вызовет ошибку в будущей версии PHP)

Это уведомление возникает, когда токен используется в коде и представляется константой, но константа с этим именем не определена.

Одной из наиболее распространенных причин этого уведомления является отказ от цитирования строки, используемой в качестве ассоциативного массива.

Например:

// Wrong
echo $array[key];

// Right
echo $array['key'];

Другие распространенные причины отсутствует значок $ (доллар) перед именем переменной:

// Wrong
echo varName;

// Right
echo $varName;

Или, может быть, у вас есть некорректная другая константа или ключевое слово:

// Wrong
$foo = fasle;

// Right
$foo = false;

Он также может быть признаком того, что необходимое расширение или библиотека PHP отсутствует при попытке доступа к константе, определенной этой библиотекой.

Вопросы, относящиеся:

204
задан Michael Kniskern 1 March 2012 в 11:24
поделиться

9 ответов

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;
    }
}

Использование Исключения для логики управления, как и в некоторых других ответах, считается плохой практикой и приводит к снижению производительности. Он также отправляет ложные срабатывания профилировщику # сгенерированных исключений, и Бог помогает любому, кто настроил свой отладчик на прерывание на сгенерированные исключения.

GetSchemaTable () также является другим предложением во многих ответах. Это не будет предпочтительным способом проверки существования поля, поскольку он реализован не во всех версиях (он абстрактный и вызывает исключение NotSupportedException в некоторых версиях dotnetcore). GetSchemaTable также является избыточной по производительности, поскольку это довольно тяжелая функция, если вы проверите источник .

Цикл по полям может привести к небольшому падению производительности, если вы часто его используете, и вы можете захотеть рассмотрите возможность кэширования результатов.

322
ответ дан 23 November 2019 в 04:54
поделиться

Можно также звонить GetSchemaTable () на DataReader, если Вы хотите список столбцов, и Вы не хотите должными быть получать исключение...

-1
ответ дан Dave Markle 23 November 2019 в 04:54
поделиться

Я думаю, что Ваш лучший выбор состоит в том, чтобы звонить GetOrdinal ("columnName") на Вашем DataReader впереди и поймать IndexOutOfRangeException в случае, если столбец не присутствует.

На самом деле, давайте сделаем дополнительный метод:

public static bool HasColumn(this IDataRecord r, string columnName)
{
    try
    {
        return r.GetOrdinal(columnName) >= 0;
    }
    catch (IndexOutOfRangeException)
    {
        return false;
    }
}

Редактирование

хорошо, это сообщение начинает собирать несколько вниз-голосов в последнее время, и я не могу удалить его, потому что это - принятый ответ, таким образом, я собираюсь обновить его и (я надеюсь), пытаются выровнять по ширине использование обработки исключений как поток управления.

другой способ достигнуть это, как отправленный Chad Grant , должно циклично выполниться через каждое поле в DataReader и сделать нечувствительное к регистру сравнение для имени поля, которое Вы ищете. Это будет работать действительно хорошо, и правдиво будет, вероятно, работать лучше, чем мой метод выше. Конечно, я никогда не использовал бы метод выше внутренней части цикл, где производительность была проблемой.

я могу думать об одной ситуации, в которой будет работать try/GetOrdinal/catch метод, где цикл не делает. Это - однако, абсолютно гипотетическая ситуация прямо сейчас, таким образом, это - очень неосновательное выравнивание. Независимо, терпите меня и посмотрите то, что Вы думаете.

Воображают базу данных, которая позволила Вам "искажать" столбцы в таблице. Предположите, что я мог определить таблицу со столбцом по имени "EmployeeName", но также и дать ему псевдоним "EmpName", и выполнение выбора для любого имени возвратит данные в том столбце. Со мной до сих пор?

Теперь предполагают, что существует поставщик ADO.NET для той базы данных, и они кодировали реализацию IDataReader для нее, которая принимает псевдонимы столбца во внимание.

Теперь, dr.GetName(i) (как используется в ответе Чада) может только возвратить единственную строку, таким образом, он должен возвратить [только 115] один из "псевдонимов" на столбце. Однако GetOrdinal("EmpName") мог использовать внутреннюю реализацию полей этого поставщика для проверки псевдонима каждого столбца на имя, которое Вы ищете.

В этот гипотетические "искаженные столбцы" ситуация, try/GetOrdinal/catch метод был бы единственным способом быть уверенным, что Вы проверяете на каждое изменение названия столбца в наборе результатов.

Неосновательный? Уверенный. Но стоящий мысли. Честно я быть очень бы "официальный" метод HasColumn на IDataRecord.

32
ответ дан Community 23 November 2019 в 04:54
поделиться
if(Enumerable.Range(0,reader.FieldCount).Select(reader.GetName).Contains("columName"))
{
     employee.EmployeeId= Utility.ConvertReaderToLong(reader["EmployeeId"]);
}

можно получить больше деталей отсюда: можно ли получить имена столбцов от SqlDataReader?

-1
ответ дан 23 November 2019 в 04:54
поделиться

TLDR:

Партии ответов с требованиями о производительности и плохой практике, таким образом, я разъясняю это здесь.

маршрут исключения быстрее для более высоких чисел возвращенных столбцов, маршрут цикла быстрее для более низкого количества столбцов, и точка перехода составляет приблизительно 11 столбцов. Прокрутите к нижней части для наблюдения графика и тестового кода.

Полный ответ:

код для части главной работы ответов, но существуют базовые дебаты здесь для "лучшего" ответа на основе принятия обработки исключений в логике, и это связало производительность.

Для убирания этого я не полагаю, что существует много руководства относительно ЛОВЛИ исключений. Microsoft действительно имеет некоторое руководство относительно Выдавания исключения. Там они действительно заявляют:

НЕ используют исключения для нормального потока управления, если это возможно.

первое примечание является мягкостью "если возможный". Что еще более важно, описание дает этот контекст:

framework designers should design APIs so users can write code that does not throw exceptions

то, Что это означает, - то, что, если Вы пишете API, который мог бы быть использован кем-то еще, дайте им способность переместиться по исключению без попытки/выгоды. Например, предоставьте TryParse свой бросающий исключение метод Синтаксического анализа. Нигде не делает это говорит, хотя это Вы не должны ловить исключение.

Далее, как другой пользователь указывает, выгоды всегда позволяли фильтровать типом и несколько недавно позволяют дальнейшую фильтрацию через когда пункт . Это походит на трату функций языка, если мы, как предполагается, не используем их.

можно сказать, что существует НЕКОТОРАЯ стоимость для вызванной исключительной ситуации, и что стоимость МОЖЕТ повлиять на производительность в тяжелом цикле. Однако можно также сказать, что стоимость исключения будет незначительной в "связанном приложении". Фактическая стоимость была исследована более чем десятилетие назад: https://stackoverflow.com/a/891230/852208, Другими словами, стоимость соединения и запрос базы данных, вероятно, затмят stackoverflow.com/a/891230/852208 [114] вызванной исключительной ситуации.

Все, что в стороне, я хотел определить, какой метод действительно быстрее. Как ожидалось нет никакого конкретного ответа.

Любой код, что циклы по столбцам становятся медленнее как число столбцов, существует. Можно также сказать, что любой код, который полагается на исключения, замедлится в зависимости от уровня, в котором запросу не удается быть найденным.

Взятие ответов и Chad Grant и Matt Hamilton, я выполнил оба метода максимум с 20 столбцами, и до 50%-го коэффициента ошибок (OP указал, что он использовал эти два теста между другим procs, таким образом, я принял только два).

Вот результаты, напечатанные с LinqPad: Results - Series 1 is Loop, 2 is Exception

зигзаги здесь являются частотами отказов (столбец, не найденный) в каждом количестве столбцов.

По более узким наборам результатов, цикличное выполнение является хорошим выбором. Однако метод GetOrdinal/Exception совсем не как чувствительный к числу столбцов и начинает превосходить право метода цикличного выполнения по характеристикам приблизительно 11 столбцов.

, Который сказал, что у меня действительно нет предпочтительной производительности мудрой как, 11 столбцов звучит разумным как среднее число столбцов, возвращенных по целому приложению. В любом случае мы говорим о частях миллисекунды здесь.

Однако от аспекта простоты кода и поддержки псевдонима, я, вероятно, пошел бы с маршрутом GetOrdinal.

Вот тест в форме linqpad. Не стесняйтесь повторно отправлять со своим собственным методом:

void Main()
{
    var loopResults = new List<Results>();
    var exceptionResults = new List<Results>();
    var totalRuns = 10000;
    for (var colCount = 1; colCount < 20; colCount++)
    {
        using (var conn = new SqlConnection(@"Data Source=(localdb)\MSSQLLocalDb;Initial Catalog=master;Integrated Security=True;"))
        {
            conn.Open();

            //create a dummy table where we can control the total columns
            var columns = String.Join(",",
                (new int[colCount]).Select((item, i) => $"'{i}' as col{i}")
            );
            var sql = $"select {columns} into #dummyTable";
            var cmd = new SqlCommand(sql,conn);
            cmd.ExecuteNonQuery();

            var cmd2 = new SqlCommand("select * from #dummyTable", conn);

            var reader = cmd2.ExecuteReader();
            reader.Read();

            Func<Func<IDataRecord, String, Boolean>, List<Results>> test = funcToTest =>
            {
                var results = new List<Results>();
                Random r = new Random();
                for (var faultRate = 0.1; faultRate <= 0.5; faultRate += 0.1)
                {
                    Stopwatch stopwatch = new Stopwatch();
                    stopwatch.Start();
                    var faultCount=0;
                    for (var testRun = 0; testRun < totalRuns; testRun++)
                    {
                        if (r.NextDouble() <= faultRate)
                        {
                            faultCount++;
                            if(funcToTest(reader, "colDNE"))
                                throw new ApplicationException("Should have thrown false");
                        }
                        else
                        {
                            for (var col = 0; col < colCount; col++)
                            {
                                if(!funcToTest(reader, $"col{col}"))
                                    throw new ApplicationException("Should have thrown true");
                            }
                        }
                    }
                    stopwatch.Stop();
                    results.Add(new UserQuery.Results{
                        ColumnCount = colCount, 
                        TargetNotFoundRate = faultRate,
                        NotFoundRate = faultCount * 1.0f / totalRuns, 
                        TotalTime=stopwatch.Elapsed
                    });
                }
                return results;
            };
            loopResults.AddRange(test(HasColumnLoop));

            exceptionResults.AddRange(test(HasColumnException));

        }

    }
    "Loop".Dump();
    loopResults.Dump();

    "Exception".Dump();
    exceptionResults.Dump();

    var combinedResults = loopResults.Join(exceptionResults,l => l.ResultKey, e=> e.ResultKey, (l, e) => new{ResultKey = l.ResultKey, LoopResult=l.TotalTime, ExceptionResult=e.TotalTime});
    combinedResults.Dump();
    combinedResults
        .Chart(r => r.ResultKey, r => r.LoopResult.Milliseconds * 1.0 / totalRuns, LINQPad.Util.SeriesType.Line)
        .AddYSeries(r => r.ExceptionResult.Milliseconds * 1.0 / totalRuns, LINQPad.Util.SeriesType.Line)
        .Dump();
}
public static bool HasColumnLoop(IDataRecord dr, string columnName)
{
    for (int i = 0; i < dr.FieldCount; i++)
    {
        if (dr.GetName(i).Equals(columnName, StringComparison.InvariantCultureIgnoreCase))
            return true;
    }
    return false;
}

public static bool HasColumnException(IDataRecord r, string columnName)
{
    try
    {
        return r.GetOrdinal(columnName) >= 0;
    }
    catch (IndexOutOfRangeException)
    {
        return false;
    }
}

public class Results
{
    public double NotFoundRate { get; set; }
    public double TargetNotFoundRate { get; set; }
    public int ColumnCount { get; set; }
    public double ResultKey {get => ColumnCount + TargetNotFoundRate;}
    public TimeSpan TotalTime { get; set; }


}
1
ответ дан 23 November 2019 в 04:54
поделиться

Ключ к целой проблеме здесь :

if (-1 == index) {
    throw ADP.IndexOutOfRange(fieldName);
}

, Если три строки, на которые ссылаются (в настоящее время строки 72, 73, и 74) вынуты, то можно легко проверить на -1, чтобы определить, не существует ли столбец.

единственный путь вокруг этого при обеспечении собственной производительности состоит в том, чтобы использовать Reflection базирующаяся реализация, как следующее:

Использования:

using System;
using System.Data;
using System.Reflection;
using System.Data.SqlClient;
using System.Linq;
using System.Web.Compilation; // I'm not sure what the .NET Core equivalent to BuildManager.cs

основанный на отражении дополнительный метод:

/// Gets the column ordinal, given the name of the column.
/// </summary>
/// <param name="reader"></param>
/// <param name="name">The name of the column.</param>
/// <returns> The zero-based column ordinal. -1 if the column does not exist.</returns>
public static int GetOrdinalSoft(this SqlDataReader reader, string name)
{
    try
    {
        // Note that "Statistics" will not be accounted for in this implemenation
        // If you have SqlConnection.StatisticsEnabled set to true (the default is false), you probably don't want to use this method
        // All of the following logic is inspired by the actual implementation of the framework:
        // https://referencesource.microsoft.com/#System.Data/fx/src/data/System/Data/SqlClient/SqlDataReader.cs,d66096b6f57cac74
        if (name == null)
            throw new ArgumentNullException("fieldName");

        Type sqlDataReaderType = typeof(SqlDataReader);
        object fieldNameLookup = sqlDataReaderType.GetField("_fieldNameLookup", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(reader);
        Type fieldNameLookupType;
        if (fieldNameLookup == null)
        {
            MethodInfo checkMetaDataIsReady = sqlDataReaderType.GetRuntimeMethods().First(x => x.Name == "CheckMetaDataIsReady" && x.GetParameters().Length == 0);
            checkMetaDataIsReady.Invoke(reader, null);
            fieldNameLookupType = BuildManager.GetType("System.Data.ProviderBase.FieldNameLookup", true, false);
            ConstructorInfo ctor = fieldNameLookupType.GetConstructor(new[] { typeof(SqlDataReader), typeof(int) });
            fieldNameLookup = ctor.Invoke(new object[] { reader, sqlDataReaderType.GetField("_defaultLCID", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(reader) });
        }
        else
            fieldNameLookupType = fieldNameLookup.GetType();

        MethodInfo indexOf = fieldNameLookupType.GetMethod("IndexOf", BindingFlags.Public | BindingFlags.Instance, null, new Type[] { typeof(string) }, null);

        return (int)indexOf.Invoke(fieldNameLookup, new object[] { name });
    }
    catch
    {
        // .NET Implemenation might have changed, revert back to the classic solution.
        if (reader.FieldCount > 11) // Performance observation by b_levitt
        {
            try
            {
                return reader.GetOrdinal(name);
            }
            catch
            {
                return -1;
            }
        }
        else
        {
            var exists = Enumerable.Range(0, reader.FieldCount).Any(i => string.Equals(reader.GetName(i), name, StringComparison.OrdinalIgnoreCase));
            if (exists)
                return reader.GetOrdinal(name);
            else
                return -1;
        }
    }
}
0
ответ дан 23 November 2019 в 04:54
поделиться

Намного лучше использовать эту логическую функцию:

r.GetSchemaTable().Columns.Contains(field)

Один вызов - без исключений. Это может вызывать исключения внутри, но я так не думаю.

ПРИМЕЧАНИЕ. В комментариях ниже мы выяснили это ... правильный код на самом деле такой:

public static bool HasColumn(DbDataReader Reader, string ColumnName) { 
    foreach (DataRow row in Reader.GetSchemaTable().Rows) { 
        if (row["ColumnName"].ToString() == ColumnName) 
            return true; 
    } //Still here? Column not found. 
    return false; 
}
64
ответ дан 23 November 2019 в 04:54
поделиться

Я написал для пользователей Visual Basic:

Protected Function HasColumnAndValue(ByRef reader As IDataReader, ByVal columnName As String) As Boolean
    For i As Integer = 0 To reader.FieldCount - 1
        If reader.GetName(i).Equals(columnName) Then
            Return Not IsDBNull(reader(columnName))
        End If
    Next

    Return False
End Function

Я думаю, что это более мощный и способ использования:

If HasColumnAndValue(reader, "ID_USER") Then
    Me.UserID = reader.GetDecimal(reader.GetOrdinal("ID_USER")).ToString()
End If
7
ответ дан 23 November 2019 в 04:54
поделиться

Я также не заставил GetSchemaTable работать, пока не нашел таким образом .

Обычно я делаю так:

Dim myView As DataView = dr.GetSchemaTable().DefaultView
myView.RowFilter = "ColumnName = 'ColumnToBeChecked'"

If myView.Count > 0 AndAlso dr.GetOrdinal("ColumnToBeChecked") <> -1 Then
  obj.ColumnToBeChecked = ColumnFromDb(dr, "ColumnToBeChecked")
End If
0
ответ дан 23 November 2019 в 04:54
поделиться
Другие вопросы по тегам:

Похожие вопросы: