2011-09-06 27 views
8

Ho una query LINQ-to-SQL molto pesante, che esegue un certo numero di join su tabelle diverse per restituire un tipo anonimo. Il problema è che se la quantità di righe restituite è abbastanza grande (> 200), la query diventa terribilmente lenta e termina il timeout. So che posso aumentare l'impostazione del timeout del contesto di dati, ma questa è l'ultima risorsa.Ottimizzazione query LINQ-SQL

Mi chiedo solo se la mia query funzionerebbe meglio se la suddivido, e faccio i miei confronti come query da LINQ a oggetti, quindi potrò anche usare PLINQ per massimizzare la potenza di elaborazione. Ma io sono un concetto estraneo per me, e non riesco a capire come lo avrei diviso. Qualcuno può offrire qualche consiglio? Non sto chiedendo che il codice venga scritto per me, solo alcune indicazioni generali su come potrei migliorarlo sarebbe fantastico.

Nota Ho assicurato che il database abbia tutte le chiavi corrette a cui sto partecipando e ho assicurato che queste chiavi siano aggiornate.

la query viene di seguito:

var cons = (from c in dc.Consignments 
      join p in dc.PODs on c.IntConNo equals p.Consignment into pg 
      join d in dc.Depots on c.DeliveryDepot equals d.Letter 
      join sl in dc.Accounts on c.Customer equals sl.LegacyID 
      join ss in dc.Accounts on sl.InvoiceAccount equals ss.LegacyID 
      join su in dc.Accounts on c.Subcontractor equals su.Name into sug 
      join sub in dc.Accountsubbies on ss.ID equals sub.AccountID into subg 
      where (sug.FirstOrDefault() == null 
       || sug.FirstOrDefault().Customer == false) 
      select new 
      { 
       ID = c.ID, 
       IntConNo = c.IntConNo, 
       LegacyID = c.LegacyID, 
       PODs = pg.DefaultIfEmpty(), 
       TripNumber = c.TripNumber, 
       DropSequence = c.DropSequence, 
       TripDate = c.TripDate, 
       Depot = d.Name, 
       CustomerName = c.Customer, 
       CustomerReference = c.CustomerReference, 
       DeliveryName = c.DeliveryName, 
       DeliveryTown = c.DeliveryTown, 
       DeliveryPostcode = c.DeliveryPostcode, 
       VehicleText = c.VehicleReg + c.Subcontractor, 
       SubbieID = sug.DefaultIfEmpty().FirstOrDefault().ID.ToString(), 
       SubbieList = subg.DefaultIfEmpty(), 
       ScanType = ss.PODScanning == null ? 0 : ss.PODScanning 
      }); 

Ecco il codice SQL generato come richiesto:

{SELECT [t0].[ID], [t0].[IntConNo], [t0].[LegacyID], [t6].[test], [t6].[ID] AS [ID2], [t6].[Consignment], [t6].[Status], [t6].[NTConsignment], [t6].[CustomerRef], [t6].[Timestamp], [t6].[SignedBy], [t6].[Clause], [t6].[BarcodeNumber], [t6].[MainRef], [t6].[Notes], [t6].[ConsignmentRef], [t6].[PODedBy], (
    SELECT COUNT(*) 
    FROM (
     SELECT NULL AS [EMPTY] 
     ) AS [t10] 
    LEFT OUTER JOIN (
     SELECT NULL AS [EMPTY] 
     FROM [dbo].[PODs] AS [t11] 
     WHERE [t0].[IntConNo] = [t11].[Consignment] 
     ) AS [t12] ON 1=1 
    ) AS [value], [t0].[TripNumber], [t0].[DropSequence], [t0].[TripDate], [t1].[Name] AS [Depot], [t0].[Customer] AS [CustomerName], [t0].[CustomerReference], [t0].[DeliveryName], [t0].[DeliveryTown], [t0].[DeliveryPostcode], [t0].[VehicleReg] + [t0].[Subcontractor] AS [VehicleText], CONVERT(NVarChar,(
    SELECT [t16].[ID] 
    FROM (
     SELECT TOP (1) [t15].[ID] 
     FROM (
      SELECT NULL AS [EMPTY] 
      ) AS [t13] 
     LEFT OUTER JOIN (
      SELECT [t14].[ID] 
      FROM [dbo].[Account] AS [t14] 
      WHERE [t0].[Subcontractor] = [t14].[Name] 
      ) AS [t15] ON 1=1 
     ORDER BY [t15].[ID] 
     ) AS [t16] 
    )) AS [SubbieID], 
    (CASE 
     WHEN [t3].[PODScanning] IS NULL THEN @p0 
     ELSE [t3].[PODScanning] 
    END) AS [ScanType], [t3].[ID] AS [ID3] 
FROM [dbo].[Consignments] AS [t0] 
INNER JOIN [dbo].[Depots] AS [t1] ON [t0].[DeliveryDepot] = [t1].[Letter] 
INNER JOIN [dbo].[Account] AS [t2] ON [t0].[Customer] = [t2].[LegacyID] 
INNER JOIN [dbo].[Account] AS [t3] ON [t2].[InvoiceAccount] = [t3].[LegacyID] 
LEFT OUTER JOIN ((
     SELECT NULL AS [EMPTY] 
     ) AS [t4] 
    LEFT OUTER JOIN (
     SELECT 1 AS [test], [t5].[ID], [t5].[Consignment], [t5].[Status], [t5].[NTConsignment], [t5].[CustomerRef], [t5].[Timestamp], [t5].[SignedBy], [t5].[Clause], [t5].[BarcodeNumber], [t5].[MainRef], [t5].[Notes], [t5].[ConsignmentRef], [t5].[PODedBy] 
     FROM [dbo].[PODs] AS [t5] 
     ) AS [t6] ON 1=1) ON [t0].[IntConNo] = [t6].[Consignment] 
WHERE ((NOT (EXISTS(
    SELECT TOP (1) NULL AS [EMPTY] 
    FROM [dbo].[Account] AS [t7] 
    WHERE [t0].[Subcontractor] = [t7].[Name] 
    ORDER BY [t7].[ID] 
    ))) OR (NOT (((
    SELECT [t9].[Customer] 
    FROM (
     SELECT TOP (1) [t8].[Customer] 
     FROM [dbo].[Account] AS [t8] 
     WHERE [t0].[Subcontractor] = [t8].[Name] 
     ORDER BY [t8].[ID] 
     ) AS [t9] 
    )) = 1))) AND ([t2].[Customer] = 1) AND ([t3].[Customer] = 1) 
ORDER BY [t0].[ID], [t1].[ID], [t2].[ID], [t3].[ID], [t6].[ID] 
} 
+4

Qual è l'SQL che viene eseguito in background? Che aspetto ha il piano della query? – Heinzi

+0

Sarei curioso di selezionare tutti i record in una stored procedure, chiamandolo con IMultilpleResults, quindi fare i join in memoria non sarebbe più veloce. – Joe

+0

Sei sicuro che Linq fa tutto in un'unica istruzione (e non un'istruzione extra per riga selezionata) - non sicuro al 100% se ciò è possibile nel nuovo scenario selezionato, quindi fai come @Heinzi ha detto: devi vedere l'SQL che viene generato utilizzando profiler o registra il tuo datacontext – Pleun

risposta

3

provare a spostare il subappaltatore unire più in alto e spingere la clausola in cui con esso. In questo modo non stai inutilmente creando join che alla fine fallirebbero. Vorrei anche modificare la selezione per l'id del subappaltatore, in modo da non ottenere l'Id di un valore potenzialmente nullo.

var cons = (from c in dc.Consignments 
      join su in dc.Accounts on c.Subcontractor equals su.Name into sug 
      where (sug.FirstOrDefault() == null || sug.FirstOrDefault().Customer == false) 
      join p in dc.PODs on c.IntConNo equals p.Consignment into pg 
      join d in dc.Depots on c.DeliveryDepot equals d.Letter 
      join sl in dc.Accounts on c.Customer equals sl.LegacyID 
      join ss in dc.Accounts on sl.InvoiceAccount equals ss.LegacyID     
      join sub in dc.Accountsubbies on ss.ID equals sub.AccountID into subg 
      let firstSubContractor = sug.DefaultIfEmpty().FirstOrDefault() 
      select new 
      { 
          ID = c.ID, 
          IntConNo = c.IntConNo, 
          LegacyID = c.LegacyID, 
          PODs = pg.DefaultIfEmpty(), 
          TripNumber = c.TripNumber, 
          DropSequence = c.DropSequence, 
          TripDate = c.TripDate, 
          Depot = d.Name, 
          CustomerName = c.Customer, 
          CustomerReference = c.CustomerReference, 
          DeliveryName = c.DeliveryName, 
          DeliveryTown = c.DeliveryTown, 
          DeliveryPostcode = c.DeliveryPostcode, 
          VehicleText = c.VehicleReg + c.Subcontractor, 
          SubbieID = firstSubContractor == null ? "" : firstSubContractor.ID.ToString(), 
          SubbieList = subg.DefaultIfEmpty(), 
          ScanType = ss.PODScanning == null ? 0 : ss.PODScanning 
       }); 
Problemi correlati