Сложный запрос Linq - Попытка добавить предикат .Where () или .Any (), чтобы уменьшить сложность запроса (в VB) - PullRequest
0 голосов
/ 14 сентября 2010

У меня проблемы с довольно сложным запросом Linq, и мне нужен совет. Это включает в себя синтаксис VB, поэтому мне нужны конкретные ответы для этой платформы, так как у меня много проблем с переводом синтаксиса C # в VB.

Мне нужно объединить две основные таблицы и отфильтровать результаты по элементам в веб-форме ASP.NET. Эти фильтры создаются на лету, поэтому мне нужно использовать множество расширений для фильтрации запросов. Я хочу выполнить запрос с максимально оптимизированным SQL.

Сначала я делаю простое соединение между TW_Sites и TW_Investigators. Тогда есть две вложенные таблицы, которые участвуют. TW_InvestigatorToArea и TW_InvestigatorToDisease. Хотя большинство предложений where работают нормально, я обнаружил проблему с производительностью, которая сейчас не будет проблемой, но будет проблемой по мере увеличения таблицы.

Массивы DiseaseCategories и DiseaseAreas будут результатом результата CheckBoxList.

    Protected Sub LoadResults()
    ' Get Dictionary of Filters
    Dim FilterDictionary As OrderedDictionary = Session.Item("InvestigatorFilterDictionary")
    ' Initialize LinqToSql
    Dim LinqDbHandler As TrialWatchDC = New TrialWatchDC(WebConfigurationManager.ConnectionStrings("DataSourceName").ConnectionString)
    ' Create List of Categories to Filter By
    Dim DiseaseCategories() As Integer = {1, 2, 3, 4, 5, 6, 11, 22, 361, 77, 82, 99, 400}
    Dim CategorySubQuery = From ic In LinqDbHandler.TW_InvestigatorsToDiseases Where DiseaseCategories.Contains(ic.DiseaseCategoryID) Select ic.InvestigatorID Distinct
    ' Dim CategorySubArray = CategorySubQuery.ToArray()
    ' Create List of Areas to Filter By
    Dim AreaCategories() As Integer = {17, 1, 3, 5}
    Dim AreaSubQuery = From ic In LinqDbHandler.TW_InvestigatorsToAreas Where AreaCategories.Contains(ic.AreaID) Select ic.InvestigatorID Distinct
    Dim AreaSubArray = AreaSubQuery.ToArray()
    Dim dc As DbCommand
    Dim ThisQuery = From Site In LinqDbHandler.TW_Sites _
                    Join Investigator In LinqDbHandler.TW_Investigators On Site.TrialSiteID Equals Investigator.TrialSiteID _
                    Join SiteType In LinqDbHandler.TW_SiteTypes On Site.SiteTypeID Equals SiteType.SiteTypeID _
                    Order By Site.ResearchCenterName, Investigator.InvestigatorName
        Select New With {.TrialSiteID = Site.TrialSiteID, _
                         .InvestigatorID = Investigator.InvestigatorID, _
                         .ResearchCenterName = Site.ResearchCenterName, _
                         .SiteTypeID = SiteType.SiteTypeID, _
                         .TypeLabel = SiteType.TypeLabel, _
                         .CenterState = Site.CenterState, _
                         .CenterCountry = Site.CenterCountry, _
                         .ContactName = Site.ContactName, _
                         .ContactEMail = Site.ContactEMail, _
                         .ContactPhone = Site.ContactPhone, _
                         .IsRcppSubscriber = Site.IsRcppSubscriber, _
                         .InvestigatorName = Investigator.InvestigatorName, _
                         .IsPublicationSubscriber = Investigator.IsPublicationSubscriber, _
                         .HasPhase01 = Investigator.HasPhase01, _
                         .HasPhase02 = Investigator.HasPhase02, _
                         .HasPhase03 = Investigator.HasPhase03, _
                         .HasPhase04 = Investigator.HasPhase04, _
                         .AreaList = String.Join(",", (From ia In LinqDbHandler.TW_InvestigatorsToAreas Join a In LinqDbHandler.Disease_Areas On ia.AreaID Equals a.Area_Number Where ia.InvestigatorID = Investigator.InvestigatorID Order By a.Area_Name Select a.Area_Name Distinct).ToArray()), _
                         .CategoryList = String.Join(",", (From id In LinqDbHandler.TW_InvestigatorsToDiseases Join d In LinqDbHandler.Disease_Categories On id.DiseaseCategoryID Equals d.Category_Number Where id.InvestigatorID = Investigator.InvestigatorID Order By d.Category_Name Select d.Category_Name Distinct).ToArray())}
    If Not String.IsNullOrEmpty(FilterDictionary.Item("CountryFilter")) Then
        ThisQuery = ThisQuery.Where(Function(s) s.CenterCountry = FilterDictionary.Item("CountryFilter").ToString())
    End If
    If Not String.IsNullOrEmpty(FilterDictionary.Item("SiteType")) Then
        ThisQuery = ThisQuery.Where(Function(s) s.SiteTypeID = Convert.ToInt32(FilterDictionary.Item("SiteType")))
    End If
    dc = LinqDbHandler.GetCommand(ThisQuery)
    If Not String.IsNullOrEmpty(FilterDictionary.Item("StateFilter")) Then
        ThisQuery = ThisQuery.Where(Function(s) s.CenterState = FilterDictionary.Item("StateFilter").ToString())
    End If
    dc = LinqDbHandler.GetCommand(ThisQuery)
    ThisQuery = ThisQuery.Where(Function(i) CategorySubArray.Contains(i.InvestigatorID))
    ThisQuery = ThisQuery.Where(Function(i) AreaSubArray.Contains(i.InvestigatorID))
    dc = LinqDbHandler.GetCommand(ThisQuery)
    Trace.Warn("Command", dc.CommandText)
    For Each dcp As SqlParameter In dc.Parameters
        Trace.Warn(dcp.ParameterName.ToString(), dcp.Value.ToString())
    Next
    Dim ThisLinqResult = ThisQuery
    InvestigatorResultGrid.DataSource = ThisLinqResult
    InvestigatorResultGrid.DataBind()

