Hin aliyên xweşbînkirina pirsên LINQ di C#.NET de ji bo MS SQL Server

LINQ ket .NET wekî zimanekî nû yê manîpulasyona daneyê. LINQ ji SQL re wekî beşek wê dihêle hûn bi DBMS-ê re bi hêsanî têkilî daynin, mînakî, Entity Framework. Lêbelê, pir caran wê bikar tînin, pêşdebiran ji bîr dikin ku lê binerin ka peydakarê pirsê, di doza we de Entity Framework, dê çi cûre pirsnameya SQL-ê çêbike.

Werin em bi mînakekê li du xalên sereke binêrin.
Ji bo vê yekê, di SQL Server de databasek Testê biafirînin, û du tabloyên tê de bi karanîna pirsa jêrîn biafirînin:

Çêkirina tabloyan

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

Naha em tabloya Ref bi xebitandina skrîpta jêrîn dagirin:

Dagirtina tabloya 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

Werin em bi heman rengî tabloya Xerîdar bi karanîna skrîpta jêrîn dagirin:

Nifûsa sifrê Mişterî

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

Bi vî rengî, me du tablo wergirtin, yek ji wan ji 1 mîlyon rêzên daneyê, û ya din jî ji 10 mîlyon rêzên daneyê zêdetir e.

Naha di Visual Studio de hûn hewce ne ku projeyek testa Visual C# Console App (.NET Framework) biafirînin:

Hin aliyên xweşbînkirina pirsên LINQ di C#.NET de ji bo MS SQL Server

Dûv re, hûn hewce ne ku pirtûkxaneyek ji bo Entity Framework zêde bikin ku bi databasê re têkilî daynin.
Ji bo lê zêde bikin, li ser projeyê rast-klîk bikin û ji menuya çarçoveyê ve Pakêtên NuGet Birêvebirin hilbijêrin:

Hin aliyên xweşbînkirina pirsên LINQ di C#.NET de ji bo MS SQL Server

Dûv re, di pencereya rêveberiya pakêtê ya NuGet de ku xuya dibe, di pencereya lêgerînê de peyva "Entity Framework" têkevin û pakêta Entity Framework hilbijêrin û saz bikin:

Hin aliyên xweşbînkirina pirsên LINQ di C#.NET de ji bo MS SQL Server

Piştre, di pelê App.config de, piştî girtina hêmana configSections, hûn hewce ne ku bloka jêrîn lê zêde bikin:

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

Di connectString de hûn hewce ne ku têkevin rêzika girêdanê.

Naha em 3 navberan di pelên cûda de biafirînin:

  1. Pêkanîna pêwendiya IBaseEntityID
    namespace TestLINQ
    {
        public interface IBaseEntityID
        {
            int ID { get; set; }
        }
    }
    

  2. Pêkanîna pêwendiya IBaseEntityName
    namespace TestLINQ
    {
        public interface IBaseEntityName
        {
            string Name { get; set; }
        }
    }
    

  3. Pêkanîna pêwendiya IBaseNameInsertUTCDate
    namespace TestLINQ
    {
        public interface IBaseNameInsertUTCDate
        {
            DateTime InsertUTCDate { get; set; }
        }
    }
    

Û di pelek veqetandî de em ê ji bo du hebûnên xwe çîna bingehîn BaseEntity biafirînin, ku dê qadên hevpar tê de bin:

Pêkanîna çîna bingehîn BaseEntity

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

Piştre, em ê du saziyên xwe di pelên cuda de biafirînin:

  1. Pêkanîna çîna Ref
    using System.ComponentModel.DataAnnotations.Schema;
    
    namespace TestLINQ
    {
        [Table("Ref")]
        public class Ref : BaseEntity
        {
            public int ID2 { get; set; }
        }
    }
    

  2. Pêkanîna çîna Mişterî
    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; }
        }
    }
    

Naha em di pelek cûda de çarçoveyek UserContext biafirînin:

Pêkanîna çîna 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; }
    }
}

Me çareseriyek amade ji bo pêkanîna ceribandinên xweşbîniyê bi LINQ ber SQL bi EF ji bo MS SQL Server wergirt:

Hin aliyên xweşbînkirina pirsên LINQ di C#.NET de ji bo MS SQL Server

Naha koda jêrîn têxe pelê Program.cs:

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

Paşê, em projeya xwe bidin destpêkirin.

Di dawiya xebatê de, jêrîn dê li ser konsolê were xuyang kirin:

