एमएस SQL ​​सर्भरको लागि C#.NET मा LINQ क्वेरीहरू अनुकूलन गर्ने केही पक्षहरू

LINQ एक शक्तिशाली नयाँ डाटा हेरफेर भाषाको रूपमा .NET प्रविष्ट गर्यो। LINQ to SQL ले यसको एक भागको रूपमा तपाईंलाई DBMS प्रयोग गरी सहज रूपमा सञ्चार गर्न अनुमति दिन्छ, उदाहरणका लागि, Entity Framework। जे होस्, यसलाई प्रायः प्रयोग गर्दै, विकासकर्ताहरूले तपाइँको मामलामा इकाई फ्रेमवर्कले उत्पन्न गर्ने SQL क्वेरी कस्तो प्रकारको 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 फ्रेमवर्क) परियोजना सिर्जना गर्न आवश्यक छ:

एमएस SQL ​​सर्भरको लागि C#.NET मा LINQ क्वेरीहरू अनुकूलन गर्ने केही पक्षहरू

अर्को, डाटाबेससँग अन्तर्क्रिया गर्नको लागि तपाईंले एकाइ फ्रेमवर्कको लागि पुस्तकालय थप्नु पर्छ।
यसलाई थप्नको लागि, परियोजनामा ​​दायाँ क्लिक गर्नुहोस् र सन्दर्भ मेनुबाट NuGet प्याकेजहरू प्रबन्ध गर्नुहोस् चयन गर्नुहोस्:

एमएस SQL ​​सर्भरको लागि C#.NET मा LINQ क्वेरीहरू अनुकूलन गर्ने केही पक्षहरू

त्यसपछि, देखा पर्ने NuGet प्याकेज व्यवस्थापन विन्डोमा, खोज विन्डोमा "एकाइ फ्रेमवर्क" शब्द प्रविष्ट गर्नुहोस् र इकाई फ्रेमवर्क प्याकेज चयन गर्नुहोस् र यसलाई स्थापना गर्नुहोस्:

एमएस SQL ​​सर्भरको लागि C#.NET मा LINQ क्वेरीहरू अनुकूलन गर्ने केही पक्षहरू

अर्को, App.config फाइलमा, configSection तत्व बन्द गरेपछि, तपाईंले निम्न ब्लक थप्न आवश्यक छ:

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

connectString मा तपाईंले जडान स्ट्रिङ प्रविष्ट गर्न आवश्यक छ।

अब छुट्टाछुट्टै फाइलहरूमा 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 सिर्जना गर्नेछौं, जसमा साझा क्षेत्रहरू समावेश हुनेछन्:

आधार वर्ग 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; }
        }
    }
    

अब एक अलग फाइलमा UserContext सन्दर्भ सिर्जना गरौं:

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

हामीले एमएस SQL ​​सर्भरको लागि EF मार्फत LINQ देखि SQL सँग अप्टिमाइजेसन परीक्षणहरू सञ्चालन गर्नको लागि तयार समाधान प्राप्त गर्यौं:

एमएस 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 };

र हाम्रो अनुप्रयोग फेरि सुरु गरौं।

आदेश कार्यान्वयन समय ३० सेकेन्डभन्दा बढी भएको कारणले त्रुटिको साथ कार्यान्वयन क्र्यास हुनेछ:

एमएस SQL ​​सर्भरको लागि C#.NET मा LINQ क्वेरीहरू अनुकूलन गर्ने केही पक्षहरू

यदि तपाइँ LINQ द्वारा उत्पन्न गरिएको प्रश्न हेर्नुभयो भने:

एमएस 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 सेकेन्ड हो:
    एमएस SQL ​​सर्भरको लागि C#.NET मा LINQ क्वेरीहरू अनुकूलन गर्ने केही पक्षहरू
  2. INNER JOIN-UNION को लागि औसत कार्यान्वयन समय 24 सेकेन्ड भन्दा कम छ:
    एमएस 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 सेकेन्डमा औसतमा चल्नेछ:

एमएस SQL ​​सर्भरको लागि C#.NET मा LINQ क्वेरीहरू अनुकूलन गर्ने केही पक्षहरू
LINQ देखि वस्तु हेरफेर को लागी एक क्वेरी को सट्टा जस्तै:

LINQ क्वेरी (पहिलो विकल्प)

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 क्वेरी (पहिलो विकल्प)

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

तसर्थ, हामीले एमएस SQL ​​सर्भरमा LINQ क्वेरीहरू अनुकूलन गर्ने केही पक्षहरू जाँच्यौं।

दुर्भाग्यवश, अनुभवी र अग्रणी .NET विकासकर्ताहरू पनि बिर्सन्छन् कि उनीहरूले पर्दा पछाडि प्रयोग गर्ने निर्देशनहरू के गर्छन् भनेर बुझ्न आवश्यक छ। अन्यथा, तिनीहरू कन्फिगरेटर बन्छन् र भविष्यमा सफ्टवेयर समाधान मापन गर्दा र बाह्य वातावरणीय अवस्थाहरूमा सानो परिवर्तनको साथमा टाइम बम रोप्न सक्छन्।

छोटो समीक्षा पनि गरियो यहाँ.

परीक्षणका लागि स्रोतहरू - परियोजना आफैं, TEST डाटाबेसमा तालिकाहरू सिर्जना गर्ने, साथै डाटासँग यी तालिकाहरू भर्ने ठाउँहरू छन्। यहाँ.
साथै यस भण्डारमा, योजना फोल्डरमा, OR सर्तहरूका साथ क्वेरीहरू कार्यान्वयन गर्ने योजनाहरू छन्।

स्रोत: www.habr.com

एक टिप्पणी थप्न