ശക്തമായ ഒരു പുതിയ ഡാറ്റ കൃത്രിമ ഭാഷയായി LINQ .NET നൽകി. LINQ to 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 ദശലക്ഷത്തിലധികം വരി ഡാറ്റയുണ്ട്.
ഇപ്പോൾ വിഷ്വൽ സ്റ്റുഡിയോയിൽ നിങ്ങൾ ഒരു ടെസ്റ്റ് വിഷ്വൽ സി# കൺസോൾ ആപ്പ് (.NET ഫ്രെയിംവർക്ക്) പ്രോജക്റ്റ് സൃഷ്ടിക്കേണ്ടതുണ്ട്:
അടുത്തതായി, ഡാറ്റാബേസുമായി സംവദിക്കാൻ എന്റിറ്റി ഫ്രെയിംവർക്കിനായി നിങ്ങൾ ഒരു ലൈബ്രറി ചേർക്കേണ്ടതുണ്ട്.
ഇത് ചേർക്കുന്നതിന്, പ്രോജക്റ്റിൽ വലത്-ക്ലിക്കുചെയ്ത് സന്ദർഭ മെനുവിൽ നിന്ന് NuGet പാക്കേജുകൾ നിയന്ത്രിക്കുക തിരഞ്ഞെടുക്കുക:
തുടർന്ന്, ദൃശ്യമാകുന്ന NuGet പാക്കേജ് മാനേജുമെന്റ് വിൻഡോയിൽ, തിരയൽ വിൻഡോയിൽ "Entity Framework" എന്ന വാക്ക് നൽകി Entity Framework പാക്കേജ് തിരഞ്ഞെടുത്ത് അത് ഇൻസ്റ്റാൾ ചെയ്യുക:
അടുത്തതായി, App.config ഫയലിൽ, configSections ഘടകം അടച്ചതിനുശേഷം, നിങ്ങൾ ഇനിപ്പറയുന്ന ബ്ലോക്ക് ചേർക്കേണ്ടതുണ്ട്:
<connectionStrings>
<add name="DBConnection" connectionString="data source=ИМЯ_ЭКЗЕМПЛЯРА_MSSQL;Initial Catalog=TEST;Integrated Security=True;" providerName="System.Data.SqlClient" />
</connectionStrings>
connectionString-ൽ നിങ്ങൾ കണക്ഷൻ സ്ട്രിംഗ് നൽകേണ്ടതുണ്ട്.
ഇപ്പോൾ നമുക്ക് പ്രത്യേക ഫയലുകളിൽ 3 ഇന്റർഫേസുകൾ സൃഷ്ടിക്കാം:
- IBaseEntityID ഇന്റർഫേസ് നടപ്പിലാക്കുന്നു
namespace TestLINQ { public interface IBaseEntityID { int ID { get; set; } } }
- IBaseEntityName ഇന്റർഫേസ് നടപ്പിലാക്കൽ
namespace TestLINQ { public interface IBaseEntityName { string Name { get; set; } } }
- 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; }
}
}
അടുത്തതായി, ഞങ്ങൾ ഞങ്ങളുടെ രണ്ട് എന്റിറ്റികൾ വെവ്വേറെ ഫയലുകളിൽ സൃഷ്ടിക്കും:
- റെഫ് ക്ലാസ് നടപ്പിലാക്കൽ
using System.ComponentModel.DataAnnotations.Schema; namespace TestLINQ { [Table("Ref")] public class Ref : BaseEntity { public int ID2 { get; set; } } }
- കസ്റ്റമർ ക്ലാസ് നടപ്പിലാക്കൽ
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 സെർവറിനായി EF വഴി LINQ മുതൽ SQL വരെയുള്ള ഒപ്റ്റിമൈസേഷൻ ടെസ്റ്റുകൾ നടത്തുന്നതിന് ഞങ്ങൾക്ക് ഒരു റെഡിമെയ്ഡ് പരിഹാരം ലഭിച്ചു:
ഇപ്പോൾ 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 സെക്കൻഡിൽ കൂടുതലായതിനാൽ എക്സിക്യൂഷൻ ഒരു പിശകോടെ ക്രാഷ് ചെയ്യും:
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 ചോദ്യങ്ങളിൽ ഒരു ചേരൽ വ്യവസ്ഥ മാത്രമേ ഉണ്ടാകൂ, അതിനാൽ ഇവിടെ ഓരോ വ്യവസ്ഥയ്ക്കും രണ്ട് ചോദ്യങ്ങൾ ഉപയോഗിച്ച് തുല്യമായ ഒരു അന്വേഷണം നടത്താനും തുടർന്ന് വരികൾക്കിടയിലുള്ള തനിപ്പകർപ്പുകൾ നീക്കം ചെയ്യുന്നതിനായി യൂണിയൻ വഴി സംയോജിപ്പിക്കാനും കഴിയും.
അതെ, പൂർണ്ണമായ ഡ്യൂപ്ലിക്കേറ്റ് വരികൾ തിരികെ നൽകിയേക്കാമെന്നത് കണക്കിലെടുത്ത്, ചോദ്യങ്ങൾ പൊതുവെ തുല്യമല്ല. എന്നിരുന്നാലും, യഥാർത്ഥ ജീവിതത്തിൽ, പൂർണ്ണമായ ഡ്യൂപ്ലിക്കേറ്റ് ലൈനുകൾ ആവശ്യമില്ല, ആളുകൾ അവ ഒഴിവാക്കാൻ ശ്രമിക്കുന്നു.
ഇനി നമുക്ക് ഈ രണ്ട് ചോദ്യങ്ങളുടെ എക്സിക്യൂഷൻ പ്ലാനുകൾ താരതമ്യം ചെയ്യാം:
- CROSS JOIN-ന് വേണ്ടിയുള്ള ശരാശരി നിർവ്വഹണ സമയം 195 സെക്കൻഡ് ആണ്:
- INNER JOIN-UNION-ന്റെ ശരാശരി എക്സിക്യൂഷൻ സമയം 24 സെക്കൻഡിൽ താഴെയാണ്:
ഫലങ്ങളിൽ നിന്ന് നിങ്ങൾക്ക് കാണാനാകുന്നതുപോലെ, ദശലക്ഷക്കണക്കിന് റെക്കോർഡുകളുള്ള രണ്ട് ടേബിളുകൾക്കായി, ഒപ്റ്റിമൈസ് ചെയ്ത 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 സെക്കൻഡിനുള്ളിൽ പ്രവർത്തിക്കും:
ഇതുപോലുള്ള ഒരു അന്വേഷണത്തിനുപകരം LINQ to Objects കൃത്രിമത്വത്തിനും:
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;
}
അങ്ങനെ, MS SQL സെർവറിലേക്ക് LINQ അന്വേഷണങ്ങൾ ഒപ്റ്റിമൈസ് ചെയ്യുന്നതിനുള്ള ചില വശങ്ങൾ ഞങ്ങൾ പരിശോധിച്ചു.
നിർഭാഗ്യവശാൽ, പരിചയസമ്പന്നരും മുൻനിര .NET ഡെവലപ്പർമാർ പോലും അവർ ഉപയോഗിക്കുന്ന നിർദ്ദേശങ്ങൾ തിരശ്ശീലയ്ക്ക് പിന്നിൽ എന്താണ് ചെയ്യുന്നതെന്ന് മനസിലാക്കേണ്ടതുണ്ടെന്ന് മറക്കുന്നു. അല്ലാത്തപക്ഷം, അവ കോൺഫിഗറേറ്റർമാരാകുകയും ഭാവിയിൽ സോഫ്റ്റ്വെയർ സൊല്യൂഷൻ സ്കെയിൽ ചെയ്യുമ്പോഴും ബാഹ്യ പാരിസ്ഥിതിക സാഹചര്യങ്ങളിൽ ചെറിയ മാറ്റങ്ങളോടെയും ടൈം ബോംബ് സ്ഥാപിക്കുകയും ചെയ്യും.
ഹ്രസ്വ അവലോകനവും നടത്തി
ടെസ്റ്റിനുള്ള ഉറവിടങ്ങൾ - പ്രോജക്റ്റ് തന്നെ, ടെസ്റ്റ് ഡാറ്റാബേസിൽ പട്ടികകൾ സൃഷ്ടിക്കുന്നതും ഡാറ്റ ഉപയോഗിച്ച് ഈ പട്ടികകൾ പൂരിപ്പിക്കുന്നതും സ്ഥിതിചെയ്യുന്നു.
ഈ റിപ്പോസിറ്ററിയിൽ, പ്ലാൻസ് ഫോൾഡറിൽ, അല്ലെങ്കിൽ വ്യവസ്ഥകളോടെയുള്ള അന്വേഷണങ്ങൾ നടപ്പിലാക്കുന്നതിനുള്ള പ്ലാനുകൾ ഉണ്ട്.
അവലംബം: www.habr.com