MS SQL సర్వర్ కోసం C#.NETలో LINQ ప్రశ్నలను ఆప్టిమైజ్ చేయడంలో కొన్ని అంశాలు

LINQ .NETని శక్తివంతమైన కొత్త డేటా మానిప్యులేషన్ లాంగ్వేజ్‌గా నమోదు చేసింది. LINQ నుండి SQL దానిలో భాగంగా మీరు DBMSతో చాలా సౌకర్యవంతంగా కమ్యూనికేట్ చేయడానికి అనుమతిస్తుంది, ఉదాహరణకు, ఎంటిటీ ఫ్రేమ్‌వర్క్. అయినప్పటికీ, దీన్ని తరచుగా ఉపయోగించడం ద్వారా, డెవలపర్లు మీ విషయంలో ఎంటిటీ ఫ్రేమ్‌వర్క్‌లో ప్రశ్నించదగిన ప్రొవైడర్ ఎలాంటి SQL ప్రశ్నను ఉత్పత్తి చేస్తారో చూడటం మర్చిపోతారు.

ఒక ఉదాహరణను ఉపయోగించి రెండు ప్రధాన అంశాలను చూద్దాం.
దీన్ని చేయడానికి, SQL సర్వర్‌లో టెస్ట్ డేటాబేస్‌ను సృష్టించండి మరియు కింది ప్రశ్నను ఉపయోగించి అందులో రెండు పట్టికలను సృష్టించండి:

పట్టికలు సృష్టిస్తోంది

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

ఇప్పుడు కింది స్క్రిప్ట్‌ని అమలు చేయడం ద్వారా రెఫ్ పట్టికను నింపండి:

రెఫ్ పట్టికను పూరించడం

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

అదేవిధంగా కింది స్క్రిప్ట్‌ని ఉపయోగించి కస్టమర్ టేబుల్‌ని పూరిద్దాం:

కస్టమర్ పట్టికను నింపడం

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

ఈ విధంగా, మేము రెండు పట్టికలను అందుకున్నాము, వాటిలో ఒకటి 1 మిలియన్ కంటే ఎక్కువ వరుసల డేటాను కలిగి ఉంది మరియు మరొకటి 10 మిలియన్ల కంటే ఎక్కువ వరుసల డేటాను కలిగి ఉంది.

ఇప్పుడు విజువల్ స్టూడియోలో మీరు టెస్ట్ విజువల్ C# కన్సోల్ యాప్ (.NET ఫ్రేమ్‌వర్క్) ప్రాజెక్ట్‌ని సృష్టించాలి:

MS SQL సర్వర్ కోసం C#.NETలో LINQ ప్రశ్నలను ఆప్టిమైజ్ చేయడంలో కొన్ని అంశాలు

తర్వాత, మీరు డేటాబేస్‌తో పరస్పర చర్య చేయడానికి ఎంటిటీ ఫ్రేమ్‌వర్క్ కోసం లైబ్రరీని జోడించాలి.
దీన్ని జోడించడానికి, ప్రాజెక్ట్‌పై కుడి-క్లిక్ చేసి, సందర్భ మెను నుండి NuGet ప్యాకేజీలను నిర్వహించు ఎంచుకోండి:

MS SQL సర్వర్ కోసం C#.NETలో LINQ ప్రశ్నలను ఆప్టిమైజ్ చేయడంలో కొన్ని అంశాలు

అప్పుడు, కనిపించే NuGet ప్యాకేజీ నిర్వహణ విండోలో, శోధన విండోలో “ఎంటిటీ ఫ్రేమ్‌వర్క్” అనే పదాన్ని నమోదు చేసి, ఎంటిటీ ఫ్రేమ్‌వర్క్ ప్యాకేజీని ఎంచుకుని, దాన్ని ఇన్‌స్టాల్ చేయండి:

MS SQL సర్వర్ కోసం C#.NETలో LINQ ప్రశ్నలను ఆప్టిమైజ్ చేయడంలో కొన్ని అంశాలు

తరువాత, App.config ఫైల్‌లో, configSections మూలకాన్ని మూసివేసిన తర్వాత, మీరు క్రింది బ్లాక్‌ని జోడించాలి:

<connectionStrings>
    <add name="DBConnection" connectionString="data source=ИМЯ_ЭКЗЕМПЛЯРА_MSSQL;Initial Catalog=TEST;Integrated Security=True;" providerName="System.Data.SqlClient" />
</connectionStrings>

connectionStringలో మీరు కనెక్షన్ స్ట్రింగ్‌ను నమోదు చేయాలి.