SQL Query hate çêkirin

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

Ango, bi gelemperî, pirsa LINQ pirsek SQL ji MS SQL Server DBMS re pir baş çêkir.

Naha em di pirsa LINQ de şerta AND-ê biguhezînin OR:

Lêpirsîna 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 };

Û em dîsa serlêdana xwe bidin destpêkirin.

Ji ber ku dema pêkanîna fermanê ji 30 çirkeyan zêdetir e, înfaz dê bi xeletiyek têk bibe:

Hin aliyên xweşbînkirina pirsên LINQ di C#.NET de ji bo MS SQL Server

Ger hûn li pirsa ku ji hêla LINQ ve hatî çêkirin binêrin:

Hin aliyên xweşbînkirina pirsên LINQ di C#.NET de ji bo MS SQL Server
, wê hingê hûn dikarin piştrast bikin ku hilbijartin bi riya hilbera Kartezî ya du koman (tabloyan) pêk tê:

SQL Query hate çêkirin

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]

Ka em pirsa LINQ bi vî rengî ji nû ve binivîsin:

Pirsa LINQ-ya xweşbînkirî

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

Dûv re em pirsa SQL ya jêrîn digirin:

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

Mixabin, di pirsnameyên LINQ de tenê yek şertek tevlêbûnê dikare hebe, ji ber vê yekê li vir gengaz e ku ji bo her şertê du lêpirsînan bi karanîna pirsek wekhev were çêkirin û dûv re wan bi navgîniya Yekîtiyê ve berhev bike da ku dubareyên di nav rêzan de jê bibe.
Erê, pirs dê bi gelemperî ne-hevhev bin, li ber çavan ku rêzên dubare yên bêkêmasî dikarin werin vegerandin. Lêbelê, di jiyana rast de, rêzikên bêkêmasî yên dubare ne hewce ne û mirov hewl didin ku ji wan xilas bibin.

Naha em pîlanên pêkanîna van her du pirsan bidin ber hev:

  1. ji bo CROSS JOIN dema darvekirinê ya navîn 195 saniye ye:
    Hin aliyên xweşbînkirina pirsên LINQ di C#.NET de ji bo MS SQL Server
  2. ji bo INNER JOIN-UNION dema darvekirinê ya navîn ji 24 çirkeyan kêmtir e:
    Hin aliyên xweşbînkirina pirsên LINQ di C#.NET de ji bo MS SQL Server

Wekî ku hûn ji encaman dikarin bibînin, ji bo du tabloyên bi mîlyonan tomar, pirsa LINQ-ya xweşbînkirî gelek caran ji ya neoptimîzekirî zûtir e.

Ji bo vebijarka bi AND di şert û mercan de, pirsek LINQ ya formê:

Lêpirsîna 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 };

Pirsa SQL ya rast hema hema her dem dê were çêkirin, ku dê bi navînî di 1 çirkeyê de bimeşe:

Hin aliyên xweşbînkirina pirsên LINQ di C#.NET de ji bo MS SQL Server
Di heman demê de ji bo LINQ to Objects manîpulasyonên li şûna pirsek mîna:

Pirsa LINQ (vebijarka 1emîn)

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

hûn dikarin pirsek wekî bikar bînin:

Pirsa LINQ (vebijarka 2emîn)

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

derê

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

, û tîpa Para bi vî rengî tê destnîşankirin:

Para Type Definition

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

Bi vî rengî, me di xweşbînkirina pirsên LINQ-ê de ji MS SQL Server re hin alî lêkolîn kir.

Mixabin, tewra pêşdebirên .NET yên xwedî tecrûbe û pêşeng ji bîr dikin ku ew hewce ne ku fêm bikin ka talîmatên ku ew bikar tînin li pişt perdê çi dikin. Wekî din, ew dibin mîhengker û dikarin di pêşerojê de hem dema ku çareseriya nermalavê pîvandin û hem jî bi guhertinên piçûk ên di şert û mercên hawîrdorê yên derve de bombeyek demkî biçînin.

Her wiha nirxandineke kurt jî hat kirin vir.

Çavkaniyên ji bo ceribandinê - proje bixwe, çêkirina tabloyan di databasa TEST de, û her weha dagirtina van tabloyan bi daneyan hene. vir.
Di heman demê de di vê depoyê de, di peldanka Plans de, ji bo pêkanîna pirsnameyên bi şert û mercên OR re plan hene.

Source: www.habr.com

Add a comment