End Sub

Большая проблема, когда вы смотрите на код, в основном я сначала преобразовываю отфильтрованные подзапросы в массив, а затем передаю его в код SQL. В результате получается SQL-запрос с большим количеством параметров, как показано ниже.

SELECT [t0].[TrialSiteID], [t1].[InvestigatorID], [t0].[ResearchCenterName], [t2].[SiteTypeID], [t2].[TypeLabel], [t0].[CenterState], [t0].[CenterCountry], [t0].[ContactName],
    [t0].[ContactEMail], [t0].[ContactPhone], [t0].[IsRcppSubscriber], [t1].[InvestigatorName], [t1].[IsPublicationSubscriber], [t1].[HasPhase01], [t1].[HasPhase02], [t1].[HasPhase03],
    [t1].[HasPhase04]
    FROM [dbo].[TW_Sites] AS [t0]
    INNER JOIN [dbo].[TW_Investigators] AS [t1] ON [t0].[TrialSiteID] = [t1].[TrialSiteID]
    INNER JOIN [dbo].[TW_SiteTypes] AS [t2] ON [t0].[SiteTypeID] = ([t2].[SiteTypeID])
    WHERE ([t1].[InvestigatorID] IN (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9, @p10, @p11, @p12, @p13, @p14, @p15, @p16, @p17, @p18, @p19, @p20, @p21, @p22, @p23, @p24, @p25, @p26, @p27, 
    @p28, @p29, @p30, @p31, @p32, @p33, @p34, @p35, @p36, @p37, @p38, @p39, @p40, @p41, @p42, @p43, @p44, @p45, @p46, @p47, @p48, @p49, @p50, @p51, @p52, @p53, @p54, @p55, @p56, @p57, @p58, 
    @p59, @p60, @p61, @p62, @p63, @p64, @p65, @p66, @p67, @p68, @p69, @p70, @p71, @p72, @p73, @p74, @p75, @p76, @p77, @p78, @p79, @p80, @p81, @p82, @p83, @p84, @p85, @p86, @p87, @p88, @p89, 
    @p90, @p91, @p92, @p93, @p94, @p95, @p96, @p97, @p98, @p99, @p100, @p101, @p102, @p103, @p104, @p105, @p106, @p107, @p108, @p109, @p110, @p111, @p112, @p113, @p114, @p115)) AND 
    ([t1].[InvestigatorID] IN (@p116, @p117, @p118, @p119, @p120, @p121, @p122, @p123, @p124, @p125, @p126, @p127, @p128, @p129, @p130, @p131, @p132, @p133, @p134, @p135, @p136, @p137, @p138, 
    @p139, @p140, @p141, @p142, @p143, @p144, @p145, @p146, @p147, @p148, @p149, @p150, @p151, @p152, @p153, @p154, @p155, @p156, @p157, @p158, @p159, @p160, @p161, @p162, @p163, @p164, @p165, 
    @p166, @p167, @p168, @p169, @p170, @p171, @p172, @p173, @p174, @p175, @p176, @p177, @p178, @p179, @p180, @p181, @p182, @p183, @p184, @p185, @p186, @p187, @p188, @p189, @p190, @p191, @p192, 
    @p193, @p194, @p195, @p196, @p197, @p198, @p199, @p200, @p201, @p202, @p203, @p204, @p205))
    ORDER BY [t0].[ResearchCenterName], [t1].[InvestigatorName] 