ఇప్పుడు వేరు వేరు ఫైల్‌లలో 3 ఇంటర్‌ఫేస్‌లను క్రియేట్ చేద్దాం:

  1. IBaseEntityID ఇంటర్‌ఫేస్‌ని అమలు చేస్తోంది
    namespace TestLINQ
    {
        public interface IBaseEntityID
        {
            int ID { get; set; }
        }
    }
    

  2. IBaseEntityName ఇంటర్‌ఫేస్ అమలు
    namespace TestLINQ
    {
        public interface IBaseEntityName
        {
            string Name { get; set; }
        }
    }
    

  3. IBaseNameInsertUTCDate ఇంటర్‌ఫేస్ అమలు
    namespace TestLINQ
    {
        public interface IBaseNameInsertUTCDate
        {
            DateTime InsertUTCDate { get; set; }
        }
    }
    

మరియు ఒక ప్రత్యేక ఫైల్‌లో మేము మా రెండు ఎంటిటీల కోసం బేస్ క్లాస్ BaseEntityని సృష్టిస్తాము, ఇందులో సాధారణ ఫీల్డ్‌లు ఉంటాయి:

బేస్ క్లాస్ బేస్ఎంటిటీ అమలు

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

తరువాత, మేము మా రెండు ఎంటిటీలను వేర్వేరు ఫైల్‌లలో సృష్టిస్తాము:

  1. రెఫ్ క్లాస్ అమలు
    using System.ComponentModel.DataAnnotations.Schema;
    
    namespace TestLINQ
    {
        [Table("Ref")]
        public class Ref : BaseEntity
        {
            public int ID2 { get; set; }
        }
    }
    

  2. కస్టమర్ క్లాస్ అమలు
    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; }
        }
    }
    

ఇప్పుడు మనం ఒక ప్రత్యేక ఫైల్‌లో వినియోగదారు సందర్భ సందర్భాన్ని సృష్టిద్దాం:

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; }
    }
}

MS SQL సర్వర్ కోసం EF ద్వారా LINQ నుండి SQL వరకు ఆప్టిమైజేషన్ పరీక్షలను నిర్వహించడానికి మేము సిద్ధంగా ఉన్న పరిష్కారాన్ని అందుకున్నాము:

MS SQL సర్వర్ కోసం C#.NETలో LINQ ప్రశ్నలను ఆప్టిమైజ్ చేయడంలో కొన్ని అంశాలు

ఇప్పుడు Program.cs ఫైల్‌లో కింది కోడ్‌ని నమోదు చేయండి:

Program.cs ఫైల్

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();
            }
        }
    }
}

తరువాత, మన ప్రాజెక్ట్ను ప్రారంభిద్దాం.

పని ముగింపులో, కిందివి కన్సోల్‌లో ప్రదర్శించబడతాయి:

రూపొందించిన SQL ప్రశ్న

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])

అంటే, సాధారణంగా, LINQ ప్రశ్న MS SQL సర్వర్ DBMSకి SQL ప్రశ్నను చాలా బాగా రూపొందించింది.

ఇప్పుడు LINQ ప్రశ్నలో AND షరతును ORకి మారుద్దాం:

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 };

మరియు మన అప్లికేషన్‌ను మళ్లీ ప్రారంభిద్దాం.

కమాండ్ ఎగ్జిక్యూషన్ సమయం 30 సెకన్లకు మించి ఉండటం వల్ల ఎగ్జిక్యూషన్ లోపంతో క్రాష్ అవుతుంది:

MS SQL సర్వర్ కోసం C#.NETలో LINQ ప్రశ్నలను ఆప్టిమైజ్ చేయడంలో కొన్ని అంశాలు

మీరు LINQ ద్వారా రూపొందించబడిన ప్రశ్నను చూస్తే:

MS SQL సర్వర్ కోసం C#.NETలో LINQ ప్రశ్నలను ఆప్టిమైజ్ చేయడంలో కొన్ని అంశాలు
, అప్పుడు మీరు ఎంపిక రెండు సెట్ల (టేబుల్స్) కార్టీసియన్ ఉత్పత్తి ద్వారా జరుగుతుందని నిర్ధారించుకోవచ్చు:

రూపొందించిన SQL ప్రశ్న

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]

LINQ ప్రశ్నను ఈ క్రింది విధంగా తిరిగి వ్రాద్దాం:

ఆప్టిమైజ్ చేయబడిన LINQ ప్రశ్న

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 });

అప్పుడు మనకు ఈ క్రింది SQL ప్రశ్న వస్తుంది:

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]

