Медленный цикл SafeDataReader
У меня есть клиентское приложение vb.net/SQL Server, в котором мне нужно добавить левое соединение к существующему запросу. Этот же код на уровне данных используется asp.net для извлечения данных для веб-версии приложения. Следующие два блока кода показывают весь исходный оператор и новый оператор со всеми объединениями. Я предлагаю вам перейти к нижним блокам кода, которые намного меньше, чтобы проиллюстрировать проблему. Исходный запрос:
select q.qasr_id as Id,
q.qasr_identification_number as IdentificationNumber,
q.status as Status,
b.business_unit_name as BusinessUnitName,
b.business_unit_id as BusinessUnitId,
u.login as Originator,
q.date_created as DateCreated,
q.po_number as PoNumber,
q.po_part_number as PoPartNumber,
q.po_part_rev as PoPartRev,
q.lot_quantity as LotQuantity,
q.control_number as ControlNumber,
q.item_number as PoItem,
q.buyer as Buyer,
q.buyercode as BuyerCode,
q.spe as Spe,
p.program as ProgramName,
l.product_line as productline,
s.ent_supplier_nbr as SupplierEsd,
s.sup_name1 as SupplierName,
c.commodity_code as CommodityCode,
a.agency_name as AgencyName,
q.verifyid as AgencyDocId,
q.activity as Activity,
case q.isdeleted when 1 then 'Deleted' else '' end as Deleted
from qasr q
join auth_user u on q.auth_user_id = u.auth_user_id
join functional_area f on q.functional_area_id = f.functional_area_id
join business_unit b on f.business_unit_id = b.business_unit_id
left join suppliers s on q.supplier_id = s.supplier_id
left join programs p on q.program_id = p.program_id
left join product_lines l on p.product_line_id = l.product_line_id
left join commodities c on q.commodity_id = c.commodity_id
left join agency a on q.agency_id = a.agency_id
where b.business_unit_id = @BusinessUnitId
and q.isdeleted = @IsDeleted
Новый запрос:
select q.qasr_id as Id,
q.qasr_identification_number as IdentificationNumber,
q.status as Status,
b.business_unit_name as BusinessUnitName,
b.business_unit_id as BusinessUnitId,
u.login as Originator,
q.date_created as DateCreated,
q.po_number as PoNumber,
q.po_part_number as PoPartNumber,
q.po_part_rev as PoPartRev,
q.lot_quantity as LotQuantity,
q.control_number as ControlNumber,
q.item_number as PoItem,
q.buyer as Buyer,
q.buyercode as BuyerCode,
q.spe as Spe,
p.program as ProgramName,
l.product_line as productline,
s.ent_supplier_nbr as SupplierEsd,
s.sup_name1 as SupplierName,
c.commodity_code as CommodityCode,
a.agency_name as AgencyName,
q.verifyid as AgencyDocId,
q.activity as Activity,
case q.isdeleted when 1 then 'Deleted' else '' end as Deleted,
case when m.d_count > 0 then 'Yes' else 'No' end as Dispositioned
from qasr q
join auth_user u on q.auth_user_id = u.auth_user_id
join functional_area f on q.functional_area_id = f.functional_area_id
join business_unit b on f.business_unit_id = b.business_unit_id
left join suppliers s on q.supplier_id = s.supplier_id
left join programs p on q.program_id = p.program_id
left join product_lines l on p.product_line_id = l.product_line_id
left join commodities c on q.commodity_id = c.commodity_id
left join agency a on q.agency_id = a.agency_id
left join (select qasr_id as qasr_id, count(1) as d_count
from qasr_metric
where d = 1
group by qasr_id) m on m.qasr_id = q.qasr_id
where b.business_unit_id = @BusinessUnitId
and q.isdeleted = @IsDeleted
Новый запрос добавляет левое соединение ко второй таблице (qasr_metric), чтобы определить, содержит ли вторая таблица какие-либо связанные строки со значением 1 в столбце "D". Таблица qasr_metric индексируется (quasr_id, d), поэтому производительность снижается минимально. Удаление всех остальных столбцов и объединений сводится к разнице между:
select q.qasr_id as Id from qasr q
а также
select q.qasr_id as Id,
case when m.d_count > 0 then 'Yes' else 'No' end as Dispositioned
from qasr q
left join (select qasr_id as qasr_id, count(1) as d_count
from qasr_metric
where d = 1
group by qasr_id) m on m.qasr_id = q.qasr_id
Производительность нового запроса на стороне приложения asp.net составляет не более 3 секунд от исходного запроса, и то же самое верно, если я выполняю запросы отдельно в SQL Server Mgmt Studio. Однако клиентская часть приложения обрабатывает результаты исходного запроса в считанные секунды, но для нового запроса требуется более часа, что абсурдно. Глядя на сам код vb, уровень данных вызывает ExecuteReader с параметризованным запросом, который почти сразу завершается как для исходного, так и для нового запроса. Проблема возникает на бизнес-уровне, который вызывается с помощью dr в качестве читателя, заполненного ExecuteReader. Данные, возвращаемые в каждой строке, используются для заполнения нового объекта (InspectionDocumentSummary), а список этих объектов используется для заполнения DataGridView, отображаемого для пользователя. Объединение нескольких подводных лодок, которые создают новые объекты для краткости, дает:
Private Sub Fetch(ByVal dr As SafeDataReader)
Using dr
While dr.Read()
Add(InspectionDocumentSummary.GetInspectionDocumentSummary(dr))
_id = dr.GetGuid("Id")
_identificationNumber = dr.GetString("IdentificationNumber")
_status = dr.GetString("Status")
_originator = dr.GetString("Originator")
_dateCreated = dr.GetSmartDate("DateCreated")
_poPartNumber = dr.GetString("PoPartNumber")
_poPartRev = dr.GetString("PoPartRev")
_lotQuantity = Convert.ToString(dr.GetInt32("LotQuantity"))
_buyer = dr.GetString("Buyer")
_spe = dr.GetString("Spe")
_agencyDocId = dr.GetString("AgencyDocId")
_agencyName = dr.GetString("AgencyName")
_activity = dr.GetString("Activity")
_controlNumber = dr.GetString("ControlNumber")
_dispositioned = dr.GetString("Dispositioned")
_programName = dr.GetString("ProgramName")
_supplierEsd = dr.GetString("SupplierEsd")
_supplierName = dr.GetString("SupplierName")
_commodityCode = dr.GetString("CommodityCode")
End While
End Using
End Sub
Для исходного запроса это выполняется почти сразу, но новый запрос занимает более часа. Насколько я понимаю, ExecuteReader извлекает данные, а цикл dr.Read просто считывает возвращаемые данные в заполненный DataGridView (в моем случае никакой другой обработки данных не происходит). Я заменил "_dispositioned = dr.GetString("Dispositioned")" в вышеприведенном коде на "_dispositioned = "Yes"" без изменений, поэтому само чтение данных не является проблемой. Какие-либо предложения?
1 ответ
Многие проблемы, подобные этой, связаны с типами, конвертируемыми в запросе в строку, например, при сравнении или присоединении varchar с одной стороны к int с другой.
Если запрос выполняется без проблем с производительностью непосредственно из вашей базы данных, попробуйте выполнить свой запрос и позвольте.NET выполнить преобразования типов данных после того, как средство чтения данных прочитает строку следующим образом:
Private Sub Fetch(ByVal dr As SafeDataReader)
Using dr
While dr.Read()
Add(InspectionDocumentSummary.GetInspectionDocumentSummary(dr))
_id = GUID.Parse(dr("Id"))
_identificationNumber = dr("IdentificationNumber")
_status = dr("Status")
_originator = dr("Originator")
_dateCreated = dr.GetSmartDate("DateCreated")
_poPartNumber = dr("PoPartNumber")
_poPartRev = dr("PoPartRev")
_lotQuantity = Convert.ToString(dr.GetInt32("LotQuantity"))
_buyer = dr("Buyer")
_spe = dr("Spe")
_agencyDocId = dr("AgencyDocId")
_agencyName = dr("AgencyName")
_activity = dr("Activity")
_controlNumber = dr("ControlNumber")
_dispositioned = dr("Dispositioned")
_programName = dr("ProgramName")
_supplierEsd = dr("SupplierEsd")
_supplierName = dr("SupplierName")
_commodityCode = dr("CommodityCode")
End While
End Using
End Sub
Если это не поможет, попробуйте запустить его без вызова Add
чтобы убедиться, что что-то в этом методе не вызывает проблемы.