Pipila ka aspeto sa pag-optimize sa LINQ nga mga pangutana sa C#.NET para sa MS SQL Server

Ang LINQ misulod sa .NET isip usa ka gamhanan nga bag-ong data manipulation language. Ang LINQ ngadto sa SQL isip kabahin niini nagtugot kanimo sa pagpakigsulti nga sayon ​​​​sa usa ka DBMS gamit, pananglitan, Entity Framework. Bisan pa, sa kanunay nga paggamit niini, ang mga nag-develop nakalimot sa pagtan-aw kung unsang klase sa SQL nga pangutana ang gipangutana nga tighatag, sa imong kaso nga Entity Framework, nga mamugna.

Atong tan-awon ang duha ka pangunang punto gamit ang usa ka pananglitan.
Aron mahimo kini, paghimo og Test database sa SQL Server, ug paghimo og duha ka lamesa niini gamit ang mosunod nga pangutana:

Paghimo og mga lamesa

USE [TEST]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[Ref](
	[ID] [int] NOT NULL,
	[ID2] [int] NOT NULL,
	[Name] [nvarchar](255) NOT NULL,
	[InsertUTCDate] [datetime] NOT NULL,
 CONSTRAINT [PK_Ref] PRIMARY KEY CLUSTERED 
(
	[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[Ref] ADD  CONSTRAINT [DF_Ref_InsertUTCDate]  DEFAULT (getutcdate()) FOR [InsertUTCDate]
GO

USE [TEST]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[Customer](
	[ID] [int] NOT NULL,
	[Name] [nvarchar](255) NOT NULL,
	[Ref_ID] [int] NOT NULL,
	[InsertUTCDate] [datetime] NOT NULL,
	[Ref_ID2] [int] NOT NULL,
 CONSTRAINT [PK_Customer] PRIMARY KEY CLUSTERED 
(
	[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[Customer] ADD  CONSTRAINT [DF_Customer_Ref_ID]  DEFAULT ((0)) FOR [Ref_ID]
GO

ALTER TABLE [dbo].[Customer] ADD  CONSTRAINT [DF_Customer_InsertUTCDate]  DEFAULT (getutcdate()) FOR [InsertUTCDate]
GO

Karon atong pun-on ang Ref table pinaagi sa pagpadagan sa mosunod nga script:

Pagpuno sa Ref table

USE [TEST]
GO

DECLARE @ind INT=1;

WHILE(@ind<1200000)
BEGIN
	INSERT INTO [dbo].[Ref]
           ([ID]
           ,[ID2]
           ,[Name])
    SELECT
           @ind
           ,@ind
           ,CAST(@ind AS NVARCHAR(255));

	SET @ind=@ind+1;
END 
GO

Susama natong pun-on ang lamesa sa Customer gamit ang mosunod nga script:

Pag-populate sa lamesa sa Customer

USE [TEST]
GO

DECLARE @ind INT=1;
DECLARE @ind_ref INT=1;

WHILE(@ind<=12000000)
BEGIN
	IF(@ind%3=0) SET @ind_ref=1;
	ELSE IF (@ind%5=0) SET @ind_ref=2;
	ELSE IF (@ind%7=0) SET @ind_ref=3;
	ELSE IF (@ind%11=0) SET @ind_ref=4;
	ELSE IF (@ind%13=0) SET @ind_ref=5;
	ELSE IF (@ind%17=0) SET @ind_ref=6;
	ELSE IF (@ind%19=0) SET @ind_ref=7;
	ELSE IF (@ind%23=0) SET @ind_ref=8;
	ELSE IF (@ind%29=0) SET @ind_ref=9;
	ELSE IF (@ind%31=0) SET @ind_ref=10;
	ELSE IF (@ind%37=0) SET @ind_ref=11;
	ELSE SET @ind_ref=@ind%1190000;
	
	INSERT INTO [dbo].[Customer]
	           ([ID]
	           ,[Name]
	           ,[Ref_ID]
	           ,[Ref_ID2])
	     SELECT
	           @ind,
	           CAST(@ind AS NVARCHAR(255)),
	           @ind_ref,
	           @ind_ref;


	SET @ind=@ind+1;
END
GO

Sa ingon, nakadawat kami og duha ka mga lamesa, ang usa niini adunay labaw pa sa 1 milyon nga mga laray sa datos, ug ang lain adunay labaw pa sa 10 milyon nga mga laray sa datos.

Karon sa Visual Studio kinahanglan nimo nga maghimo usa ka pagsulay nga Visual C# Console App (.NET Framework) nga proyekto:

Pipila ka aspeto sa pag-optimize sa LINQ nga mga pangutana sa C#.NET para sa MS SQL Server

Sunod, kinahanglan ka magdugang usa ka librarya alang sa Entity Framework aron makig-uban sa database.
Aron idugang kini, pag-right-click sa proyekto ug pilia ang Manage NuGet Packages gikan sa menu sa konteksto:

Pipila ka aspeto sa pag-optimize sa LINQ nga mga pangutana sa C#.NET para sa MS SQL Server

Dayon, sa bintana sa pagdumala sa package sa NuGet nga makita, isulod ang pulong nga "Entity Framework" sa search window ug pilia ang Entity Framework package ug i-install kini:

Pipila ka aspeto sa pag-optimize sa LINQ nga mga pangutana sa C#.NET para sa MS SQL Server

Sunod, sa App.config file, pagkahuman sa pagsira sa configSections nga elemento, kinahanglan nimong idugang ang mosunud nga bloke:

<connectionStrings>
    <add name="DBConnection" connectionString="data source=ИМЯ_Π­ΠšΠ—Π•ΠœΠŸΠ›Π―Π Π_MSSQL;Initial Catalog=TEST;Integrated Security=True;" providerName="System.Data.SqlClient" />
</connectionStrings>

Sa connectionString kinahanglan nimo nga mosulod sa koneksyon string.

Karon maghimo kita og 3 ka interface sa lain nga mga file:

  1. Pag-implementar sa interface sa IBaseEntityID
    namespace TestLINQ
    {
        public interface IBaseEntityID
        {
            int ID { get; set; }
        }
    }
    

  2. Pagpatuman sa interface sa IBaseEntityName
    namespace TestLINQ
    {
        public interface IBaseEntityName
        {
            string Name { get; set; }
        }
    }
    

  3. Pagpatuman sa interface sa IBaseNameInsertUTCDate
    namespace TestLINQ
    {
        public interface IBaseNameInsertUTCDate
        {
            DateTime InsertUTCDate { get; set; }
        }
    }
    

Ug sa usa ka bulag nga file maghimo kami usa ka base nga klase nga BaseEntity alang sa among duha nga mga entidad, nga maglakip sa sagad nga mga natad:

Pagpatuman sa base nga klase nga BaseEntity

namespace TestLINQ
{
    public class BaseEntity : IBaseEntityID, IBaseEntityName, IBaseNameInsertUTCDate
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public DateTime InsertUTCDate { get; set; }
    }
}

Sunod, maghimo kami sa among duha ka entidad sa managlahing mga file:

  1. Pagpatuman sa klase sa Ref
    using System.ComponentModel.DataAnnotations.Schema;
    
    namespace TestLINQ
    {
        [Table("Ref")]
        public class Ref : BaseEntity
        {
            public int ID2 { get; set; }
        }
    }
    

  2. Pagpatuman sa klase sa Customer
    using System.ComponentModel.DataAnnotations.Schema;
    
    namespace TestLINQ
    {
        [Table("Customer")]
        public class Customer: BaseEntity
        {
            public int Ref_ID { get; set; }
            public int Ref_ID2 { get; set; }
        }
    }
    

Karon maghimo kita usa ka konteksto sa UserContext sa usa ka lahi nga file:

Pagpatuman sa klase sa UserContex

using System.Data.Entity;

namespace TestLINQ
{
    public class UserContext : DbContext
    {
        public UserContext()
            : base("DbConnection")
        {
            Database.SetInitializer<UserContext>(null);
        }

        public DbSet<Customer> Customer { get; set; }
        public DbSet<Ref> Ref { get; set; }
    }
}

Nakadawat kami usa ka andam nga solusyon alang sa pagpahigayon sa mga pagsulay sa pag-optimize sa LINQ hangtod sa SQL pinaagi sa EF alang sa MS SQL Server:

Pipila ka aspeto sa pag-optimize sa LINQ nga mga pangutana sa C#.NET para sa MS SQL Server

Karon isulod ang mosunod nga code sa Program.cs file:

Program.cs file

using System;
using System.Collections.Generic;
using System.Linq;

namespace TestLINQ
{
    class Program
    {
        static void Main(string[] args)
        {
            using (UserContext db = new UserContext())
            {
                var dblog = new List<string>();
                db.Database.Log = dblog.Add;

                var query = from e1 in db.Customer
                            from e2 in db.Ref
                            where (e1.Ref_ID == e2.ID)
                                 && (e1.Ref_ID2 == e2.ID2)
                            select new { Data1 = e1.Name, Data2 = e2.Name };

                var result = query.Take(1000).ToList();

                Console.WriteLine(dblog[1]);

                Console.ReadKey();
            }
        }
    }
}

Sunod, ilunsad nato ang atong proyekto.

Sa pagtapos sa trabaho, ang mosunod nga ipakita sa console:

Nahimo nga SQL Query

SELECT TOP (1000) 
    [Extent1].[Ref_ID] AS [Ref_ID], 
    [Extent1].[Name] AS [Name], 
    [Extent2].[Name] AS [Name1]
    FROM  [dbo].[Customer] AS [Extent1]
    INNER JOIN [dbo].[Ref] AS [Extent2] ON ([Extent1].[Ref_ID] = [Extent2].[ID]) AND ([Extent1].[Ref_ID2] = [Extent2].[ID2])

Sa ato pa, sa kinatibuk-an, ang LINQ nga pangutana nakamugna og SQL nga pangutana sa MS SQL Server DBMS nga maayo.

Karon atong usbon ang AND nga kondisyon sa OR sa LINQ nga pangutana:

Pangutana sa LINQ

var query = from e1 in db.Customer
                            from e2 in db.Ref
                            where (e1.Ref_ID == e2.ID)
                                || (e1.Ref_ID2 == e2.ID2)
                            select new { Data1 = e1.Name, Data2 = e2.Name };

Ug atong ilunsad ang atong aplikasyon pag-usab.

Ang pagpatay ma-crash sa usa ka sayup tungod sa oras sa pagpatuman sa mando nga sobra sa 30 segundos:

Pipila ka aspeto sa pag-optimize sa LINQ nga mga pangutana sa C#.NET para sa MS SQL Server

Kung imong tan-awon ang pangutana nga gihimo sa LINQ:

Pipila ka aspeto sa pag-optimize sa LINQ nga mga pangutana sa C#.NET para sa MS SQL Server
, unya imong masiguro nga ang pagpili mahitabo pinaagi sa Cartesian nga produkto sa duha ka set (mga lamesa):

Nahimo nga SQL Query

SELECT TOP (1000) 
    [Extent1].[Ref_ID] AS [Ref_ID], 
    [Extent1].[Name] AS [Name], 
    [Extent2].[Name] AS [Name1]
    FROM  [dbo].[Customer] AS [Extent1]
    CROSS JOIN [dbo].[Ref] AS [Extent2]
    WHERE [Extent1].[Ref_ID] = [Extent2].[ID] OR [Extent1].[Ref_ID2] = [Extent2].[ID2]

Atong isulat pag-usab ang LINQ nga pangutana sama sa mosunod:

Na-optimize nga LINQ nga pangutana

var query = (from e1 in db.Customer
                   join e2 in db.Ref
                   on e1.Ref_ID equals e2.ID
                   select new { Data1 = e1.Name, Data2 = e2.Name }).Union(
                        from e1 in db.Customer
                        join e2 in db.Ref
                        on e1.Ref_ID2 equals e2.ID2
                        select new { Data1 = e1.Name, Data2 = e2.Name });

Dayon atong makuha ang mosunod nga SQL query:

Pangutana sa SQL

SELECT 
    [Limit1].[C1] AS [C1], 
    [Limit1].[C2] AS [C2], 
    [Limit1].[C3] AS [C3]
    FROM ( SELECT DISTINCT TOP (1000) 
        [UnionAll1].[C1] AS [C1], 
        [UnionAll1].[Name] AS [C2], 
        [UnionAll1].[Name1] AS [C3]
        FROM  (SELECT 
            1 AS [C1], 
            [Extent1].[Name] AS [Name], 
            [Extent2].[Name] AS [Name1]
            FROM  [dbo].[Customer] AS [Extent1]
            INNER JOIN [dbo].[Ref] AS [Extent2] ON [Extent1].[Ref_ID] = [Extent2].[ID]
        UNION ALL
            SELECT 
            1 AS [C1], 
            [Extent3].[Name] AS [Name], 
            [Extent4].[Name] AS [Name1]
            FROM  [dbo].[Customer] AS [Extent3]
            INNER JOIN [dbo].[Ref] AS [Extent4] ON [Extent3].[Ref_ID2] = [Extent4].[ID2]) AS [UnionAll1]
    )  AS [Limit1]

Alangan, sa mga pangutana sa LINQ mahimo ra adunay usa ka kondisyon sa pag-apil, busa dinhi posible nga maghimo usa ka katumbas nga pangutana gamit ang duha nga mga pangutana alang sa matag kondisyon ug dayon isagol kini pinaagi sa Union aron makuha ang mga duplicate taliwala sa mga linya.
Oo, ang mga pangutana sa kasagaran dili katumbas, nga gikonsiderar nga ang kompleto nga doble nga mga laray mahimong ibalik. Bisan pa, sa tinuud nga kinabuhi, dili kinahanglan ang kompleto nga mga duplicate nga linya ug ang mga tawo naningkamot sa pagtangtang niini.

Karon atong itandi ang mga plano sa pagpatuman niining duha ka pangutana:

  1. alang sa CROSS JOIN ang kasagaran nga oras sa pagpatuman mao ang 195 segundos:
    Pipila ka aspeto sa pag-optimize sa LINQ nga mga pangutana sa C#.NET para sa MS SQL Server
  2. alang sa INNER JOIN-UNION ang kasagaran nga oras sa pagpatuman dili mubu sa 24 segundos:
    Pipila ka aspeto sa pag-optimize sa LINQ nga mga pangutana sa C#.NET para sa MS SQL Server

Sama sa imong makita gikan sa mga resulta, alang sa duha ka mga lamesa nga adunay minilyon nga mga rekord, ang na-optimize nga LINQ nga pangutana sa daghang mga higayon nga mas paspas kaysa sa wala ma-optimize.

Alang sa kapilian nga adunay AND sa mga kondisyon, usa ka pangutana sa LINQ sa porma:

Pangutana sa LINQ

var query = from e1 in db.Customer
                            from e2 in db.Ref
                            where (e1.Ref_ID == e2.ID)
                                 && (e1.Ref_ID2 == e2.ID2)
                            select new { Data1 = e1.Name, Data2 = e2.Name };

Ang husto nga pangutana sa SQL hapit kanunay mabuhat, nga modagan sa kasagaran sa mga 1 segundo:

Pipila ka aspeto sa pag-optimize sa LINQ nga mga pangutana sa C#.NET para sa MS SQL Server
Usab alang sa LINQ sa Objects manipulations imbes sa usa ka pangutana sama sa:

LINQ nga pangutana (1st nga kapilian)

var query = from e1 in seq1
                            from e2 in seq2
                            where (e1.Key1==e2.Key1)
                               && (e1.Key2==e2.Key2)
                            select new { Data1 = e1.Data, Data2 = e2.Data };

mahimo nimong gamiton ang pangutana sama sa:

LINQ nga pangutana (2st nga kapilian)

var query = from e1 in seq1
                            join e2 in seq2
                            on new { e1.Key1, e1.Key2 } equals new { e2.Key1, e2.Key2 }
                            select new { Data1 = e1.Data, Data2 = e2.Data };

diin:

Paghubit sa duha ka array

Para[] seq1 = new[] { new Para { Key1 = 1, Key2 = 2, Data = "777" }, new Para { Key1 = 2, Key2 = 3, Data = "888" }, new Para { Key1 = 3, Key2 = 4, Data = "999" } };
Para[] seq2 = new[] { new Para { Key1 = 1, Key2 = 2, Data = "777" }, new Para { Key1 = 2, Key2 = 3, Data = "888" }, new Para { Key1 = 3, Key2 = 5, Data = "999" } };

, ug ang tipo sa Para gihubit ingon sa mosunod:

Kahulugan sa Para Type

class Para
{
        public int Key1, Key2;
        public string Data;
}

Busa, among gisusi ang pipila ka mga aspeto sa pag-optimize sa LINQ nga mga pangutana sa MS SQL Server.

Ikasubo, bisan ang mga eksperyensiyado ug nanguna nga mga developer sa NET nakalimot nga kinahanglan nila nga masabtan kung unsa ang gibuhat sa mga panudlo nga ilang gigamit sa luyo sa mga talan-awon. Kung dili, mahimo silang mga configurator ug mahimo’g magtanom usa ka bomba sa oras sa umaabot kung gi-scale ang solusyon sa software ug adunay gamay nga pagbag-o sa mga kondisyon sa gawas sa palibot.

Usa ka mubo nga pagrepaso ang gihimo usab dinhi.

Ang mga tinubdan alang sa pagsulay - ang proyekto mismo, ang paghimo sa mga lamesa sa TEST database, ingon man ang pagpuno niini nga mga lamesa sa datos nahimutang dinhi.
Usab sa kini nga repository, sa folder nga Mga Plano, adunay mga plano alang sa pagpatuman sa mga pangutana nga adunay mga kondisyon sa O.

Source: www.habr.com

Idugang sa usa ka comment