MS SQL සේවාදායකය සඳහා C#.NET හි LINQ විමසුම් ප්‍රශස්ත කිරීමේ සමහර අංග

LINQ ප්‍රබල නව දත්ත හැසිරවීමේ භාෂාවක් ලෙස .NET ඇතුලත් කර ඇත. LINQ to SQL එහි කොටසක් ලෙස ඔබට DBMS භාවිතා කරමින් ඉතා පහසුවෙන් සන්නිවේදනය කිරීමට ඉඩ සලසයි, උදාහරණයක් ලෙස, Entity Framework. කෙසේ වෙතත්, එය නිතර භාවිතා කරමින්, විමසිය හැකි සැපයුම්කරු, ඔබේ නඩුවේදී, Entity Framework උත්පාදනය කරන්නේ කුමන ආකාරයේ 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

දැන් අපි පහත ස්ක්‍රිප්ට් එක ක්‍රියාත්මක කරමින් Ref වගුව ජනනය කරමු:

Ref වගුව පිරවීම

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 කට වඩා තිබේ.

දැන් Visual Studio තුළ ඔබට පරීක්ෂණ Visual C# Console App (.NET Framework) ව්‍යාපෘතියක් සෑදිය යුතුය:

MS SQL සේවාදායකය සඳහා C#.NET හි LINQ විමසුම් ප්‍රශස්ත කිරීමේ සමහර අංග

මීළඟට, දත්ත සමුදාය සමඟ අන්තර් ක්‍රියා කිරීම සඳහා ඔබ ආයතන රාමුව සඳහා පුස්තකාලයක් එක් කළ යුතුය.
එය එක් කිරීමට, ව්‍යාපෘතිය මත දකුණු-ක්ලික් කර සන්දර්භය මෙනුවෙන් NuGet පැකේජ කළමනාකරණය කරන්න තෝරන්න:

MS SQL සේවාදායකය සඳහා C#.NET හි LINQ විමසුම් ප්‍රශස්ත කිරීමේ සමහර අංග

ඉන්පසු, දිස්වන NuGet පැකේජ කළමනාකරණ කවුළුව තුළ, සෙවුම් කවුළුවේ "Entity Framework" යන වචනය ඇතුළත් කර Entity Framework පැකේජය තෝරා එය ස්ථාපනය කරන්න:

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 පාදක පන්තියක් සාදනු ඇත, එයට පොදු ක්ෂේත්‍ර ඇතුළත් වේ:

මූලික පන්තියේ 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 පන්තිය ක්‍රියාත්මක කිරීම
    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 Server 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 විමසුම්වල තිබිය හැක්කේ එක් සම්බන්ධ වීමේ කොන්දේසියක් පමණි, එබැවින් මෙහි එක් එක් කොන්දේසි සඳහා විමසුම් දෙකක් භාවිතා කර සමාන විමසුමක් සාදා පේළි අතර අනුපිටපත් ඉවත් කිරීමට Union හරහා ඒවා ඒකාබද්ධ කළ හැකිය.
ඔව්, සම්පූර්ණ අනුපිටපත් පේළි ආපසු ලබා දිය හැකි බව සැලකිල්ලට ගනිමින් විමසුම් සාමාන්‍යයෙන් සමාන නොවේ. කෙසේ වෙතත්, සැබෑ ජීවිතයේ දී, සම්පූර්ණ අනුපිටපත් රේඛා අවශ්ය නොවන අතර මිනිසුන් ඒවා ඉවත් කිරීමට උත්සාහ කරයි.

දැන් අපි මෙම විමසුම් දෙකේ ක්‍රියාත්මක කිරීමේ සැලසුම් සංසන්දනය කරමු:

  1. CROSS JOIN සඳහා සාමාන්‍ය ක්‍රියාත්මක කිරීමේ කාලය තත්පර 195 කි:
    MS SQL සේවාදායකය සඳහා C#.NET හි LINQ විමසුම් ප්‍රශස්ත කිරීමේ සමහර අංග
  2. INNER JOIN-UNION සඳහා සාමාන්‍ය ක්‍රියාත්මක කිරීමේ කාලය තත්පර 24 ට වඩා අඩුය:
    MS SQL සේවාදායකය සඳහා C#.NET හි LINQ විමසුම් ප්‍රශස්ත කිරීමේ සමහර අංග

ප්‍රතිඵල වලින් ඔබට පෙනෙන පරිදි, මිලියන ගණනක් වාර්තා සහිත වගු දෙකක් සඳහා, ප්‍රශස්ත LINQ විමසුම ප්‍රශස්ත නොකළ එකට වඩා බොහෝ ගුණයකින් වේගවත් වේ.

සහ කොන්දේසි සහිත විකල්පය සඳහා, පෝරමයේ 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 ට Objects manipulations සඳහා ද මෙවැනි විමසුමක් වෙනුවට:

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

, සහ Para වර්ගය පහත පරිදි අර්ථ දක්වා ඇත:

පැරා වර්ගය අර්ථ දැක්වීම

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

මේ අනුව, අපි MS SQL Server වෙත LINQ විමසුම් ප්‍රශස්ත කිරීමේ දී සමහර පැති පරීක්ෂා කළෙමු.

අවාසනාවකට මෙන්, පළපුරුදු සහ ප්‍රමුඛ පෙළේ .NET සංවර්ධකයින් පවා ඔවුන් භාවිතා කරන උපදෙස් තිරය පිටුපස කරන්නේ කුමක්ද යන්න තේරුම් ගත යුතු බව අමතක කරයි. එසේ නොමැති නම්, ඔවුන් වින්‍යාස කරන්නන් බවට පත්වන අතර අනාගතයේදී මෘදුකාංග විසඳුම පරිමාණය කිරීමේදී සහ බාහිර පාරිසරික තත්ත්වයන්හි සුළු වෙනස්කම් සමඟ කාල බෝම්බයක් සවි කළ හැකිය.

කෙටි සමාලෝචනයක් ද සිදු කරන ලදී මෙහි.

පරීක්ෂණය සඳහා මූලාශ්ර - ව්යාපෘතියම, TEST දත්ත ගබඩාවේ වගු නිර්මාණය කිරීම මෙන්ම දත්ත සමඟ මෙම වගු පිරවීම පිහිටා ඇත මෙහි.
එසේම මෙම ගබඩාවේ, සැලසුම් ෆෝල්ඩරය තුළ, OR කොන්දේසි සහිත විමසුම් ක්රියාත්මක කිරීම සඳහා සැලසුම් ඇත.

මූලාශ්රය: www.habr.com

අදහස් එක් කරන්න