అయ్యో, LINQ ప్రశ్నలలో ఒక చేరిక షరతు మాత్రమే ఉంటుంది, కాబట్టి ఇక్కడ ప్రతి కండిషన్‌కు రెండు ప్రశ్నలను ఉపయోగించి సమానమైన ప్రశ్నను తయారు చేయడం సాధ్యమవుతుంది, ఆపై వరుసల మధ్య నకిలీలను తొలగించడానికి యూనియన్ ద్వారా వాటిని కలపడం సాధ్యమవుతుంది.
అవును, పూర్తి డూప్లికేట్ అడ్డు వరుసలు తిరిగి ఇవ్వబడవచ్చని పరిగణనలోకి తీసుకుంటే, ప్రశ్నలు సాధారణంగా సమానమైనవి కావు. అయితే, నిజ జీవితంలో, పూర్తి డూప్లికేట్ లైన్లు అవసరం లేదు మరియు ప్రజలు వాటిని వదిలించుకోవడానికి ప్రయత్నిస్తారు.

ఇప్పుడు ఈ రెండు ప్రశ్నల అమలు ప్రణాళికలను పోల్చి చూద్దాం:

  1. CROSS JOIN కోసం సగటు అమలు సమయం 195 సెకన్లు:
    MS SQL సర్వర్ కోసం C#.NETలో LINQ ప్రశ్నలను ఆప్టిమైజ్ చేయడంలో కొన్ని అంశాలు
  2. INNER JOIN-UNION కోసం సగటు అమలు సమయం 24 సెకన్ల కంటే తక్కువ:
    MS SQL సర్వర్ కోసం C#.NETలో LINQ ప్రశ్నలను ఆప్టిమైజ్ చేయడంలో కొన్ని అంశాలు

మీరు ఫలితాల నుండి చూడగలిగినట్లుగా, మిలియన్ల కొద్దీ రికార్డ్‌లతో ఉన్న రెండు టేబుల్‌ల కోసం, ఆప్టిమైజ్ చేయబడిన LINQ ప్రశ్న ఆప్టిమైజ్ చేయని దాని కంటే చాలా రెట్లు వేగంగా ఉంటుంది.

షరతులలో AND తో ఎంపిక కోసం, ఫారమ్ యొక్క LINQ ప్రశ్న:

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 };

సరైన SQL ప్రశ్న దాదాపు ఎల్లప్పుడూ ఉత్పత్తి చేయబడుతుంది, ఇది సగటున 1 సెకనులో అమలవుతుంది:

MS SQL సర్వర్ కోసం C#.NETలో LINQ ప్రశ్నలను ఆప్టిమైజ్ చేయడంలో కొన్ని అంశాలు
ఇలాంటి ప్రశ్నకు బదులుగా LINQ నుండి ఆబ్జెక్ట్‌ల మానిప్యులేషన్‌ల కోసం:

LINQ ప్రశ్న (1వ ఎంపిక)

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 };

మీరు ఇలాంటి ప్రశ్నను ఉపయోగించవచ్చు:

LINQ ప్రశ్న (2వ ఎంపిక)

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 };

పేరు:

రెండు శ్రేణులను నిర్వచించడం

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" } };

, మరియు పారా రకం క్రింది విధంగా నిర్వచించబడింది:

పారా రకం నిర్వచనం

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

అందువలన, మేము MS SQL సర్వర్‌కు LINQ ప్రశ్నలను ఆప్టిమైజ్ చేయడంలో కొన్ని అంశాలను పరిశీలించాము.

దురదృష్టవశాత్తూ, అనుభవజ్ఞులైన మరియు ప్రముఖ .NET డెవలపర్‌లు కూడా వారు ఉపయోగించే సూచనలు తెరవెనుక ఏమి చేస్తాయో అర్థం చేసుకోవాలని మర్చిపోతారు. లేకపోతే, అవి కాన్ఫిగరేటర్‌లుగా మారతాయి మరియు భవిష్యత్తులో సాఫ్ట్‌వేర్ సొల్యూషన్‌ను స్కేలింగ్ చేసేటప్పుడు మరియు బాహ్య పర్యావరణ పరిస్థితులలో చిన్న మార్పులతో టైమ్ బాంబ్‌ను అమర్చవచ్చు.

స్వల్ప సమీక్ష కూడా నిర్వహించారు ఇక్కడ.

పరీక్ష యొక్క మూలాలు - ప్రాజెక్ట్ కూడా, TEST డేటాబేస్లో పట్టికల సృష్టి, అలాగే ఈ పట్టికలను డేటాతో నింపడం వంటివి ఉన్నాయి ఇక్కడ.
ఈ రిపోజిటరీలో, ప్లాన్స్ ఫోల్డర్‌లో, OR షరతులతో ప్రశ్నలను అమలు చేయడానికి ప్రణాళికలు ఉన్నాయి.

మూలం: www.habr.com

ఒక వ్యాఖ్యను జోడించండి