Это много параметров и будет только хуже. По сути, вместо небольшого предложения IN с условиями у меня есть гораздо большее предложение IN с идентификаторами следователя.

Итак, я пытаюсь понять, как вместо того, чтобы преобразовывать запросы Area и Category в массив и затем добавлять их к третьему запросу, чтобы запросы включали вложенные таблицы и выполняли прямой поиск для соответствующих идентификаторов областей и категорий. Мне нужно иметь возможность использовать синтаксис предиката, так как области и категории представляют собой две вложенные таблицы, и иногда обе или ни одна не будут включены. Я знаю, что это связано с предикатами .Any (), .Join () или .Where (), я просто не знаю, как заставить его работать.

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

WHERE ([t1].[InvestigatorID] IN (SELECT InvestigatorID FROM TW_InvestigatorsToAreas
WHERE DiseaseCategoryID IN (@p101, @p102, @p103))) 

Буду признателен за любую помощь или руководство.

Ответы [ 2 ]

0 голосов
/ 19 сентября 2010

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

If DCHash.Count > 0 Then
    ThisQuery = ThisQuery.Where(Function(i) (From ic In LinqDbHandler.TW_InvestigatorsToDiseases Where DiseaseCategories.Contains(ic.DiseaseCategoryID) Select ic.InvestigatorID).Contains(i.InvestigatorID))
End If
If AreaHash.Count > 0 Then
    ThisQuery = ThisQuery.Where(Function(i) (From ia In LinqDbHandler.TW_InvestigatorsToAreas Where DiseaseAreas.Contains(ia.AreaID) Select ia.InvestigatorID).Contains(i.InvestigatorID))
End If
0 голосов
/ 14 сентября 2010

Это LINQ to SQL или EF?

Большинство ORM генерируют динамический SQL с каждым идентификатором для оператора IN в качестве параметра. Некоторые умные создадут временную таблицу и присоединятся к ней вместо этого или используют вложенный подзапрос (или, если вы действительно креативны, вы можете расширить ORM для этого).

Я знаю, что DataObjects .NET выполняет работу с временной таблицей, и LLBLGen может использовать вложенные подзапросы для объединений (так называемые предварительные выборки), и я уверен, что есть хотя бы пара других, которые тоже делают.

Стоит отметить: ваш пробег может отличаться. Один большой плюс с временной таблицей состоит в том, что вы достигли предела 2400 параметров в SQL Server (хотя я не уверен, что это проблема для вас ...). Однако, наверняка, может быть, 1 из 20 запросов будет выполнять гораздо более медленное объединение с временной таблицей (даже с индексированной) по сравнению с простой передачей каждого идентификатора в качестве параметра. Тем не менее, в целом, производительность будет намного выше, потому что план выполнения не нужно перекомпилировать для каждого запроса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...