Beruflich Dokumente
Kultur Dokumente
WhyLINQbeatsSQL
LINQPad
Home
Download
Purchase
Upgrade
FAQ
Resources
Resellers
Support
WhyLINQbeatsSQL
Ifyou'renotaLINQaddict,youmightwonderwhatthefussisabout.SQLisn'tbroken,sowhyxit?
Whydoweneedanotherqueryinglanguage?
ThepopularansweristhatLINQisINtegratedwithC#(orVB),therebyeliminangtheimpedance
mismatchbetweenprogramminglanguagesanddatabases,aswellasprovidingasinglequerying
interfaceforamultudeofdatasources.Whilethat'strue,it'sonlypartofthestory.Moreimportantly:
whenitcomestoqueryingdatabases,LINQisinmostcasesasignicantlymoreproducvequerying
languagethanSQL.
ComparedtoSQL,LINQissimpler,dier,andhigherlevel.It'sratherlikecomparingC#toC++.Sure,
therearemeswhenit'ssllbesttouseC++(asisthecasewithSQL),butinmostsituaons,workingin
amoderndylanguageandnothavingtoworryaboutlowerleveldetailsisabigwin.
SQLisaveryoldlanguageinventedin1974.Sincethenit'sbeenextendedendlessly,butnever
redesigned.ThishasmadethelanguagemessyratherlikeVB6orVisualFoxPro.Youmighthave
becomesoaccustomedtothisthatyoucan'tseeanythingwrong!
Let'stakeanexample.Youwanttowriteasimplequerythatretrievescustomersasfollows:
SELECTUPPER(Name)
FROMCustomer
WHERENameLIKE'A%'
ORDERBYName
Thatdoesn'tlooktoobad,right?Butnowsupposetheseresultsarefeedingawebpage,andwewant
toretrievejustrows2130.Suddenly,youneedasubquery:
SELECTUPPER(Name)FROM
(
SELECT*,RN=row_number()
OVER(ORDERBYName)
FROMCustomer
WHERENameLIKE'A%'
)A
WHERERNBETWEEN21AND30
ORDERBYName
Andifyouneedtosupportolderdatabases(priortoSQLServer2005),itgetsworse:
http://www.linqpad.net/WhyLINQBeatsSQL.aspx
1/7
1/28/2017
WhyLINQbeatsSQL
SELECTTOP10UPPER(c1.Name)
FROMCustomerc1
WHERE
c1.NameLIKE'A%'
ANDc1.IDNOTIN
(
SELECTTOP20c2.ID
FROMCustomerc2
WHEREc2.NameLIKE'A%'
ORDERBYc2.Name
)
ORDERBYc1.Name
Notonlyisthiscomplicatedandmessy,butitviolatestheDRYprinciple(Don'tRepeatYourself).Here's
samequeryinLINQ.Thegaininsimplicityisclear:
varquery=
fromcindb.Customers
wherec.Name.StartsWith("A")
orderbyc.Name
selectc.Name.ToUpper();
varthirdPage=query.Skip(20).Take(10);
OnlywhenweenumeratethirdPagewillthequeryactuallyexecute.InthecaseofLINQtoSQLorEnty
Framework,thetranslaonenginewillconvertthequery(thatwecomposedintwosteps)intoasingle
SQLstatementopmizedforthedatabaseservertowhichit'sconnected.
Composability
Youmighthavenocedanothermoresubtle(butimportant)benetoftheLINQapproach.Wechoseto
composethequeryintwostepsandthisallowsustogeneralizethesecondstepintoareusable
methodasfollows:
IQueryable<T>Paginate<T>(thisIQueryable<T>query,intskip,inttake)
{
returnquery.Skip(skip).Take(take);
}
Wecanthendothis:
varquery=...
varthirdPage=query.Paginate(20,10);
Theimportantthing,here,isthatwecanapplyourPaginatemethodtoanyquery.Inotherwords,with
LINQyoucanbreakdownaqueryintoparts,andthenreusesomeofthosepartsacrossyour
applicaon.
Associaons
http://www.linqpad.net/WhyLINQBeatsSQL.aspx
2/7
1/28/2017
WhyLINQbeatsSQL
AnotherbenetofLINQisthatyoucanqueryacrossrelaonshipswithouthavingtojoin.Forinstance,
supposewewanttolistallpurchasesof$1000orgreatermadebycustomerswholiveinWashington.
Tomakeitinteresng,we'llassumepurchasesareitemized(theclassicPurchase/PurchaseItem
scenario)andthatwealsowanttoincludecashsales(withnocustomer).Thisrequiresqueryingacross
fourtables(Purchase,Customer,AddressandPurchaseItem).InLINQ,thequeryiseortless:
frompindb.Purchases
wherep.Customer.Address.State=="WA"||p.Customer==null
wherep.PurchaseItems.Sum(pi=>pi.SaleAmount)>1000
selectp
ComparethistotheSQLequivalent:
SELECTp.*
FROMPurchasep
LEFTOUTERJOIN
CustomercINNERJOINAddressaONc.AddressID=a.ID
ONp.CustomerID=c.ID
WHERE
(a.State='WA'||p.CustomerIDISNULL)
ANDp.IDin
(
SELECTPurchaseIDFROMPurchaseItem
GROUPBYPurchaseIDHAVINGSUM(SaleAmount)>1000
)
Extendingthisexample,supposewewanttosorttheresultsinreverseorderofprice,andalsowantthe
salesperson'snameandnumberofpurchaseditemsinthenalprojecon.Nocehownaturallywecan
expresstheseaddionalcriteriawithoutrepeon:
frompindb.Purchases
wherep.Customer.Address.State=="WA"||p.Customer==null
letpurchaseValue=p.PurchaseItems.Sum(pi=>pi.SaleAmount)
wherepurchaseValue>1000
orderbypurchaseValuedescending
selectnew
{
p.Description,
p.Customer.SalesPerson.Name,
PurchaseItemCount=p.PurchaseItems.Count()
}
Here'sthesamequeryinSQL:
SELECT
p.Description,
s.Name,
(SELECTCOUNT(*)FROMPurchaseItempiWHEREp.ID=pi.PurchaseID)
PurchaseItemCount
FROMPurchasep
LEFTOUTERJOIN
Customerc
INNERJOINAddressaONc.AddressID=a.ID
http://www.linqpad.net/WhyLINQBeatsSQL.aspx
3/7
1/28/2017
WhyLINQbeatsSQL
INNERJOINAddressaONc.AddressID=a.ID
LEFTOUTERJOINSalesPersonsONc.SalesPersonID=s.ID
ONp.CustomerID=c.ID
WHERE
(a.State='WA'ORp.CustomerIDISNULL)
ANDp.IDin
(
SELECTPurchaseIDFROMPurchaseItem
GROUPBYPurchaseIDHAVINGSUM(SaleAmount)>1000
)
ORDERBY
(SELECTSUM(SaleAmount)FROMPurchaseItempiWHEREp.ID=pi.PurchaseID)DESC
Aninteresngpointisthatit'spossibletotransliteratetheaboveSQLquerybackintoLINQ,yieldinga
querythat'severybitasrepeveandatleastasclumsy.You'lloensee(typicallynonworking
versionsof)suchqueriespostedonforumsthishappensasaresultofthinkinginSQL,ratherthan
thinkinginLINQ.It'sratherliketransliterangaFortranprogramintoC#6,andthencomplainingabout
theclumsysyntaxforGOTO.
ShapingData
SelecngfrommorethanonetableinSQLrequiresjoiningtheendresultbeingrowsofattuples.If
you'veusedSQLformanyyears,youmayhavebecomesoaccepngofthisthatitmaynotoccurtoyou
thatthisforceddenormalizaonisoenundesirable:itleadstodataduplicaonandmakesresultsets
awkwardtoworkwithontheclient.Incontrast,LINQletsyouretrieveshapedorhierarchicaldata.This
avoidsduplicaon,makesresultseasiertoworkwith,andinmostcasesitevenobviatestheneedfor
joining.Forexample,supposewewanttoretrieveaseleconofcustomers,eachwiththeirhighvalue
purchases.InLINQ,youcandothis:
fromcindb.Customers
wherec.Address.State=="WA"
selectnew
{
c.Name,
c.CustomerNumber,
HighValuePurchases=c.Purchases.Where(p=>p.Price>1000)
}
HighValuePurchases,here,isacollecon.Andbecausewewerequeryinganassociaonproperty,we
didn'tneedtojoin.Whichmeansthedetailofwhetherthiswasainnerorouterjoinisnicelyabstracted
away.Inthiscase,thequery,whentranslatedtoSQL,wouldbeanouterjoin:LINQdoesn'texcluderows
justbecauseasubcolleconreturnszeroelements.Ifwewantedsomethingthattranslatedtoaninner
join,wecoulddothis:
fromcindb.Customers
wherec.Address.State=="WA"
letHighValuePurchases=c.Purchases.Where(p=>p.Price>1000)
whereHighValuePurchases.Any()
selectnew
{
c.Name,
c.CustomerNumber,
http://www.linqpad.net/WhyLINQBeatsSQL.aspx
4/7
1/28/2017
WhyLINQbeatsSQL
c.CustomerNumber,
HighValuePurchases
}
LINQalsosupportsatouterjoins,adhocjoins,subqueries,andnumerousotherkindsofqueries
througharichsetofoperators.
Parameterizaon
Whatifwewantedtoparameterizeourpreviousexample,sothatthestate"WA"camefromavariable?
Thisisallwedo:
stringstate="WA";
varquery=
fromcindb.Customers
wherec.Address.State==state
...
NomessingwithparametersonDbCommandobjectsorworryingaboutSQLinjeconaacks.LINQ's
parameterizaonisinline,typesafe,andhighlyreadable.Itdoesn'tjustsolvetheproblemitsolvesit
reallywell.
AsbecauseLINQqueriesarecomposable,wecanaddpredicatescondionally.Forexample,wecould
writeamethodasfollows:
IQueryable<Customer>GetCustomers(stringstate,decimal?minPurchase)
{
varquery=Customers.AsQueryable();
if(state!=null)
query=query.Where(c=>c.Address.State==state);
if(minPurchase!=null)
query=query.Where(c=>c.Purchases.Any(p=>p.Price>
minPurchase.Value));
returnquery;
}
IfwecallthismethodwithnullstateandminPurchasevalues,thefollowingSQLisgeneratedwhenwe
enumeratetheresult:
SELECT[t0].[ID],[t0].[Name],[t0].[AddressID]
FROM[Customer]AS[t0]
However,ifwespecifyvaluesforstateandminPurchase,LINQtoSQLwillnotonlyaddthepredicatesto
thequery,butthenecessaryjoinsaswell:
SELECT[t0].[ID],[t0].[Name],[t0].[AddressID]
FROM[Customer]AS[t0]
LEFTOUTERJOIN[Address]AS[t1]ON[t1].[ID]=[t0].[AddressID]
http://www.linqpad.net/WhyLINQBeatsSQL.aspx
5/7
1/28/2017
WhyLINQbeatsSQL
LEFTOUTERJOIN[Address]AS[t1]ON[t1].[ID]=[t0].[AddressID]
WHERE(EXISTS(
SELECTNULLAS[EMPTY]
FROM[Purchase]AS[t2]
WHERE([t2].[Price]>@p0)AND([t2].[CustomerID]=[t0].[ID])
))AND([t1].[State]=@p1)
BecauseourmethodreturnsanIQueryable,thequeryisnotactuallytranslatedtoSQLandrununl
enumerated.Thisgivethecallerachancetoaddfurtherpredicates,paginaon,customprojecons,and
soon.
StacTypeSafety
Intheprecedingqueries,ifwehaddeclaredthestatevariableasanintegerratherthanastring,the
querywouldfailatcompilemeratherthanrunme.Thesameappliesifyougetanyofthetableor
columnnameswrong.Thisisofrealbenetwhenrefactoring:thecompilerwilltellyouifyouhaven't
completelydoneyourjob.
ClientProcessing
LINQletsyoueortlesslyshipartofthequeryontotheclientforprocessing.Whywouldyouwantto
dothis?Withaheavilyburdeneddatabaseserver,itcanactuallyimproveperformance.Aslongasyou
don'ttakemoredatathanyouneed(inotherwords,youslldoallthelteringontheserver)youcan
oenhelpperformancebyshiingsomeoftheburdenofreordering,transformingandregroupingthe
resultsontoalessloadedapplicaonserver.WithLINQ,allyouneedtodoistoslipAsEnumerable()into
thequery,andeverythingfromthatpointonexecuteslocally.
WhennottouseLINQforqueryingdatabases
Despiteitspower,LINQdoesn'tdeprecateSQL.Ittakesmorethan95%ofthequeryingbrunt,butyou
sllsomemesneedSQLfor:
Handtweakedqueries(especiallywithopmizaonorlockinghints)
Queriesthatinvolveselecngintotemporarytables,thenqueryingthosetables
Predicatedupdatesandbulkinserts
AndofcourseyousllneedSQLfortriggers.(SQL'salsoneededforstoredproceduresandfuncons,
althoughtheneedforthesecropsuplessoenwhenyou'reusingLINQ).YoucancombineLINQwith
SQLbywringtablevaluedfunconsinSQL,andthencallingthosefunconswithinmoreelaborate
LINQqueries.
Havingtoknowtwoqueryinglanguagesisnotreallyanissuebecauseyou'llwanttolearnLINQanyway
LINQissousefulforqueryinglocalcolleconsandXMLDOMs.Ifyou'resllusingtheold
XmlDocumentbasedDOM,you'llndLINQtoXML'sDOMadramacstepup.
AndbecauseLINQiseasiertomasterthanSQL,thegoalofwringreallygoodqueriesismore
achievablewithLINQthanwithSQL.
LINQintheField
http://www.linqpad.net/WhyLINQBeatsSQL.aspx
6/7
1/28/2017
WhyLINQbeatsSQL
IuseLINQalmostexclusivelyforqueryingdatabasesbecauseit'smoreproducve.
Forwringapplicaons,mypersonalexperienceisaLINQenableddataaccesslayer(usinganAPIsuch
asLINQtoSQLorEntyFramework)cutsthedataaccessdevelopmentmebymorethanahalf,aswell
asmakingmaintenancefareasier.
follow@linqpad
ViewonTwier
LucasTeixeira
@Flash3001
FollowLINQPadonFacebook
@Nick_Craver@linqpadwaybetterthanopening
Ildasm
Like
Share 2.7Kpeoplelikethis.Bethefirstof
yourfriends.
MakeaFeatureRequest
VisittheLINQPadForum
ContactCustomerSupport
JosephAlbahari20072017
http://www.linqpad.net/WhyLINQBeatsSQL.aspx
7/7