Поиск по имени DataReader — что на самом деле происходит под капотом, что делает их намного медленнее, чем поиск по порядковому номеру в циклах?

Имеет смысл, что поиск по имени может быть немного медленнее с несколькими правилами сопоставления, чем с целочисленным индексом. Однако мне трудно поверить, что это объясняет 3% (или 5-7% в моем проекте) относительную стоимость для каждой строки, когда мы говорим о среднем количестве 10-15 [столбцов] записей в наборе данных. . Стоит уточнить в предыдущем утверждении, что я имею в виду количество столбцов, с которыми происходит это «разыменование», а не количество записей в наборе данных. Эти затраты указаны в контексте того, что эти поиски выполняются один раз для каждой строки. Так что их может быть много.

idr.GetOrdinal(name) // Name based lookup
idr[name]  // Name-based lookup
idr.GetValue(index) // Ordinal-based lookup

Есть ли на самом деле дополнительная связь с базой данных, необходимая для поиска полей на основе имени? Что делает их такими медленными?

Я также заметил для получения имен столбцов следующий код:

List<string> columnList = new List<string>();
for (int i = 0; i < idr.FieldCount; i++)
{
    columnList.Add(idr.GetName(i));
}

return columnList;

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

Этот вопрос возникает в духе: Порядковый поиск по сравнению с поиском по имени


person Joshua Enfield    schedule 29.04.2013    source источник
comment
Мне было бы любопытно, есть ли разница между одним поиском по имени и 100 поисками по имени. Другими словами, загружает ли первый поиск все виды данных схемы в память.   -  person Mike Christensen    schedule 29.04.2013


Ответы (2)


Вот код GetOrdinal для SqlDataReader, .NET 4.0, полученный с помощью ILSpy.

public override int GetOrdinal(string name)
{
    SqlStatistics statistics = null;
    int ordinal;
    try
    {
        statistics = SqlStatistics.StartTimer(this.Statistics);
        if (this._fieldNameLookup == null)
        {
            if (this.MetaData == null)
            {
                throw SQL.InvalidRead();
            }
            this._fieldNameLookup = new FieldNameLookup(this, this._defaultLCID);
        }
        ordinal = this._fieldNameLookup.GetOrdinal(name);
    }
    finally
    {
        SqlStatistics.StopTimer(statistics);
    }
    return ordinal;
}

Как видите, дополнительного доступа к базе данных нет. FieldNameLookup использует Hashtable для хранения имен полей и кэшируется после загрузки при первом вызове GetOrdinal. Таким образом, вызовы GetOrdinal добавят очень небольшие накладные расходы для поиска, выполняемого через FieldNameLookup, а вызовы SqlStatistics также могут добавить немного накладных расходов (хотя, вероятно, не много).

Хорошим тестом было бы использование Hashtable для хранения индексов и поиска индексов для каждого столбца в этом Hashtable для каждой строки. Если производительность такая же, как при вызове GetOrdinal для каждой строки, то можно с уверенностью предположить, что накладные расходы связаны с вызовами FieldNameLookup.

Надеюсь, это поможет.

person rsbarro    schedule 29.04.2013

Вы можете просмотрите исходный код объектов .NET Framework, чтобы увидеть, что происходит внутри. Из этого примера:

override public  Int32 GetOrdinal (string name) { 

            ValidateOpen("GetOrdinal");
            ValidateReader(); 
            DataColumn dc = currentDataTable.Columns[name];

            if (dc != null) {
                return dc.Ordinal;// WebData 113248 
            }
            else{ 
                throw ExceptionBuilder.ColumnNotInTheTable(name, currentDataTable.TableName); 
            }
        } 
person Andrew Lewis    schedule 29.04.2013