MS SQL Server အတွက် C#.NET ရှိ LINQ queries များကို အကောင်းဆုံးဖြစ်အောင် ပြုလုပ်ခြင်း၏ အချို့သော ကဏ္ဍများ

LINQ သည် .NET အား အစွမ်းထက်သော ဒေတာခြယ်လှယ်မှုဘာသာစကားအသစ်တစ်ခုအဖြစ် ထည့်သွင်းခဲ့သည်။ ၎င်း၏တစ်စိတ်တစ်ပိုင်းအနေဖြင့် LINQ မှ SQL သည် ဥပမာအားဖြင့် Entity Framework ကို အသုံးပြု၍ DBMS နှင့် အတော်လေး အဆင်ပြေစွာ ဆက်သွယ်နိုင်စေပါသည်။ သို့သော်၊ ၎င်းကိုမကြာခဏအသုံးပြုခြင်းဖြင့် developer များသည် သင့်ကိစ္စတွင် Entity Framework သည် queryable provider မှ SQL query အမျိုးအစားကိုကြည့်ရှုရန်မေ့လျော့နေပါသည်။

ဥပမာတစ်ခုသုံးပြီး အဓိက အချက်နှစ်ချက်ကို ကြည့်ကြရအောင်။
၎င်းကိုလုပ်ဆောင်ရန်၊ SQL Server တွင် Test database တစ်ခုကိုဖန်တီးပြီး အောက်ပါမေးခွန်းကိုအသုံးပြု၍ ၎င်းတွင်ဇယားနှစ်ခုဖန်တီးပါ။

ဇယားများဖန်တီးခြင်း။

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

ယခု အောက်ပါ script ကို run ခြင်းဖြင့် Ref table ကို ဖြည့်ကြပါစို့။

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

အောက်ဖော်ပြပါ script ကိုအသုံးပြု၍ ဝယ်ယူသူဇယားကို အလားတူဖြည့်စွက်ကြပါစို့။

ဖောက်သည်ဇယားကို ဖြည့်သွင်းခြင်း။

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 သန်းကျော်ရှိသည်။

ယခု Visual Studio တွင် Visual C# Console App (.NET Framework) ပရောဂျက်ကို စမ်းသပ်ဖန်တီးရန် လိုအပ်သည်-

MS SQL Server အတွက် C#.NET ရှိ LINQ queries များကို အကောင်းဆုံးဖြစ်အောင် ပြုလုပ်ခြင်း၏ အချို့သော ကဏ္ဍများ

ထို့နောက်၊ သင်သည် ဒေတာဘေ့စ်နှင့် အပြန်အလှန်တုံ့ပြန်ရန် Entity Framework အတွက် စာကြည့်တိုက်တစ်ခုကို ထည့်ရန်လိုအပ်သည်။
၎င်းကိုထည့်ရန်၊ ပရောဂျက်ပေါ်တွင် right-click နှိပ်ပြီး context menu မှ Manage NuGet Packages ကိုရွေးချယ်ပါ။

MS SQL Server အတွက် C#.NET ရှိ LINQ queries များကို အကောင်းဆုံးဖြစ်အောင် ပြုလုပ်ခြင်း၏ အချို့သော ကဏ္ဍများ

ထို့နောက်ပေါ်လာသော NuGet ပက်ကေ့ဂျ်စီမံခန့်ခွဲမှုဝင်းဒိုးတွင်၊ ရှာဖွေရေးဝင်းဒိုးတွင် "Entity Framework" ဟူသောစကားလုံးကိုရိုက်ထည့်ကာ Entity Framework ပက်ကေ့ဂျ်ကိုရွေးချယ်ပြီး ၎င်းကိုထည့်သွင်းပါ။

MS SQL Server အတွက် C#.NET ရှိ LINQ queries များကို အကောင်းဆုံးဖြစ်အောင် ပြုလုပ်ခြင်း၏ အချို့သော ကဏ္ဍများ

ထို့နောက်၊ App.config ဖိုင်တွင် configSections ဒြပ်စင်ကိုပိတ်ပြီးနောက်၊ သင်သည် အောက်ပါဘလောက်ကို ထည့်ရန် လိုအပ်သည်-

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

connectionString တွင် သင်သည် connection string ကိုရိုက်ထည့်ရန် လိုအပ်သည်။

ယခု သီးခြားဖိုင်များတွင် အင်တာဖေ့စ် 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. Ref class ကို အကောင်အထည်ဖော်ခြင်း။
    using System.ComponentModel.DataAnnotations.Schema;
    
    namespace TestLINQ
    {
        [Table("Ref")]
        public class Ref : BaseEntity
        {
            public int ID2 { get; set; }
        }
    }
    

  2. Customer class ကို အကောင်အထည်ဖော်ခြင်း။
    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; }
    }
}

MS SQL Server အတွက် EF မှတဆင့် LINQ နှင့် SQL မှ ပိုမိုကောင်းမွန်အောင်လုပ်ဆောင်ခြင်းအတွက် အဆင်သင့်လုပ်ထားသော ဖြေရှင်းချက်တစ်ခုကို လက်ခံရရှိသည်-

MS SQL Server အတွက် C#.NET ရှိ LINQ queries များကို အကောင်းဆုံးဖြစ်အောင် ပြုလုပ်ခြင်း၏ အချို့သော ကဏ္ဍများ

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

ထို့နောက် ကျွန်ုပ်တို့၏ ပရောဂျက်ကို စတင်ကြပါစို့။

အလုပ်၏အဆုံးတွင်၊ console တွင်အောက်ပါတို့ကိုပြသလိမ့်မည်-

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

ဆိုလိုသည်မှာ ယေဘူယျအားဖြင့် LINQ query သည် MS SQL Server DBMS သို့ SQL query ကိုထုတ်ပေးသည်။

ယခု 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 စက္ကန့်ထက်ကျော်လွန်သော command execute အချိန်ကြောင့် လုပ်ဆောင်ချက်သည် အမှားအယွင်းတစ်ခုနှင့် ပျက်သွားလိမ့်မည်-

MS SQL Server အတွက် C#.NET ရှိ LINQ queries များကို အကောင်းဆုံးဖြစ်အောင် ပြုလုပ်ခြင်း၏ အချို့သော ကဏ္ဍများ

LINQ မှထုတ်ပေးသောမေးခွန်းကိုကြည့်လျှင်-

MS SQL Server အတွက် C#.NET ရှိ LINQ queries များကို အကောင်းဆုံးဖြစ်အောင် ပြုလုပ်ခြင်း၏ အချို့သော ကဏ္ဍများ
ထို့နောက် ရွေးချယ်မှုသည် နှစ်စုံ (ဇယားများ) ၏ Cartesian ထုတ်ကုန်မှတစ်ဆင့် ဖြစ်ပေါ်လာကြောင်း သေချာစေနိုင်ပါသည်။

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]

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 query ကို ကျွန်ုပ်တို့ ရရှိသည် ။

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]

Alas၊ LINQ queries တွင် Join condition တစ်ခုသာရှိနိုင်သည်၊ ထို့ကြောင့် ဤနေရာတွင် အခြေအနေတစ်ခုစီအတွက် queries နှစ်ခုကို အသုံးပြု၍ ညီမျှသော query တစ်ခုပြုလုပ်နိုင်ပြီး အတန်းများကြားမှ ထပ်နေသောများကိုဖယ်ရှားရန်အတွက် Union မှတစ်ဆင့် ၎င်းတို့ကို ပေါင်းစပ်လိုက်ပါ။
ဟုတ်ကဲ့၊ ပြီးပြည့်စုံသော အတန်းများကို ပြန်ပေးမည်ကို ထည့်သွင်းစဉ်းစားခြင်းဖြင့် မေးခွန်းများသည် ယေဘူယျအားဖြင့် တူညီမည်မဟုတ်ပါ။ သို့သော်၊ လက်တွေ့ဘဝတွင်၊ ပြီးပြည့်စုံသော ထပ်တူလိုင်းများကို မလိုအပ်ဘဲ လူများက ၎င်းတို့ကို ဖယ်ရှားရန် ကြိုးစားကြသည်။

ယခု ဤမေးခွန်းနှစ်ခု၏ အကောင်အထည်ဖော်မှုအစီအစဉ်ကို နှိုင်းယှဉ်ကြည့်ရအောင်။

  1. CROSS JOIN အတွက် ပျမ်းမျှလုပ်ဆောင်ချိန်သည် 195 စက္ကန့်ဖြစ်သည်။
    MS SQL Server အတွက် C#.NET ရှိ LINQ queries များကို အကောင်းဆုံးဖြစ်အောင် ပြုလုပ်ခြင်း၏ အချို့သော ကဏ္ဍများ
  2. Inner JOIN-UNION အတွက် ပျမ်းမျှလုပ်ဆောင်ချိန်သည် 24 စက္ကန့်ထက်နည်းသည်-
    MS SQL Server အတွက် C#.NET ရှိ LINQ queries များကို အကောင်းဆုံးဖြစ်အောင် ပြုလုပ်ခြင်း၏ အချို့သော ကဏ္ဍများ
  3. .
    ဤသိမ်းဆည်းမှုတွင်၊ Plans ဖိုင်တွဲတွင်၊ OR အခြေအနေများဖြင့် မေးမြန်းမှုများကို လုပ်ဆောင်ရန် အစီအစဉ်များရှိပါသည်။

source: www.habr.com

မှတ်ချက် Add