From ca03be64e6188594bdf65820d8f3c22c149dddc5 Mon Sep 17 00:00:00 2001 From: Apress Date: Sun, 16 Oct 2016 17:19:51 +0100 Subject: [PATCH] First commit --- 9781430245964.jpg | Bin 0 -> 11060 bytes LICENSE.txt | 27 + .../9781430245964_Scripts.docx | Bin 0 -> 72357 bytes ...45964_Natarajan_Ch05_StroredProcedures.sql | 618 ++++++++++++++ .../Ch06/ch06.sql | 476 +++++++++++ ...nTableExpressionsAndWindowingFunctions.sql | 417 ++++++++++ .../Ch10/ch10.sql | 772 ++++++++++++++++++ ...30245964_Natarajan_Ch12_XQueryandXPath.sql | 502 ++++++++++++ .../Ch14/ApressExamples/ApressExamples.sln | 22 + .../Ch14/ApressExamples/ApressExamples.suo | Bin 0 -> 27136 bytes .../ApressExamples/ApressExamples.csproj | 79 ++ .../ApressExamples/ApressExamples.csproj.user | 10 + .../ApressExamples/ApressExamples.sqlproj | 56 ++ .../ApressExamples.sqlproj.user | 3 + .../ApressExamples/ApressExamples/Complex.cs | 245 ++++++ .../ApressExamples/EmailAddressTrigger.cs | 63 ++ .../ApressExamples/ApressExamples/EmailUDF.cs | 35 + .../ApressExamples/GetEnvironmentVars.cs | 43 + .../ApressExamples/ApressExamples/Median.cs | 101 +++ .../ApressExamples/PostDeployScript.sql | 1 + .../ApressExamples/PreDeployScript.sql | 1 + .../ApressExamples/Properties/AssemblyInfo.cs | 31 + .../ApressExamples/ApressExamples/Range.cs | 57 ++ .../ApressExamples/Test Scripts/Test.sql | 34 + .../ApressExamples/ApressExamples/YahooRSS.cs | 44 + .../Ch14/ch14.sql | 149 ++++ .../Ch15/ch15-01.cs | 52 ++ .../Ch15/ch15-02.cs | 58 ++ .../Ch15/ch15-03.cs | 61 ++ .../Ch15/ch15-04.cs | 9 + .../Ch15/ch15-05.cs | 6 + .../Ch15/ch15-06.cs | 68 ++ .../Ch15/ch15-07.sql | 12 + .../Ch15/ch15-08.cs | 104 +++ .../Ch15/ch15-09.sql | 10 + .../Ch15/ch15-10.cs | 109 +++ .../Ch15/ch15-12.cs | 30 + .../Ch15/ch15-18.cs | 41 + .../Ch15/ch15-20.sql | 5 + .../Ch15/ch15-21.cs | 33 + .../Ch15/ch15-23.cs | 25 + .../Ch15/ch15-24.cs | 31 + .../Ch16/ch16-01.cs | 45 + .../Ch16/ch16-02.cs | 13 + .../Ch16/ch16-03.cs | 13 + .../Ch16/ch16-04.java | 49 ++ .../Ch16/ch16-05.cs | 22 + .../Ch16/ch16-06.cs | 70 ++ ...45964_Natarajan_Ch18_PerformanceTuning.sql | 364 +++++++++ README.md | 15 + contributing.md | 14 + 51 files changed, 5045 insertions(+) create mode 100644 9781430245964.jpg create mode 100644 LICENSE.txt create mode 100644 Pro T-SQL 2012 Programmer's Guide/9781430245964_Scripts.docx create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch05/9781430245964_Natarajan_Ch05_StroredProcedures.sql create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch06/ch06.sql create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch08/9781430245964_Natarajan_Ch08_CommonTableExpressionsAndWindowingFunctions.sql create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch10/ch10.sql create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch12/9781430245964_Natarajan_Ch12_XQueryandXPath.sql create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples.sln create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples.suo create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/ApressExamples.csproj create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/ApressExamples.csproj.user create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/ApressExamples.sqlproj create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/ApressExamples.sqlproj.user create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/Complex.cs create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/EmailAddressTrigger.cs create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/EmailUDF.cs create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/GetEnvironmentVars.cs create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/Median.cs create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/PostDeployScript.sql create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/PreDeployScript.sql create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/Properties/AssemblyInfo.cs create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/Range.cs create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/Test Scripts/Test.sql create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/YahooRSS.cs create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch14/ch14.sql create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-01.cs create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-02.cs create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-03.cs create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-04.cs create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-05.cs create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-06.cs create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-07.sql create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-08.cs create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-09.sql create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-10.cs create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-12.cs create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-18.cs create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-20.sql create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-21.cs create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-23.cs create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-24.cs create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch16/ch16-01.cs create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch16/ch16-02.cs create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch16/ch16-03.cs create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch16/ch16-04.java create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch16/ch16-05.cs create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch16/ch16-06.cs create mode 100644 Pro T-SQL 2012 Programmer's Guide/Ch18/9781430245964_Natarajan_Ch18_PerformanceTuning.sql create mode 100644 README.md create mode 100644 contributing.md diff --git a/9781430245964.jpg b/9781430245964.jpg new file mode 100644 index 0000000000000000000000000000000000000000..400a66cca4de4488c8a11d8083f3bec4118d1105 GIT binary patch literal 11060 zcmbWd2UHWm_b98&^x|KHCsI8^_VK>~0P?O%j{)H?~H`;WW;qW@P}j4~jO|H7C2 zFIoG4WDLe%9{)T+zL*=VP2gX@w+KKMz`c7Hd>0273*KZ!AjJjY0)1GZhrk_D5Ed!uwhLgwP{hIX&fgCD*8#eNg^hC;jEjd)fXPrz z0^9*%Vco&T!ok7*+n7O^_W(924%q{KnY-j#FToGpDIjm+zu~ext7xax9z9?cu=EJU z!@o~OO+(A}h@FFzOHfGosfehU>~lGJ1w|!g9bG+r14AQYD{C8DJA0Uernub+QF z*xT@k$an9f5)wZpeNO)JH6=SIH!r`Su&DTFWmR=eZC!msM`u@ePjBC^{xSIY#N^cU z%e~7Sa&v3@@aXvD^z8iN^6D=y5P{EtoJ^Z#rb+`N1EVA)f0HxEwDcj98Q0@ zXHijfUB1V;c9jANQZqw$Pbl63an6f3mkxd--X88ZU%hXE*B^OdMC(E3iJ&VbRs^6<@}%53Ll9J<8fl4)*mFjGffqo~y4LSCr#>hAC* zzSgp{Tju56-f_N{+Zh;OqjG`lw zt^3ut0Im5#@-c5yuy)riFkBxr*z@aD(E2I`aSObKn%x4KOxN#PSJfHsO=(=$7hd(Z zuBsr$FjXX-(i>c(lII3_{;AFWg{;sZnR5={Jve|C3YEvB0fVbext;|R)F+ur8VjGr zJKT(5a51gHMrr%-pNg?j0QLW%tt1CL(J0DcA0N|_CP>`2$LR1$^A08xCr1{sts^`W z-K_91?)17FQO7rya$r^ zxqqvgT59JrmxkUs3beZgO0|N@4Y@mumj#Y-)+_q1cZP3)nrh<93#Rkk;EvecQdx9h zEb6Rv+2E8{HXW&(Qrmi-@quzE*V+}LR`ko9;%rs$qkpl}2N!`#B&vQs^Ps(4{J*NF zXhj4>UZ4LHtqA(y7H}S|Zr*P?VQZLR2(7&I2lG%Wf@{R#9~bhF>X3ZN$bM$Rytjr!RS8WI?7R&i?&HN^O;|dGWdabe{BS^shq`+dL(mqGE#8Q>rW% zOKyQkNpN=q!Wj10nQ1_1NN0}ykZJ2;DsAkuEox9>d?TTeLrd4HKbDH=<7Y^DmBTqGS^B0OHVQSuP?b2kj$w-?)s#^r~%0N>dt zT4xd*b`438x4{e_c`BDujyk70ViEf*Ok?*%PGtm4fmd!IaD_wqN#WQj`IX?5O|$l=Cq9-*5jT zL;*VEW7-+{2_g6#c|b)Q_6-M!qiAf*ck|0tn(kJ91?mWNLX(R_%WWh$4Sw9*O0V~2 z_eJ#jL)DnCG})^aZwvPzg@ck24u6cFONV{{?>Z2#$@#$#P`cuQjf1kP*MAw6KL9M zFZzJX^bvZrxI8ju90Rs<@N;jjC=0ed7&-(MjZm&6dtjk6Y@9CGEc6Uw_*~PS_7h#c ztIn>TY@^HNbR?=*@^#gE(oyNgQ!Fg@luVl^x4_itjcIjsc1`5^A{@E#r-jna&B!<_ z&Yy(WKhLm->LzI=-%u0sr}n)y0#z7ADBrr^8Zb=Q#xd6k4j~4v;9>Q{L+X3f$WmTQ z)Gc5!b$P!M_i@fmutiLHwn3NM+hr_yjdOWh*MmIH&#&GR$1d~%T~?@^g+e@fXYqNT z1JS$z=k_7c(1>5owA4S;F_A`(#2)>{nkzp`wHc*-z3)T-Kj)`pI)`i11^Vc;h%884 z?~&NbpD<471g>TBC5|KnY8r(w2^7j69bXro@wT4c1b>m}U>d;a5uW|M+#jJ)9se#+ z+?6jj_1wyd8!cp3<^1@J>Bj?|qpmLxa-j<%n;blAe7~5};)pFuA0$Y3y{Gdnl5FNU z)85BmOq`PUj0JXLl;X`yJW@$Twm7xeI2X|(sANiJGEQ<1Zn_1MR2{3T4tgUJWk-00f(&$O>n5?@9z9Tqft1&# z9mgo_$T>Z?&s`4+Vo584yuAiMz?qM#LJzb$V40^beEaKxvW1@@IVo&r~kGX(W0QA|h>`E3mc zxGj~*fU~l2cJ^?Wd~^3V6|ZqPh8c)0VGw{m4z#}zLSEjYz^W5S94 z+{Obp1I(y_9=z3ItZTLkgBP?Qf zYOLwxNX;k(y}dCuITjcZ7^vBSJH$_Il^9!8<|NOc@7D5bey>3YK3=GiB=9q{E2Zid zxaV!AYWC~i`dONd`LAD@JsC_R??9B6GReq0ml$bVB&6j{xyHqGk$n~%@sEs4SktnFkQ!UxI% zGs-Mk6?S7E`L7*^&Bl4gx`!$_Nc+(rj%S##4C56}LNa{6%Okbgb z<;6?d6RTT5W8@atw5)gkl&-8k=3dhvPN_C)!kD^pk9^rTLP(}yTR$Ll&fik{VS)j* zXs_s5IlInz#Rz%ym4W-ApI|0QJgJJfQCD#>M2TnmPJQBXD@mc{EKx26VS;mJ>{vYn zAxlh~#3j6zc%HciGkPkVfT)1`@pd$*ZG;zIWOlheJ=061G<;}D_|}Rwei-v5Mp6 zj}S+`JD!TYbLw0>tQ|ich*L|rXF(2L zk5R=w!-Wo2e=nX&_b;mcbucqy#=WzM9QpJ~SFcvMiG0~HNVc~Y%{cW9hLfi4LfWUp z^F51?C5^=MVoKbX%w+p2yh{4J+uSeOJD%bC%$_Hd+bNbj7L2F1S|6`G>aXINE(|_z zgVF3)<bC0nObOCK1Z`RJjNM{}mDlusJU1BUxH&m8*I9Bp?mDNmV* zHVrb!QoCBn>zeqCr4S`1IiInjGc6&+ZVF?Sf=~j-7B)v1&-WP~>!5PKHEccby_mHa zKgy9;LR#$MuPhwqdPY5no7ACs9xdb(LMmnkvNd7ajZvdzEBEfGwo?lbery&k`=Z+= zFI4o*z%4-3F~`i+?@OzNhq^sS1&@d4Nw=huoDaP!g)<+<$7;U4aG9bOB~%`SGs#HuZQ3L zS&Cw&6Tt`5X8=0}at7t$+2emw)YY$M=b6e@l6madTxGvaT?^!!__<7Vq3IjzQq__= zlqlN=6cVG6Gj+^p?_us&O;2BYnq|Jrwjg-L_v4^zjK$%pI%_-y(sj5wc7*;(;1orx z7qJVd)T2MKNLgKd=WFhw-e^>nu8r#hB|E~4=|2$RE`41 z6Ppe`;Wujjt>)@ml-P>)qy*ex50byHoQ{+N5{mEkLqu;W}zg^u~nY? zNE`f&B-}oKrF3r6J5$`vM|JyT|E{q^@V!0nM)`(RomwCLT5bGgL%a1J&mYKA*H^Ul z7(cO^_r+kZvZ048(o(6;ie}S2ZKduvxGSYKqKq|JrB2VteM9!)oopq`dP($7{?G29cayqG|D7;#p3N-G`mAKCr1vOO0}tc@v1#sB2(S{ z&0@;x2?Ft(@rrw1%oC@}3ct;pp`D@qq^uHsKl6QUm#T4ae_PA6A_RmGU6q=kj()`T zB)fAKUUMT@iRx7^W$)ki5`z+L;}yPgokV^NlmGl1|Az!aI0ZUu3a1FxP&nl+Y{qq& zRJLyO(rJyv^oWS7D}%EkvO788>>~^(Z0><9joB&~I}zc|lBRcf$lKj|d33@@TJXZWYBt+s3S!pjfq( zd1eR=(Sd#=2GHH>&6YslfVlux1ZO4*bUQI$})gJUyozJg8n*=GTh$r*&6qgf#@v1}bts_1R0Y(^@hz(YOd) z3DVcp=hW>bh4wt7Tt#Cq8E|ItCYNto)iu2hUhOE`k>Z$?kR|k)`YFTk-HBSpC6y$8 zB*Vi|jK+L&gWwCUk@K1n$&bD{A_dB7ddcU-LjlS`;xOyyNV;wXYp+(aOh)mqty^Qf zH!0Ok;*OZ9wmhtLnaxZ}W!Lu9-I2D(Vn83&EQ*EY4wW%AFA4T9vyeQ?9&PSqR_#fZ zA5C+=n5<$HJCw|8(w{sRE}`J4$P!+CAl-%(8a4o%Epv{1tg02RX>M??$aR39C8S=` zP_Y{|HYV##?7BuDF)gSG5*9ayti1qDYnI00GG8Sc2Si3o$bN?%8(PdsPmZm+Dc(s8 z#YJv&b9nWF>nJyuZ?eOS<+!~}O!nl-*IOI*x1l0fg}`XyCLXx@sYHgGhG4*9k66^) zo0%TB0LJ{dldTY9>fE7V(l)`b$dPzyqT%1u$8j9_SPA0D)&LHyWJxujnhffL@c^pG zeW4%?ozPW7BJ#fXj3WIl3!X!wjLkdmC3i-Vli#acu`VEi>12!A=~fsocHNtlFLBh* zWj=^Il7Z_(g5bW-mGLb=5acyCmY}|SatjEgv>d#yw=0|9@|mQfTC}SZUa^13W>&Mu zBwL&2Q|z`GFgUB@P_gwi_C?6&dc&ADuui7Tj+rW&WuoB1rg%HkDS>Wa-is#pO%xfS z^e*)p5qRCCCYhYKs~o?Ry!8w=%0dT{GK%g(9f^7Gm=4ptd&>kdFxW5WYD8V#FOsWv zX)Qwf7z8$f#8%`*JshYzyOEPZJfAGf<#f$8?;qfnZntD7;~3yPVfHRV zs0XfVM69X^+s*~0NGax({9IHsp4w|&-eBxD-oF}IBbS>@RYp(H$}RsOXxzQSQt4U_U(Az&%TDpW zH2QuLxV6lG+41~s5@(tVxUNrV)7UB4YKn34YWobOZ$bRSPS0>RD{|mclY_=;#CkO| zpeWbeR3Z4F2RlP9JUgz+p^N)fmxuVF<}q!}t0Lc>YD@@9?c{{mvLQ{V=WFqdF~ZCY z=IdrM&qtzoNW#jV#fYpnbOv{kc5k#q7GfX2ic@P0x?XjW^z8Q1GB-8zQ!#LSzm^pF zeucTOYc*q{pvb|uGx z2<+rW9j>DW2@>0W{7luU)_&-GSe5AJ7T`(<;x?=vw}h7orq=C0qRS5H@>K+Dw=@$g z?+gB#*sG%1BOFt1A^%ds^7?q`YgGIyj~Dv5dS}`pwB;6HstHr*cLg(Kk+|M zJORgv;qUX>B^v~p>Fs;PlC}Bd!l~)EXU?1g34_WDdi>Yn1uz3Cik}m31>xGshP=GmS=FHh z-@(Qqc-s~JP_utWrLT`^@vMo-{w;vz7!bqP^!gazzC7MlVY-0W7CQDu7M0qos%fh-8O#H1BHL|j^^5N5qT#g};Tfit~e3C7ms(aXF z@JU3tB}KskUTh{)xE)WB0-Fs2ZmxDC8i5I5S_EIx$CP9b=e1|yL~ZS#ikM|gSC#Zvnm)mSD&wkeY{VX{6l{iiH;(!{2dIA`U7MM1@)wl>xEje(s` zQk(IW!)xwFw?McAQ}EEw2->tq+a~%tMeK|^F3|gapE*jcVvXwMYUnHx(Ql#wb*UF% ztCS+flA_#6fydX`Duf^OSNYb~R%4BG?}?Ra=GH`^l20ez4{7)qsqC!PB+R?48`{)S zJxP0+$H26s-I7^u#CiU>4OyL57**{2+ z5cRg)$*!7X4LeVUiQHkFE(I?Sc{iC7gR9Bf;*z|*B8kFv!vg%QA~r>UkVHvBkHjCH zopz5b4^QQ5w{X)Nr9A6_^9FQtkqi%2N_E$d0qU6NBKSLLN5I;F=#J_KgGuJ`p1k`x zd`+aduNEMhgg7kJEHNv^Sn$HLJaB>Mu6CieN|Kz(C~<~C^MH;+LwEOX^`F`F&Uu7& zJEx1)uV1|tC)p^iaUr9eWX(qZK?gk%{6eub4uEh%GYDOuH^d(&Pwe3EQKV{IKjWE4 z;(W|7<8AdAy(qv)#>fmkB!b;3cSYTIP3MdoTz8=p)otzdP-vT=YcF<*MGCRhc6w@2xYK6?U zc$Ksf?5{m;Fwd#6oMDZ1Wn!fCF9A#-Ek`mi5H27e;B%G*MqX-Vj(Z6XqSS=|aYawwMUX_zrIKn3_1 z^?ibGyh#KP>`9pgmmK(lY2BnQvaG|dQ#1ntbOcmVfaZJ!N8utzhLaYx5=HFcG-vbc zDOrZvx33DD25MpHcrPli3zzs231Bcjm_5ONrM^^ZLtOG1!Pl3^Iqy5G{CoCYhZ^`- z1|E%0jZ{5cT@-z++J}!WW_H?u$%C;4lNL z!|LLsV?p~hd{k$8{V%7)zLUP*x|1x>cL~NHE|L1jHrTIzDr+gWA;mq2eCdO5bR&rG z(KUWbSbuBj!7K#><>(Em5FT|MPJN@tEG6NoRn8+c9;MO!Iol;G?|;?NWs}K_WM&%P z`Qj=e>*|R!t6=#s!J4D=ZJTym+I9U9#fz9osc9hSgppCHKt(*+ue-rt8#6ZZ-dR-< zNo%DA#^xEfz^_Fa)4ka;vOaoz_!2i~{TZMIiiS+adlH!@_X=MRE zxx<$fz=1zgf7A# zo|FTMZqRh#gUw`Fhrr(yzB$kP4JZ<~Qq*s$4zH?YG#7%F(1P{nZTYs*BUs0~@Udj6 z8%YO=Ti|Nzuf>jQrPu~1(O||v3h+AMc)0n21e3B$(3Zd@CiieT#(w=NH1BF{+h?7Y z)HJB=p4O&2Q>9>in*ILN8VF!2B}<5EBvboTqFTBb!r0x1s;S{lsE0 z*4X`E?>nht6_1ICp7-M(al1L)KtuB>+4Z34S>qN#2Gutow3SdKNFyjU_F|myTkeZOVQrL)H$=Vbw){^Dk)73_j8Y&Vu<;b+T!@|{d@e|uY3HY zA~5dV+t-(N88nR8_Etu8=cy5N1>Bi~2O_^VlB*-#k1C;p^(Ez5+oS!5Hy`J6{;*N= zvDb)h?JL>kEG*2cITcr^;Df!BCGJQwfO@rlE%_GgzI2=uH8Q{y)3R^)R58Fl6AdFv zP)V}`V}r)w2-$JD+^0&k1N0S_ArIQMLi~pW&LdTiyLlZ6W|#Z>{R?cr=YJ@c;AO6U zSt0Ap^OR32to5>6*4@V6I(*N0mW_yHE$o;yPDCmVuE#u|NWlNKz%~vsA8w0cy^!PS z>X8OK)L^E5u6i}{mK93xg+X*kC|8X?H6oHeQ+7+X1pCjlN2Lg@X3%!$=4>_zPO6lq z$aLFjFGE0dbnMn|uu5Z$C*=TU!Q!=};O~&I*X?Oo zeTrf!ll-mt3d;5Sq2xIJk4HQ2MG<)DElI?*zIKu*j3ryd81>hAnrun=BY-BOJ8i9R zd_T&#JRtUoP*l4PIX&Kb@X6uLOjrl?VJ3&Q+g+&gJNu`qkH;Z5*-INDxZby|vbV^~Zyd(8#<#cKN#em|taMWHq4d)z$# z;9*U}$xWJL{>-x{R8rzoKbJDK!w#S)dXmS;)Bx!O zB?zTY4JHU=&at1ToiHgsmPasO)jUE$cQkg8)8t(NY3{g_x|(>*?|S@)FH+v^P_;TH zY?Ibzck)Ox3cNX?S;=mgc;c#A+98c(3-kA5PP0a~ z&NDdlo>8oQGzfKe6L53-BS_@J8syMuao)-BKROLRB!pR9 z3~qw;PvP$DQ$loK@?wlw`1=}-p1*Jp6**bT9LjYibtt>jblBj`U6D=ArPL?Y`zXH3 zPA)^%n>3W^(;#Se|E3##m|v4{IDieAL-Y!UdFHf8`ulQ^Jb&o9$bVspvzy5 z>|Ev5J>;}9UCot^21Krb>tdGT1)=uzlt-2s=9AHsh(@>7=_4+1%sfj{_la6XO?J3w z;#>f6sSHoW0}$)6$gc;A?I#aK3p*btv+qPyTtuJ?74$;bILbLYJydh%(Odr9v+Ts`(+`OnS2gI9G`1iUR_ zTz{0D=CZaDwNmrZBsem1_dy#9F5m6*wN&x6SL|&JqF|(n;zov{t;$J4+;yul{QUgA z>|YAoDCf6jb}lBYFf01*8eXOj>f!J_y>9~kocue`MWZGn@=); zea_Kt8`z0)TQcak)u`r{Y(;Umz6oZZk1;ynYTL1_PNME*p!C`1f_24Zq`CLT7SMx=S7#bOs#L-yh2s_`9%rF3Hr$B<8xMH&1bLseRA- z$mqGai>W`1+2uhjs*u0^Z`ls%Y{h5O1v8~RmT%IM7H`g0H-z5HG4?RLS#@l!z6|(l znSGPa0n1rQ_?!to^X~js4O%yIXBIDI4k?kd&o|?8SYMky(0QkT!m2p`p2CUZ@L}1( zY@7M|cjVuA&oy|PR65iwSiTFpe2c>*9#DJUN~5R3 zjsIi2qZ?1V-(YvmqCY#adpgFN?CNmPeW%iz!?|At6wP>of>N2nY8!NG@Fbh= z1Ge)nL^G+Z6)wghVNU=4kvwvsjV&D<4d#&FGIK+ z$JN(i%I)_N`EeK}1!_`dq(@ag+J`b)UssSr)br@qyV> z$~{-;0#V#-En{PW1Kcm2DbViteFBvMRtfcgH*0ikv4k5Rpyk6tzk1;^=Ft2zv0iumw!9 zHO!dk;AB+4^A!M5C2M*EA&A>3#{^JQ-w-FgbD~zQNWK=|f$SI@1F-Mb#g@H=3;@!; zyhYvKE}$a=oe;C`{6XfBPy}^HtzLDPB55{N3UYw>+ko+o_WoZX!IUhwI(gVyh@d;w zu=B&Z{+8>E!aK?!b3$)-#7z3vh4*MP)ftwjcK1)TS!+s%H$&+u!+2`Zn&5Sm1o5*t5sL7LGa|oO42>wfevb?Z7SEo z>H{B>y$Y8RZh?o)m`ZoL1CS9v^wFXnNQ84;z&qL>W=__|u^J1;(p@g%AA>)IS6hMQ zl}4Bx;JoDKs=^!LEU`^qGuLpHSE&Lk`_pV2&aaRF06#y#0P_DYL`VCd^Z!Nc|G_Z*-#$G$aYGVV;7{lc=#Igd58DP5;lkRsc!3+?1#oc2 zC7>JZRPlOGcM*Pv{D$uF)(T5?2{FSgnD^a{g+TzSxeHyG?5pxV*-gR%W0X zjMz+_g{|ER9d|-<0e3V*RpEYPr~ol9p(#^ck#@*fk8(#mwgjY7T*4IAQ*>2F&*-BX zaArp7x(1Ae*$sd5s5-0O3akzrrZ1G~&FW+X${4tT-5RYSrM1L}V~i!m6E&ta{VYjN zf?+ij>#jxN@{{n7UQ}!dHDm!~#O#B&OE$2kE}ZjPp~q+~7aoBIHL8!-hfq)>ojLEX z4g3FM#G5OPgYdVf9T)%r8vp{(&Cb!7?!Q1Wwli|E`89O^v2gzjG=RTm?pON%>{gv5 zBNt5n-(`NG6IoR!W~~8@Wv!~_Zj(mIJUG>P1n#>Vr4eeP5S&)VG_z-RQKD0`cUk2T z2sfrNu*6}2ux{dL1T~Py+D?vEBbeBRmdZ?^4mJL7<3Z6fj&kAv0kuq!f_Oc-y2O&U zLu_TRrr{-Koq-sao)lCGojcKT616{FqVc#7Uyw=Alz9^LG20tORErJLn4cNf-BcGVGZX=ed~$)$7XKQ8g>c zeQVhWwbiX3&y?!8tl5lKX7b^K0bU} z-iYn;k}~2m4h59ugO`~UpvVOqX1?p6*S&tO^m^f}T_?Z#9CL~UE84X5H_zIACfj)_ zwVmbMw$aq_rOWKrYX;+>Ubx9Wt={~;<9fGnp0~E``*8YCDYAEOXD)D1Y=duM{ay)9 zw{8P4kik`F-GE;tk-e^W!C%fN&hPN9wR?XCw{0!dd`{kMGX^@Etfe*=oo>F-`C`13(cfnb9s^xP>BPS$ z3|<2Z>F(@ak!}c2ZbExK5On~b(EM)OWor9f0mJ$|l>39qn%m z6e)+?nomzF?oSlcl;zK z0T!gSzQkH@Z54QsuzzhWY}>vD`MxrLS6qbqpbZ_s{KUY&~(S$R7{wu(9w3?fOp{E+byWky>Hxu_O4&FaUma_Ym_XrtXB_x zzv=x<%We^@`I)SzdkuGR<>+19A3;487NynH@N_e6g>^>f9c1&dF?wF7>`4)ad&S|q zUS$Qv()%C?SRK!*6j%i}ycx;(y1G!o-wP+ZN)9iTNw80O5rrZZ=;1VG?7jin35i0V z+bzAcYE78f}eul}; z3Q8ME7~N0hng+&R(A!bU=KCpUPCnAH8n>B-k?GT>u^EsX zc_npldz4v^?;sM8;*H_#IXU6)4f>$IUWRjg(neix9%MFgQ8{N!j8ipaaEigL|5fSu z6SBS6`=u3i&-(8T)0ZOhrm*gb!9_w}lly&~Ci7 zpNylcxTHt)?+T`R$qIxLp z+}HASi(F_zVmy&#nh%$;uaEfHLjSxSN7AAVf1S5$|J90`;wvRl(tjtkflXnvJ~oOn z21Dgo%eI6!95!sT8bh?fdirKZfC~0F;JS9pGuMMNGg~u=FVi!jz>5|SeK_yLHssrw zz_YG59A>>5!rPQqRHvhqUs{TCe|)}&pL#)w!&i;SO6lpQcT=kES%)Z63Nw$&UEk&F zk=qq3mG6Oudt z+<(l(D|7C&V$gkG>W=hYqG+)Z|5*G1PuncZa}-uUFiL@y;+`uhB>O7VU>0{bPK|)ZZ};U>hJ#00>-E z+SHV#18Uc>ZV#Zps@x!pe*T=FRh8Jp|NLO>E$v0cgLsch{k*t8!7+Fio^E&410REk zNh4rZO|T!SjoakZ5SX!y0fRL4wOzqH3(uG@!nI-@Ycic?T1FwYah*Xb@uTB}x%Z%J zr^w6b;eKmJnk~(>a$dJ2d%Qnn65|tSCEGDP&mUJqE^n0-V|WVpq;=Zw89$N25q$?Bq&uA^E17_?r>N)nTD>=&HSWKZcLoL2*H!MHcNCWyOtQ0Re0xp8Y&J; z!E>E3ngCyBiH~JEpg;4SZY1k2_ddTJobhl0DUtx-Uf1MFh0hwN7ukm+Byut#ItbIl zSB^w#(NK>W)s41ptA|+L({9UuC7j~Vqod$@h1IyAGX85*kx3%cvFjtcLm`rdaejqo zW1&bA(I$Jhi3HBD(Qx`e9zCn3>@4Vu;GPi`EjSTPR<-RR>h-qRyrybg$>vJ2OPM40Ajx}yH8peN-tB6b?54XRG4gk9lp0N;)Y%gBX zzTP(u3z;QQgEWgRCAzTZ<4`ZSeq>I*3YFqDnPAEW3Q?)hv#O;%W>!K&O@ndE^N@-6 zvH9s*O}baHJ(DryJ;@Gq-oIUiHnvOmydU%zvbt z%_7yJBzP#llh+{`?v3zv4q|myiqL0D>tI)-P^iS2nb^*Ws+C+MT7>M(A2?wfs(b== z*%0j`5L8M=Sj8B4$)}mB+hv^w&U_+O%aPaYV229bT`4R1JU)H@{g1*)T!u#>#z!i3 zxOXl3>qtE}#O$z;+e^>x+@Ih~c%edyldQ9_`D=$l+F~J6#YJ-|lXr4Mk1Q{x1DBqG zo`G5Jh~a?1Kd&8Ly9#;tkj+%KqkIV+VHbjZ0s^EM2Xh>7i9C7990MOLd>r3Ybuy{) zW?VQ&6dq94UkOZh0brUn6>1mIopCo~8MVYxa{+b!m9Y=fexgrey-+N=2{_O{|4-mO zOrmM5zWRiCEH6l*fBB!nVwu90S>|Qznd#KtT)$o(aaIXl-8{ae=M+KT10$(#!~7V-;6P2Q6s5=oX~n(cqRw+btIlMFZQN zTR;+4c$(TttxvY0U_J7+l+e>13g`!5J>V`k9z|LYy8n(QZ4bEmx~|%1H)>X&WtShR zHN^)CE5llMcHGpy$sTcB$cZF;Z?%2c>}PM0u6 z%Z!*iny>L3ZWkC5`ufMX}y`(b4-g? z74_7q+Mh)X$b{t|?aQ7x*#ki}69GIH-sSx_GrG_K8B31OzDwCzvqdW3HlNVd#gWSM zO=$(Xt);890_w7@RM+1S0Pi<}4~IH3Y)sSpzhW;g=N6W54suad=@hD?Uri$R)4Xfo ztjx^ykUF9ox7E06%o6Xlp5|4cZIh!8veU9}n|527LAfJ%CTo*0P0p~}%1hi7#!)r_ zU@X%!FiWv-mKkht{aXg(lmA5IKM510jehmHL>7R=Kg*MGwj$>Xs*Aw!l{oF!cMVB* zZ854cs8P8q3}kjKuxeyhgV7(wpE4dUQ#h$f(=LvZQ((!*=i>|gB+L%Z&cJ;sZ+LHG8*?ehB3;9dvTsTF+clI}B#RZtX$*>@+SGlQXnjtJES9aj)vEkMFqLz@;eoxy z|C%iJduw_o)r7A9Jo=KBewdbSzm6H^kOp6WkEBDF%`wRv@K{0 zXzx7p7C)ZILnMHF+E~KWS?M}$q3x&u4&Fr-8iAnJ0sswM$sk0?jHt&}U;9z$*)AUA zu|!CKAv#aUNVhcKlp$kx!%5o|5q_CbF-^;R&qiPN3~@Y_3%yag-aK(!fod<01Ndji z8?j7ma^H_KK0LC@Y1lalL@mydsyl%&NQT3QH{X+lFf4ZW=*TY6XK}WdT)j_*-$V=` zh)m)Y6_jg&gR&4gcm(;U87P6y3~X55FKu(TS7|nu5OVL7A511*;LSntD%nJ$5Y~#h zv)VzSc953N%K1=wX_^c-{2GcBiAk{bvZ-+e*SdB>W3T z?2@4szDDY=Zqo+y-GNoBtF3cK$^&Yk8^{e4*bPIXD9Ag)#%|BeI~D)}KQmFdF&I10 zoSyaF9o@fuqK%L;p=-9hO}js}4ybKtW&y=xEkKHTOeU@A7xJL?5kT%Bns$V9kzwT~ z<Us}54b_BwL;minxDMB5LvnE$~ zj3l&%{dNBi1ngOwi1bnz>F7vHgBvU`Oiz4ldEXcHvP>^DVh4=qL|v(&NcU;13AeqP zifDUqSju2jSV|PZ&x>H7G3Dp|Bf*rhobD03VBU7g<&oc>6q)l0VKT*o?h69Jl$rC7 ziGX3sg_8zirB@M{^J$AqELpHT=KP_c84U#Pzor+M^93n8xD^#-0)tqU5?Nv~H4f7| zo#Q4qv3%7QxsI?3u|ZMSr4b-!Ik(tC6B&^QVzxuRTW8Hbz*= zSQk%Hyoz>a@-7#k%PXw;G4leB+P>?TTLpHbK*k9!- zP!6>*ZaN;w7`WV;TufiSYSsn3g?4UwcZE4SeKn zk87pXz+dl{-(K&Ec`P2QJ*r!@HqqH_**DI96%EKedOJT%xQOf@KgpM8&68t!5&?e2 zYoAKqD1Jv`F6_54pUo8L6>A}ThTMqxTr`_cv__9CX%*-_0axwFb`8JQfFi>x;Vrv^ z|Fz+GT$j38-bi6v7s|S(7s<&A82i!B`io2^ zNkD(^JK1$S%FEVE;cn4T`Ps8~bcT`ZWmrQyvao+JUn^^M8W%I$Jdzmxb4O6QXS&|} zxDMw~!Tk7&ocBr5)4!tZy)O6JWJC*KQ!wO9EHPf<(t%7RTc(8fFwxw?Ju+M_78m;I zt+=IlvC)5<4*hZO@Xj3i#Zapa9ico)3d5Q_sA6eZ9y!Z4vgN!%{c7qlwYM`|3Fhp? z6KID?QS}e#Wvociw=>5!&b#cbq@2ap+-vH|YcY)bCy5R7f-BP5hWGt*W+?VZdvSmf zGmkX=M019UC8kT8ZvWkTn`%YlH5pEJ{XEPPZMKTMS(+K1wmRTeh@nJ#?vF0|jYT4! z%SvL`isp>BT$XIn>GI5bqLRqiZ#<}e={Z%O?_IYE*{W9Q0*MBw;m5&sG*FK%rCw_D zCDBRi$K*5F)ud+uZtx83t@~uFNy(FD)uFw&tG%5VJTCrd9)iVUh&Y}|R7mrz=6ahD zb3G_(Vy!Hz+I=QnuEfoYm3z~nCd`kxMQGl6rm(lp20k9a(YW3(rBdEi9m+eZ*jjzM zve~CsrZKRLgPi)mC6(ACRxm349Gfs?8c}U3^f;<*Fo5(j|Sb=zb^SiQ%C% z2~wR}{$M2ROSYf&F9332$0U6sc#d^0GSqPN-Y`|kiI`TL$tpt<6id+5d-SpL-Kz87 z-9?v22pVk;`Bk5-OAPCV+0SR~k15U0)!p|Tn~i3LfEKb$*=%J>tiT8BVcXzuE@gLq z?g<8DxB8jzA{CO5*tdzP~lY0eUAG{wqUoj9$~2qlBq@^0x{ z+*s!UPtDJ5=7c(p#7F6dB&l6KCx#KNoxtjDX-$|D+M3ufl%$WtfT&j;UqzDaHc&{DWF&SSQD<@A*LGoGfEpm&C zChs^Lelb@Cf;Ym**GfbBEy5|G%nH2;*2RNemgc3gwUY#6AO@*OspL6Y^WZ^A)sZkj zs~<#mA<)Q)KtK`0z<6(-bM=sqD)_+d`YJIeId9^?ouUO3oo&obI-^4F1bcfvquGy zZg`;O4`6P2Vll5$jH#7DnwC^gkM86C@Zf5k%EzB|pwx=tJxHeqfrCjfo41{HD?g)V z)yNUJe^9rJ-LEM3w?_$KCu(57 zy;QyMsgkn=6)*nd;F82_NNY|ol6{9topDy3yv<+aGq|iGYsuK8dXB#N_x(ZC7%4xc z$^KNTxMOj0D%1EXsPT4Q+Y+?*$w%W#hK9@vBppn^qFk{UE*kCsVzHfIBOe7KkxznI zWhNPguv^0M3ujWQSp|mJ43|IHN>JUAni;vftu*%dV(!QVSKQkn|&K{>sB#Eu9m4R z7gfV-*#%Rur}WmKhp_bnQo}(#Awymyy?67O1Zxwi(7esJ1#;6r700Xf%%#@8XSn7t zpbMD>G`^Qji?pn?$3oC|nfomK7tzyQ%X`9936u!%zmV8r%+*Ps`z>7@{@aCpqWFPz;J(46EH%@yN8zIAH(yH*CQwJ z+hnEe^^X~S9aG>k@Mw}4Hk3yPLO0UqHwceiunsObn(rG4it)3u_SWs<64G(K3Kctc z3zIdQDKpzP+w3_SIM1l=596+<^S%dsBQUwnX&<^X;y$a9WY$zhm46wwu}IXe%vI6U z!F27M{eJWN!Lxd!o$vT6uv@P_f97Dy;jL2TR2T#0QYmV6%P>zHDM0A-9_eZ-+o$7th2!h0){9c(>HBnnM!+te4A z|9v9+YF*ZH=~5oH8hp%ot49~XxSsQvV7cqh@s3{(C~FFU;CEE(ny!0`O>ro)(+|DBy2k( zG)08lcp?d_eM<;D3c4;>mHWOat<@+JOfV)D6yX6XMVhiepI<}@X#n%8w&(jag)a^) zI(W*x2>8<+qg<5JC^j6x|XtWeV86z%}5k$1Iy1K=a z{Ierg9iUE>k)MtJG=XQ)MZKMDtW?i-WHkcmXRG;Ex*a7fNF1u7F^(J6n8+*he#>D7 z_*c#G9k5YC@v2w6vH@||J%Svkuy;7(dpigztMjz>92Uob)!k$fM3T1>vEre(yeS^Qo{2aiCgp z7o>g*%)!4$k8PPl0CND$m~-A7OqkY1nrr$xPh0E{TmEc<{i1B)9Kq9QeWjml*0>qz zrIjg>!F3ib1~vd#6`fLWd&>hO<%`G_FoUqKTQD;F0cL_B>TxL}kU(f4v>km%O&;wv9Nm}z~)+Uho~-r*$}^f3b?4xWWA4{zbn^$Qjm_&C5^;LX+b zw1qw;h#924BxqnfRXK6EM3%Wa6dLI;nud!pM^kaG%-3K0@A(Jh?@67ihZVJPc|YPB zKQ;ifu4iiF3cMqp;h2(kr({S}rs=pqn?|)(B8p*t%B(eYO|zDRr+k;B4$wH$7ooU3 z=MI4Pxu_=9RemxCs!U#LsHwtrhuvCf_C5oYR_a7lIGNPVz`Xe!NPlmrs(<{IQrB`P z00?^AImiF9=M@XuVHj+0ktu^y0#Pbv5i!rmn=CDEdu<{t8U3sw0H%3ZNYtls;N_lU zyr~PlfRom`Miq`_@rq?xqYO^7O7+0F3?&KPm2+jg zfYgdYG|nYNrg;dxL6RNn9*u+g*TX@d?HiuAyn2CcBZ}QT6{q^Q&_tJ3jNi7}(Wq%^ z&?4U?7xNGgk8x~HAeSJ{cNwnQR|_*`qQ5^(vI2g`<$q(W@s=EQf;|NM@oQK^g2fknjO7pc-0E-d;3NiESsB2WmtQP7OD zE*zF6?o9XuUpirYck4BHL^kbze~t5q zkR8P-EHvs={EiLafQ#W2C=3$*mCU>~92RB?pEf<~?z-uBbx1jl*8Y7p2-UhiXvFUY zF(*1Wr-?z?imN#ijDpK`6>_-GvU0`kWzwL9MP{C z?!#1e=1=6NoUd>cdo7KD#aVKapEda9xd>xpd@hP0y&(Se4N#)CR7;^$u9tAk92PM%+9Qvs zg!m;JAu3WC{wKlI!IphQt5<~AUcufE%(nnt$_@J5NBLm&@V*3PC>D_?{_p*P?EFaiHomH_(40oHX%Gky zk~&h<({|ri6%ui zsZj5H@eclwa16zSL=qSo092+27zoulB9*q{cdgWEp?LANCHNG6t(g3cyKPES2M6Z+ z=FPEX{sKY+K^HdSLkPi#nh(O8XV#|*ilWw+llP)l%LCyMI)~3O%ONJ8g8g$2I@v%p z&)jpseP&OX!|;H)$JB7tV1EmsAe3T6AgQiX;hgG3Nk_kDlm6o&{>+f8aP?N>W%B9> z7}oak`akhDj=?*jH>}>+pkyb1T$|!Fz zudFT`ZP~rRx>~l2=gLXe&gGrB;i05-Ud-cin*L}j?Z@OI$$YbI|E{Ect>ty|#y!F{ zG1bCb+JB4KxH0Gdriw`~CMt9r@Uq-!tJ5F+$P56Z4x?-ZUW&sR(LVHfz)a6Jtp$^p zls#h25y)arZg^0rYxdXms%zmp=*#dkfK(>=gF=52PE!&1X2Dh*X@X}cud6vIgnxy} zYbeHp7p~2368fln>u&-^T<3NRDc5L=&N*-=~wH~hg92TM8QKHOY zw;TTRAj^3Yp=Qxhb2p8@^EM4~kg>GG_9yRkO-hz>Zff&zgF4+g&D`to=-tR;aZS78 zbxvbN0Z25mip1FQK*dv`$r2-=^%3j*dY*VI34werMFpx<3=8%j=tKZgh0VmPAtC*D z2(2Xyc;UJw_SziC7Gn4zOS{6bL;`bA{^mL|l-&Yzk}TBzBG9Rq(x(Y%nFWaus4Kkd3SH~hzI6r{V@g@;YJM8WPtkudgL_&&O0!={R#V-61W(>=w=gL_T z_NY93^kl}ef-sSTK1%j0aUC&Sy(?>cb0*Y)fn&l#`OeX{t74u*m{dzVB z<0(5e+Ao49`)!Lh|1k6B!?2p^c6YfScB}1su@5zG2iK?QC?i-|89)0vmFXZC~% z$rS%O|9L)|GN)INrWd;mCtyw@J@n}R^b9r)E}(@~cFs=>3lDu`@vUmAGD~?r{$4?K z%rV0&M}b|##HyycvX8)UX&CREGS9YV2%HNh&_(0>x)5o!Vx~L+i{1yHkVAF=+Pj~x5Njd(Idt2gi8;<2%gw@lRnPf%9G=F|un z@za7bzrnqaJ|5UwJBQYWwOv*PL%g^}vJ{}aV}KxqjOy{~}II zL9-QiVHCWw6>~J}N4rEPE*B|G6>X)T7&A0dAAIr(WysC^IWYAeV@{S(D5K z;%ps3tfoWp)#q@gn~rSIbAaHPPj7P#e(kJ7aiVvFe)Fb@+>bfDk%l_wI1a7H;wLk zDa!G!OO@d@nkWnhGnJ1C60*foe2fPM1BnSoT8xUZAXdd2p6u_Ee&>Bb)^`cI5_D30I_C>-NG{+MF2hq_n8X6e3V78mos&=;*^7QW~9 zHyIWxLCDZgl5oi0#|1$eshU|m*!bSQkLWJAWc@Kc*dV@yUi^d-Y!goS0bi7qUVcb0 z8V7App%ldOBVeUT$gQPd;IkQIFeBLk5^DWDXQi^*{oA<_Nmso)0$ zCo1eXd18@4`@ua`<`&RfnF&P%Y9%wv2^dW|*&kJsU&IyS3%UJq9~TGThMr9-JXP*n zTBqjv(CI29Qi}#Z423!@FJ04Ry^oi|jyBEiA@6Y#-=cZ-f0kT=lM(4I*DXDoFj@|1 z%-aLeqR!%$Rf$L{W_971PBm5(3cLm_#LQ)BrP3}-_o>GR%9jUjl6%0HY~))7w!na% z&N>c1qc!r7efSlFi^*fdnY&%YBjb^2OcaCpSy)^MB~6+fI-#D;#4*hl(L*CtP0+^( zj8KyZw66U!e2f1bXuS#;36F)yoG z6p>Ho`Xkl;%Zt>M{8sgUOb95U&1c_vtK4e}M^i|4aRTjE3!>ZjdmLJ9CjY#v1k~KZ zI5XCQKC&1X<{p1PaDhHM%)}A3RQMOKH;*WyC%~nltjt5Db+?{eY!Z_i1 zL_>Jk+MvVu_JhvL#I27f%l`I(ScugQwsY`V)NS)D^}jad&0`Z5dA+~4JHIJ+^w|y+ z2oI7MH_4mGirFMtlWi`B>~F-LxKBibb;D^jiU)e)LjuO0Ea13ohE9k>6;xurie*_1 zeCdAhrb$AVVUo~fmgp<90m%_Jo|R%x90xn6_baUotJWz+=fDPVV3V+aR9uHaZf0aa za8xKL=3}A3`4N4OS!Y_uB_L?>!R4~aa{u)k$T-i1bPZC8#*BLT066_&Wv22%ryM{s z+`#L|hnPhQj`QjtyjKt6g8DY`Z)D!oBX#e;6i0uHl4|b7v+66}qG)(UT~FMzY@ELc zcJgb>N^x#sRFv%={22vyq1qPqEU|Ack4Cw4FfQjr5xv;$0f+%27OIMAEYpEyVWVwq zl`o~Pp_MfAjR$vxG-CsqaqiHbF)hAb(3KW}KR!E2IeGt6IO z!_u=_kC!Hgd&}T!JvJ6Ca@k%=RjxPhx3r{WjR71uw*PX*YB-(sgze^umG>|^cmV7x z3Kw5)y6>fXVLqfTH<~sY)geHqG!W*~qVz^0&;Lg8?9>mDWvO@&7$!DE`eJ=Glb_tR zs~rwF+vu?5_Tb786tjeRxrbvr+Tv*MMbpVxnsNH9KDr&s#x*%(O{p6#Qy#OM0fsw0 zZQpE!JFH7WmA%9=}yr7OL*>=&^M}-HCn)C*#}Dv?e8O$$0%GQ#n$Q9LbiAzm*CqL0cO8p)9oL zPwNz2v|Tl&Q@n|$#)mqY$R`B3&(kGtt))Ai3|}r&HfM#`q4aXey(>Q{Q8=ojY-c~oo>7EYS%ZKC#n_-jp+n^qu(S0pZr5ErF(deFH-eRR$G&ZC zU$bs(5E9k5YHfxD_y@orNrJNjzzV;uzG_OEJ?#T0Xh{hq3!j>A`9x%=+g*xH@%}Bz?x-z(E>l2^V@D${%a+@nsOt{Q$}F9 zlKYQ{K8-oAt3QD=PiFDu8~3i|KwsGmpYQ9}4w2H-3v zW*s6-b>`gu5b|*ehP~qUoV+&i-@ZuK4vQfNKnnHg=RV68xyWq*d4S|Fe?ut`dkEy@ z-CZwZBfRCiD&8_5SWA*{62Jj#OEG=q@>On;j6&(oT$A-XUhv`QaS@(sEsh#2esD&d zjX%O!No%(r{}YljlI|>plFKldGEJxUl~RHYif^1tA0ZkV7Xa(RGR_Av=^IO6=Z7!_0X|ovEn0!co^)fP6{nwuE|kb_p8&3=36~( zgKC@>bnRZ*DXlLpCQaNC_BExk>eQm{KF~5E`&Xa_bT2einDAM5D1y2(W*B{nR z7w=Au;GJp}6FYz1pjotGY+zM`j9>Z*&TKpvAH&5+Kp$*1zku!FOj)6DYXC42#Gk+c zoelG7q$Gl0M=AztAq9|BK_5T^D1Nv}3?nXjkMEA1o_IzcKoUTbVJIPb4yTT&j`)`( zLXx2mf?g1vq73R?+#_K!+_6I&G82dSBi%HPHE>YAPd?o7=iUkm0@FlDEWIZlw8vce zFiZEI)~8g+1jIa=D2BmQud5)TXA zd(}pqfLv}lO9$rwGy21Wp=9kFhStMo)`5s3FZX=za^g;}o}ni)W%E@g#>^!?(4|A6 z!sO>=)LJ6T=ky3d<3;#Irg79+cxTCUs{6E0#Cv)V zocr6yj~kR!78xa9;eJ{%eZ+2SQxo&iCn-Ds;x)*%bmzOezdchfnZGSL^mwjc${*aL za$2%ZsN3Ko1zXU-q~){3|LX~acJN870-+YKT4XmqQ5&aZ!H|wXQgazbII}|Im=r=P zq2>ocr9l)hK|MoquCPDO3YPJ`N`ltUje4=Y1S~UM)dI>$LaZKls7|aTV$mr{P-AQ5 ze7Qww;QoRB6U9i4d_qhk6{xKLo0=vtNv0$3L?VN`oXnVKJskWp|BG#c{D@>_ZMYCf z5(8y`b4=wQT?n+fuD1UmrtN3TMyGlC3a1Z(KL12WqZ5MK9H(q!1ZjyEv5CN--OkOn^X`$gd zv0x4{lCp>=Y-XPN(t~#uW%o?O-yUCJ}0HH+59VxF{JSPrc7u$qIKawP2~?5(x*y8%>xM zF%Tq!`$WvS&euBfh}8?h1gV-R>vM4x{uH85_($75MYBZjbQ%m=B$@*X`oqg)W7$?i zOQhO7OW3&O*Fy0?m>^6{$UjeN{I#u<%S(=UTR&6nK*OoTziz1G7EZ8bp$1Yu*OAP$ zj6phhaRB76R4-I4Lav4Xf_4INDrOMFDUvEV_gk&x&k0@x`=C0HpZyd2&*;y1E!uR= ze0esO6?-&sZ%WNp$?{27f!xk|VbId5KC`hPZpGbjq^~$Az&sp^!>KXAEw@pO(1|Wa zkKAAV|K*BS2Yu3>aM!mucq>#rt)*$cMBA`pJ!uZ&%l+f#*?J#ig}3EA|*ClB6%w+Rkl!hW#sT)I9Y1DdO2i?mxogJU{=?K>s#Ox zPb_;jCS5}XfSqSVKS&nWEzv7i{L5OFL_4jMV8i??8oz|rfTU2`2J#KeFaIa>(wZzp zf*)Ee(58^y^I2Z9IzuWYbx9QrC7S({)`!j>1lwYX{DM5)r61)DW$Y}B@ejU+RkqJ_<$0X;WNs^4GS^$Q9xWJQCuM0QKlE>}e@xM>K z+TC8ll(i>zMwQI<)=(N%xGS$x4F2d)oRt}>r|Ir3o0irT-IuVo>v@MQOXQ|jVn)RT ztooCRU6?s_IrGELy(#g*CbgBJH!8NpodzW`;fIzYM&y}U&s;o3LN@d3p;?(s72Hkm z;mzse#$EHH*tqNgoNA-^XA0W!u^i5|=Hopcbz4|O)n^0qCOl?*gJ=TFg&AskxeO%s z+$U%5w*ajWPg3Qautzf}w(BFm+uV4on*huuKoJcfVHF=k|2cJP%}9L$EN@b^ZbV^$ z9)-96kWYu)mg(^6@G@hGDujSkbz~j4sdb5SWPlX>6UfEUDpJ1=8Y3vV5e<_6y>x!H z7y^c2>8E{O%=0Y%wpdqf#Ea6fF(S^Gj<7%U?)}eN=X%LVJ@E%6TX>8s129M_Zx$4` zO?3HvQvy%o7E5y@63zf>J_UgX2}b63^G%&yn_PoOEwPrGIDQa)i!&u`l$Z79Exac^Yu9bhDcU^y* zADVe~tNOODTaD#6`aFyv&h&~I9YaPG@S z(&NH9c(~ati0J+jbGWz8-n689IyEhGufp{**ofz{OSHUEym$M#e!dO|$0b-xf5Ot@ zN&Nwvk(e;HBol-DN)KeY;K`1fblkohHlzAP_hE6BLgyVtm~Z^_*L!m7l<~MXo5AvM zZ{j=^qF4P@L}s(#3LN|fsC=y2g7DZelpWaVb%y0uHXn8ITscySr|&Q}ZOlDNQ}C5^>9!h#^;4oiV|Ktk>)Li>({OW}-bt(* z&m0J|$cn*Sp+zdscU_gm8MNFAY*oTO&l z4Got6D4Yw9(nP*{WapCkvIk`e&Fp7%8Xo+3k`_(ik zZ$+=qvUQb`VKD5!pDOI85S%M5!FY zU@>9jdrEL#5;hh(iG`W?cT59j&hhkzBI!@-Bg9<8^82%j0!BR<-&+3(QOj)Trxig4 ze+3eEWHyq?MixgPZP08r29WrGZ0tHK1y*c#dNIV7^7O$y7Zl~=b0dQwSSZ*$v;$df zv?QZ^#_$#i7}^Z9vvg$ac&LiCXZSPRl`PU6ptIuK-jTaFUHPc^J}Koyzkp|g@XtP} zUE<>a>nA^x1vkUfEo`*ZLprChuRQUD32;npq}C zb4AC}Y|hvsyImP+t|01M+Tm0`lrOdV{ZkQ)Xw7Uh4z!>ijqcTb%~SKzx^wUg_#a9WC$4 zy|Rbk_VHHp%vSMWp21%|V14!d{PW0mPo^@%`^s>9gr8|eiwHjUmv6(YPLRKbP9c+O zSsmRDuPa8%!W?VSc?vFoJPYfi>v*Kra`i$^&ikJGp1J#?0Rt9YQ#;L9~;%AMA`zw-t2^;HS^QS8Se{PvLiTnzgP`0@z- z!th3UK0&@?x|~(IoP~G>e?Gx@7P|=s6aUwbKLA8y6d>$ZZm&CMlfPs5<{Jg#8z0mk z#?v201JBGZf((Q8h>aD@{fFt~9`g;aGKj`{PNS13dQJlzN>z9PBY_z&vCcm#+1)3V z6x9)7%jnkyjibG`SS$8$)_bS(En>4j%ubiXa3P<*IbP*k#{|7zVl@9=4UVJe<>7|o zbM?MkgW?m_`+ulq)fY31$pN(n@?IzHv{*b)MG3YUeb!!rKS!X>xuqQw#MBM3U41usJp==74 z;|?X2T$>WLFGJ0Iy#~DoB`{(a+U~EZLtsLhCLQ#{7t}^>;$}4VqPZNDAOT6X^DW*K+#FY ziY}%cZDit1R#s;6V!?q$hSt$tEu=;(h6)VlEpL>$=r`9%G`%^E$Anjtoo@Cr%dNI$ zopFZpe`1`O>xXNEeUcdAWM;(0LSiJQBZQ1Zb9M8Pc~-IC!B^Sq?JcQ+=}L2Oee`JB zlR}Vmo>~p(D|KMEusQuimxF7_*)Ps0a|x}17;f1cdm3Q*XJyQdypBx_bh1YBEI$kC zNmou|RO*~3}v>0;;L|rxi>lzQ?nE3wx4)Mk|;3jTOOuQ*C%mJdKdw_WGz) zfR5Ss$+#sM-&u~WyrQ-A#v4&p0R*xLgX4lW09(`?$=)u$C^5t9fy8C-;^pNLTnMfx zkz4|GeI+HbQf@w2m>IYTL=n14lviV`eUUP~Rbnw0Xd(+nq) zk83$xOZ`hpzl>z#YVrVO&p&J9vV3Cq0Z|$I3QPz+gf3Jlz154z7W-L$@4dB&9;l(vS`T@ zmvjo)sOf9?45Gr5h>GgYCkd({r6c@r;P4P?eQuaqWSv$P^Kl&))WlqGSO5|K2sReX z%WG~SduPGB*W0U{?I@R~egB{bmfql;PClZ<2apevkJ3VPzR}RXcN<{^G7|Gev^3`6 z;=u0ginT9HGE#r03;m5T$asB#i=AbnBkEc|t`Mr~^{lg_Z@*|^*2wdY|* zX%{msR9=AaEoIdUtX;Ls&2)A_wVpcNy6z27E3@PLOuD`*S6%30yIDX$uTIu4`$-?| zhfc|??BHkctwxpG_M}ojluU~1=Ako45w%zALwmMs&~r16aueK)CgK!n!HdyPotm!1 zDAcxXA@QO})nn>vv05oa#VxRH$SR=v{9~wUJkl`1srSNXu)rC2QD2g;Kr)p!k*DA) zjgnK~OF2lbHWAlS;Ol}3)hcgP4!aL*B#1!|>Yx;x5O@LD8pz2yINAV@2<@~a(rcer zCuawB9W6B7%q-TAS*ZX{cnP3XOx7s!_TQfCG zNlZn9NvI$xrw-cmu*6$BPt3AD$o%5L5^v3xxK1>aLMO~(RyI_$rd+@zI3C%Np+y2Q zDJNP>H(MZF0dZ*D?_;QKa|j5if*DKYp(u4(Fk#-Grz;@bt)bWsS*ala~9!L@h=a@=zh zWhRAZ;Whqr!cU_m`w~KuGSDDd#8`h|6oPdmc{Os70tgk z3+L{eOZ+#|_JWCkq98c?nBVG|)3FS7!$IpRY`Ko{9Z``ff-b18JKOt{QM~S10%(f@le2J2H_sX&Kv2Ct5o-^=#7gG0R zY$iHTc2-`O1!Q7lPw&MZZO#)C$OTKyINv@=#t!J6>V!y>?`?D#Cd>ox23%HG2?j)( zZfJ@CElKnm3kjQLe#zCC2#@GQ<#6+T8Q1UkoxN{9`aFky2g=C?kiaIj(slfTn!%6L zD*vTF$WwaRccXE-#oi@g@$U1WE7q@EOX<=v0x})U&k0hO1^Yj`$iS0nzbl=Zzb?KY zgnu5em%WaI!!M2|eysZ>S}NtWDslUuvVQTp2t}q*)xQ+Wua5nrryu>EClv5`u- zqsD#ohZMKEDz%7_ytbLsvL6}pQX*oEaZvT>b}!B;Dp=BeuzI{}4y_dwQn4^Z;tC$K zcHEGy z;@WOt^tuq!Z@|Zu{~;_nC^<@O;GPm51vJ?NPA)2IcCUmLpjb&d-E-e7nfE1p3vr{4rR8V>=D9r=1F+!eWgnAEf7~Vf6ro4tTjt$i!$+}?B}eAoCgaZMP39TG=jp}na;>Y?puDcnd&w?6 zxoAvC7DpIISbu^1F&H;a`c=4cGb|e`??kRd0?=>XA7J4dCOdo~beKAn^;$bfHXKe( z<1zhW`%^~MzDo&u;hO-9nhZXKtw~io%6PyKO7M-(Sz$Q8+qbz((I!i`otXX`qobA@5`(&HZR3K!$10%~Q z%xrsta>!c3IpO%QgcD;noUf|iSLOHj-)&G1HrQiq+*9GQYEM_9j$>{`@}+?rUXg(S zey2w$FRg_Vg9$9zkz+Cwej}NgWJPQ@m=vFZo${nDTg+fa0MRy2V?NvSI8x~f&DdQOyKb&mzI`gQwURY#&rzV|_ZQ+eu;3*ff zGIKewJ6` z`u(+6y(=Vgx9QM-l5v6fS%(?cah>Ij0_}FFatwp;AVc6egmw648Oufg0%M+n~nB$t@L$WoNMNxk(-1L z)=@RWFkvBdO7Msfmu=W(oafE!lX;>Y$1b#W7^E^fAY}ObQiwxX zN=o$Y{IdKDr)<1>A-u@W6bJM*B%TovTOWy;1=~ElF3b1cgLG~dowuNe4vf`<|5A1{ zZs%pE96l7M`uTsNmT{9#ha$an9YoKly)wR6r2s_h8C09U0rrUAxba^~0!&2mq4^G9 zJhC;q8k{*qbp}W4zRlNNkDjckebu@D<&uX5Q?D}yYkr(*y~)^*(bWPTeF17EXD>sZ zSLx)3e0h-LJO~!wk4!{0~WWf@II!Xwxg>%PO8nO{#}mI*9RGQym)t zF8;kDcgp_A-*Z7O?-ycC63KZj8qdMkPJ-Nxb^Jxw+McCZQODhgnz?4PzDP_?5(2<6 zBH=F7&iuQIDl*SQg_`>eyzroFPXF1=!jLZOH;>5KsDme8^FHi9_g)Oh{XK28@yO$K za1vjBgPP~;^pYxUFY~lJ&1T%g$*?V6=aTm~FS=~0xHj0G$T(JM?N~4cyF4&Cbv!jm zfoIDwJe@G{D`f1DX)b6KwNzLFoiz?xsY@}Dx-sd{EpNk5hpA2XfnAMZ%Xj!M1!z4E zbC#qs-6VD%*n3U-;zbz172pK`_L98p} z%XCj0q0Y7O8u@O&Ed9?z&ZoWINHMJ9Qt-rGOa#Zm3o3bi(BJE^g*7OQEQdiO733&g z8Pi1MkwSsV3735JndKaPmA$=qqL3=;E%(t5T{nAVZfI<_yRk-IKX zX)JImkAmtrqF%6`oH7jl0*$c=zJSG zk0k|KP2_F%K}9j;RbhYFm2nm;79>Pb7BYiy1o}FI3QYS^M3pxutY&DdkMuFnvgl#- zej{B8lK!ra?d6Ot~2ZF?j5wnW?)C^N8Y}+lGKkB+%&ia zHqv}y`H*%r<&JzbgE7mo6vL`fil<_@q}GTZio}ORBD-*p0uU zT(Fz=TJyPVm`tlH;1yV=X#*+$Sp}L5c0#h4TnZ53j$pckl>?fSftFMZ*Y1I9uf-&! z48$%`cEaT=s$wKAl(zKoMd1m2|9NMDRTJ4H?V}lEB6VdhTN7TZf_gu|0KfzohwzZT ziAIEs5x@K=`q$0uzNlAGhtktg)zrk~mByc>6Q(~o6K_zkytQYzv_f6{@Y_onp~**Y z%L1a`%qoX{C4(A?+0aM({aD&8lB{?g?=P8WTMZ6S7dl3Ea;uSu7!ec*ild04Zc^Z2 z0oERbIPmGjNh`h@9DHui0zh3bkP5!EZyxk^Q^-)EoSO^{_$69P|397`^bV;i2s;YCV zJMX%z%h6$5>ozc+j3t;k=sG6rY_S@1|9{!#W;aD&pS0MS{-8{g=D-`C&NeF#9jurG zg`SkEUBqB)U%|})d)h2ZoE`+^tkqr&cr!`82@O5BggvSuDDS-NI;P)1&mm6eRp8kYTX3PnW%_s zSA-Q>$85FLdm@NBS+VKOgbi-hMGU*zR*PslpAS467U2(M#7Nh+kgbhR^&RQMl)=m9 z?V0sPck^%(GSCIk@{p^Zp>tMOWGMY`6m_uuDgJyM2wHqqnd6jvhVb7SDQHd@Ck)DP z&;A5J$tZ*iWPm|~=(-^&qmHO~9+bR1EflAF3>7%}q4}oQLg<-pvkx9J-@iS==9qB! zrkAV|b^lz$^*cVxg+Er>J^-mI|C_6|kgk%u2bc?`b64ml@2oMlgrMwabVAP82xAB4AS4 zb)tSt#`PgvN2)o}5pJQZ1z0-VY&wvE@HY;{pY=374{JIkiB@W!)TvL=sm`b?v~Lo5 zgv_^}PsmV(-={y~Q(vfWeGev-U*5@S`6;B#O)Pu(^IUi{iZ1xii`hyv@fE}uyacB@ z%?X7voW@(d%*#Q1ecQr=^S4V3S3ky!a>(}XwnTj0qE5iKZNr9+bCI-Ko!|O+nksC; zgdC7*MCMIvE?sB+NH#_lQzYpTFumkq;+tzy?p?%i_MsEr>)Jvp8-%l*xP^G15t5zt z!t)q&5uG_=8i`-(lcYN_REl$j#A<_Tzf1}-e80q$eESa`iE~dm@?b-D=s+i^a9$i^zFKpA!a6IAtpiS(Ix) zegYO9DQk5yAho8&Iw%?mf zM6(d1YPr?9KQbReaBfaX)VvH`?H;2^y#kb zaa3tDfD<^PH^a;&U2GWZsUb~Vd0vtP9W+@VvO5cp3T{N+$wyWlJ z4m(EIppgW2dCLU-gPd-1z!xyWxo8zevgV1nXpw+~nv66iiFgTp8I7}TwvjG!4>JnD zBubD>q8Wf`PzGT}#02L6s1wy2DzOn&txo7cp?gb^!W;m5UuyKGBt;Iq3&1e}NUkyj zz}_p%xXDuyDEJ0&6r6M4XD!PuqUp3So;24hXtFli7;Um9g#Y~!iHVVRbyMgk0QIS) z1%08CpTZbo1q48{a(6~E_+E7hq_Wb%nA`hweH;ey1e#Ik>Fw8d-SI&oG!)oozeB)D zpO&)&j$nllFf+HDd?hbKAB-OYNo5vvsG#NZ^s)>0N(s*Ye_A&8(5{^oo~^Feu&b&z z4EI+eSIvz|_NB}L{YFhK^Y&PbG3bikYK!}NXTOE8TGdXmne0S8#eb{K@F7PTi|Xp5 z-DEAACQ1VX?S^wPKLl-9CE9?^2+QHHsI8AZ0Hc5m;UER?y`+^esL{A2oErPtZK!L0 zhjJmxlZZ$U{FGrJ7${Z>4s6t_K7=ly3#h0I=&5F?x|DKWtweUoQ_mOOT4L7G-1+IH z#+<;^SmYzy#ZQ@qgnz;}ij@DZ!LGqP99gy&W^;?7I?eJg{FZq;`(nw!%E%HbFRaLo zGLG#EN}?T?XS`)z3r<2X9yA&apWzKgBhWixYb*G&Z5?nEmo#JP(6w*k(5a6aEUvNk zV}vk@4rF-lgZu`%MFoRb(HrQEc9>Tjx$731=>7!JD&fa}71yGx0WMYost*3UmAaJ@ z?N-{%2=;+QaUg`!u+T38$WXJLiH2}IV9ExP3Q!`Xa8*sMg7m*wxR3aSd~wOwjtq*ZH!Y+Q@nTepyhk7FqV89=XY`Kn4y&an zri#^BTMj?2*bc9+S_f(S(`~bt_0JV(%T?Cb_lj}DDdakBrhba%g4rZs%+bYOsS-`d z(}K6nmjY1SM^`anMwi)HUx#c>g%?0@VDMx&22hvT>zsZb!FS-}Mg(Wh6+!ae;417% zdB%qWsfAKQ7Xue4Ok4_7-3?KZE)fx!n-I+BTvJx_Mt32V;EFN}6=ZlzQIX(?G6fOh zW)Y(WDIy{`_Id_%3ZN*cLJGy`n#350qO*Cmef=lSN{8+b+ z0st@o${-9%E-9mLf5~?-`@$t+(E2x827Ey7GEOkNa27)>dr z=@oN&;0Z1@hKO4Gtxi5}BmWv9N)e~{Nm1acbMq1Bg4^a6)oN!k;aHxVBk&Ss1YaQzp0FA}NT>l?SRF)+kz^`1G_WA0aQcW10?p5&GEr@1*t+H0{LLMGns z3GHj66=&Uf?UUt4D0}TO0P#^~0KQ*8LhZ@Cx$47z_=RwC*S<3R9dOO)Li#6lGQrUj z&%5mIV71GEmpPQhAYrF%-@P%3*cuO`e{Oxtv^>qWX?NU2j^%=aVcIr%ORI0`^Z$-< z8i%!wuiUS%ua&;{{ve>0Ue=Zh{|mStCMr1hFeq=~{IH>f$;Pmy4+#ZxT><{9ETuT;kdCJ*OBeY?ZA~;SlG6d^SZ0w0 zgX~Cv6rlqw;`dQy0d*3#D(`liX(9a%Yyp(@2Y%}E&VD0;9#Owe*k|qkT^^*d89;ib zk><#K_@u5blqGj775y0w$KJD>p7~iynde=;m%g9xr4o=I`ZLx5)0ceS-ww{aL#63n z(c%U73H8h~!{A(hO+Z~HSkI>7Ki*43F;k_O>7GZQeEfpeirL&+AqW23Ll_u0#CqO! zGF<5E$^WguLh&e&safENfO&40U9Wc`wyg2cj%Y`Nl_#C(Q6ob?eOoChis^Zdwcugs z*2N;JEU0ki-F41|6`VJ(`%S2RG5@Gy7tS>OdRg^u+_J&FmC}UB$S!gzrb(FSFCO_C ziM2WFNx8KiNz>gtS($Ht%ZK$1gGHx2Z5W8xpnGl$=SlrESnFijBSjoP$7bXTgR8;<% z{g~mCtEnA1&eX?!gef$y(3qKJ$>S68p|8CFaRA6E>OH%C+xqW+gTSDSO>E==6s{B4 z-+=9B110yGf#g&~f}9-MPYwQsDGz|RobR}5fC4i))mUu6VaM z)JuapKM1VX30DAo$^*&eD=y#;c65YCL5T_RMokJ4oITLE157>d7{=*G_|7R%NTr0;#fX8 z#Oeg>HBG8nkx8Y$zioDyY9hKw-9cEP^7X$#%#6;+&y!-PnM!3Df8u&}4SB_aco%UZ zXLeBV5pw4;lbqZ=c}2!)GHKw9;mm7_$iaV8D-dAST>$Vz3sNCSQb$`lJO+A!`|`fN zx-?a}hZ)Fe$aSWeJE7}qr5%Xyoj0B1C@@!!B7njviIBI=ETE!T?SnBlm%=v1!;M>s zJ9m!+st;riqPp(ledY=%1~h51Jydp z(*3^3d!5w3w#J1#z_;fn;7TIo85h5TWe8F1_{+e~^z6FC#emj;)`Z&8mu_+FPM47` zU`J^=d|ZqpoReX)@-j}_kxqvzSkXZ2&HwBuHr5qxFmRB6pM_;;ci|Bce_W5&q4wgG z{t7dZtpD#OoQ!tk;uEQ{hSG&#QM4i(`CKWSsTZihNUJ8pR+r0GH?ep<>n(SBh0QY; z`3V1(+ZfA;^+{&uzhgaZebw@^zT<_kI!Fdntu#ZmaBh?ZEfMyOoYJ%|)A5XwYBI`3 zjVQQr%5CV>MvH-f7$lGuXd|ar?Tk=Ou*S(2|2}jmeweDzH?a%e2HHkC&sI@>H{4HMW0W zhxFz8o;F7A>k&LsS*IDD#ObW!3=|_;V9sIc@LNVkCoO4i8@f}nd02M=G65){gq}+Y z+R1xSm-NH&B)%UmK)qj8T)HIZe*OGUMrOqdfby6?v5#~ee_7xPTVk_)ID;#6X)M( zz}3mPos*a)8g}MF>)rh$C52DkdPbR>g-bsu(2UdPfcWtsyRW~c*Qtc*0-zs?(tX&Q zEIftp1$2l40+$X(oS{LSk$G{skgsVPAsY5}eY$*QADkF%2x4BiHP2gfBoBb!RmR~9 z+>?Q^pLhHMaD?5@a?x4Hy1qN&0iMCirnn>MmP_@hN1Ad~S^3Lmqo><5bbn`>9&b#V zMUlU?F%D>&Tw`OEWA%4^Lw^1GBN+GnWNqf=afmHr z@slnBg^LDPGXO4Xo49dQ;l7j8CEvZ?yd6n%ZEo@4qE#jke*@dhbm(BcEpc?6e1u_ntN?Z1NLt++EB(2-?}y*e9f*&oO7qIuodYxPoLZqF%S(itC?V=Yp5@Q~lRun4jzB4P{Cg+h8J_ zcN)%ieT(z0p4irX>R1DU$EE+m|DFDChkMl??=rEQV)g}qS<#a@Q8!0*G49!yp!Hg5 z;G&@RVyEIO6+7mP{^t(A?PI&-t^+_G^iu_op9f)1hQGka3NJ#(^tDM0MQB02-Q~#LMSs|YyUK~k@ z;rl*jcX<0fy@!QB8ooA8AMavN9sr%tasBP!8gO)eerz7j^*(CCx}MqUzI3XyC(E6h zjl#uBAdzg`AKuY~nxt2(xw@FQqY#s+gzo#;i{D4i_qPH)9m@g$N5`mDtp&g?HQMD1 z7<{Rmo-1n}k8JG@^&>78R*ann-ZNt^#qK>zT`+B`7esr)q%HjcjW}-a=piOn^lL&N ztHgjlGpJh>O3e$3Eyoj1-eD^m+O0p7%6SW$yC*stC@EBTj8aId)lJdet)XG-pg$yp z&u~a%O~bs|kZ?6}8J~d@?k~Mu;z3F}I|{b)%}c*&5)Thy zDM|XG)HEqMyp$Z?3{|f=1ACVI9pJ_?W&y*DWs z)S&Jb=x43n9sRNT0QHfPk?xpR0F z)=Wgc8luF_DP-OEGTegY`{rxv@@P{~iM2A|u;AfxG2b|UrIwbwFk)ofZ93L2J zy(Ws)N%$rF(&YWNy#QgOGrF?3YW?H}hpD{uGV}G-)VI%`D*LPuGlV)w-^K9h=W9s@ zqOAclJT;PN%;$!qOS6vT`VNcAMX>Eh9)T|Uv9JTjEHVndU+@+@nJxp#1Ve8MMW-VZ zBW4RCO|vS%e;j|$wvHZ_OLxI4QQmx9QEWH)zqs2!oF#~Ak^{inz}m@8?QH^M;F8Qq z$6Y0F|9J$@iVu2zjdl1H9Q0f}!^l)2j7E^wixiF9HH(IzqlHann*4ISblJkxKNv18 zH$C|d=pq^%tNgF^8=E`68#Xmu*sE#ilu!!59T{HjW+rl#$XZG3m)a4n7h;MSa4Vo5{_3ZZXeT#5I z@_P&Z#Ge5Qgk(|gK;**~v8H4djW%QvjXYObV}jb#=xdF;r_GPg6Me)}VoRM2@pSL{ ztfXLF53xD@bM9Ic!Wn0x*GzkhXp}iK#niQ-l!FUAQeKgp^g!pzhs>_9l}4GAnno52 zt=*|$NVE8mI=)u(G-LacO8}yq{B~30<5OQy~VN!5S+6|5Y7KXEansQ znh}*B{Yuu(w6@4b6ApTe515S+*awuJxwni5!UN&q1cXPm_qd+b?}vDbxSKeuls-1` ze;(@_*+loB+n_MnY_4404euYz>`wKPYCy8KJmpI*D`OASx0vrF^};f(V{p8ke~q^c zf9BRfrv0xter%MBsCl>1*Dz>+P|l+_Bwur87+4@I1_Ic$SSZzG$cVJ47=ro++GX(K z2OsTP8#2M0jjg>zbLi#6ODM|d5NSc1PtuG%+#xKB>++8QjnRE-lQ!!-PH?CgW?CjlNB z`t;ZK6{Xt9ek`usu>BF)LPgG9HBf#B%a%X&rti7g>U3$JoiZ>lyICOMb+ZOlC*oT= zt4zQ*C}%du(WoOHgHdzJAqdpwhSU*Cv9Dhe$E1j_GxlsoeL|({M^Y<^#RLOQoij;~ z2ZSYwj0=ItH*W?2zgk^D;d^-ndmkHm>3=`)qOQ$=S z(ZgU_3i!wOvblDo+DoSs-_DLDhkR?c*l+=o_M25rbdK|!zzJ4%;QqX8u-r;m4+tZC ztZ`Oq*tv?>VPqsX0%Scy&=K5AOtUrQX_bOuC(!XHJ_OXL?_1zXH`w1>}dmrsrb|8f5=PPqUQI@J9R= z^P=7V_!EN-)a-5RbU4DF{e6)lhXU^Z`L*xAV7TYIjlRfj#vI?-{f1pAH`y>IPw-{1 zLXP!0Zu&SZ<2L5c(k(t=%$Cc=|2eeJ5_*%atNnRn`(5VxvnoeMrdGTz_iZLXG2{mU%=utT3KU!SoIpoTWu7?XY)(b zY{04iB;1_DjCPk7o&L28`JaQdkEy7B>JwYzN7#Z$vj#<`o?F&a(u9{LEn}G;1uX9J z^G#jk)?E$b$5NLu@W6eE&(YBge95^ckPln{VIo{6(AVhE(0z)70Y_UruD)N)wO&rq z@P2IT_t&KirqveQj=pTm>%|!mFb?mLR4nGoZ| z)kCakN_g89rZE1_WD;2B@%V`A{<*zF{_yPa1jr?tH{!xubCfCJ|1dS=M#u!9D?Fs= zcsurUAnM5YF~5P%URZ9V{TavXtipZa=n%Lhxw2D6559B=4g~8+mU*F0hiR-uPw+^p zKhS0KIOB(x)VPuP2ka|L?46aPLPmYN9YP7b z={9L*5#+D^s6jMaQ;embFf0C&(9rInbT92Uko-G zlXbA3D{QGlc;3w2P*_rX_EU5|IG7X$ZkUf_G`D1Mao2?w4zqsB%g{#$!Das9w4~d@ zcj~HNlCLQsh%y$6Z05nB6%srVD3PedGxZu$1m?NKsy8Lq0g0m2|uQF%ciO%(eAA5-= z11HAygqXP=P4t4XVvb{(Z!1u#6dt5_b8ukIMi2Jk^UHz1feS(*F^h{wlp(uQfirjw zM=m$nvY`h!6k|h0Ks{{mixC`2U(Hy{g*Mtf?jXRXvz|fV@fK)=Vt(uEwe0| z|3cd};K5Ves9X@VU!I(O=*GEbp^wg?9P@Z?ZI*dxlKHRQ2pLu>Ud8*(NS;PVpXvMQ z@KBON=L3snBfcY3bo$vI7V_Nz$JVx5{fsxR_%*&5T(sw81W{j&H&)W9Hl3Sja^;Wy z9G#1CfY86_M|aa^M%KKJ_SHrVgta>-&Z4b>0Vgo_=7ytkF{!uJ*E4-& z&JL*@g4|!F`zrRjx?#!NAUL2Lv1(Ge>d@)M^y;H zN?lGS*rL5fXhmx4L8`VAwQN9N6`J;Dp~re8&$hm{6S1$*xPPX(mh^6Zi#bkCc4D+e zUIkwvq4}4}Xk3|*{e#{utyL)WHXOEH7{asJs-ND4=#3=T{t54~yV;#b|Mp*oNvB65q8cj{(gxYLV3@~Zba`QMq>v)b=elN9Nc0g1u-`}gu%?Ch^>=*a}s z)zVD2246?TQm~rcdr#By_|zodi@;gH517kVH1adSzicwQ*pKkg99|RbO7y$}5&VCC z(=h`*m6~knWxhVzTw#wh$={Yj1w~6n%bH$R{GB_lLu#wZX8a68^pG)}n>BZGf^h@F z?pYK(jB*#4YmC~sv9QXF`I)tw^LA!QfP5GOfW@_S$%VFn3=&$lGYYD8dF-Um;F$yq zN)}@Hzt^ThS_}5e1;z(*I}VE_*A~?WhRbC;`#e~S7sTmNs=F0o*y)LsW-F7CFa5Fu>8_X z#@w(&$cKanp&hCVVGpTo88E6KFzkKSyd>I1dKqYKXMH6J|K;h3xMd|?Hdhk#D9D6B zOaC`ELEk_xmqlk7L(T}+c~3~kijB6dxD-btFzqHO&=pf|lRzFVJZ49|b%QDdnSl2# z&4x!KuvRbkDU|z*#r{Pn^Nhqm!Nc>xkt52YSVYInqFHAi6l~BG2qRKrL7xu zu^T-?#Y^TWHS42L44TZM*YUDu18}9I;X3HS-MJVjNB` zG)h(leM4xkL+!sPQK9iNZRIlHa;GqGFJnZ?p5v;r#C+#IK|k3M5{rphtz*&Rt)vEq z?gbhN+6?Q7tajSH_YLT;do9-@h|WyzrI|o@i(N0qbxRdpNFv6;jDT}9_4|ZR=nWLS z#1J%M+XR&XK!zjhn#0Y(=3u{s67xhr?_W-REFs&8yo5#}4CCe1X1wCPf2|t5itE{h z>bT|MKt(L{9MxOI_0uW9i`9vbn*ylr>h?~gfIbRpdN`q{is4jQ$q2q4nmTeV{w{Ov zJyG+TDNeM?*2?GOC&2EjOoXNZJ#+EX%?fLyxrIC2+tj*Cn~E~-GV_??<6N_T^&!{rM3s zU^P10VJS`l4T9Y{4UBkvF@@X%RhNe?7@)T#Vud==&J zEi{5;N1N{T@5|YF>8UqaAq1blR}#;Gu7%zhx}ebLPB-zzxf}|pCi_h|Cw(n|@~u^w z0exKBE(Viv3W7lI^TN5*Naqgupdg4K$eZ=)YSKsnp&xxtoDV4+>oAp`reHv}V6Ttw%U=u)@ z9riww_=;#hI`XikA0Ey5EN{uHZA;Lj5|hgs^H>6t3xx*5X9e9Aj-SUrGc3JBR$W3b zp(})ho~XTK0evMl>VN0S8sRXV8}iOnz%bpu=-dXTRffdXH6|OjJ9X-O5NLOBIju z6^%3NcV1HUw)0%Wq>^GE9hzI6i)po~CZWp9@4g`&n}J#s#jr`BFDbQHEzmEWww_yT zf908xAfcY#=c&yPv2DAQPcnNSb!@2LlKEV%C)4fsbMA9lgPZ`Lse;lI+T!w0c5K9g8B>Ip$kw{Lhf5w%V(1|HT zHmy31j^{cXoRp}DB?5*Dnw&%vGBRflnIsN55+T6CM(IAm!K4r>5X`AW-0|$|fH;j! zKy6+blha9lKgLRKLw6$En5GE|9!G4JA;WHChJC5VOWKlP#5{#mRrr&Ypv;1ZUw#?z zCC{&bpo5@e)>rC!`y@!3n>hvtX9E;(w+VL!EsM^8WjUCF*i|#Oh5JL?i!|fFtHtBZ zn{~C^=R)Dwx1WHaD(z4cWr-z^#(bT@*dTkP$4b_`wajo8S%(KGTTh5U8z72H| zp{4x;rYK?`u^R!@N$kmN~;`F^0)Ibp>~q*L}|monm_hZmf0nHkXJ#%`?K= zCRO5Ta6#CsB4rMx4;zoW%to z0g%L*Fu6(y6sf&J3Xc@iWJ`t_$2EN%2F9n@FD^~Tae8gF&7)QqbI3u*D)C+sc&If zXUSs)ywvQ|-!8I^9aB+aCsJw@YyTeRSH2}LlZvb1a}&Cpntm2MmCKM-KprBwl`f}NH4a*B-QafkuGPBnEv(1LwSB#a z>7Fx5cHmdBtE6pue6s(Bd{x#@T3vLEP^bYgNe{@b79}E%&3InIp5dY3v zNIm8+B;3tx?_{BHZZBm|9g2%HXHQ?ZKOw3n1ab{oat%m(@QNS7%7+sOZ}RuOe&q2{WZOSk{4n7Nc}!e2efQ_x$r%Kx~0h1(Q4_miMkBJYOCqj8_esPccp z4@WVKY(yAWag`d48H{0p#wThT+!7zCWR~cM|p%x7S*tR>X6D z4hfotRi``v0ruW^vWs=QDBC`DUGEN)=^-a7{pDcx`8F~6Uy&qv8M4qo%$R=mg%{2G zm7*DlU!5^|cPkVIebQg7uWKkM^GW3)@lwdT%F^0hOTXbOuUkSsF>iWa`Og%&>(Q`i z@vycZoF9izLYj~on%Ne?dDj22`|;Gjbj5J*DW(p(!mdCbMT{589j@5xmuxP0rjQJ$ z1|2_bsvci`E&)YiG}2l-v_fbPC&g-X4|&N&phxN7b+~GK(Z;NLNu-gf0=2D7jRswD z(Wa@QlDgGtqAk4BkKU)2;s)&Aw1~HbA2CX7y`;B`A3_W@rb@TtTC#@AL&Y`wxS z^shVJEgh?0UFAoubQ^Z97>KzS<^?4SH0j}{hTQc>P8qF5K#h%eO*-NKq3NB11X;VL z(Y9^dwr$(Sv~AnAZQC}dZQFKF`}BUl|3qCqQ4w`jv1%nM^W8B|3v?dvNEpwASY?`VXQ-nriN;*Os4DCU}_W^Y2v1uV?DAYIOe5t zK)9Y_sa!h8FfE{`#$N8N_!esIaw+-XJ-J~^+S^In1VWqs%ENfu5?6Al6{Cz+S_0ci zXE{5bJN}ac_fxJhvsmL$x6;Lm`*^G?x#z39IjW(2!cnpg&O?1%rQj9$t<-+=nD!5G#+O8$n*D=dCoe%8YK2don}?ACb-nNBdH2eQ38(jX=WRHa+VscJ zMQIP*6(}w~7tv2dJ=Asyrl(??f6Zx@`K;^d4viWl_cg*c|J8mN+T-EA*d6KV19^ytYllVM1GOJGl|eAe@!-p ziQ9~6PPUC*Q9x|*n>H&f>Onux4UGb-csw~xZ z??iL8QZ3tN8gxtrbcVn&_{~cYwn1{vO!5%Hs-=>2@3|x`AKt@j=w}XmxQ-40M}ESP z7Q8oLq_bj=?W9#c2@mYnUb0Y2-qfAKv(~pvOv#TbDzb5qws8(#06nU&30LAec+NQD z$Na>S8Y;X=9b@D8u-%$jgwj@GSG@yy1{rDm%VWAF>dn?-6Y64>THwW2ex8-4ZJ(H( z`SWbILTEBOy>KbzV{I+5=Y8Xoc5lhh()I`y^xd{WJE${8tt{x_^3xqcSK4J4j7HcJ{pX|dV3EHU? z)@;fi$popvW#uw^3nM$A@wDaTL#L-?q0E<>V)mcss)WL;me9fp?bX8iu>39Aa<(bs550HVHeh)g)(XuKvaZJC z4;v5bw#kzVNop<^C{0D%DXR*J_FAFUCVD8yc)ajXmU`cssHRxwuV-8KCSzSkbrG`d z2|6lP<2r3hhyq?7NqW1jX8lZE2gfdC%j*O5g$xV9)_>@4uIgiC?ubimgv+EYQKLF?ZL>Ih)6>0I%_(6zk!amuFn~I_I;*7qfKD+#)ukl=?P1cT?~344@mW?%=||tFEZxAXuqeDN?Q(36o0Eje2gWV=4^gY zpUYh_95}c*^G}x2{g+pLAzX#c^ffa~>k<(4#ZhKJog~^;sC<9q=uBki`>a+MsXBH| zu5fJN56BOuQbOQQbjZ&Z1sM5qVNA*1Nk&E5eY}C@7FDAe|KeUccPMOH1PT}AA4N!P2TI!g+3geV zSNdG|lyCSr)CUBt2_*k-Qbx+_D|Oef;xy(Vr=KsE*7MP~phuO>`A)gN|JZfd-fsQA z@Mka<=q*Sg-P{+u_wpoOVk+*gElBzOQKR!vRfbjmv1Ct4lF@H$SLuBOvOaYcP^m#t z3cFTZR7!G5Sq@B8ay~4-MRXfb*fA^Z1oDIUap-zJI%Dm$0Q+IkF@pL@6y~Xu{`>3X zG5WZWe>{L$e+=LU@jZ78Pff2%GCqaCp(F|&z#&8Ga8{Otz>2^s^h9uZ9TDU9fHC4L z7U|;;jK-@n1|DUgP@b<4LE#K=3%El}lZ`V7f722s%O_G%1KbvpLbzD0aU_?mXxB{F zZe>@rUC9=g&dMh9aYwNK0GDenxYS5(3|X2>4A1glmZLT=en*-}K!)bZJNXS_F5nJy zvK@jT=!-@+#3uxd)CE>-8a+BHf?r<&Ra{18`U40B{|}$`mrED~hJvTyNp`UlWZ(oV z1(q2*Pn7i^C#8DW2(d?PJB4mmpLM8@fN3 zk)H2;+7!L#l39~?Z+a_Sdaa=!doc9lwxe&1lDf<{khfh1`q-Vy;`If_49aq}UbvoF zv1A{|;PXg|Wu@krexRuQZ8fW=X*Dyshhz%mT9kc!8&a*#Te%YqEip}4WV9HHGsTjN9%U<&J$>ylVw%5 zY~sMVWPk*mgi&t@nG1h%3`53vmOY{x-&a&~w2l~De`;S)@U1c$fe{yi#4Lw`msUX)n4W89&ZJ1mrlj}wPzfM}q2 zI3kpKEee)biUpteDMIo#01mwIG#tJ@Yy4boZ%0++Eb($+v-o%prQdRlY+}8O#L#s6 z`pbHE^bXKEc-cmjFl9bBuwT-5CXz_+{^{`CN!CzrArsy->ch;eg-f@o&t+u%ph`;F zB-DB>=MuQIPFY!xvpkw<^^ugEOet)bSH1mim@z3Mri{5P47!Y`UwHlD0Fn-Q4Ah_! z0bcdzS4c|(d9Uf_Qw3DBu;Ah?Wrdm zmM~1BiqvJ@?piD=uMBaLv%LjSE?TUtRN!|HtF+;cNEld(iXbJoXkWavcAQQy5jd`Hm|j1>U3S7inq3B zUk3{HC_d#siKjf*?|8iDOs@@d9+eDs#Iy3^@Bco=!TL(p$s=OxNm}RK3j5#1aGE?f z>$4y;Wy%Q%z=Z39I!$nXt^00-YrZp4P9!m>;q1^~ zg+syZ0qufj5UVbqV~G8CTnr+VHBFSudm-MJdxhIQ7cKOk;?fW8lf7DifOlCK-y&52zrBn}*+1wy7ri*nkOioSo~o`e-Ds8s@Xg0_9N&WF4KQZDXF z@KP=isygzO;+%v^*gTgXUs|SEawrK@B1WQ2K>m6Ke6hG4gh|q@B2^{_GO~2rc3<12 z@P$f?BB5ziU8y(XZS4+mU={)v2E<)Vh@Ru&z|KhulTTZOJ-)TN>_j%3?;lKNP-`Mh zjJVDR-o5bNTsvWy$CNa(l8tis$SUt0xcJ29G#EE<2oFh2#)AUl~lG&;SocK{?{mdcWAHf(6)gKehA!2q8 zSc1Kpu|JC1;qD2g{zJE)vd+NOV!`10a5>e^&gRvi|D57Ctg z9cpWQUo?gF$^Vw&nDjT2ARSS-lv33+LSLOOrSVD9EK*JDWvoQgbNm5-Ne@RL1JV_l zQz_6Dsg*MLKX^gODA{+K)QHgrZsdNf#T?3_)f*0n!^&YfzJbjE0AtOEd|q0TL(guj zb@8=P4ouuX9-nIKs!C-k>oTW!v+P9W=?6g3+zt);JTpRkhH^tdLge{k2JMp|JAM z+)UccF)borym{Su&85-Zj~^gx3Nle$a^ZCFjBNrtsf;Z#dTwIA%|sdyv;OM*%SHrB z6rx+9#9bTJiu05MN{+2ta+|ER{8$>S8y@%!nKoE@)E-U+C;h47PiCEQ4#{Ge>OO1q z*Ykwc5I-OC(K%3iA$TISQhw(P2X@_M_HwMrPF+?EjXjl#ksPgSE|dyMk))8Y`wTzq zMW4d}%Y}%;o5Nw74NbMaDKk-z%Rha4aBy@V$}odfa+y642r;vT zwi~8K&az(OM14L@my(;FTkUvvamu#V;Z6i50q)b?B1^{IV@yg9c{>;&SaM z*4t~^X3t)I=##nB!ujF&7FC$KzqhM2MnWzp#(LD@paYVmo8!@dXdPxi-KuM1Cf6BB zg8gn{@=f49VZ*snRyCkJybm&cM~$S$PFe4_hA^9D<`ZIeP0pG9V#F-w+?VnO0$b-Y-t0Dh3c%Q~*M&U3WX_3+*q z+oRSp(!1!M0kx$C3XQz@LT2GnAS;=}_A<3N{go6dg+#N?3;p`!GE911JC+{>JDbwZ zf3EEl+4;a1a7`%ChJ-quI{(bQq;;I2{o}v$bNbw+U(}R=gVA^G`eI>f!XxCqK@nl~ zL$rj?Y#k%kvk5sm{@E1&A)E2UPEEbkXZx+BuN+gg8V>TU9d--w3BHXw{b&T+@5Dmh z3T>`}gEh%Ne=U%shs?uV14^BwHCSM;7_BzD%wg^AC}MSBvx(^1pXpziwnT*ZFlxFSn+Dq z@U;+Y0rUG4yy93ZtW3`XQ<@#dNJJp=NUe_Zrh1pYF05&`LzKE0UR|{b1Uy>ipq`Jl zNVP^#$%8(>wC+S6pS4638L3i~`#Ojt`MmF8lm{VtMyz66dD_1+;uWsFp{H&GY zETnS}|CBW%>U*<#B*-SD?e#5&ON!Q-Os;mwafiVpjy*DhHOIFCgcqo-;l*>}KR(oI zV~&GBN2ue}a(c_r1%~fb9U@3?#%1e}t=zoU>}ypSx^^go{Z@a?q*ozdB6G8HAp1rs z431Vw)~NXuAYU$!Sy;^yT4DdF^=s1w_C)6NRAy8fIepa*(n+MfsgW6yU9OM1muvc# zag)RpUg^|C$w{^%WQ}(H$6>Q*qwf6Q0BgEX2LP2BPL8mzjQZasV}vI$udP|v`KxB5 z7?6tXVW#&mgY+=7_BeC#oAfsS1po2dbM*2HWOqYt*U2X6ERs^6e^30T*wKF2oM4X8 znz4#DlS=3v<0^3XkYqS7sTBJ`!B)Bi5E!X39Yp z4}V+r_aR@1%dW--0~XDfOYEw*q2~PcF@cx+PI2-lD9wM>OcQ|<^TuL$oM=cACNcmf z5Lu2yTCsDOKW&ieV3$Ef00S$Zzz)r@%)5Y;o;QJ zBdJ~Q3qU>K1HVYaKU23;xzfi-&Ddmk+X5H+`5V3G4RlN`iFz@nZchUO7Q;t2793uR zpdnzCcnSjHf+EL4>+8v~=Eq7y5y?=>xsN2zOf`+>=x103A*}7<{49<*K zPJE~w7a;1>LC!jF?nu3nuIZ}=I zQ?ofi9q>95!jA@G^JCx&3#8;uJL@xK{s{#BXuE#y2A0ALHCz804Q$HdA*@f$kLW7r=C1BlUgDh(C|y^!)m2Euf1^r%+j&;5l4qAGbLo zZF|fg+T&mFrY%zl`@nC;W87SNrBCE}Md-Od61E1dUIe@OiAniWHcl~j+g`B6W(=!Wcn?vUE){!dYt2%T?Ta2wuxk2?Gf;3w6IJGS2da;JFc&Bz3$PEl zvshD1U;RWk|L_(k*%!Njb~n#`Fq}0f*!rdHif+_4i#8E>Fs|s2&I-t#M;i7r#Lsit zu#}DFZX{?U=VUlC#bX!wlD*srLl|NeeSaf_#R-O+Z2_H#zElY#n%Lwzo?*!oY(59L!c{u+_T0Gm%6ISjPZTQw~(EgA=*%Um|?22m)UTx!V7CG&F`@Jkr62)4NAg+uUoR zF`5IrAw;1mluRBza~?h;K@u+^^U=Oub&=hUBcR>wr%m;@M852{qFz`PT#t@)GP(pd ziC}{WWluyq`DkHmF$un`gvlIw4(&W1j~0}}JZjlfjbv1kY!r2%)I${Cj3VC!HeaTxYQ;>w+wO?88-CQ)q_k$0xTb#vA6REy83 zZzh5$z{Y^Pr&uI@0>!V5bDIC9xq_Y?ri+iZ_w|$kmk=r)gsaK}sxPKx1;orc86gQl zlN(h?GXRbhxDz%xG|oH-ULp}50`I~a6Ik`+68}q>i{r9Pc}?K_Msb5>ye%hA4xm(% z8~-smaY1G_=LH!9RBTau{gHyoTcK7KRsvLMF-oO38?=HlIke&q7n1%__8HLu#eoIZ zq4@zmqZQ{1{QdLnv8-j5o(=gQKqfOk#;OmDD}#@tU~z6oNoJg70n=qF{`_Q%KF@k0 z>(}-GA=e3qiL%2a9R$CO%O4)FbG}08n?l#gu`CT-aLGPBCw=w?pY48LM`#}JQSi(v6jSbV%Z_=#$5rAeR7PtCuIv}tH&;B-vxM79y zP*_pgQ`kW`>EA^6^!qBBucBI2znU zO%YpVh{sFIQS=+$K>M4)?%7WSNXtd0PSDZWV;gSG{c!DJ2n$FcmCa>_?>*`%ftx{y`~5Y9;- zfX4~|+y-!iNVgeu$|sD4uXwHBB-4TO`91^z9l63HM?yFKBrwLVGrjS#VBu$9<;bgW zHRdBys0)MPUe?G(YAep^H7BaQjshh=ccD=+ENC)y{jELE0!|U?X9A?V++VrwL!qgh z^|>5o7OF)4liBldE2PNPzOLYxbC3LnNe3zo&MuFIbLew-K2tAk)LHt`uK79ib>-yR zYB~}}eKkg_GZ(k?>9RB(inYXMHz>JJ-u!g%<>U~d5TTc=$`ozX?Fg-UEOD zIIIiI*pA?F_zJSO4)PNE+;W%SaH+#9{PK?4iJGK+Py# zP@2=XG>;5hb5kEGSJ7rBsqb>OU#I>Ek?JX=sB)9_O_wHBk53%$Z~20810Tz|jr_sD z*t30*B(Gh^z^^lw#+KcFfPlJ9czN4luMhGAp=W{BkI4^b>|w7bD6}36wT>_|s60ge zDorGS3gfTx(7%BBADK-qh?Y!S6;T6g$I|uTpHlhg1m=RhI(1I}JzwN;)f5U6TL*fkCbQ8Qz^xf;61{i9yuD?2W<- zGUv_bC&1%Qx{C|6vRXlau3LucGGL&8ozO=^>fZ@;7arSl{9I=xhkZnKa<%?P2su7? zQ#SCsr&7Rv&GetqirFo4dP0YF)5;4BAZvX1TB! zyEVHKAY>=jhseDInH8%dcQIKppRft9Ldr%3=UNpYr@AO8t%``#0?fiTc2Z*(76jfg zJMEUo=weo$11R-BzcJLxP!`TN6`$_C%~m<`ibQw z&Ii~BUBcMVBplwxvV^HSAOzs08fqG(E|pURE)ZN=y1@^gp{(3D*xC|4tN*)&0%%FF zBv2lZD>sjYWa~d4H;6N_PS<%&?RS$=*P-a;cK z$1QLT!)PldF5dTRG5%&_cU2{Nl@`1?O)@m71YE45tjlFw_OI{wsK=Cv5Ap-~NhkYm zU_vebi2RKe4|iyMuiL2ivcWc%HRRoFF~7hT4%%diKoh&O40D`6bTefJoiTe75ZP}{ zvz`$W&!StlLVbS^+|(fGObJ7oy!@D`7}jfSU{`gk-N7uSGQI3C3zW~1GZn`h5Xswx zT>7=2OO+DNisi=eWL4ENXtt5;>(!8K{0-(#&l31h`EU9a`f#a;ek^HD2B%W4Ep0bd zpq6oSUGsf`I-|1O&GmD?gb3-z@xXiRkk3fDt2j_Vult2vU@SXiRvzwo2HQt!8UV0j z3*g3}esz>YmpcntMlLz8R#=R#m#-38$Za&2A4qfmL|;L((I--+HVL&*@17S!va7 zVd%FS#q%uG@gdRfcT;3JOhN!NK9G>^vYAzvu^t73GNxaBW(O9zGGiQhx~A} zz!W2geVWItnv5MI&6o9mhNR0Gzp1ZoMY>Bhoh_Sbq&~|T{_Q%Wjz8a(Y^;A*p1jnk zdU$1ThFJ5^BUl^ z6lxebL^_+&5)?aw;X|U^{1yfrcziAz|1QV&Dqf%6!-SES;Um*sI`l3BcAI%k-(gE* zfQ!ig1!ywwu0?Q^iEd?6&`L;J&iMLP7yc5HBPWRKm3tCU4cU9xt(%_%nthp&Cn1U0 z%-TuwHI{|$yOng1={oR9LQJPX%FIu0H+BJX`OI2nz56l1>0w=cAuE%&Kd{DE9o96A~JhaPM@yM4-D+HuK49tEO@UC_wIqC z#(7#N#>y=!kBYP^pEyon*l~IywR($=&hw6cLTAQp6Sbz7;*(Q03nFC@GmM~^L|F*k z8Yq1Dwt++N5`@H&9XI{}xP0foplo+CIR|}0tkBdOc^nFP9hnOiLUzo9{au3(Q3#id zAY>t&58mqNrbj9L@6H72vxNXfW_)B$`nKY)$lx2fhqDXELKrrT2bA;pEKfy5mKmSW ztZ^Qxn8ilOc^pG`6feJb$F#N>ctb0y>} zfbRIy6)&IpCEzo@3nMH3gZMSJ{fZy7e40NeSYQ?;^y=J~ep)brEF&$_>=2c63RSY8 zn|Ovx;i7P1QLq#=>kD6TQ#UgQ|DFs{Td~K2&GmZ8Fm>wYmqpQ8v(At-P>~m?%wLUq zG!0O&A>PvWVcExIw*B2`!x>X!{m$KwRxb-^J<;bwNNck+G3*1ihzSkQ4^&SHP+j-T zCwyuRhoRgrWgh1m6wnmUxx1Q7W8SaK&(kYt!Lp}b_3C=Qdcm8Au(^%%58h7%n2#LR zy@FC#lD1=P!#JzkIG&lvY|Cc`V`{VrIZn=-jqCobzX&)lQn8@GXkAwozr2*|jN1c5wX)GNZEJBH00;EpXeFF9K4V<}K)$v)GH8(P{xOETYfLx7b%1cqR^%0b2d`gVf731HES=9K3wBFrb{4JQ z4Ukf=|mWrz3Gr_IVOYj3_NO{W82(Z3rge%cm0HyJy3k7b@F z4By_{d)6&w`Cu%Cuv(7+Ca?>f1qgu=;cpj zaG=YAM6Sy!b3H4ikbgv1v2auQy1mF7jkWtaT;Qc|chyJS)Kf3DhK3fh+IFMtV0~rl zeB9~%9#XADdT~;BJfJn}Xr*t=p8I+*yB@TE@(LLgwlbI(h6@n*iFX4mSf>Ld{StbM zw4ZMjza2;-Q9bT#&vpN@H}##mF^0ugW)3MI<$<)t{uXGmjznB? z4q5JEseK3gVn?=X3yr7}4d{c&veIwW8oZ)o$P2+ zfV$aOo7F0FfG?J;xx2Q(&{s~x=d?Gm9bgL=Jaku{xjpII5*SQ-qZTMS_$>f&2rB`w zhXP}NM5iM#lN=(Zr>E2uwP#%*T40hGh)(q@!LcxA!Z5w*>UM=8jco^=D25!T@poCSf_+NH}4&?=nAlVjO;5`H|xRU@cTh=4{g&Fo*hq+Kg#(khh& zh9P+$Exyv#swon0aKySDzoX{C-zJ`s61X5%qNTahd8%6ec}JTLWgc5uyZ;xb_>Mw< zl77UjB#k`RJNI$w7t4AW#b+;>*rbodE{25JDQB5;(e)L{f$^&T@ZJ9E48Z%1A1Hc6HnIng85w63vdcI< zu)FRt1zN_~;y*TII@r@}EfnX@BIK4T+^VJcLcyEGo;DM=ds`pwo;s1rLIWA28L?`1 z{53byAfqA-Jc-n44%rdFnCn~nFYJ1ikZ&VT98o)x*=CWWi9`sihxL&+MLoUJI9KFw ztyhY}^P}@*B3ioCsz$Y1@VnY{F)hs| zu}XR>{0(dq6Q5I&1|v2Jj=r7Uw?wx(_H_eVE(@N80tqJDo`uEyTRb8I@$famqK&q1 z3MTmP1tG32APSU33lJ28;VF;QUl$_``x-_hn65vWJr-K@)wXe9xP;*osDg8Klt zBC_O4tWsHA`>cv&1bWY348*sr6W=CEcF~zOsP{UM%q=$OrQrl&Wc_w9rDO6HX|1Vz zhI_qWQah__8XZs?of(aGXe$=B@IOU#veSPIv$GFaAN*zac9_O{Lc$yYh2%sy!m4{x zM$G9Lsm{2Ee)|v0v)5>bJtVjN+7lrD{u7hIKt&oCl1gEULH)Sz!uC{}3<7Ev1TxQ1 zBc7vb%blsWuR;~?;KNWTO9s|PJx54yTw*dARMSQg={y89ThQ9=ruO?OeZP6;zF*im zV#Q#B@hD{tFYmX;a@J1uK(7BoDn@`(_}UlWUn(~VR>abEdH#C9*JF2dms4z%zwz^V z`}4T)igQ0Dr%lxy3k+qo_?IInfScV0V7(ty`9P zc)RZKrJ_;QLVawdpK4BwVVv8s4dX;NWBdMby1qp0Yqh4kc44z)yqAn7kZMYGrfcAm z#L=g+?eaZ%DY@~&=>!UEf(k1VBOLI}7P0sI^)Yj(D(-4lk`;VJg(yDtWYSAmr6L#;-Uto)ij-V90@KgG-_=eG-mB)yQ_RV2~9aHEK< z;oBZPJ<-fzozbo_Epn4$4*Vo&+${T&&>CWb*+J*sz0eHdB&VH`dLNoC;aoN6aq*HS zIe}^yGp8!@3U2f+w&G9pXPUVdZ-Fy;J0U)R(3g|qwhT#6&hoJmfO;QVSnJuTQE*e# z@u>JQCzC|Rc_4ZO^f%CBL81}n-#Mr1zudtb=IrMjsRI@dMRUj%2q1_S*mi~+XM&S% ztL5fe+wAJFclB0mz?p+FM>~bYu@8C~&!pRv zStmALxWQOh^n^tQppoPdn@f{!0)GrPHMNLO*$Uq~Rm!2Py{e@Q=a8Vpskz7#A>B?27cSF3yT2wA^fEf z9rQFTxDq66f%q*G|)2yoWQIsy+X^p}-A|8^&p_LCFALd&zgS>&F5fpZahDk*22KEn%? z1)%GbkMneOgj%NW1`sbsn?k)+(w6=4| z8diXpEc=!hqNO=HS{%a@@FWN)H%C%fwBVnRy(_ADi6fi1S00uw^9KgP6U9?*rgBM& zuXSFnWJC*a(p=vf#{!%{IGZK@Efy9C5X+lMfdMlOzc->9dUL0ogPLOCF@>gSK}CLS z#Xh~(Yub*&N^V0oyI~@D2959R#fAMIYkp5>z1K#{=fXv&MQ4o4RsmdplAG3^SR9{P z>sHIK4GFC~ z5yNAVmULq&c;92b%_M_HMolzR{O4?5f$O>zm90zLV|_s{op#EV*OXhkFRS14|39ox zV7Hc`Z=y0R8I0E3DOk`j(RBE#1%pV_~opA1Ktyjw|xlayGs%zvRQnPWIVlIjleWxn7P&iT3V~`d6x=IZ9-w zgCRg8x*0YZ_I^2syFE#bh+W?F?h(4#>Ntk$_nhm=Xnr?Iy#xF}7Xbl5-TC@T?n9OI zcvDk|Ja0IpST}!4s5^Sq9WxmE%-;}e`*K9=EuW7g{nN@HCDntmqUv`{dtuA*P<8Wl zK%!yk6XB0yS-Uw5Xrj7$Dm`dAp)2%nbhSZ>qxB$%E&(Ka9GrgEvR5o7Igxi)1Qb zioCHF%ct-aVULI?Py$A$O!WPB?s1_n^O;IiLhgG|lzw(FIb#;r;OSN2NymDdpK7rf z`Bjg5DkxH$Gx1;yF?RccX5-GllJ$2#e@5bRF%Jg`gnAY-J=M8S^Z-DCL?+9+{}~OG z`?z4IjdrZl^)JhV!R*_jeY-4m--y;0yVu9Whqcz-HjvDex`Ns0T>L)6~HuzxkrjRt>J&ZR74RK4sNET@L~W zox4JTX2w8@voAElnT6vcG>_B32k!IiJl+(=QA2lCHsPmoKSt0G7@CV?k_wTP1@4aiV!JNQ5^7X4MW$PTx`?ptb~1E^SWV7`zA zk2E0?{h08F)9B?gRO%w?(2lu{%<6P)7a6D!fudT{SsM4gp0Zf4#)A{cOKG;zoWsFa zkd6L4v}_fUmT~u~`tEv`h-HLxFsbBA@TjW5>vu-RX`ir9-|SktI#9>8On!gI&e}u* z)BKxWPRiO?Yw0Y^_1GfGBOs0GT}|&<$n`V1i)%P^eag~)UBa2_p@=MsY^qQz!vniH z5pa6FE5RYdl2{&0PeaH^QpOz5a2TP@}ylKJBS^^7joPb z%EDX+xNO@*FI&z$aupBs&mquM^i=Pr5M<=q;N zEfYJLn%Rg_Lp{SqbRODP>b2f|MyHtvrwIEx>eq^C-b4{kruo{joyQw1{@gH$sIz${ z*0n1zjgPy!C~QuHT>wwMD|4xcb&1vEblqCm z#HAVU)h}b+cPZb)r#95qt(1|s%R;OEm_h_A)fr5eIo)@zeD;S?u^t=1fFOrJ3_BNY z(v8e{FJF_;frnS;u>Y=rbXEE~&AznGf(?RBNidfu>D)fr_z^}{>nOn{} z{-#;tO&ijp{DR=P9M2^qQDt*Dc1tbCEcqlgae>Wn#LMZ#x)b^#?5(w!UUYqKZEj2ca2*bzj)#hCTmId-&$}9Vop!9e zcn7Fw;Y~Jbrga(>wiqkI5!dy*ZLTON(N3iWilu^zMgTKd3s>HwMEl=isRR_Q8ub98 z5MrJiDFe_`2;-i^a<dh!iYh>q@IroQFxG+sbGNxk z%RpiMa;=rq4U-Y1^FL1euNJoZPQ_d}v_R zw?jRKyp`^FSCpPQ&ieK(`COGy*Xyp?Uf8_yKPQ-V;P2e_)kqzdNHgfB+g`}F8OF%2 zz5s{)twm>cUp45f-%Vb#lU+MT_dPz-6S2SSEDGxB`CaiI6mBWy} z@Pd5B6K<=2pQ1}>KX)+Ed#(ZLr*405eqvYi+aDhCPNA4)g3(p4*u0eMXyJL5d|%Td z%{XGm%pn?;G^24x6{Bv8iNdUC?s~5pw1bES7sFa{EKGqj!i`2r&5}W)S!ukCHy#6+ zVs?Z1s@Q|3;|0|dz#0lR+vsnEGN|P7VsHU72lL(eL9D=7$px`WUqo3m&HZr3{T(xc z>SaA(vvPW!Y3!47R{M#q_o_F<(5Qc#D{6H=#S<|^2qC3|syZ*VYPjaj3smiIFrfA3 z?i=a&(3z|r3L2TbwYCChAt&l*5%J$AqiUb!C}XQ@LMrXHbJ&a>f;IkoLM(jg^dieV znytB7nUN0_OVw_o1^YL9hEr>Z2ckjC7|Zgl^1X*>C=Evj_ds@m9AbA z&~)~8Qy~`~&M6em={v@yq7dO^#^|QH;WN`&FlR=KK39Cq^w{S4NLJDX#|a&ntfJSE zaHq^q(mf-gEM0Ry)5}^8{@?2_2V(%3hSSyl5>a%k&*H$cI7^sqt>AGjp0GW6!%=}L z)dJ^cljFBIjs0@Ff@)=cB7i>^3>OATm5gigrL9D`@$@%?^NCutqIx*_BBuPwB$GVXsoB+|dwGTmP4lR09=8e7y)3IYqZshax-)5a}xi@_=I$+Av)`&l4Wt{|Lmmj3ji_hC%Y0&Jx zvxo)n{2`^|uOq3T;O+sdCy()ZBZ~)`yCdR*r0Vc>{%(n>-1Ad}0zO5#o1*un-xqt9nh@LHXkvo&;NupK;(SbW zq(iDiF3y{}NY5LmC>6>M*Fv%K3-ZZG?auKQY4IyEHX1t3Ly4esv8$4f*!Ad&M3wf) z%O%?ZYNgbevJ;40iUfm-quGE+yC#^dbIy=WjY`bRw8z_hQqnn%w(xt8-R;A0bW#7` zrK6HeAH__Y^Bbm3m^9M}^wpV+YAf}ScL;?ifh8km+T778vC3_WhS&kN;U9b|43NPf z1KS~nK|yEjr?@&f4YP?Lv_oL>Qq(h7OsXALLOD9`r0D zJr;RAIhqC31kb6ni}zpVOHL|CPFgl;vI%#AlPvgUqD0E{9gxQ%?kKQdazs*&-yO2W zOU3pq#>?QsyX~l5DmJX96ds%$i=2y^yrc4Gm|k{^mw0Y&^%&O5uMyX=W!Px_!`;It zq%pcNySVWe+hOXGS}-`IIm9@`G&;ug+Gjs=4M=Y^AisqFUH4MBUC>mIMX$0wN~h&{ zGLpaI#e6-7`8els2?*-M3v+K+w&SZDI^5wJ6yZU1Sv+-NOQ-8w#5#FWYuqsJF&FYG z!58GPccGtYdAgT3nsRn|@i%R_xec)(EE zsvfZ)2)p0Xpt-zRi(U*PS>9RM&Gv0K(F8UNS-3M_BOhoY_^4#rafW+z3Xc0dSxBor zMVQho&x%I){1FHN^7|HwaWUM+?k#CRt*SbR4_;#3U*=M>k72V4l{{#^!^vkm6SD-_ ztP}APN!i{*ojIGiHn7WPO?c{d1cHodkIGTvVH!RXyo7R{B(UGTX@|hbzqwDMx9%-I zq-zLcGL6x_Ykwt0)-&xKyWMi4b?#pzx}A-q{&LlMPFs)F_(n%UCvOc_%x8sH@5c^l za60Q88VOm6Dpltal-S`Et2%`R(Vl|24y|)8B1H2cdG2cIhji_Suq}e9k|>wNaDQw z9Jq0sF^u1}&|t#NOKrzgY+a1bmCh#G`_UWQ%}-FhU73O|8!c4@B76k#=WWLQthFvE zTzDMBFomtKP6s?;x_oLKuLp?9q?15*8sR&Q{c)oV!RXJrGaQN$ttyTo#D4R$WfyU9 zFE2MYx1-J#lZl?S%D=C-2C)gX-8r&Z9J)6L( z@Zy!Z(Yc1yVPPU685_N*c`YX^*|-EN$RK)J{JS{B>RI;0wUmq1+)>FwXcTTp4#`qr zM0)=edXhT(DrRqongz2yzMv{I9+dgn5JR0`#~O4M2UZhj5E#*wgzo=f@9-j0zN3^-Rmx6! zV>k$!wS8`HU2x-N`u`;qs!j6YDpYtvU7_1p-nym@UMiZ4Jm`Q%(QNeH$2hHRS|#T7+K>T@uYEoU8~!g<9Up7AS?yM5LgXm;&PIMaq1I##XF^8mdRW~ut=B3CfeV)T*P~f?@#!sQT zIf5a^d4y+SBn)gQITHjQMpjA!93~z$HDL<}8wPgy+@vXquB3z3dU~PE@i+Aa9Rv;m z$21tpqDpcH*_>hI`S=hT4lLL`6kNm1=Y~2@B;4l=BeCkg_1p940s(=M7n7{9_a6cT z{=JTz7Io1cJ2(t{&X_Ez;5tvH8n=^8geR5?65&q9$*d@-xam9vWSAK1%S~{W3Zz=B zL*&6vQXjZAQP(cYlAyx>tQzcioa?A3gqKqYEU=TYjT^Nw1|}ibwT*L^4PVEc{d4Si z{kN8j`-SN9P!@&;brmb>^7)!^mH4@?DJZmOgDWF3m57Q;n8ekNcfJu43c*M&3g0p$=54OitATRvtU@!&j13(yy<_0XN?>+yfP%5>-tH z<0`-z&q5|PQ0nMhQ|4;{JpxEuqZP6mdEyJI(H%z+ zjtRvjzYtU!V5USPKog<~RjCB0={E?aSJ74T0B8YL`>6?3I#UGX8rJpL-%$eA`l(Y6 zRd#cje0BX>)jh3K0fVLrfk9JntZM6Uiy}H$=x+uMQbM#$wffRNwhw&K%}0L{g`JHs zR8gUneXlGaM#r5cWUptC5v4axc$Vr*u>!95(fZfiZP&{FW5IFGx_@MH;~Mk!)?F~DnSW590g%s$gI;RFD|evj|_F0zmGO|K_TIz~LvAWS^VQcx!#fz7r0 zIxNUxRQ7$wbM`QQ{xbRblRD5h%3`Php&%WD9Pm|$mZqmd^x)MUz)iy7%MED=R1brA*21P!~D zqJ{<9B09=Cq_;N(7}ks68!z8mof+eU(d<@nGuhh|05iW+ui%__j{+aolhgsC07a2Z zmK5Q{uu>=-7Y$CqdEB+1WtNvtZa^fgD{tU1jAH8?e#gmz9H&skfV7m!`jCs$kw8w<|c-zk4IbSLZ~8_A1BJ&jCJMUIBta^z=2 zGAI$UkOm{273mc80A)x=g<7$y=I|TL3dQMXF^dLo@KcJ$SxiYd!bk~p$SxGt9~ zL6Ib5XfyTP`0T;5yn{O_mc_FZoMRM@7X(;Tp*l^hrL@$wym1q-i_mXi86Yf?v5CdB zVaBWt-yXMN$gJ9dbyDaAcABo!w&vWvHZUVFR1s?sR+=2FNNNWZ>#sK0K8+jY*2b-| zz}!dIJbL6&FAd8P314p^Mi>gCNVu9(K%;GHKPK zrA{~97pg+x54E?I$nF(lfjEsK+_^!EetbkWML)O{@jGdFl`i^g_-j~ALpWCrmX!69!Gi1ZVUe7q+i_a^W z|E~1};M_QVV7E>r()(hew=A6m)=&9@kC>tvAua+la>kXJE-?!Vcq0uD=o6jl{(&84a{sHR!2%O+b|>ka&XF&UrIb=!V@`pY%znPN0idetid$e2FbZh!2#uTeE$p2L!% zUjL<9fK^NXQXn4XQEh;kzm{=5SsJEjrpuEOlL}^8D@B(+<_KAsM`*6}+>RyCi0>BqzBlfVwap?nUKe z!zdkTN#>WSjcA1s)6#ONT97yn=iQGqXJh){-7vPPEVMp_|aq zS&eml>5jF2sF9+g#=|~fB|TtZfY8k{)jlFBJ16 zbp0gl|6D1XNx_Oelg?=uEH#*!EE2<8P~fJq5Rw;Ql!MDl##H(tP1rRlGvoyN6-7h| z#_XMyZ={C_D0=^d98}gAE98I=3@m0k#t)6?L-r%#6{QZ5rPLW{z{Lw0p2iYQ15}u* z`YDz%e!LQ{oTstI z0yj7%GZ{^OS+L_W3Zyfq1DS8kP{RVcU+?wNSUpd&I8H=7p=27S<`m2vY3PkVF4a}8 z1;RXtq-ACZ&AMLyd0dPvkil$2ax42J0TjPIJm$ygMs~_v~SNSkjHC&-di?x<@X|zpB`QJ-@ zwf?G-vfb^^;L%Tht`VYfbxuf{A;yTjxI_l zpD%NC4-8r5@qcxy4oI#0Sp!`$x>uv^Z#q;(R^UIZ>BU!L7N)w_5mBRW#kLri^Ls7K zAO1h{U_0igY}G`)jhD64HMS9Uv>*92=kVH*X7_#7*mfwRSck*N^Gv_B?0tToPrt1C zv({qxPTe6lH;4N|i+da${cgHMivkR6(L|=Z5=aI_(I{C0Z z?B{d6G_R3El9~&KTwan1%~qruE0gzoV+~0s zR9wY;Bkr*X?xp+L5}}A0508Z?51i(ioF8Z#dP=apaixZKw6*Any*>TI>dE!yu;WNX zhUj50Uw*dQGHh7c&g@m%*;>E-U=8HfrdxV$9v1d;AWpYy@kO9=*TQrRqAdi=;WRRP z%5w2ry3u(Wd+K09E)lk-$}yI^#to;1-?3z&BmH4wd)P!?SF{st6f_fZh>P;JI$l8Q zrotTZBCdXg2z5kCPqvf%_cyO*q=6K3dpM7nvwwRP>Re!=xdhc%Hj z@O6^YuwtAmMf16brpy=?y14NffXLqHGOl3UA)}*kMr0>-E&In-k79O$GF7y(g|?Es|DT}+DB&uv#5hm zv}Y^$&cR^#Od`WS>q)zet=^@}tAF2eRW(q4k){y14Pmb1lG6XgIjnliso(*nt2Ztg z3&i2l?I;-k`7uO(W{Ng?!&stv*ba&1%R8OXGGQ1qG{PLWt(h-{2mP2kso!2ZkT~2@ zw!cvBZHB35ya`UJ_>m6}C1xNDd9^uks1Fiz-~DrgQ0VU!x8cf)Bf^zq5>JFrTZCX3 zkMTo5Y=BH3jTmBWLbNAKm=d*>PP+?x*6>hE1l>DSCVkb^b0|VGzk+JRSxHhIGdz45 zC9q2t))tp^b%dd9<&%rTi#@Yxett-x(r*4;Zxtm?R4(uN1 z^-}iIGSW6dSi@SRJz%d?h=`%do}cnmhdk=LKuuhMF7+WSMXgQ+h>&3AJ8X(uzyhEC zq#bvHm%y=3M27_Z;#4(b%n=gmKt0Ma9`Gs!M_BGWI8 z_;ipD=20dWG%6B%=#I@)xunimGN_xH`-r8PCt(6xsGq_^X-z_{FmokbMVqfRb2BJW z$x%UmSAWhHDGgHU<7`v-um^pH)q5 zAS1dB78xu9-dMBO$Fg_;Nx5h+Q@YfoYu+N&OG@+7KFNF(ojg+v|GC{+%tn*D#G@G%KmGRAxEg{n$beg>)80hT7Yf|kK zHRVP|+iF8sTZH%OQNO3Gb#>8qclc91j2toQJrS;=ATm2Y=Zr;4fws*$5@Q9c`Oq^X zZa9UTXakY`PAQvrndOFN3h#&oAJpk`Pf?x(5wx~I(=y)IEfNB(JZQY3-L1W4@W?-v z#jhZ3pu3Ex5G_A#?QxOpCb0F#JJYc_1ez0MfU>gktf9J`6{5nD_;~ zKebNp(CY{|WMFs<8SZ`S|M8QRD%P=yv!njG8j4%BstdS2yfM-c;h?LmgjH$9Od-5q zpwr<@j4>p_uzaEsX=V^4vm2d@Hp<-3sjAUas2$mgTj@*4Xj~$^HxRg3s5~63FnksL z+H#B;MUU1GwCEX0cD_(2)G4nuxGRPWtz<=!Nwf61rC3@G$Sh$!pY>N450J-@#7#v6 zR=)Fuw}XQ1Gd5J%DJmdhV`a^$l9HsYX0{%Db2{{iAyhBQ2!*Fi(-%9|&aiP66JPPS zF+fCf@AT##8NvWIK8)ayu-uF6krS?=%T4t}MBB0+pgu*J-CHi*62c+Ejrrz$i}nn^ zNZMDlvLs@v5y$hv#oD;7OZDgHoom$>>LpqM=*6p~Vb{#x2u1P?yT~;Ga^_bcQ_{Vr zsnPPpe4eMaZL5NOxCoMRb8tC?1@Np}W#kMLerYv-WU!Lhw}nvz`LGB8dmd9w z6`WU#b#j%PnVs{`N^DFv#oFI{TO6t}!(0yqKVV&!(TaWVnTz(FONs30-mA6lA3~D% z$7dZ2GDzNKBqfn!gan84SjHa!wz3NK@*bauXD^k6dcTf}G;k_QilfsGPy#^v1rNx^LsK}WEN@o@|5!z^=5f{U1H*wm0?SGhk&^LR@ z$q`3EH+wVu{Xf88;IDJR_+F852Z(nMk2xS_E-BSbn3B&3{{hKE&wQaffhlp^{Wyrd zK|2600rq`LzXV_r+84w%&KvFxb*y{zeFVOM-~3pw1Vdav(M;Nfh1t?2aB|tKaQ9vp zg+9HImwz}68|~*VA1cTd>UM6HaHYOv=Vy|9RCcA(hyNEO+eTW&Tzr*&{xxnF&51;~ zmv^|1{Ja>@0Iqm{nNYrf>?yRbeL?x@+NBNs0aotF6eH+p{VeyjyVCYTYVY4ZnSM(x zwYBQ^Ef!lMN`-Gf3*ZfnV>B$9y0$I8M33Y7=v>&+wz_PHH?-)bl7OQtj0$;A(9GrkLS$amOPFj2vD*#{-Ct9%R^(rK2wwd7c) z3Gg1K^21M4P=SFK+9Cb}`VYX2$ej)0J+>uYSQwv1LqX!gNe2-PM95cZ4{(-eE{_<= z6ycpAo?Hebh%askduOj!2BK7Fd1y)~2p-X=45F7zC%s4z708`-b0>(-gQm!bnZ78+ zz*_EKVkAUMX~bR~Bc8lPX(HAgVv>ii#-HQvyAKF)y<~?73^}Cd(?rj%J-drEy=Xhz zq1z)4*p7bDxH5vrf|~O z|3myy>}xe-tnimb0z&*Zs_mIgc5TgLcwO7Denw0T1V}*%oPvd`m|TDV1_UMfYl$0^ zWLu(382RRQ%y)lH&is?|Gb74s3+q$s-{-n?cxJN6B9ZLv>2fhqPu)wiZ9;Jae9ADc z``MeNX3F*|8|dhKqh!sH9L!b7+++xQ_#vD@O48!i;vf59R{fKe zm49C1fAl1x%LNPNxi)d^|I37ncpdjj#q3n$;atGv*~l?u$H%W=$LCPS-)KFJwQO&E>*e~XZz(MFeJxLW3_q{q+Z4pZ@8aQOLj{I)evB`2 z41M?|j1OUgplZ8wU7PP5xiyl%p9^yuSTb_(Y4N$&Cahi@N(Aw6l7e9>2hV~!KoN)K z@bX*KMX|hRhwxDW54>%_&@p&V#!;f~M?wxF(tmjJxS*+(sG)z9O9cZChTNP(kB^;u z3NlupX~2#B8a{ZL9^I)@`|3a^hPl=tL`v1SgpiPzGraGW$0j3dJ`bO~eUU5)gMd?A z!m$TzJe_0wY^gEe@>t%;mft-(3k$W)I-jR1R!j(lRX`{rUJVgAyhLR=2$;0zb1sI` zbE?C3oz3N@9iAAg+fA6;QNYYY7Z^alTTrLDb-Jql%(A1s`8^Hck^R)p z!TFamcY)Ak68CNOt#QNpHLEtm-2vY|lMYIWRku|7T#R#<;o+wGT$&pVp&9Y|PD<0K z0c8adqs~%s7TMq8i8eNXR(`BVR+jk5lgBqGhu`!}u}h<^aXv!tZ&8oPS!S!Zhv_p~ z5pEC{Ji1=UjS23B z3suS=qvIx>bKJ$(JN?P3&$MA5V9j51cWq9?QA7!z3VECb0y_$QU5$l*F9l)^3&g7z zE_Gekb@RbO1@l~wm(O)q0WS#(Le8rV{rvj`h+vGhj9c58*>rlx&GDs^LB-GNx2E+H zUGS1$5~*Po(O^UfP-4b~KB5T5hV{?wl|CkZOH!WuBC`ja_BIq)5-K4HUf(;E3xK01 z6$AwONpwF`8S6_{0hF{@c)ne5cmBbK=_El%92ot8WgtW#voqVy^ia0=8mStu8|2M3 zIUNB!mO`K{c?w>fk9+{vFy!!Mf`AhUh2%>50BJcO!epcqX=?`2)v8hhBmqb;^9xc^ zb?gQI4Xlr{CnDD>TnF=@zE>08u2}DYQ1HP7>7B-;zKkSOr`0}Xcy528mq$(F=oC~5 ztJydN6}u#7ykaG79~gB_I;0{=URtUV`ukmL!4t7fpN}ivb4a>PKGC2csb1Kv&52yE z-E@fr?ZInMd44(*aWK<(Y;*NhU{y&z?@FBjGY%`ksUk6A6uu0b0*JS(C8GnaDyGK7 z_gsu)xt)?xEjHXCrkF$%0k?LV#*r+X!maNJ@lWbUmFJz=>+wwcbXNLAT`E_gd83+5!}5hczRc&DOer0ssVL0BZ#Iu9Y<6&jBcs z6oJHj5c)$P#QAI#(s*To&S>aCF?1jK&B_ZO`n|m#=LB#VscLsoM0(hUAVGiDJ5H)B zV$rT<)H;;q^>()xj}Glh)IJLZCw8%9=T=jA`zXMmumB78cFC2Awl^||`am2IK_N)l zM(wmgKvA3cH;dF>Ov~n0?#r|d2}$a@j$?F$-YcBk@JZU`ww744fwM&Tkj@m!Gyb-*8l4;gdMVWS@3cMy_WWKZLso4s0 zn=y7K(7 zm-z93MN24yW9#Lb#SdgO7tyvko*7m@q5*(mc5AXwE##wH3{lW18a#WS_sy902`V91 zv@n_!)sM$h9Iq^lD0MlMYa2(YdhEh}H>+O&KZCNwA+8b8eoa7iw3hcyoBT863!^2b z%utcQoUW#|FU=$ZBZEd|=wW*`^lK#SE#fsiVeRWl|AqZI-Y!!zS((p8h`Oh#e?Gts z+BEO;vUVG$njW2q!FiY}mE$uF7BGDY_na88rGDlm1Fpp0*$*V&+lr!g7|oaPU-h{x+-+-%5xsS?j^zfD z8!XSnehYu4tjW{z;j~`lY-BnI{!5O{j$^TbG#vQ#XU<^Q-Ux*b$@qDvH7w8u!&%f? zZM7ylNGtz&7AjG*^)&2E=phP+zy`$-7N z5o7)dyjv)EjSUkV3PKb+~8LcK6zuS;|r=-wQk)18_=#RL3H zh^;tQ31;Zt#i=gc0TVi&OvgH&#F?toP5In$T(y<**G&ca3~0HAUZO^B(Y<(!Z^G!5 zHP0c!qe(Yt1Q&N~WSRkD=&mo%k-jECg9z>+sKbbanS)T6cyZT5S0!Nyt7|>eNS^Cx zR=W+>LJAAWmAhUJU;mr$h0lzxJHJwK)CE7J<%NJ2g(58r4-+QxMkKBCQ2ENL)z}Sx zz?t8Zu6cNP1}0m%*e-OYQ*f)0y2RTqyjHjG>$T=hWX2ce;h&|?TaIn+pIMqlXBLc| zCF{Egf9{e|{t)8n90uT=?W;DyUAWR0@GgF*r<@I5Mg(s;;JHU3@0H|-x%2BSk~p`e z+QNg;QgTWlK_2m*$efMtXPmoq@aeFX>^$k4`@!@sDFIv`+w>>b&22Z2Kp$hK!@|5< zegFV~pC4cV1!-Uq6aX*)h~LkI01R8glx1K507cjU0I0v;xY;=x(>t4+*qHqH$VlgI zV|}S9Z@0mQ(hI-pM&N8`MDB_O?h>(DpGhl=J!Bh-XAz#Vs!$*>FX@fFe&7c<*kKTg zx@r?@pzV*gTKvJX#$}B=TU#ftlrXIECxLDCcN-oF(%Zqu4<3Gb<;wLa3@#EOrTqGz zFUKiik~?hwB8*J6-KiMMj`)Y0uJI0I036Ih~_!Zao3TfIGQ zks|QykmK6AT*a#MH~jlw)?~F@>4LRMgGK?EF+R4_YxBCLk<(f~cHA)g$!+wsX=~^C zF6`L8`pNAVb@ft6Bzs=i+@U^PHSYS2*&_5Sn)cM^(am|L#?VodJCiFU#%Xi2?UmF8}EWf)Ax!k6>6 zm)abD(NXvuT>jUy*S*05aDFYJ=?a^ypd(fK-mlAV98RvG=3QLCqe3WmZ^s!#cZBfp0@N(Gf>pq4$7wDd z6)gkt2dZ=r4$0pVZH7uCS4Mv|hC3sXM|-;^p7wnTm+XGfX2}EMR=n)y`nPU9B6vz+E^h=RG5FIwZ!R zCaHq(k&e^uxN<0P)Z=mX(~@C*fGf+S#`;xWO?2d`Zk;7C#Jl|@G@Fl80KBE;cKQ|S ze``cdl+;I}MBu5+b@_AB6Suw!vJW_D1>??=P#N*Zfg==PTsf$M?i|^MoVwit%b|6A zz>&su%GZLmTAYTlKqK%nBck?n$+aQ(22x@h8A`#TxRk1mvb=Q;Oi`%VPVDVNu_ygQ z*W<5=`>>&1%dGQKJ(_$D7wU#^Yv6HfhdABP%Wl0TStE`^c+g(#-2bfM@cMb%@1n@1 z?;)9~U_de7id+@jXxh3PsPU4)#4e*GW-qenBE94l#0Puga@r>urlg79RWxH_u(!|e zGlfOc@fJ~K3A2Zi_1anx5lWATBb90o8Z5x9DXPAkeWmtVv_2OOtHwd9)WuufL*>oN zWrboAtC+k8que44XT#0ZAEhQcnmN_{XK$?Y| z`o|G@(o+#iMWwJ&vc3VV|F%{V(A8O2dc&gnk|$PVW0o~8a-1wQ3c}d!# zvcicQy$JQ@@TQ#=x^^iiUUr&@yo^?jSRQHsjq_@=|K`70MLAB?tf-f>Mkc0oIBxg5 zMi?|$L?|~23BSOf`KkVz_PUarLj<2 ztyj6st4*+9KwmpU_mD?blX6G)&RAR)a5}AVw!6oVOw(MhuE)@_u)04RM-0qz@Q-1P zrp*{1(QE#7hR5j!-GL#Kkwlic&6d(X%?BrgCwQY3Z*)GO|38{GT}Km^4PXEO5-0!w z#Q$a5oJ^dZEo{x4{$tjf)HZB4eq)04^ndziAGi{57$br?g0D0>Y4@)Rr1|xdC-JoP5aMKjod~+e}n>Y8;wrGXupUr7mDa z31dd58&%<{3{O;#$WitNEQia!nymo3<2aWKJjv)zz%Os9+~=i5RjZkt1z3juV}k!8 zM5V)IUZI=?(mJcSQ*@Z9yo{hK7>@?{D2kv35D?!vpf3!tO+A?(jV3q@ffkcb#=Xh( z;5RWa29_3sfg@wq{nw=rNI?*S>`CIE+>WY3XG3mGChejp2Ty#Fo0hge$C*Rna^ zp0tDQ8y0@s$q&Nnjala?)JfPeKo;8KJvYiFc?{F0MTN4QF9Hi|&n-H*C#)rl;mq<1 zUayFy2S%|;#Jnd;myTw`PlP$C_C^dbX!*_(M^Z6pByR0UHQ}Uij3O;y|`1I9C1?A zdyCuwJ5hJ1SmniBmyIFIL?<+u?(9mAnXB4Qsjc1S_@6RJw^WB{k8+9}>noN}tMNu9 z-kP#ocS-%sc>MSRT3(K9E&-=W4LIac;4O@OO<5Nwao23EmSIc&(<{zJ3e=K~QZrpw z7Nn=v1e=4ZY!ur~(Yxb)lCvj8gKNqc=dG0BR3wIYUq3BHUp}fz2Ap}Pe=s!nw!51~ z^)D~}VP`7?4FlbV6?GFi)(#7O`k*O}`1C+xFW|@)O0ljYE$imrj(?69^0mc2n z<;u167i-ZKBgs=9iWb|Ev0<~Z5G+zs48aWP*R{pj%l*1Adfw7G>Q z`-DuI2$Uar{2t~yuO=D8{1DkDNLUM9G==iWXTYlKneXmGwNy=4gD7EBBdH)xjSAl3 zv4)PPdGV(nisB~4M-nkB-)+piPekAtay*f6L?l6I$?WL=n<-2F3M%MPpapVlca{8h= zQ>N~YN*H6_yU$yi%`|hE&9q{gL6Ngu`YNlVlfRV;@huobZDbvs2=U%uUv3~bMzpe5 zZ^*C6-YZP*>R4v2S&-w^b+jx=@{l?vV8hF8g0J7EgoltCpacrZPlopKEP6pJZqvL( zSB$ZZDSKsXkzLAbhH77E{wZCYWfj%j>;MM)CC!5S}6&8M&C?*D1hpG{{86+N>d-Mn6 zMw%S=LQh1$puwvPC8zx+2KEzP#sozILObB_pQ+}~0WKVmq}T3gH_l`PB?1A^P^8gG zam8taOtYlK#J-+?540fcQ(k)c96l)nVpWf7Ci2?=|7~D& zIf)JTWHVD8TW9Yf3F?^8XM}ADfIesEg2s_^r%j(px=<}?w2~0QLx!`X$yb>)Z;Uy% z>zZ(=f@b5%rA*KmbZuU)B92Y-IKv+wUNCIOErwQfL6@B@@7&FgzVqJY|Go0dkUFWX zl0+eEro3b$pxdhJD-3rL@0E&(CwrymQb736?m`CgHPkv(RY_q~Zu2c56o;B#>mjT?_ufRQa!1hGFvFS1@y zs_6uyYHU@2w5lFZjlYH!fQjrkQ}o;ky}xXtzrKa8m21dDkpVaXXujnse=E&JP5vls zR;Xxu_^ykj6fMqr5#fEE`M+G_9_BiS-XSx9MS5T?fG6o-Fag-_Ih}{otT6DiTpZZC zC<{$9#ywYa(;RH(MldF6K;R=;k*_6$?x`MysZ0nIBVg4BGN9pAu40{%--cCPS9qW0 z7}gXUm1D(tLDYjOqJ%n77%HsrHbkDm7&agaj0dsbd!ifrnEhdNAJZ||RJrKA7(lqV z4Bxp`9?|c~sCL1IY)`YK6JEmNufZ#T!|306DOKD`BBWMi9 zu>+Z7{9gY5b$iT(KrpHVb|48J!vWalL)wDx;c;w#L+d;$4`PL=YGoo7-6Ep8C|H$& z`QK3?#inrt4j@h2G?e`gxxV0kk2{3W4n)EC{u&H3rVCMHWHXPmSz;T4z{qn};6IbV zbP}fZh}8en{e+R7++J_a=g(xt<=#}`NSvms5g%Ke$|BB`R7XA0(7AzCaf4a8|I$6g5b{%=l z^N;w?Zyba9OruRl7Q~iv!OHpZn#JW>3INAoM+JeZKz|CYu=v*qt$TA7k?Z#e0S|$_ z^wHhn8tKt)_Fqc|2Il2|s_rs76n$uB%~P@v$sE&WgSh$XXyF?J%b4WYmwzR)&+@8R zqa$5Ob~!*iPUaYyuZ2G(9_e7QK>RKkE&-8v3fbimN;E&+j(5#~XFG&(V9;;RH}nS$9$!CEHG7}G<8aaj<#D;oM1ySnw_I5rH)CgyWFn7 z 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 @ProductID +GROUP BY +od2.ProductID, +p1.ProductSubcategoryID +) +SELECT TOP(10) ROW_NUMBER() OVER +( +ORDER BY rp.TotalQtyOrdered DESC +) AS Rank, +rp.TotalQtyOrdered, +rp.ProductID, +rp.TotalDollarsOrdered, +p.[Name] +FROM RecommendedProducts rp +INNER JOIN Production.Product p +ON rp.ProductID = p.ProductID +WHERE rp.ProductSubcategoryID <> +( +SELECT ProductSubcategoryID +FROM Production.Product +WHERE ProductID = @ProductID +) +ORDER BY TotalQtyOrdered DESC; +END; +GO + +/*Listing 5 - 10. Getting a Recommended Product List */ +EXECUTE Production..GetProductRecommendations 773; + +/*Listing 5 - 11. The Towers of Hanoi Puzzle */ +-- This stored procedure displays all the discs in the appropriate +-- towers. +CREATE PROCEDURE dbo.ShowTowers +AS +BEGIN +-- Each disc is displayed like this "===3===" where the number is the disc +-- and the width of the === signs on either side indicates the width of the +-- disc. +-- These CTEs are designed for displaying the discs in proper order on each +-- tower. +WITH FiveNumbers(Num) -- Recursive CTE generates table with numbers 1...5 +AS +( +SELECT 1 +UNION ALL +SELECT Num + 1 +FROM FiveNumbers +WHERE Num < 5 +), +GetTowerA (Disc) -- The discs for Tower A +AS +( +SELECT COALESCE(a.Disc, -1) AS Disc +FROM FiveNumbers f +LEFT JOIN #TowerA a +ON f.Num = a.Disc +), +GetTowerB (Disc) -- The discs for Tower B +AS +( +SELECT COALESCE(b.Disc, -1) AS Disc +FROM FiveNumbers f +LEFT JOIN #TowerB b +ON f.Num = b.Disc +), +GetTowerC (Disc) -- The discs for Tower C +AS +( +SELECT COALESCE(c.Disc, -1) AS Disc +FROM FiveNumbers f +LEFT JOIN #TowerC c +ON f.Num = c.Disc +) +-- This SELECT query generates the text representation for all three towers +-- and all five discs. FULL OUTER JOIN is used to represent the towers in a +-- side-by-side format. +SELECT CASE a.Disc +WHEN 5 THEN ' =====5===== ' +WHEN 4 THEN ' ====4==== ' +WHEN 3 THEN '===3=== ' +WHEN 2 THEN ' ==2== ' +WHEN 1 THEN ' =1= ' +ELSE ' | ' +END AS Tower_A, +CASE b.Disc +WHEN 5 THEN ' =====5===== ' +WHEN 4 THEN ' ====4==== ' +WHEN 3 THEN ' ===3=== ' +WHEN 2 THEN ' ==2== ' +WHEN 1 THEN ' =1= ' +ELSE ' | ' +END AS Tower_B, +CASE c.Disc +WHEN 5 THEN ' =====5===== ' +WHEN 4 THEN ' ====4==== ' +WHEN 3 THEN ' ===3=== ' +WHEN 2 THEN ' ==2== ' +WHEN 1 THEN ' =1= ' +ELSE ' | ' +END AS Tower_C +FROM ( +SELECT ROW_NUMBER() OVER(ORDER BY Disc) AS Num, +COALESCE(Disc, -1) AS Disc +FROM GetTowerA +) a +FULL OUTER JOIN ( +SELECT ROW_NUMBER() OVER(ORDER BY Disc) AS Num, +COALESCE(Disc, -1) AS Disc +FROM GetTowerB +) b +ON a.Num = b.Num +FULL OUTER JOIN ( +SELECT ROW_NUMBER() OVER(ORDER BY Disc) AS Num, +COALESCE(Disc, -1) AS Disc +FROM GetTowerC +) c +ON b.Num = c.Num +ORDER BY a.Num; +END; +GO +-- This SP moves a single disc from the specified source tower to the +-- specified destination tower. +CREATE PROCEDURE dbo.MoveOneDisc (@Source nchar(1), +@Dest nchar(1)) +AS +BEGIN +-- @SmallestDisc is the smallest disc on the source tower +DECLARE @SmallestDisc int = 0; +-- IF ... ELSE conditional statement gets the smallest disc from the +-- correct source tower +IF @Source = N'A' +BEGIN +-- This gets the smallest disc from Tower A +SELECT @SmallestDisc = MIN(Disc) +FROM #TowerA; +-- Then delete it from Tower A +DELETE FROM #TowerA +WHERE Disc = @SmallestDisc; +END +ELSE IF @Source = N'B' +BEGIN +-- This gets the smallest disc from Tower B +SELECT @SmallestDisc = MIN(Disc) +FROM #TowerB; +-- Then delete it from Tower B +DELETE FROM #TowerB +WHERE Disc = @SmallestDisc; +END +ELSE IF @Source = N'C' +BEGIN +-- This gets the smallest disc from Tower C +SELECT @SmallestDisc = MIN(Disc) +FROM #TowerC; +-- Then delete it from Tower C +DELETE FROM #TowerC +WHERE Disc = @SmallestDisc; +END +-- Show the disc move performed +SELECT N'Moving Disc (' + CAST(COALESCE(@SmallestDisc, 0) AS nchar(1)) + +N') from Tower ' + @Source + N' to Tower ' + @Dest + ':' AS Description; +-- Perform the move - INSERT the disc from the source tower into the +-- destination tower +IF @Dest = N'A' +INSERT INTO #TowerA (Disc) VALUES (@SmallestDisc); +ELSE IF @Dest = N'B' +INSERT INTO #TowerB (Disc) VALUES (@SmallestDisc); +ELSE IF @Dest = N'C' +INSERT INTO #TowerC (Disc) VALUES (@SmallestDisc); +-- Show the towers +EXECUTE dbo.ShowTowers; +END; +GO +-- This SP moves multiple discs recursively +CREATE PROCEDURE dbo.MoveDiscs (@DiscNum int, +@MoveNum int OUTPUT, +@Source nchar(1) = N'A', +@Dest nchar(1) = N'C', +@Aux nchar(1) = N'B' +) +AS +BEGIN +-- If the number of discs to move is 0, the solution has been found +IF @DiscNum = 0 +PRINT N'Done'; +ELSE +BEGIN +-- If the number of discs to move is 1, go ahead and move it +IF @DiscNum = 1 +BEGIN +-- Increase the move counter by 1 +SELECT @MoveNum += 1; +-- And finally move one disc from source to destination +EXEC dbo.MoveOneDisc @Source, @Dest; +END +ELSE +BEGIN +-- Determine number of discs to move from source to auxiliary tower +DECLARE @n int = @DiscNum - 1; +-- Move (@DiscNum - 1) discs from source to auxiliary tower +EXEC dbo.MoveDiscs @n, @MoveNum OUTPUT, @Source, @Aux, @Dest; +-- Move 1 disc from source to final destination tower +EXEC dbo.MoveDiscs 1, @MoveNum OUTPUT, @Source, @Dest, @Aux; +-- Move (@DiscNum - 1) discs from auxiliary to final destination tower +EXEC dbo.MoveDiscs @n, @MoveNum OUTPUT, @Aux, @Dest, @Source; +END; +END; +END; +GO +-- This SP creates the three towers and populates Tower A with 5 discs +CREATE PROCEDURE dbo.SolveTowers +AS +BEGIN +-- SET NOCOUNT ON to eliminate system messages that will clutter up +-- the Message display +SET NOCOUNT ON; +-- Create the three towers: Tower A, Tower B, and Tower C +CREATE TABLE #TowerA (Disc int PRIMARY KEY NOT NULL); +CREATE TABLE #TowerB (Disc int PRIMARY KEY NOT NULL); +CREATE TABLE #TowerC (Disc int PRIMARY KEY NOT NULL); +-- Populate Tower A with all five discs +INSERT INTO #TowerA (Disc) +VALUES (1), (2), (3), (4), (5); +-- Initialize the move number to 0 +DECLARE @MoveNum int = 0; +-- Show the initial state of the towers +EXECUTE dbo.ShowTowers; +-- Solve the puzzle. Notice you don't need to specify the parameters +-- with defaults +EXECUTE dbo.MoveDiscs 5, @MoveNum OUTPUT; +-- How many moves did it take? +PRINT N'Solved in ' + CAST (@MoveNum AS nvarchar(10)) + N' moves.'; +-- Drop the temp tables to clean up - always a good idea. +DROP TABLE #TowerC; +DROP TABLE #TowerB; +DROP TABLE #TowerA; +-- SET NOCOUNT OFF before we exit +SET NOCOUNT OFF; +END; +GO + +/*Listing 5 - 12. Creating a Table Type */ +CREATE TYPE HumanResources.LastNameTableType +AS TABLE (LastName nvarchar(50) NOT NULL PRIMARY KEY); +GO + +/*Listing 5 - 13. Simple Procedure Accepting a Table-valued Parameter */ +CREATE PROCEDURE HumanResources.GetEmployees +(@LastNameTable HumanResources.LastNameTableType READONLY) +AS +BEGIN +SELECT +p.LastName, +p.FirstName, +p.MiddleName, +e.NationalIDNumber, +e.Gender, +e.HireDate +FROM HumanResources.Employee e +INNER JOIN Person.Person p +ON e.BusinessEntityID = p.BusinessEntityID +INNER JOIN @LastNameTable lnt +ON p.LastName = lnt.LastName +ORDER BY +p.LastName, +p.FirstName, +p.MiddleName; +END; +GO + +/*Listing 5 - 14. Calling a Procedure with a Table-valued Parameter */ +DECLARE @LastNameList HumanResources.LastNameTableType; +INSERT INTO @LastNameList +(LastName) +VALUES +(N'Walters'), +(N'Anderson'), +(N'Chen'), +(N'Rettig'), +(N'Lugo'), +(N'Zwilling'), +(N'Johnson'); +EXECUTE HumanResources.GetEmployees @LastNameList; + +/*Listing 5 - 15. Procedure to Retrieve SP Statistics with DMVs and DMFs */ +CREATE PROCEDURE dbo.GetProcStats (@order varchar(100) = 'use') +AS +BEGIN +WITH GetQueryStats +( +plan_handle, +total_elapsed_time, +total_logical_reads, +total_logical_writes, +total_physical_reads +) +AS +( +SELECT +qs.plan_handle, +SUM(qs.total_elapsed_time) AS total_elapsed_time, +SUM(qs.total_logical_reads) AS total_logical_reads, +SUM(qs.total_logical_writes) AS total_logical_writes, +SUM(qs.total_physical_reads) AS total_physical_reads +FROM sys.dm_exec_query_stats qs +GROUP BY qs.plan_handle +) +SELECT +DB_NAME(st.dbid) AS database_name, +OBJECT_SCHEMA_NAME(st.objectid, st.dbid) AS schema_name, +OBJECT_NAME(st.objectid, st.dbid) AS proc_name, +SUM(cp.usecounts) AS use_counts, +SUM(cp.size_in_bytes) AS size_in_bytes, +SUM(qs.total_elapsed_time) AS total_elapsed_time, +CAST +( +SUM(qs.total_elapsed_time) AS decimal(38, 4) +) / SUM(cp.usecounts) AS avg_elapsed_time_per_use, +SUM(qs.total_logical_reads) AS total_logical_reads, +CAST +( +SUM(qs.total_logical_reads) AS decimal(38, 4) +) / SUM(cp.usecounts) AS avg_logical_reads_per_use, +SUM(qs.total_logical_writes) AS total_logical_writes, +CAST +( +SUM(qs.total_logical_writes) AS decimal(38, 4) +) / SUM(cp.usecounts) AS avg_logical_writes_per_use, +SUM(qs.total_physical_reads) AS total_physical_reads, +CAST +( +SUM(qs.total_physical_reads) AS decimal(38, 4) +) / SUM(cp.usecounts) AS avg_physical_reads_per_use, +st.text +FROM sys.dm_exec_cached_plans cp +CROSS APPLY sys.dm_exec_sql_text(cp.plan_handle) st +INNER JOIN GetQueryStats qs +ON cp.plan_handle = qs.plan_handle +INNER JOIN sys.procedures p +ON st.objectid = p.object_id +WHERE p.type IN ('P', 'PC') +GROUP BY st.dbid, st.objectid, st.text +ORDER BY +CASE @order +WHEN 'name' THEN OBJECT_NAME(st.objectid) +WHEN 'size' THEN SUM(cp.size_in_bytes) +WHEN 'read' THEN SUM(qs.total_logical_reads) +WHEN 'write' THEN SUM(qs.total_logical_writes) +ELSE SUM(cp.usecounts) +END DESC; +END; +GO + +/*Listing 5 - 16. Retrieving SP Statistics */ +EXEC dbo.GetProcStats @order = 'use'; +GO + +/*Listing 5 - 17. Simple Procedure to Demonstrate Parameter Sniffing */ +CREATE PROCEDURE Production.GetProductsByName +@Prefix NVARCHAR(100) +AS +BEGIN +SELECT +p.Name, +p.ProductID +FROM Production.Product p +WHERE p.Name LIKE @Prefix; +END; +GO + +/*Listing 5 - 18. Overriding Parameter Sniffing in an SP */ +ALTER PROCEDURE Production.GetProductsByName +@Prefix NVARCHAR(100) +AS +BEGIN +DECLARE @PrefixVar NVARCHAR(100) = @Prefix; +SELECT +p.Name, +p.ProductID +FROM Production.Product p +WHERE p.Name LIKE @PrefixVar; +END; +GO + +/*Listing 5 - 19. SP to Retrieve Orders by Salesperson */ +CREATE PROCEDURE Sales.GetSalesBySalesPerson (@SalesPersonId int) +AS +BEGIN +SELECT +soh.SalesOrderID, +soh.OrderDate, +soh.TotalDue +FROM Sales.SalesOrderHeader soh +WHERE soh.SalesPersonID = @SalesPersonId; +END; +GO + +/*Listing 5 - 20. Retrieving Sales for Salesperson 277 */ +EXECUTE Sales.GetSalesBySalesPerson 277; + +/*Listing 5 - 21. Executing an SP with Recompilation */ +EXECUTE Sales.GetSalesBySalesPerson 285 WITH RECOMPILE; + +/*Listing 5 - 22. Adding Statement-Level Recompilation to the SP */ +ALTER PROCEDURE Sales.GetSalesBySalesPerson (@SalesPersonId int) +AS +BEGIN +SELECT +soh.SalesOrderID, +soh.OrderDate, +soh.TotalDue +FROM Sales.SalesOrderHeader soh +WHERE soh.SalesPersonID = @SalesPersonId +OPTION (RECOMPILE); +END; +GO + +/*Listing 5 - 23. SP to Retutn List of Stored Procedures That Have Been Recompiled */ +CREATE PROCEDURE dbo.GetRecompiledProcs +AS +BEGIN +SELECT +sql_text.text, +stats.sql_handle, +stats.plan_generation_num, +stats.creation_time, +stats.execution_count, +sql_text.dbid, +sql_text.objectid +FROM sys.dm_exec_query_stats stats +Cross apply sys.dm_exec_sql_text(sql_handle) as sql_text +WHERE stats.plan_generation_num > 1 +and sql_text.objectid is not null --Filter adhoc queries +ORDER BY stats.plan_generation_num desc +END; +GO + +/*Listing 5 - 24. Retrieving SP Statistics */ +EXEC dbo.GetRecompiledProcs; +GO diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch06/ch06.sql b/Pro T-SQL 2012 Programmer's Guide/Ch06/ch06.sql new file mode 100644 index 0000000..13e51fe --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch06/ch06.sql @@ -0,0 +1,476 @@ +-------------------------------------------------- +-- Listing 6-1. Disabling and Enabling Triggers -- +-------------------------------------------------- + +DISABLE TRIGGER HumanResources.EmployeeUpdateTrigger +ON HumanResources.Employee; +GO + +SELECT + name, + OBJECT_SCHEMA_NAME(parent_id) + '.' + OBJECT_NAME(parent_id) as Parent +FROM sys.triggers +WHERE is_disabled = 1; +GO + +ENABLE TRIGGER HumanResources.EmployeeUpdateTrigger +ON HumanResources.Employee; +GO + +-- disabling and enabling all triggers on the object +DISABLE TRIGGER ALL ON HumanResources.Employee; +ENABLE TRIGGER ALL ON HumanResources.Employee; +GO + +------------------------------------------------------------ +-- Listing 6-2. HumanResources.EmployeeUpdateTrigger Code -- +------------------------------------------------------------ +CREATE TRIGGER HumanResources.EmployeeUpdateTrigger +ON HumanResources.Employee +AFTER UPDATE +NOT FOR REPLICATION +AS +BEGIN + -- stop if no row was affected + IF @@ROWCOUNT = 0 RETURN + -- Turn off "rows affected" messages + SET NOCOUNT ON; + + -- Make sure at least one row was affected + -- Update ModifiedDate for all affected rows + UPDATE HumanResources.Employee + SET ModifiedDate = GETDATE() + WHERE EXISTS + ( + SELECT 1 + FROM inserted i + WHERE i.BusinessEntityID = HumanResources.Employee.BusinessEntityID + ); +END; +GO + +--------------------------------------------------------------- +-- Listing 6-3. Testing HumanResources.EmployeeUpdateTrigger -- +--------------------------------------------------------------- +UPDATE HumanResources.Employee +SET MaritalStatus = 'M' +WHERE BusinessEntityID IN (1, 2); + +SELECT BusinessEntityID, NationalIDNumber, MaritalStatus, ModifiedDate +FROM HumanResources.Employee +WHERE BusinessEntityID IN (1, 2); +GO + +------------------------------------------ +-- Listing 6-4. DML Audit Logging Table -- +------------------------------------------ +CREATE TABLE dbo.DmlActionLog ( + EntryNum int IDENTITY(1, 1) PRIMARY KEY NOT NULL, + SchemaName sysname NOT NULL, + TableName sysname NOT NULL, + ActionType nvarchar(10) NOT NULL, + ActionXml xml NOT NULL, + LoginName sysname NOT NULL, + ApplicationName sysname NOT NULL, + HostName sysname NOT NULL, + ActionDateTime datetime2(0) NOT NULL DEFAULT (SYSDATETIME()) +); +GO + +-------------------------------------------- +-- Listing 6-5. DML Audit Logging Trigger -- +-------------------------------------------- +CREATE TRIGGER HumanResources.DepartmentChangeAudit +ON HumanResources.Department +AFTER INSERT, UPDATE, DELETE +NOT FOR REPLICATION +AS +BEGIN + -- stop if no row was affected + IF @@ROWCOUNT = 0 RETURN + + -- Turn off "rows affected" messages + SET NOCOUNT ON; + + DECLARE @ActionType nvarchar(10), @ActionXml xml; + + -- Get count of inserted rows + DECLARE @inserted_count int = ( + SELECT COUNT(*) + FROM inserted + ); + -- Get count of deleted rows + DECLARE @deleted_count int = ( + SELECT COUNT(*) + FROM deleted + ); + + -- Determine the type of DML action that fired the trigger + SET @ActionType = CASE + WHEN (@inserted_count > 0) AND (@deleted_count = 0) THEN N'insert' + WHEN (@inserted_count = 0) AND (@deleted_count > 0) THEN N'delete' + ELSE N'update' + END; + + -- Use FOR XML AUTO to retrieve before and after snapshots of the changed + -- data in XML format + SELECT @ActionXml = COALESCE + ( + ( + SELECT * + FROM deleted + FOR XML AUTO + ), N'' + ) + COALESCE + ( + ( + SELECT * + FROM inserted + FOR XML AUTO + ), N'' + ); + + -- Insert a row for the logged action in the audit logging table + INSERT INTO dbo.DmlActionLog + ( + SchemaName, + TableName, + ActionType, + ActionXml, + LoginName, + ApplicationName, + HostName + ) + SELECT + OBJECT_SCHEMA_NAME(@@PROCID, DB_ID()), + OBJECT_NAME(t.parent_id, DB_ID()), + @ActionType, + @ActionXml, + SUSER_SNAME(), + APP_NAME(), + HOST_NAME() + FROM sys.triggers t + WHERE t.object_id = @@PROCID; +END; +GO + +-------------------------------------------------------- +-- Listing 6-6. Testing the DML Audit Logging Trigger -- +-------------------------------------------------------- +UPDATE HumanResources.Department SET Name = N'Information Technology' +WHERE DepartmentId = 11; + +INSERT INTO HumanResources.Department +( + Name, + GroupName +) +VALUES +( + N'Customer Service', + N'Sales and Marketing' +); + +DELETE +FROM HumanResources.Department +WHERE Name = N'Customer Service'; + +SELECT + EntryNum, + SchemaName, + TableName, + ActionType, + ActionXml, + LoginName, + ApplicationName, + HostName, + ActionDateTime +FROM dbo.DmlActionLog; +GO + +---------------------------------------------- +-- Listing 6-7. Turning Off Nested Triggers -- +---------------------------------------------- +EXEC sp_configure 'nested triggers', 0; +RECONFIGURE; +GO + +------------------------------------------------------- +-- Listing 6-8. Turning Off Recursive AFTER Triggers -- +------------------------------------------------------- +ALTER DATABASE AdventureWorks SET RECURSIVE_TRIGGERS OFF; +GO + +---------------------------------------------------- +-- Listing 6-9. Trigger to Enforce Standard Sizes -- +---------------------------------------------------- +CREATE TRIGGER Production.ProductEnforceStandardSizes +ON Production.Product +AFTER INSERT, UPDATE +NOT FOR REPLICATION +AS +BEGIN + -- Make sure at least one row was affected and either the Size or + -- SizeUnitMeasureCode column was changed + IF (@@ROWCOUNT > 0) AND (UPDATE(SizeUnitMeasureCode) OR UPDATE(Size)) + BEGIN + -- Eliminate "rows affected" messages + SET NOCOUNT ON; + -- Only accept recognized units of measure or NULL + IF EXISTS + ( + SELECT 1 + FROM inserted + WHERE NOT + ( SizeUnitMeasureCode IN (N'M', N'DM', N'CM', N'MM', N'IN') + OR SizeUnitMeasureCode IS NULL + ) + ) + BEGIN + -- If the unit of measure wasn't recognized raise an error and roll back + -- the transaction + RAISERROR ('Invalid Size Unit Measure Code.', 10, 127); + ROLLBACK TRANSACTION; + END + ELSE + BEGIN + -- If the unit of measure is a recognized unit of measure then set the + -- SizeUnitMeasureCode to centimeters and perform the Size conversion + UPDATE Production.Product + SET SizeUnitMeasureCode = CASE + WHEN Production.Product.SizeUnitMeasureCode IS NULL THEN NULL ELSE N'CM' END, + Size = CAST ( + CAST ( CAST(i.Size AS float) * + CASE i.SizeUnitMeasureCode + WHEN N'M' THEN 100.0 + WHEN N'DM' THEN 10.0 + WHEN N'CM' THEN 1.0 + WHEN N'MM' THEN 0.10 + WHEN N'IN' THEN 2.54 + END + AS int + ) AS nvarchar(5) + ) + FROM inserted i + WHERE Production.Product.ProductID = i.ProductID + AND i.SizeUnitMeasureCode IS NOT NULL; + END; + END; +END; +GO + +--------------------------------------------------------------- +-- Listing 6-10. Testing the Trigger by Adding a New Product -- +--------------------------------------------------------------- +UPDATE Production.Product +SET Size = N'600', + SizeUnitMeasureCode = N'MM' +WHERE ProductId = 680; + +UPDATE Production.Product +SET Size = N'22.85', + SizeUnitMeasureCode = N'IN' +WHERE ProductId = 706; + +SELECT ProductID, + Name, + ProductNumber, + Size, + SizeUnitMeasureCode +FROM Production.Product +WHERE ProductID IN (680, 706); +GO + +------------------------------------------------ +-- Listing 6-11. INSTEAD OF Trigger on a View -- +------------------------------------------------ +CREATE TRIGGER Sales.vIndividualCustomerUpdate +ON Sales.vIndividualCustomer +INSTEAD OF UPDATE +NOT FOR REPLICATION +AS +BEGIN + -- First make sure at least one row was affected + IF @@ROWCOUNT = 0 RETURN + -- Turn off "rows affected" messages + SET NOCOUNT ON; + -- Initialize a flag to indicate update success + DECLARE @UpdateSuccessful bit = 0; + + -- Check for updatable columns in the first table + IF UPDATE(FirstName) OR UPDATE(MiddleName) OR UPDATE(LastName) + BEGIN + -- Update columns in the base table + UPDATE Person.Person + SET FirstName = i.FirstName, + MiddleName = i.MiddleName, + LastName = i.LastName + FROM inserted i + WHERE i.BusinessEntityID = Person.Person.BusinessEntityID; + + -- Set flag to indicate success + SET @UpdateSuccessful = 1; + END; + -- If updatable columns from the second table were specified, update those + -- columns in the base table + IF UPDATE(EmailAddress) + BEGIN + -- Update columns in the base table + UPDATE Person.EmailAddress + SET EmailAddress = i.EmailAddress + FROM inserted i + WHERE i.BusinessEntityID = Person.EmailAddress.BusinessEntityID; + + -- Set flag to indicate success + SET @UpdateSuccessful = 1; + END; + -- If the update was not successful, raise an error and roll back the + -- transaction + IF @UpdateSuccessful = 0 + RAISERROR('Must specify updatable columns.', 10, 127); +END; +GO + +----------------------------------------------------------------- +-- Listing 6-12. Updating a View Through an INSTEAD OF Trigger -- +----------------------------------------------------------------- +UPDATE Sales.vIndividualCustomer +SET FirstName = N'Dave', + MiddleName = N'Robert', + EmailAddress = N'dave.robinett@adventure-works.com' +WHERE BusinessEntityID = 1699; + +SELECT BusinessEntityID, FirstName, MiddleName, LastName, EmailAddress +FROM Sales.vIndividualCustomer +WHERE BusinessEntityID = 1699; +GO + +---------------------------------------------------- +-- Listing 6-14. CREATE TABLE DDL Trigger Example -- +---------------------------------------------------- +-- Create a table to log DDL CREATE TABLE actions +CREATE TABLE dbo.DdlActionLog +( + EntryId int NOT NULL IDENTITY(1, 1) PRIMARY KEY, + EventType nvarchar(200) NOT NULL, + PostTime datetime NOT NULL, + LoginName sysname NOT NULL, + UserName sysname NOT NULL, + ServerName sysname NOT NULL, + SchemaName sysname NOT NULL, + DatabaseName sysname NOT NULL, + ObjectName sysname NOT NULL, + ObjectType sysname NOT NULL, + CommandText nvarchar(max) NOT NULL +); +GO + +CREATE TRIGGER AuditCreateTable +ON DATABASE +FOR CREATE_TABLE +AS +BEGIN + -- Assign the XML event data to an xml variable + DECLARE @eventdata xml = EVENTDATA(); + + -- Shred the XML event data and insert a row in the log table + INSERT INTO dbo.DdlActionLog + ( + EventType, + PostTime, + LoginName, + UserName, + ServerName, + SchemaName, + DatabaseName, + ObjectName, + ObjectType, + CommandText + ) + SELECT + EventNode.value(N'EventType[1]', N'nvarchar(200)'), + EventNode.value(N'PostTime[1]', N'datetime'), + EventNode.value(N'LoginName[1]', N'sysname'), + EventNode.value(N'UserName[1]', N'sysname'), + EventNode.value(N'ServerName[1]', N'sysname'), + EventNode.value(N'SchemaName[1]', N'sysname'), + EventNode.value(N'DatabaseName[1]', N'sysname'), + EventNode.value(N'ObjectName[1]', N'sysname'), + EventNode.value(N'ObjectType[1]', N'sysname'), + EventNode.value(N'(TSQLCommand/CommandText)[1]', 'nvarchar(max)') + FROM @eventdata.nodes('/EVENT_INSTANCE') EventTable(EventNode); +END; +GO + +------------------------------------------------------------------------- +-- Listing 6-15. Testing the DDL Trigger with a CREATE TABLE Statement -- +------------------------------------------------------------------------- +CREATE TABLE dbo.MyTable (i int); +GO + +SELECT + EntryId, + EventType, + UserName, + ObjectName, + CommandText +FROM DdlActionLog; +GO + +------------------------------------------ +-- Listing 6-16. Dropping a DDL Trigger -- +------------------------------------------ +DROP TRIGGER AuditCreateTable +ON DATABASE; +GO + +------------------------------------------------------------------- +-- Listing 6-17. Creating a Test Login and Logon Denial Schedule -- +------------------------------------------------------------------- +CREATE LOGIN PublicUser WITH PASSWORD = 'p@$$w0rd'; +GO + +USE Master; + +CREATE TABLE dbo.DenyLogonSchedule ( + UserId sysname NOT NULL, + DayOfWeek tinyint NOT NULL, + TimeStart time NOT NULL, + TimeEnd time NOT NULL, + PRIMARY KEY (UserId, DayOfWeek, TimeStart, TimeEnd) + ); + GO + +INSERT INTO dbo.DenyLogonSchedule ( + UserId, + DayOfWeek, + TimeStart, + TimeEnd +) VALUES ( + 'PublicUser', + 7, + '21:00:00', + '23:00:00' + ); +GO + +---------------------------------------- +-- Listing 6-18. Sample Logon Trigger -- +---------------------------------------- +USE Master; + +CREATE TRIGGER DenyLogons +ON ALL SERVER +WITH EXECUTE AS 'sa' +FOR LOGON +AS +BEGIN + IF EXISTS ( SELECT 1 + FROM Master .dbo.DenyLogonSchedule + WHERE UserId = ORIGINAL_LOGIN() + AND DayOfWeek = DATEPART(WeekDay, GETDATE()) + AND CAST(GETDATE() AS TIME) BETWEEN TimeStart AND TimeEnd + ) BEGIN + ROLLBACK TRANSACTION; + END; +END; diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch08/9781430245964_Natarajan_Ch08_CommonTableExpressionsAndWindowingFunctions.sql b/Pro T-SQL 2012 Programmer's Guide/Ch08/9781430245964_Natarajan_Ch08_CommonTableExpressionsAndWindowingFunctions.sql new file mode 100644 index 0000000..ac4eb9d --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch08/9781430245964_Natarajan_Ch08_CommonTableExpressionsAndWindowingFunctions.sql @@ -0,0 +1,417 @@ +/*Listing 8-1. Simple CTE */ +WITH GetNamesCTE ( +BusinessEntityID, +FirstName, +MiddleName, +LastName ) +AS ( +SELECT +BusinessEntityID, FirstName, MiddleName, LastName +FROM Person.Person ) SELECT +BusinessEntityID, +FirstName, +MiddleName, +LastName FROM GetNamesCTE +; + +/*Listing 8-2. Multiple CTEs */ +WITH GetNamesCTE ( +BusinessEntityID, +FirstName, +MiddleName, +LastName ) +AS ( +SELECT +BusinessEntityID, FirstName, MiddleName, LastName +FROM Person.Person ), +GetContactCTE ( +BusinessEntityID, +FirstName, +MiddleName, LastName, Email, HomePhoneNumber +) +AS ( SELECT gn.BusinessEntityID, gn.FirstName, gn.MiddleName, gn.LastName, ea.EmailAddress, +pp.PhoneNumber FROM GetNamesCTE gn LEFT JOIN Person.EmailAddress ea +ON gn.BusinessEntityID = ea.BusinessEntityID LEFT JOIN Person.PersonPhone pp ON +gn.BusinessEntityID = pp.BusinessEntityID AND pp.PhoneNumberTypeID = 2 ) +SELECT BusinessEntityID, FirstName, MiddleName, LastName, Email, +HomePhoneNumber FROM GetContactCTE; + +/*Listing 8-3. Simple Recursive CTE */ +WITH Numbers (n) AS ( SELECT 1 AS n +UNION ALL +SELECT n + 1 FROM Numbers WHERE n < 10 ) +SELECT n FROM Numbers; + +/*Listing 8-4. Recursive CTE with MAXRECURSION Option */ +WITH Numbers (n) AS ( SELECT 0 AS n +UNION ALL +SELECT n + 1 +FROM Numbers +WHERE n < 1000 ) +SELECT n FROM Numbers OPTION (MAXRECURSION 1000); + +/*Listing 8-5. Recursive BOM CTE */ +DECLARE @ComponentID int = 774; +WITH BillOfMaterialsCTE +( +BillOfMaterialsID, +ProductAssemblyID, +ComponentID, +Quantity, +Level +) +AS +( +SELECT +bom.BillOfMaterialsID, +bom.ProductAssemblyID, +bom.ComponentID, +bom.PerAssemblyQty AS Quantity, +0 AS Level +FROM Production.BillOfMaterials bom +WHERE bom.ComponentID = @ComponentID +UNION ALL +SELECT +bom.BillOfMaterialsID, +bom.ProductAssemblyID, +bom.ComponentID, +bom.PerAssemblyQty, +Level + 1 +FROM Production.BillOfMaterials bom +ComponentID 774 +ComponentID 516 +ComponentID 497 +ProductAssemblyID 774 +ProductAssemblyID 516 +INNER JOIN BillOfMaterialsCTE bomcte +ON bom.ProductAssemblyID = bomcte.ComponentID +WHERE bom.EndDate IS NULL +) +SELECT +bomcte.ProductAssemblyID, +p.ProductID, +p.ProductNumber, +p.Name, +p.Color, +bomcte.Quantity, +bomcte.Level +FROM BillOfMaterialsCTE bomcte +INNER JOIN Production.Product p +ON bomcte.ComponentID = p.ProductID +order by bomcte.Level; + +/*Listing 8-6. ROW_NUMBER with Partitioning */ +SELECT +ROW_NUMBER() OVER +( +PARTITION BY +LastName +ORDER BY +LastName, +FirstName, +MiddleName +) AS Number, +LastName, +FirstName, +MiddleName +FROM Person.Person; + +/*Listing 8-7. OFFSET/FETCH Example */ +CREATE PROCEDURE Person.GetContacts +@StartPageNum int, +@RowsPerPage int +AS +SELECT +LastName, +FirstName, +MiddleName +FROM Person.Person +ORDER BY +LastName, +FirstName, +MiddleName +OFFSET (@StartPageNum - 1) * @RowsPerPage ROWS +FETCH NEXT @RowsPerPage ROWS ONLY; +GO + +/*Listing 8-8. Ranking AdventureWorks Daily Sales Totals */ +WITH TotalSalesBySalesDate +( +DailySales, +OrderDate +) +AS +( +SELECT +SUM(soh.SubTotal) AS DailySales, +soh.OrderDate +FROM Sales.SalesOrderHeader soh +WHERE soh.OrderDate > = '20060101' +AND soh.OrderDate < '20070101' +GROUP BY soh.OrderDate +) +SELECT +RANK() OVER +( +ORDER BY +DailySales DESC +) AS Ranking, +DailySales, +OrderDate +FROM TotalSalesBySalesDate +ORDER BY Ranking; + +/*Listing 8-9. Determining the daily sales rankings partitioned by month */ +WITH TotalSalesBySalesDatePartitioned +( +DailySales, +OrderMonth, +OrderDate +) +AS +( +SELECT +SUM(soh.SubTotal) AS DailySales, +DATENAME(MONTH, soh.OrderDate) AS OrderMonth, +soh.OrderDate +FROM Sales.SalesOrderHeader soh +WHERE soh.OrderDate > = '20050101' +AND soh.OrderDate < '20060101' +GROUP BY soh.OrderDate +) +SELECT +RANK() OVER +( +PARTITION BY +OrderMonth +ORDER BY +DailySales DESC +) AS Ranking, +DailySales, +OrderMonth, +OrderDate +FROM TotalSalesBySalesDatePartitioned +ORDER BY DATEPART(mm,OrderDate), +Ranking; + +/*Listing 8-10. Using DENSE_RANK to Rank Best Daily Sales Per Month */ +WITH TotalSalesBySalesDatePartitioned +( +DailySales, +OrderMonth, +OrderDate +) +AS +( +SELECT +SUM(soh.SubTotal) AS DailySales, +DATENAME(MONTH, soh.OrderDate) AS OrderMonth, +soh.OrderDate +FROM Sales.SalesOrderHeader soh +WHERE soh.OrderDate > = '20050101' +AND soh.OrderDate < '20060101' +GROUP BY soh.OrderDate +) +SELECT +RANK() OVER +( +PARTITION BY +OrderMonth +ORDER BY +DailySales DESC +) AS Ranking, +DENSE_RANK() OVER +( +PARTITION BY +OrderMonth +ORDER BY +DailySales DESC +) AS Dense_Ranking, +DailySales, +OrderMonth, +OrderDate +FROM TotalSalesBySalesDatePartitioned +ORDER BY DATEPART(mm,OrderDate), +Ranking; + +/*Listing 8-11. Using NTILE to Group and Rank Salespeople */ +WITH SalesTotalBySalesPerson +( +SalesPersonID, SalesTotal +) +AS +( +SELECT +soh.SalesPersonID, SUM(soh.SubTotal) AS SalesTotal +FROM Sales.SalesOrderHeader soh +WHERE DATEPART(YEAR, soh.OrderDate) = 2005 +AND DATEPART(MONTH, soh.OrderDate) = 7 +GROUP BY soh.SalesPersonID ) SELECT +NTILE(4) OVER +( ORDER BY +st.SalesTotal DESC +) AS Tile, +p.LastName, +p.FirstName, +p.MiddleName, +st.SalesPersonID, +st.SalesTotal FROM SalesTotalBySalesPerson st INNER JOIN Person.Person p +ON st.SalesPersonID = p.BusinessEntityID ; + +/*Listing 8-13. Using the OVER Clause with SUM */ +SELECT +PurchaseOrderID, +ProductID, +OrderQty, +UnitPrice, +LineTotal, +SUM(LineTotal) +OVER (PARTITION BY PurchaseOrderID +ORDER BY ProductId +RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) +AS CumulativeOrderOty +FROM Purchasing.PurchaseOrderDetail; + +/*Listing 8-14. Query results due to default framing specification */ +SELECT +PurchaseOrderID, +ProductID, +OrderQty, +UnitPrice, +LineTotal, +SUM(LineTotal) +OVER (PARTITION BY PurchaseOrderID +ORDER BY ProductId +) +AS TotalSalesDefaultFraming, +SUM(LineTotal) +OVER (PARTITION BY PurchaseOrderID +ORDER BY ProductId RANGE BETWEEN UNBOUNDED PRECEDING +AND UNBOUNDED FOLLOWING +) +AS TotalSalesDefinedFraming +FROM Purchasing.PurchaseOrderDetail +ORDER BY PurchaseOrderID; + +/*Listing 8-15. Using the OVER Clause define frame sizes to return two-day, moving average */ +SELECT +PurchaseOrderID, +ProductID, +Duedate, +LineTotal, +Avg(LineTotal) +OVER (ORDER BY Duedate +ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) +AS [2DayAvg] +FROM Purchasing.PurchaseOrderDetail +ORDER BY Duedate; + +/*Listing 8-16. Defining frames from within the OVER clause to calcualte running total */ +SELECT +PurchaseOrderID, +ProductID, +OrderQty, +UnitPrice, +LineTotal, +SUM(LineTotal) OVER (PARTITION BY ProductId ORDER BY DueDate +RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS CumulativeTotal, +ROW_NUMBER() OVER (PARTITION BY ProductId ORDER BY DueDate ) AS No +FROM Purchasing.PurchaseOrderDetail +ORDER BY ProductId, DueDate; + +/*Listing 8-17. Using the CUME_DIST function */ +SELECT +round(SUM(TotalDue),1) AS Sales, +LastName, +FirstName, +SalesPersonId, +CUME_DIST() OVER (ORDER BY round(SUM(TotalDue),1)) as CUME_DIST +FROM +Sales.SalesOrderHeader soh +JOIN Sales.vSalesPerson sp +ON soh.SalesPersonID = sp.BusinessEntityID +GROUP BY SalesPersonID,LastName,FirstName; + +/*Listing 8-18. Using the PERCENT_RANK function */ +SELECT +round(SUM(TotalDue),1) AS Sales, +LastName, +FirstName, +SalesPersonId, +CUME_DIST() OVER (ORDER BY round(SUM(TotalDue),1)) as CUME_DIST +,PERCENT_RANK() OVER (ORDER BY round(SUM(TotalDue),1)) as PERCENT_RANK +FROM +Sales.SalesOrderHeader soh +JOIN Sales.vSalesPerson sp +ON soh.SalesPersonID = sp.BusinessEntityID +GROUP BY SalesPersonID,LastName,FirstName; + +/*Listing 8-19. Using PERCENTILE_CONT AND PERCENTILE_DISC */ +SELECT +round(SUM(TotalDue),1) AS Sales, +LastName, +FirstName, +SalesPersonId, +AccountNumber, +PERCENTILE_CONT(0.4) WITHIN GROUP (ORDER BY round(SUM(TotalDue),1)) +OVER(PARTITION BY AccountNumber ) AS PERCENTILE_CONT, +PERCENTILE_DISC(0.4) WITHIN GROUP(ORDER BY round(SUM(TotalDue),1)) +OVER(PARTITION BY AccountNumber ) AS PERCENTILE_DISC +FROM +Sales.SalesOrderHeader soh +JOIN Sales.vSalesPerson sp +ON soh.SalesPersonID = sp.BusinessEntityID +GROUP BY AccountNumber,SalesPersonID,LastName,FirstName + +/*Listing 8-20. Using the LAG function */ +WITH ProductCostHistory AS +(SELECT +ProductID, +LAG(StandardCost) OVER (PARTITION BY ProductID ORDER BY ProductID) AS PreviousProductCost, +StandardCost AS CurrentProductCost, +Startdate,Enddate +FROM Production.ProductCostHistory +) +SELECT +ProductID, +PreviousProductCost, +CurrentProductCost, +StartDate, +EndDate +FROM ProductCostHistory +WHERE Enddate IS NULL + +/* Listing 8-21. Using the LEAD function */ +Select +LastName, +SalesPersonID, +Sum(SubTotal) CurrentMonthSales, +DateNAME(Month,OrderDate) Month, +DateName(Year,OrderDate) Year, +LEAD(Sum(SubTotal),1) OVER (ORDER BY SalesPersonID, OrderDate) TotalSalesNextMonth +FROM +Sales.SalesOrderHeader soh +JOIN Sales.vSalesPerson sp +ON soh.SalesPersonID = sp.BusinessEntityID +WHERE DateName(Year,OrderDate) = 2007 +GROUP BY +FirstName, LastName, SalesPersonID,OrderDate +ORDER BY SalesPersonID,OrderDate; + +/*Listing 8-22. Using FIRST_VALUE and LAST_VALUE */ +SELECT DISTINCT +LastName, +SalesPersonID, +datename(year,OrderDate) OrderYear, +datename(month, OrderDate) OrderMonth, +FIRST_VALUE(SubTotal) OVER (PARTITION BY SalesPersonID, OrderDate ORDER BY +SalesPersonID ) FirstSalesAmount, +LAST_VALUE(SubTotal) OVER (PARTITION BY SalesPersonID, OrderDate ORDER BY +SalesPersonID) LastSalesAmount, +OrderDate +FROM +Sales.SalesOrderHeader soh +JOIN Sales.vSalesPerson sp +ON soh.SalesPersonID = sp.BusinessEntityID +ORDER BY OrderDate; + diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch10/ch10.sql b/Pro T-SQL 2012 Programmer's Guide/Ch10/ch10.sql new file mode 100644 index 0000000..8aa04a1 --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch10/ch10.sql @@ -0,0 +1,772 @@ +------------------------------------ +-- Listing 10-1. Unicode Handling -- +------------------------------------ +DECLARE + @string VARCHAR(50) = 'hello earth', + @nstring NVARCHAR(50) = 'hello earth'; + +SELECT + DATALENGTH(@string) as DatalengthString, + DATALENGTH(@nstring) as DatalengthNString, + LEN(@string) as lenString, + LEN(@nstring) as lenNString; +GO + +----------------------------------------------------------------- +-- Listing 10-2. Comparison of .WRITE Clause and String Append -- +----------------------------------------------------------------- +-- Turn off messages that can affect performance +SET NOCOUNT ON; +-- Create and initially populate a test table +CREATE TABLE #test ( + Id int NOT NULL PRIMARY KEY, + String varchar(max) NOT NULL +); + +INSERT INTO #test ( + Id, + String +) VALUES ( + 1, + '' +), ( + 2, + '' +); +-- Initialize variables and get start time +DECLARE @i int = 1; +DECLARE @quote varchar(50) = 'Four score and seven years ago...'; +DECLARE @start_time datetime2(7) = SYSDATETIME(); +-- Loop 2500 times and use .WRITE to append to a varchar(max) column +WHILE @i < 2500 +BEGIN + UPDATE #test + SET string.WRITE(@quote, LEN(string), LEN(@quote)) + WHERE Id = 1; + + SET @i += 1; +END; + +SELECT '.WRITE Clause', DATEDIFF(ms, @start_time, SYSDATETIME()), 'ms'; + +-- Reset variables and get new start time +SET @i = 1; +SET @start_time = SYSDATETIME(); + +-- Loop 2500 times and use string append to a varchar(max) column +WHILE @i < 2500 +BEGIN + UPDATE #test + SET string += @quote + WHERE Id = 2; + + SET @i += 1; +END; + +SELECT 'Append Method', DATEDIFF(ms, @start_time, SYSDATETIME()), 'ms'; + +SELECT + Id, + String, + LEN(String) +FROM #test; + +DROP TABLE #test; +GO + +----------------------------------------------------------------------------- +-- Listing 10-3. Use the Full Range of 32-bit Integer for IDENTITY Columns -- +----------------------------------------------------------------------------- +CREATE TABLE dbo.bigtable ( + bigtableId int identity(-2147483648,1) NOT NULL +); + +INSERT INTO dbo.bigtable DEFAULT VALUES; +INSERT INTO dbo.bigtable DEFAULT VALUES; + +SELECT * FROM dbo.bigtable; + +----------------------------------- +-- Listing 10-4. Date Comparison -- +----------------------------------- +SELECT * +FROM Person.StateProvince +WHERE ModifiedDate = '2008-03-11'; +GO + +------------------------------------------------------ +-- Listing 10-5. Date Comparison Executed Correctly -- +------------------------------------------------------ +SELECT * +FROM Person.StateProvince +WHERE ModifiedDate BETWEEN '2008-03-11' AND '2008-03-12'; +-- or +SELECT * +FROM Person.StateProvince +WHERE CONVERT(CHAR(10), ModifiedDate, 126) = '2008-03-11'; +GO + +-------------------------------------------------- +-- Listing 10-6. Correcting the Date Comparison -- +-------------------------------------------------- +SELECT * +FROM Production.Product +WHERE ModifiedDate BETWEEN '2008-03-11' AND '2008-03-11 23:59:59.997'; +-- or +SELECT * +FROM Person.StateProvince +WHERE ModifiedDate >= '2008-03-11' AND ModifiedDate < '2008-03-12'; + +----------------------------------------------- +-- Listing 10-7. Sample Date Data Type Usage -- +----------------------------------------------- +-- August 19, 14 C.E. +DECLARE @d1 date = '0014-08-19'; + +-- February 26, 1983 +DECLARE @d2 date = '1983-02-26'; +SELECT @d1 AS Date1, @d2 AS Date2, DATEDIFF(YEAR, @d1, @d2) AS YearsDifference; +GO + +------------------------------------------------------ +-- Listing 10-8. Demonstrating Time Data Type Usage -- +------------------------------------------------------ +-- 6:25:19.1 AM +DECLARE @start_time time(1) = '06:25:19.1'; -- 1 digit fractional precision +-- 6:25:19.1234567 PM +DECLARE @end_time time = '18:25:19.1234567'; -- default fractional precision +SELECT @start_time AS start_time, @end_time AS end_time, +DATEADD(HOUR, 6, @start_time) AS StartTimePlus, DATEDIFF(HOUR, @start_time, @end_time) AS + EndStartDiff; +GO + +-------------------------------------------------------------- +-- Listing 10-9. Declaring and Querying Datetime2 Variables -- +-------------------------------------------------------------- +DECLARE @start_dt2 datetime2 = '1972-07-06T07:13:28.8230234', + @end_dt2 datetime2 = '2009-12-14T03:14:13.2349832'; +SELECT @start_dt2 AS start_dt2, @end_dt2 AS end_dt2; +GO + +---------------------------------------------------- +-- Listing 10-10. Datetimeoffset Data Type Sample -- +---------------------------------------------------- +DECLARE @start_dto datetimeoffset = '1492-10-12T13:29:59.9999999-05:00'; +SELECT @start_dto AS start_to, DATEPART(YEAR, @start_dto) AS start_year; +GO + +------------------------------------------------------- +-- Listing 10-11. Demonstration of Datetime Rounding -- +------------------------------------------------------- +SELECT CAST('2011-12-31T23:59:59.999' as datetime) as WhatTimeIsIt; +GO + +-------------------------------------------------------- +-- Listing 10-12. CONVERT() and FORMAT() Usage Sample -- +-------------------------------------------------------- +DECLARE @dt2 datetime2 = '2011-12-31T23:59:59'; + +SELECT FORMAT(@dt2, 'F', 'en-US') as with_format, + CONVERT(varchar(50), @dt2, 109) as with_convert; +GO + +--------------------------------------------------------------------- +-- Listing 10-13. How to Check the Current Language of the Session -- +--------------------------------------------------------------------- +SELECT language +FROM sys.dm_exec_sessions +WHERE session_id = @@SPID; +-- or +SELECT @@LANGUAGE; +GO + +------------------------------------------------------------------- +-- Listing 10-14. Language Dependent Date String Representations -- +------------------------------------------------------------------- +DECLARE @lang sysname; + +SET @lang = @@LANGUAGE + +SELECT CAST('12/31/2012' as datetime2); --this works + +SET LANGUAGE 'spanish'; + +SELECT + CASE WHEN TRY_CAST('12/31/2012' as datetime2) IS NULL + THEN 'Cast failed' + ELSE 'Cast succeeded' +END AS Result; + +SET LANGUAGE @lang; +GO + +-------------------------------------------- +-- Listing 10-15. Usage of SET DATEFORMAT -- +-------------------------------------------- +SET DATEFORMAT mdy; +SET LANGUAGE 'spanish'; +SELECT CAST('12/31/2012' as datetime2); --this works now +GO + +------------------------------------------------------ +-- Listing 10-17. Using the Date and Time Functions -- +------------------------------------------------------ +SELECT SYSDATETIME() AS [SYSDATETIME]; +SELECT SYSUTCDATETIME() AS [SYSUTCDATETIME]; +SELECT SYSDATETIMEOFFSET() AS [SYSDATETIMEOFFSET]; +GO + +--------------------------------------------------------- +-- Listing 10-18. Adding an Offset to a Datetime Value -- +--------------------------------------------------------- +DECLARE @current datetime = CURRENT_TIMESTAMP; +SELECT @current AS [No_0ffset]; +SELECT TODATETIMEOFFSET(@current, '-04:00') AS [With_0ffset]; +GO + +------------------------------------------------------------------------ +-- Listing 10-19. Converting a Datetimeoffset to Several Time Offsets -- +------------------------------------------------------------------------ +DECLARE @current datetimeoffset = '2012-05-04 19:30:00 -07:00'; +SELECT 'Los Angeles' AS [Location], @current AS [Current Time] +UNION ALL +SELECT 'New York', SWITCHOFFSET(@current, '-04:00') +UNION ALL +SELECT 'Bermuda', SWITCHOFFSET(@current, '-03:00') +UNION ALL +SELECT 'London', SWITCHOFFSET(@current, '+01:00'); +GO + +------------------------------------------- +-- Listing 10-20. Using Uniqueidentifier -- +------------------------------------------- +CREATE TABLE dbo.Document ( + DocumentId uniqueidentifier NOT NULL PRIMARY KEY DEFAULT (NEWID()) +); + +INSERT INTO dbo.Document DEFAULT VALUES; +INSERT INTO dbo.Document DEFAULT VALUES; +INSERT INTO dbo.Document DEFAULT VALUES; + +SELECT * FROM dbo.Document; + +------------------------------------------------ +-- Listing 10-21. Generating Sequential GUIDs -- +------------------------------------------------ +CREATE TABLE #TestSeqID ( + ID uniqueidentifier DEFAULT NEWSEQUENTIALID() PRIMARY KEY NOT NULL, + Num int NOT NULL +); + +INSERT INTO #TestSeqID (Num) +VALUES (1), (2), (3); + +SELECT ID, Num +FROM #TestSeqID; + +DROP TABLE #TestSeqID; +GO + +--------------------------------------------------------------------- +-- Listing 10-22. Creating the Hierarchyid Bill of Materials Table -- +--------------------------------------------------------------------- +CREATE TABLE Production.HierBillOfMaterials +( + BomNode hierarchyid NOT NULL PRIMARY KEY NONCLUSTERED, + ProductAssemblyID int NULL, + ComponentID int NULL, + UnitMeasureCode nchar(3) NULL, + PerAssemblyQty decimal(8, 2) NULL, + BomLevel AS BomNode.GetLevel() +); +GO + +----------------------------------------------------------------------- +-- Listing 10-23. Converting AdventureWorks BOMs to hierarchyid Form -- +----------------------------------------------------------------------- +;WITH BomChildren +( + ProductAssemblyID, + ComponentID +) +AS +( + SELECT + b1.ProductAssemblyID, + b1.ComponentID + FROM Production.BillOfMaterials b1 + GROUP BY + b1.ProductAssemblyID, + b1.ComponentID +), +BomPaths +( + Path, + ComponentID, + ProductAssemblyID +) +AS +( + SELECT + hierarchyid::GetRoot() AS Path, + NULL, + NULL + UNION ALL + + SELECT + CAST + ('/' + CAST (bc.ComponentId AS varchar(30)) + '/' AS hierarchyid) AS Path, + bc.ComponentID, + bc.ProductAssemblyID + FROM BomChildren AS bc + WHERE bc.ProductAssemblyID IS NULL + + UNION ALL + + SELECT + CAST + (bp.path.ToString() + + CAST(bc.ComponentID AS varchar(30)) + '/' AS hierarchyid) AS Path, + bc.ComponentID, + bc.ProductAssemblyID + FROM BomChildren AS bc + INNER JOIN BomPaths AS bp + ON bc.ProductAssemblyID = bp.ComponentID +) +INSERT INTO Production.HierBillOfMaterials +( + BomNode, + ProductAssemblyID, + ComponentID, + UnitMeasureCode, + PerAssemblyQty +) +SELECT + bp.Path, + bp.ProductAssemblyID, + bp.ComponentID, + bom.UnitMeasureCode, + bom.PerAssemblyQty +FROM BomPaths AS bp +LEFT OUTER JOIN Production.BillOfMaterials bom + ON bp.ComponentID = bom.ComponentID + AND COALESCE(bp.ProductAssemblyID, -1) = COALESCE(bom.ProductAssemblyID, -1) +WHERE bom.EndDate IS NULL +GROUP BY + bp.path, + bp.ProductAssemblyID, + bp.ComponentID, + bom.UnitMeasureCode, + bom.PerAssemblyQty; +GO + +------------------------------------------------- +-- Listing 10-24. Viewing the Hierarchyid BOMs -- +------------------------------------------------- +SELECT + BomNode, + BomNode.ToString(), + ProductAssemblyID, + ComponentID, + UnitMeasureCode, + PerAssemblyQty, + BomLevel +FROM Production.HierBillOfMaterialsORDER BY BomNode; +GO + +---------------------------------------------------------------- +-- Listing 10-25. Retrieving Descendant Nodes of Assembly 749 -- +---------------------------------------------------------------- +DECLARE @CurrentNode hierarchyid; + +SELECT @CurrentNode = BomNode +FROM Production.HierBillOfMaterials +WHERE ProductAssemblyID = 749; + +SELECT + BomNode, + BomNode.ToString(), + ProductAssemblyID, + ComponentID, + UnitMeasureCode, + PerAssemblyQty, + BomLevel +FROM Production.HierBillOfMaterials +WHERE @CurrentNode.IsDescendantOf(BomNode) = 1; +GO + +-------------------------------------------------------------- +-- Listing 10-26. Representing Wyoming as a Geometry Object -- +-------------------------------------------------------------- +DECLARE @Wyoming geometry; +SET @Wyoming = geometry::STGeomFromText ('POLYGON ( +( -104.053108 41.698246, -104.054993 41.564247, +-104.053505 41.388107, -104.051201 41.003227, +-104.933968 40.994305, -105.278259 40.996365, +-106.202896 41.000111, -106.328545 41.001316, +-106.864838 40.998489, -107.303436 41.000168, +-107.918037 41.00341, -109.047638 40.998474, +-110.001457 40.997646, -110.062477 40.99794, +-111.050285 40.996635, -111.050911 41.25848, +-111.050323 41.578648, -111.047951 41.996265, +-111.046028 42.503323, -111.048447 43.019962, +-111.04673 43.284813, -111.045998 43.515606, +-111.049629 43.982632, -111.050789 44.473396, +-111.050842 44.664562, -111.05265 44.995766, +-110.428894 44.992348, -110.392006 44.998688, +-109.994789 45.002853, -109.798653 44.99958, +-108.624573 44.997643, -108.258568 45.00016, +-107.893715 44.999813, -106.258644 44.996174, +-106.020576 44.997227, -105.084465 44.999832, +-105.04126 45.001091, -104.059349 44.997349, +-104.058975 44.574368, -104.060547 44.181843, +-104.059242 44.145844, -104.05899 43.852928, +-104.057426 43.503738, -104.05867 43.47916, +-104.05571 43.003094, -104.055725 42.614704, +-104.053009 41.999851, -104.053108 41.698246) )', 0); + +SELECT @Wyoming as Wyoming; +GO + +------------------------------------------------------------------------- +-- Listing 10-27. Using GML to Represent Wyoming as a Geography Object -- +------------------------------------------------------------------------- +DECLARE @Wyoming geography; +SET @Wyoming = geography::GeomFromGml (' + + + + 41.698246 -104.053108 41.999851 -104.053009 + 43.003094 -104.05571 43.503738 -104.057426 + 44.145844 -104.059242 44.574368 -104.058975 + 45.001091 -105.04126 44.997227 -106.020576 + 44.999813 -107.893715 44.997643 -108.624573 + 45.002853 -109.994789 44.992348 -110.428894 + 44.664562 -111.050842 43.982632 -111.049629 + 43.284813 -111.04673 42.503323 -111.046028 + 41.578648 -111.050323 40.996635 -111.050285 + 40.997646 -110.001457 41.00341 -107.918037 + 40.998489 -106.864838 41.000111 -106.202896 + 40.994305 -104.933968 41.388107 -104.053505 + 41.698246 -104.053108 + + + +', 4269); +GO + +---------------------------------------------------------------------- +-- Listing 10-28. Are the Statue of Liberty and Laramie in Wyoming? -- +---------------------------------------------------------------------- +DECLARE @Wyoming geography, + @StatueOfLiberty geography, + @Laramie geography; + +SET @Wyoming = geography::GeomFromGml (' + + + + 41.698246 -104.053108 41.999851 -104.053009 + 43.003094 -104.05571 43.503738 -104.057426 + 44.145844 -104.059242 44.574368 -104.058975 + 45.001091 -105.04126 44.997227 -106.020576 + 44.999813 -107.893715 44.997643 -108.624573 + 45.002853 -109.994789 44.992348 -110.428894 + 44.664562 -111.050842 43.982632 -111.049629 + 43.284813 -111.04673 42.503323 -111.046028 + 41.578648 -111.050323 40.996635 -111.050285 + 40.997646 -110.001457 41.00341 -107.918037 + 40.998489 -106.864838 41.000111 -106.202896 + 40.994305 -104.933968 41.388107 -104.053505 + 41.698246 -104.053108 + + + +', 4269); + +SET @StatueOfLiberty = geography::GeomFromGml(' + + 40.689124 -74.044483 + + ', 4269); + +SET @Laramie = geography::GeomFromGml(' + + 41.312928 -105.587253 + + ', 4269); + +SELECT 'Is the Statue of Liberty in Wyoming?', + CASE @Wyoming.STIntersects(@StatueOfLiberty) + WHEN 0 THEN 'No' + ELSE 'Yes' + END AS Answer +UNION +SELECT 'Is Laramie in Wyoming?', + CASE @Wyoming.STIntersects(@Laramie) + WHEN 0 THEN 'No' + ELSE 'Yes' + END; +GO + +--------------------------------------------- +-- Listing 10-29. Creating a Spatial Index -- +--------------------------------------------- +CREATE SPATIAL INDEX SIX_Location ON MyTable (SpatialColumn); +GO + +-------------------------------------------------------------- +-- Listing 10-30. Enabling FILESTREAM Support on the Server -- +-------------------------------------------------------------- +EXEC sp_configure 'filestream access level', 2; +RECONFIGURE; +GO + +----------------------------------------------------------------- +-- Listing 10-31. Viewing FILESTREAM Configuration Information -- +----------------------------------------------------------------- +SELECT + SERVERPROPERTY('ServerName') AS ServerName, + SERVERPROPERTY('FilestreamSharename') AS ShareName, + CASE SERVERPROPERTY('FilestreamEffectiveLevel') + WHEN 0 THEN 'Disabled' + WHEN 1 THEN 'T-SQL Access Only' + WHEN 2 THEN 'Local T-SOL/File System Access Only' + WHEN 3 THEN 'Local T-SOL/File System and Remote File System Access' + END AS Effective_Level, + CASE SERVERPROPERTY('FilestreamConfiguredLevel') + WHEN 0 THEN 'Disabled' + WHEN 1 THEN 'T-SQL Access Only' + WHEN 2 THEN 'Local T-SOL/File System Access Only' + WHEN 3 THEN 'Local T-SOL/File System and Remote File System Access' + END AS Configured_Level; +GO + +---------------------------------------------------------------- +-- Listing 10-32. CREATE DATABASE for AdventureWorks Database -- +---------------------------------------------------------------- +CREATE DATABASE [AdventureWorks] + CONTAINMENT = NONE + ON PRIMARY +( NAME = N'AdventureWorks2012_Data', FILENAME = N'C:\sqldata\MSSQL11.MSSQLSERVER\MSSQL\DATA\AdventureWorks2012_Data.mdf' , SIZE = 226304KB , MAXSIZE = UNLIMITED, FILEGROWTH = 16384KB ), + FILEGROUP [FILESTREAM1] CONTAINS FILESTREAM DEFAULT +( NAME = N'AdventureWordsFS', FILENAME = N'C:\sqldata\MSSQL11.MSSQLSERVER\MSSQL\DATA\AdventureWordsFS' , MAXSIZE = UNLIMITED) + LOG ON +( NAME = N'AdventureWorks2012_Log', FILENAME = N'C:\sqldata\MSSQL11.MSSQLSERVER\MSSQL\DATA\AdventureWorks2012_log.ldf' , SIZE = 5696KB , MAXSIZE = UNLIMITED, FILEGROWTH = 10%); +GO + +-------------------------------------------------------------------------- +-- Listing 10-33. Adding a FILESTREAM Filegroup to an Existing Database -- +-------------------------------------------------------------------------- +ALTER DATABASE AdventureWorks +ADD FILEGROUP FILESTREAM1 CONTAINS FILESTREAM; +GO +ALTER DATABASE AdventureWorks +ADD FILE +( +NAME = N' AdventureWordsFS', +FILENAME = N' C:\sqldata\MSSQL11.MSSQLSERVER\MSSQL\DATA\AdventureWordsFS' ) +TO FILEGROUP FILESTREAM1; +GO + +----------------------------------------------------------------- +-- Listing 10-34. Production.Document FILESTREAM-Enabled Table -- +----------------------------------------------------------------- +CREATE TABLE Production.DocumentFS ( + DocumentNode hierarchyid NOT NULL PRIMARY KEY, + DocumentLevel AS (DocumentNode.GetLevel()), + Title nvarchar(50) NOT NULL, + Owner int NOT NULL, + FolderFlag bit NOT NULL, + FileName nvarchar(400) NOT NULL, + FileExtension nvarchar(8) NOT NULL, + Revision nchar(5) NOT NULL, + ChangeNumber int NOT NULL, + Status tinyint NOT NULL, + DocumentSummary nvarchar(max) NULL, + Document varbinary(max) FILESTREAM NULL, + rowguid uniqueidentifier ROWGUIDCOL NOT NULL UNIQUE, + ModifiedDate datetime NOT NULL +); +GO + +INSERT INTO Production.DocumentFS + (DocumentNode, Title, Owner, FolderFlag, FileName, FileExtension, Revision, ChangeNumber, Status, DocumentSummary, Document, rowguid, ModifiedDate) +SELECT + DocumentNode, Title, Owner, FolderFlag, FileName, FileExtension, Revision, ChangeNumber, Status, DocumentSummary, Document, rowguid, ModifiedDate +FROM Production.Document; +GO + +-------------------------------------------------------- +-- Listing 10-35. Querying a FILESTREAM-Enabled Table -- +-------------------------------------------------------- +SELECT + d.Title, + d.Document.PathName() AS LOB_Path, + d.Document AS LOB_Data +FROM Production.DocumentFS d +WHERE d.Document IS NOT NULL; +GO + +-------------------------------------------------------------------- +-- Listing 10-36. Creating a Database with a FILESTREAM Filegroup -- +-------------------------------------------------------------------- +CREATE DATABASE cliparts +CONTAINMENT = NONE +ON PRIMARY +( NAME = N'cliparts', FILENAME = N'C:\sqldata\MSSQL11.MSSQLSERVER\MSSQL\DATA\cliparts.mdf' , SIZE = 5120KB , FILEGROWTH = 1024KB ), +FILEGROUP [filestreamFG1] CONTAINS FILESTREAM +( NAME = N'filestream1', FILENAME = N'C:\sqldata\MSSQL11.MSSQLSERVER\MSSQL\DATA\filestream1' ) +LOG ON +( NAME = N'cliparts_log', FILENAME = N'C:\sqldata\MSSQL11.MSSQLSERVER\MSSQL\DATA\cliparts_log.ldf' , SIZE = 1024KB , FILEGROWTH = 10%); +GO + +ALTER DATABASE [cliparts] SET FILESTREAM( NON_TRANSACTED_ACCESS = FULL, DIRECTORY_NAME = N'cliparts' ); +GO + +------------------------------------------- +-- Listing 10-37. Creating the Filetable -- +------------------------------------------- +USE [cliparts]; +GO + +CREATE TABLE dbo.OpenClipartsLibrary AS FILETABLE +WITH + ( + FILETABLE_DIRECTORY = 'OpenClipartsLibrary' + ); +GO + +INSERT INTO dbo.OpenClipartsLibrary (name,is_directory) +VALUES ('import_20120501',1); +GO + +----------------------------------------------------------- +-- Listing 10-38. Inserting a Directory in the Filetable -- +----------------------------------------------------------- +INSERT INTO dbo.OpenClipartsLibrary (name, is_directory) +VALUES ('directory01',1); +GO + +--------------------------------------------- +-- Listing 10-39. Inserting a Subdirectory -- +--------------------------------------------- +INSERT INTO dbo.OpenClipartsLibrary + (name, is_directory, creation_time, path_locator) +SELECT + 'directory02',1, dateadd(year, -1, sysdatetime()), path_locator.GetDescendant(NULL, NULL) +FROM dbo.OpenClipartsLibrary +WHERE name = 'directory01' +AND is_directory = 1 +AND parent_path_locator IS NULL; +GO + +---------------------------------------------- +-- Listing 10-40. Using FileTableRootPath() -- +---------------------------------------------- +USE cliparts; + +SELECT FileTableRootPath(); +SELECT FileTableRootPath('dbo.OpenClipartsLibrary'); +GO + +-------------------------------------------------- +-- Listing 10-41. Using GetFileNamespacePath(). -- +-------------------------------------------------- +SELECT file_stream.GetFileNamespacePath(1) as path +FROM dbo.OpenClipartsLibrary +WHERE is_directory = 1 +ORDER BY path_locator.GetLevel(), path; +GO + +------------------------------------------------ +-- Listing 10-42. Using Hierarchyid Functions -- +------------------------------------------------ +SELECT l1.name, l1.path_locator.GetLevel(), l2.name as parent_directory +FROM dbo.OpenClipartsLibrary l1 +JOIN dbo.OpenClipartsLibrary l2 ON l1.path_locator.GetAncestor(1) = l2.path_locator +WHERE l1.is_directory = 1; +GO + +----------------------------------------------------- +-- Listing 10-43. Using Parent_path_locator Column -- +----------------------------------------------------- +SELECT l1.name, l1.path_locator.GetLevel(), l2.name as parent_directory +FROM dbo.OpenClipartsLibrary l1 +JOIN dbo.OpenClipartsLibrary l2 ON l1.parent_path_locator = l2.path_locator +WHERE l1.is_directory = 1; +GO + +-------------------------------------------------------------------------- +-- Listing 10-44. Using a CTE to Travel Down the Directories’ Hierarchy -- +-------------------------------------------------------------------------- +;WITH mycte AS ( + SELECT name, path_locator.GetLevel() as Level, path_locator + FROM dbo.OpenClipartsLibrary + WHERE name = 'Yason' + AND is_directory = 1 + + UNION ALL + + SELECT l1.name, l1.path_locator.GetLevel() as Level, l1.path_locator + FROM dbo.OpenClipartsLibrary l1 + JOIN mycte l2 ON l1.parent_path_locator = l2.path_locator + WHERE l1.is_directory = 1 +) +SELECT name, Level +FROM mycte +ORDER BY level, name; +GO + +----------------------------------------------------------------------------------------- +-- Listing 10-45. Using hierarchyid Functions to Travel Down the Directory’s Hierarchy -- +----------------------------------------------------------------------------------------- +SELECT l1.name, l1.path_locator.GetLevel() as Level +FROM dbo.OpenClipartsLibrary l1 +JOIN dbo.OpenClipartsLibrary l2 ON l1.path_locator.IsDescendantOf(l2.path_locator) = 1 OR l1.path_locator = l2.path_locator +WHERE l1.is_directory = 1 +AND l2.is_directory = 1 +AND l2.name = 'Yason' +ORDER BY level, name; +GO + +--------------------------------------------------------- +-- Listing 10-46. Using the GetPathLocator() function. -- +--------------------------------------------------------- +DECLARE @path_locator hierarchyid + +SET @path_locator = GetPathLocator('\\Sql2012\mssqlserver\cliparts\OpenClipartsLibrary\import_20120501\Yason'); + +SELECT * +FROM dbo.OpenClipartsLibrary +WHERE path_locator = @path_locator; +GO + +------------------------------------------------------------------------------------------- +-- Listing 10-47. Creating an Audit Table and a Trigger on the OpenClipartsLibrary Table -- +------------------------------------------------------------------------------------------- +CREATE TABLE dbo.cliparts_log ( + path nvarchar(4000) not null, + deletion_date datetime2(0), + deletion_user sysname, + is_directory bit +) +GO + +CREATE TRIGGER OpenClipartsLibrary_logTrigger +ON [dbo].[OpenClipartsLibrary] +AFTER DELETE +AS BEGIN + IF @@ROWCOUNT = 0 RETURN; + SET NOCOUNT ON; + + INSERT INTO dbo.cliparts_log (path, deletion_date, deletion_user, is_directory) + SELECT name, SYSDATETIME(), SUSER_SNAME(),is_directory + FROM deleted +END; +GO + diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch12/9781430245964_Natarajan_Ch12_XQueryandXPath.sql b/Pro T-SQL 2012 Programmer's Guide/Ch12/9781430245964_Natarajan_Ch12_XQueryandXPath.sql new file mode 100644 index 0000000..a10e9a5 --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch12/9781430245964_Natarajan_Ch12_XQueryandXPath.sql @@ -0,0 +1,502 @@ +/*Listing 12 - 1. Retrieving Names and E-mail Addresses with FOR XML PATH */ +SELECT +p.BusinessEntityID AS "Person/ID", +p.FirstName AS "Person/Name/First", +p.MiddleName AS "Person/Name/Middle", +p.LastName AS "Person/Name/Last", +e.EmailAddress AS "Person/Email" +FROM Person.Person p INNER JOIN Person.EmailAddress e +ON p.BusinessEntityID = e.BusinessEntityID +FOR XML PATH, ROOT('PersonEmailAddress'); + +/*Listing 12 - 2. FOR XML PATH Creating XML Attributes */ +SELECT p.BusinessEntityID AS "Person/@ID", +e.EmailAddress AS "Person/@Email", +p.FirstName AS "Person/Name/First", +p.MiddleName AS "Person/Name/Middle", +p.LastName AS "Person/Name/Last" +FROM Person.Person p INNER JOIN Person.EmailAddress e +ON p.BusinessEntityID = e.BusinessEntityID FOR XML PATH; + +/*Listing 12 - 3. Using Columns without Names and Wildcards with FOR XML PATH */ +SELECT p.BusinessEntityID AS "*", ',' + e.EmailAddress, +p.FirstName AS "Person/Name/First", +p.MiddleName AS "Person/Name/Middle", +p.LastName AS "Person/Name/Last" FROM Person.Person p INNER JOIN Person.EmailAddress e +ON p.BusinessEntityID = e.BusinessEntityID FOR XML PATH; + +/*Listing 12 - 4. Two Elements with a Common Parent Element Separated */ +SELECT p.BusinessEntityID AS "@ID", +e.EmailAddress AS "@EmailAddress", +p.FirstName AS "Person/Name/First", +pp.PhoneNumber AS "Phone/BusinessPhone", +p.MiddleName AS "Person/Name/Middle", +p.LastName AS "Person/Name/Last" +FROM Person.Person p +INNER JOIN Person.EmailAddress e +ON p.BusinessEntityID = e.BusinessEntityID +INNER JOIN Person.PersonPhone pp +ON p.BusinessEntityID = pp.BusinessEntityID +AND pp.PhoneNumberTypeID = 3 FOR XML PATH; + +/*Listing 12 - 5. The FOR XML PATH XPath data Node Test */ +SELECT DISTINCT soh.SalesPersonID AS "SalesPerson/@ID", ( +SELECT soh2.SalesOrderID AS "data()" +FROM Sales.SalesOrderHeader soh2 +WHERE soh2.SalesPersonID = soh.SalesPersonID FOR XML PATH ('') ) AS +"SalesPerson/@Orders", +p.FirstName AS "SalesPerson/Name/First", +p.MiddleName AS "SalesPerson/Name/Middle", +p.LastName AS "SalesPerson/Name/Last", +e.EmailAddress AS "SalesPerson/Email" +FROM Sales.SalesOrderHeader soh +INNER JOIN Person.Person p +ON p.BusinessEntityID = soh.SalesPersonID +INNER JOIN Person.EmailAddress e +ON p.BusinessEntityID = e.BusinessEntityID +WHERE soh.SalesPersonID IS NOT NULL FOR XML PATH; + +/*Listing 12 - 6. FOR XML with the ELEMENTS XSINIL Option */ +SELECT +p.BusinessEntityID AS "Person/ID", +p.FirstName AS "Person/Name/First", +p.MiddleName AS "Person/Name/Middle", +p.LastName AS "Person/Name/Last", +e.EmailAddress AS "Person/Email" FROM Person.Person p INNER JOIN Person.EmailAddress e +ON p.BusinessEntityID = e.BusinessEntityID FOR XML PATH, +ELEMENTS XSINIL; + +/*Listing 12 - 7. Using WITH XMLNAMESPACES to Specify Namespaces */ +WITH XMLNAMESPACES(' http://www.apress.com/xml/sampleSqlXmlNameSpace ' as ns) +SELECT +p.BusinessEntityID AS "ns:Person/ID", +p.FirstName AS "ns:Person/Name/First", +p.MiddleName AS "ns:Person/Name/Middle", +p.LastName AS "ns:Person/Name/Last", +e.EmailAddress AS "ns:Person/Email" +FROM Person.Person p +INNER JOIN Person.EmailAddress e +ON p.BusinessEntityID = e.BusinessEntityID +FOR XML PATH; + +/*Listing 12 - 8. FOR XML PATH Using XPath Node Tests */ +SELECT +p.NameStyle AS "processing-instruction(nameStyle)", +p.BusinessEntityID AS "Person/@ID", +p.ModifiedDate AS "comment()", +pp.PhoneNumber AS "text()", +FirstName AS "Person/Name/First", +MiddleName AS "Person/Name/Middle", +LastName AS "Person/Name/Last", +EmailAddress AS "Person/Email" +FROM Person.Person p +INNER JOIN Person.EmailAddress e +ON p.BusinessEntityID = e.BusinessEntityID +INNER JOIN Person.PersonPhone pp +ON p.BusinessEntityID = pp.BusinessEntityID +FOR XML PATH; + +/*Listing 12 - 9. Retrieving Job Candidates with the query Method */ +SELECT Resume.query +( +N'//*:Name.First, +//*:Name.Middle, +//*:Name.Last, +//*:Edu.Level' +) +FROM HumanResources.JobCandidate; + +/*Listing 12 - 10. Querying with an Absolute Location Path */ +DECLARE @x xml = N'< ?xml version = "1.0"?> + + + + 37.859609 + −122.291673 + + + APress, Inc. + + + + + 37.423268 + −122.086345 + + + Google, Inc. + + + '; +SELECT @x.query(N'/Geocode/Info/Coordinates'); + +/*Listing 12 - 11. Sample Processing-instruction Node Test */ +SELECT CatalogDescription.query(N'/processing-instruction()') AS Processing_Instr +FROM Production.ProductModel +WHERE ProductModelID = 19; + +/*Listing 12 - 12. Sample comment Node Test */ +SELECT CatalogDescription.query(N'//comment()') AS Comments +FROM Production.ProductModel +WHERE ProductModelID = 19; + +/*Listing 12 - 13. Sample node Node Test */ +SELECT CatalogDescription.query(N'//*:Specifications/node()') AS Specifications +FROM Production.ProductModel +WHERE ProductModelID = 19; + +/*Listing 12 - 14. Querying CatalogDescription with No Namespaces */ +SELECT CatalogDescription.query(N'//Specifications/node()') AS Specifications +FROM Production.ProductModel +WHERE ProductModelID = 19; + +/*Listing 12 - 15. Prolog Namespace Declaration */ +SELECT CatalogDescription.query +( +N'declare namespace +p1 = " http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription "; +//p1:Specifications/node()' +) +FROM Production.ProductModel +WHERE ProductModelID = 19; + +/*Listing 12 - 16. Prolog Default Namespace Declaration */ +SELECT CatalogDescription.query +( +N'declare default element namespace +" http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription "; +//Specifications/node()' +) +FROM Production.ProductModel +WHERE ProductModelID = 19; + +/*Listing 12 - 17. Query with and Without Default Axes */ +SELECT CatalogDescription.query(N'//*:Specifications/node()') AS Specifications +FROM Production.ProductModel +WHERE ProductModelID = 19; +SELECT CatalogDescription.query(N'//child::*:Specifications/child::node()') +AS Specifications +FROM Production.ProductModel +WHERE ProductModelID = 19; + +/*Listing 12 - 18. Sample Using the parent:: Axis */ +DECLARE @x xml = N'< ?xml version = "1.0"?> + + + + 37.859609 + −122.291673 + + + APress, Inc. + + + + + 37.423268 + −122.086345 + + + Google, Inc. + + + '; +SELECT @x.query(N'//Location/parent::node()/Coordinates'); + +/*Listing 12 - 19. XQuery Dynamic XML Construction */ +DECLARE @x xml = N'< ?xml version = "1.0"?> + + + + APress, Inc. + + + + + Google, Inc. + + + '; +SELECT @x.query(N'< Companies> +{ +//Info/Location/Name +} + '); + +/*Listing 12 - 20. Element and Attribute Dynamic Constructors */ +SELECT CatalogDescription.query +( +N'declare namespace +p1 = " http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription "; +//p1:Specifications/node()' +) +FROM Production.ProductModel +WHERE ProductModelID = 19; +DECLARE @x xml = N'< ?xml version = "1.0"?> + + + + APress, Inc. +
+ 2560 Ninth St, Ste 219 + Berkeley + CA + 94710-2500 + US +
+
+
+
'; +SELECT @x.query +( +N'element Companies +{ +element FirstCompany +{ +attribute CompanyID +{ +(//Info/@ID)[1] +}, +(//Info/Location/Name)[1] +} +}' +); + +/*Listing 12 - 21. Value Comparison Examples */ +DECLARE @x xml = N'< ?xml version = "1.0" ?> + +Cat + '; +SELECT @x.query(N'9 eq 9.0 (: 9 is equal to 9.0 :)'); +SELECT @x.query(N'4 gt 3 (: 4 is greater than 3 :)'); +SELECT @x.query(N'(/Animal/text())[1] lt "Dog" (: Cat is less than Dog :)') ; + +/*Listing 12 - 22. Incompatible Type Value Comparison */ +DECLARE @x xml = N''; +SELECT @x.query(N'3.141592 eq "Pi"') ; +Msg 2234, Level 16, State 1, Line 2 +XQuery [query()]: The operator "eq" cannot be applied to "xs:decimal" and "xs:string" operands. + +/*Listing 12 - 23. General Comparison Examples */ +DECLARE @x xml = ''; +SELECT @x.query('(3.141592, 1) = (2, 3.141592) (: true :) '); +SELECT @x.query('(1.0, 2.0, 3.0) = 1 (: true :) '); +SELECT @x.query('("Joe", "Harold") < "Adam" (: false :) '); +SELECT @x.query('xs:date("1999-01-01") < xs:date("2006-01-01") (: true :)'); + +/*Listing 12 - 24. General Comparison with Heterogeneous Sequence */ +DECLARE @x xml = ''; +SELECT @x.query('(xs:date("2006-10-09"), 6.02E23) > xs:date("2007-01-01")'); + +/*Listing 12 - 25. Mixing Nodes and Atomic Values in Sequences */ +DECLARE @x xml = ''; +SELECT @x.query('(1, Testing)'); + +/*Listing 12 - 26. Node Comparison Samples */ +DECLARE @x xml = N'< ?xml version = "1.0"?> + + Test Node + Test Node + Test Node + '; +SELECT @x.query('((/Root/NodeA)[1] is (//NodeA)[1]) (: true :)'); +SELECT @x.query('((/Root/NodeA)[1] is (/Root/NodeA)[2]) (: false :)'); +SELECT @x.query('((/Root/NodeA)[2] is (/Root/NodeB)[1]) (: true :)'); + +/*Listing 12 - 27. Node Comparison That Evaluates to an Empty Sequence */ +DECLARE @x xml = N'< ?xml version = "1.0"?> + + Test Node + '; +SELECT @x.query('((/Root/NodeA)[1] is (/Root/NodeZ)[1]) (: empty sequence :)'); + +/*Listing 12 - 28. The sql:column Function */ +DECLARE @x xml = N''; +SELECT @x.query(N'< Name> + +{ +sql:column("p.BusinessEntityID") +} + + +{ +sql:column("p.FirstName"), +sql:column("p.MiddleName"), +sql:column("p.LastName") +} + + ') +FROM Person.Person p +WHERE p.BusinessEntityID < = 5 +ORDER BY p.BusinessEntityID; + +/*Listing 12 - 29. XQuery sql:column and sql:variable Functions Example */ +/* 10 % discount */ +DECLARE @discount NUMERIC(3, 2); +SELECT @discount = 0.10; +DECLARE @x xml; +SELECT @x = ''; +SELECT @x.query('< Product> + { sql:column("ProductModelID") } + { sql:column("Name") } + { sql:column("ListPrice") } + +{ sql:column("ListPrice") - +(sql:column("ListPrice") * sql:variable("@discount") ) } + + +') +FROM Production.Product p +WHERE ProductModelID = 30; + +/*Listing 12 - 30. Basic XQuery for . . . return Expression */ +SELECT CatalogDescription.query(N'declare namespace ns = +" http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription "; +for $spec in //ns:ProductDescription/ns:Specifications/* +return fn:string($spec)') AS Description FROM Production.ProductModel WHERE ProductModelID = 19; + +/*Listing 12 - 31. XQuery for . . . return Expression with XML Result */ +SELECT CatalogDescription.query ( +N'declare namespace ns = +" http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription "; +for $spec in //ns:ProductDescription/ns:Specifications/* return < detail > { +$spec/text() } ' ) AS Description +FROM Production.ProductModel WHERE ProductModelID = 19; + +/*Listing 12 - 32. XQuery Cartesian Product with for Expression */ +SELECT CatalogDescription.query(N'declare namespace ns = +" http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription "; +for $spec in //ns:ProductDescription/ns:Specifications/*, +$feat in //ns:ProductDescription/*:Features/*:Warranty/node() +return < detail> +{ +$spec/text() +} + +{ +fn:string($feat/.) +} + ' +) AS Description +FROM Production.ProductModel +WHERE ProductModelID = 19; + +/*Listing 12 - 33. Using a Bound Variable in the for Clause */ +SELECT CatalogDescription.query +( +N'declare namespace ns = +" http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription "; +for $spec in //ns:ProductDescription/ns:Specifications, +$color in $spec/Color +return < color> +{ +$color/text() +} + ' +) AS Color +FROM Production.ProductModel +WHERE ProductModelID = 19; + +/*Listing 12 - 34. where Clause Demonstration */ +SELECT CatalogDescription.query +( +N'declare namespace ns = +" http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription "; +for $spec in //ns:ProductDescription/ns:Specifications/* +where $spec[ contains( . , "A" ) ] +return < detail> +{ +$spec/text() +} + ' +) AS Detail +FROM Production.ProductModel +WHERE ProductModelID = 19; + +/*Listing 12 - 35. order by Clause */ +SELECT CatalogDescription.query(N'declare namespace ns = +" http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription "; +for $spec in //ns:ProductDescription/ns:Specifications/* +order by $spec/. descending +return < detail > { $spec/text() } ') AS Detail +FROM Production.ProductModel +WHERE ProductModelID = 19; + +/*Listing 12 - 36. let Clause */ +SELECT CatalogDescription.query +( +N'declare namespace ns = +" http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription "; +for $spec in //ns:ProductDescription/ns:Specifications/* +let $val := $spec/text() +order by fn:string($val[1]) ascending +return < spec> +{ +$val +} + ' +) AS Detail +FROM Production.ProductModel +WHERE ProductModelID = 19; + +/*Listing 12 - 37. Create Record to Demonstrate UTF-16 */ +declare @BusinessEntityId int +INSERT INTO Person.BusinessEntity(rowguid, ModifiedDate) +VALUES (NEWID(),CURRENT_TIMESTAMP) +SET @BusinessEntityId = SCOPE_IDENTITY() +INSERT INTO [Person].[Person] +([BusinessEntityID] +,[PersonType] +,[NameStyle] +,[Title] +,[FirstName] +,[MiddleName] +,[LastName] +,[Suffix] +,[EmailPromotion] +,[AdditionalContactInfo] +,[Demographics] +,[rowguid] +,[ModifiedDate]) +VALUES +396 +(@BusinessEntityId, +'EM', +0, +NULL, +N'T' + nchar(0xD834) + nchar(0xDD25), +'J', +'Kim', +NULL, +0, +NULL, +'< IndividualSurvey xmlns = " http://schemas.microsoft.com/sqlserver/2004/07/ +adventure-works/IndividualSurvey">0', +NEWID(), +CURRENT_TIMESTAMP) + + +/*Listing 12 - 38. SQL Server to Check for Presence of Surrogates */ +SELECT +p.NameStyle AS "processing-instruction(nameStyle)", +p.BusinessEntityID AS "Person/@ID", +p.ModifiedDate AS "comment()", +FirstName AS "Person/Name/First", +Len(FirstName) AS "Person/FirstName/Length", +MiddleName AS "Person/Name/Middle", +LastName AS "Person/Name/Last" +FROM Person.Person p +WHERE BusinessEntityID = 20778 +FOR XML PATH; + +/*Listing 12-39. Surroage Pair with UTF-16 and _SC collation */ +SELECT +p.NameStyle AS "processing-instruction(nameStyle)", +p.BusinessEntityID AS "Person/@ID", +p.ModifiedDate AS "comment()", +FirstName AS "Person/Name/First", +Len(FirstName COLLATE Latin1_General_100_CS_AS_SC) AS "Person/FirstName/Length", +MiddleName AS "Person/Name/Middle", +LastName AS "Person/Name/Last" +FROM Person.Person p +WHERE BusinessEntityID = 20778 +FOR XML PATH; diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples.sln b/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples.sln new file mode 100644 index 0000000..8094f5f --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ApressExamples", "ApressExamples\ApressExamples.csproj", "{13E56BB3-CB08-4B05-BE52-0ACA0EA080E1}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {13E56BB3-CB08-4B05-BE52-0ACA0EA080E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {13E56BB3-CB08-4B05-BE52-0ACA0EA080E1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {13E56BB3-CB08-4B05-BE52-0ACA0EA080E1}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {13E56BB3-CB08-4B05-BE52-0ACA0EA080E1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {13E56BB3-CB08-4B05-BE52-0ACA0EA080E1}.Release|Any CPU.Build.0 = Release|Any CPU + {13E56BB3-CB08-4B05-BE52-0ACA0EA080E1}.Release|Any CPU.Deploy.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples.suo b/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples.suo new file mode 100644 index 0000000000000000000000000000000000000000..e4572c4a9707324800892090199c923efa52b1b4 GIT binary patch literal 27136 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 literal 0 HcmV?d00001 diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/ApressExamples.csproj b/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/ApressExamples.csproj new file mode 100644 index 0000000..93d1f12 --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/ApressExamples.csproj @@ -0,0 +1,79 @@ + + + + Debug + AnyCPU + {c252feb5-a946-4202-b1d4-9916a0590387};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + + + + + {13E56BB3-CB08-4B05-BE52-0ACA0EA080E1} + Library + false + ApressExamples + v4.0 + false + + + 2 + + + true + full + false + bin\Debug\ + false + DEBUG;TRACE + 4 + true + + + false + true + bin\Release\ + false + TRACE + 4 + true + + + ApressExamples + + + + + + + + + + + + + + + + + + + + + + Content + + + Content + + + Content + + + + \ No newline at end of file diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/ApressExamples.csproj.user b/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/ApressExamples.csproj.user new file mode 100644 index 0000000..4bb0595 --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/ApressExamples.csproj.user @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/ApressExamples.sqlproj b/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/ApressExamples.sqlproj new file mode 100644 index 0000000..48681ad --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/ApressExamples.sqlproj @@ -0,0 +1,56 @@ + + + + + Debug + AnyCPU + ApressExamples + 2.0 + 4.1 + {be1f9cbf-1c1c-4b1d-b322-c48d02988eb4} + Microsoft.Data.Tools.Schema.Sql.Sql110DatabaseSchemaProvider + Database + + + ApressExamples + ApressExamples + 1033, CI + BySchemaAndSchemaType + True + v4.0 + CS + Properties + False + + + bin\Release\ + $(MSBuildProjectName).sql + False + pdbonly + true + false + true + prompt + 4 + + + bin\Debug\ + $(MSBuildProjectName).sql + false + true + full + false + true + true + prompt + 4 + + + + 10.0 + + + + + + \ No newline at end of file diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/ApressExamples.sqlproj.user b/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/ApressExamples.sqlproj.user new file mode 100644 index 0000000..ace9a86 --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/ApressExamples.sqlproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/Complex.cs b/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/Complex.cs new file mode 100644 index 0000000..d102290 --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/Complex.cs @@ -0,0 +1,245 @@ +using System; +using System.Data.SqlTypes; +using Microsoft.SqlServer.Server; +using System.Text.RegularExpressions; + +namespace Apress.Examples +{ + [Serializable] + [Microsoft.SqlServer.Server.SqlUserDefinedType + ( + Format.Native, + IsByteOrdered = true + )] + public struct Complex : INullable + { + + #region "Complex Number UDT Fields/Components" + + private bool m_Null; + public Double real; + public Double imaginary; + + #endregion + + #region "Complex Number Parsing, Constructor, and Methods/Properties" + + private static readonly Regex rx = new Regex( + "^(?[+-]?([0-9]+|[0-9]*\\.[0-9]+))[i|I]$|" + + "^(?[+-]?([0-9]+|[0-9]*\\.[0-9]+))$|" + + "^(?[+-]?([0-9]+|[0-9]*\\.[0-9]+))" + + "(?[+-]?([0-9]+|[0-9]*\\.[0-9]+))[i|I]$"); + + public static Complex Parse(SqlString s) + { + Complex u = new Complex(); + if (s.IsNull) + u = Null; + else + { + MatchCollection m = rx.Matches(s.Value); + if (m.Count == 0) + throw (new FormatException("Invalid Complex Number Format.")); + String real_str = m[0].Groups["Real"].Value; + String imaginary_str = m[0].Groups["Imaginary"].Value; + if (real_str == "" && imaginary_str == "") + throw (new FormatException("Invalid Complex Number Format.")); + if (real_str == "") + u.real = 0.0; + else + u.real = Convert.ToDouble(real_str); + if (imaginary_str == "") + u.imaginary = 0.0; + else + u.imaginary = Convert.ToDouble(imaginary_str); + } + return u; + } + + public override String ToString() + { + String sign = ""; + if (this.imaginary >= 0.0) + sign = "+"; + return this.real.ToString() + sign + this.imaginary.ToString() + "i"; + } + + public bool IsNull + { + get + { + return m_Null; + } + } + + public static Complex Null + { + get + { + Complex h = new Complex(); + h.m_Null = true; + return h; + } + } + + public Complex(Double r, Double i) + { + this.real = r; + this.imaginary = i; + this.m_Null = false; + } + + #endregion + + #region "Useful Complex Number Constants" + + // The property "i" is the Complex number 0 + 1i. Defined here because + // it is useful in some calculations + + public static Complex i + { + get + { + return new Complex(0, 1); + } + } + + // The property "Pi" is the Complex representation of the number + // Pi (3.141592... + 0i) + + public static Complex Pi + { + get + { + return new Complex(Math.PI, 0); + } + } + + // The property "One" is the Complex number representation of the + // number 1 (1 + 0i) + + public static Complex One + { + get + { + return new Complex(1, 0); + } + } + + #endregion + + #region "Complex Number Basic Operators" + + // Complex number addition + + public static Complex operator +(Complex n1, Complex n2) + { + Complex u; + if (n1.IsNull || n2.IsNull) + u = Null; + else + u = new Complex(n1.real + n2.real, n1.imaginary + n2.imaginary); + return u; + } + + // Complex number subtraction + + public static Complex operator -(Complex n1, Complex n2) + { + Complex u; + if (n1.IsNull || n2.IsNull) + u = Null; + else + u = new Complex(n1.real - n2.real, n1.imaginary - n2.imaginary); + return u; + } + + // Complex number multiplication + + public static Complex operator *(Complex n1, Complex n2) + { + Complex u; + if (n1.IsNull || n2.IsNull) + u = Null; + else + u = new Complex((n1.real * n2.real) - (n1.imaginary * n2.imaginary), + (n1.real * n2.imaginary) + (n2.real * n1.imaginary)); + return u; + } + + // Complex number division + + public static Complex operator /(Complex n1, Complex n2) + { + Complex u; + if (n1.IsNull || n2.IsNull) + u = Null; + else + { + if (n2.real == 0.0 && n2.imaginary == 0.0) + throw new DivideByZeroException + ("Complex Number Division By Zero Exception."); + u = new Complex(((n1.real * n2.real) + + (n1.imaginary * n2.imaginary)) / + ((Math.Pow(n2.real, 2) + Math.Pow(n2.imaginary, 2))), + ((n1.imaginary * n2.real) - (n1.real * n2.imaginary)) / + ((Math.Pow(n2.real, 2) + Math.Pow(n2.imaginary, 2)))); + } + return u; + } + + // Unary minus operator + + public static Complex operator -(Complex n1) + { + Complex u; + if (n1.IsNull) + u = Null; + else + u = new Complex(-n1.real, -n1.imaginary); + return u; + } + + #endregion + + #region "Exposed Mathematical Basic Operator Methods" + + // Add complex number n2 to n1 + + public static Complex CAdd(Complex n1, Complex n2) + { + return n1 + n2; + } + + // Subtract complex number n2 from n1 + + public static Complex Sub(Complex n1, Complex n2) + { + return n1 - n2; + } + + // Multiply complex number n1 * n2 + + public static Complex Mult(Complex n1, Complex n2) + { + return n1 * n2; + } + + // Divide complex number n1 by n2 + + public static Complex Div(Complex n1, Complex n2) + { + return n1 / n2; + } + + // Returns negated complex number + + public static Complex Neg(Complex n1) + { + return -n1; + } + + #endregion + + } +} diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/EmailAddressTrigger.cs b/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/EmailAddressTrigger.cs new file mode 100644 index 0000000..320441c --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/EmailAddressTrigger.cs @@ -0,0 +1,63 @@ +using System; +using System.Data; +using System.Data.SqlClient; +using Microsoft.SqlServer.Server; +using System.Text.RegularExpressions; +using System.Transactions; // you need to add the reference. Right-click on Project and add reference, SQL Server tab, System.Transactions for framework 4.0.0.0 + +namespace Apress.Examples +{ + public partial class Triggers + { + private static readonly Regex email_pattern = new Regex + ( + // Everything before the @ sign (the "local part") + "^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*" + + + // Subdomains after the @ sign + "@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+" + + + // Top-level domains + "(?:[a-z]{2}|com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum)\\b$" + ); + + // Enter existing table or view for the target and uncomment the attribute line + [Microsoft.SqlServer.Server.SqlTrigger(Name = "EmailAddressTrigger", Target = "[Person].[EmailAddress]", Event = "FOR INSERT, UPDATE")] + public static void EmailAddressTrigger() + { + SqlTriggerContext tContext = SqlContext.TriggerContext; + + // Retrieve the connection that the trigger is using. + using (SqlConnection cn + = new SqlConnection(@"context connection=true")) + { + SqlCommand cmd; + SqlDataReader r; + + cn.Open(); + + cmd = new SqlCommand(@"SELECT EmailAddress FROM INSERTED", cn); + r = cmd.ExecuteReader(); + try + { + while (r.Read()) + { + if (!email_pattern.IsMatch(r.GetString(0).ToLower())) + { + Transaction.Current.Rollback(); + } + } + } + catch (SqlException ex) + { + // Catch the expected exception. + } + finally + { + r.Close(); + cn.Close(); + } + } + } + } +} \ No newline at end of file diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/EmailUDF.cs b/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/EmailUDF.cs new file mode 100644 index 0000000..61fd406 --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/EmailUDF.cs @@ -0,0 +1,35 @@ +using System.Data.SqlTypes; +using System.Text.RegularExpressions; + +namespace Apress.Examples +{ + public static class UDFExample + { + private static readonly Regex email_pattern = new Regex + ( + // Everything before the @ sign (the "local part") + "^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*" + + + // Subdomains after the @ sign + "@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+" + + + // Top-level domains + "(?:[a-z]{2}|com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum)\\b$" + ); + + [Microsoft.SqlServer.Server.SqlFunction + ( + IsDeterministic = true + )] + public static SqlBoolean EmailMatch(SqlString input) + { + SqlBoolean result = new SqlBoolean(); + if (input.IsNull) + result = SqlBoolean.Null; + else + result = (email_pattern.IsMatch(input.Value.ToLower()) == true) + ? SqlBoolean.True : SqlBoolean.False; + return result; + } + } +} diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/GetEnvironmentVars.cs b/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/GetEnvironmentVars.cs new file mode 100644 index 0000000..5960109 --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/GetEnvironmentVars.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections; +using System.Data; + +using System.Data.SqlClient; +using System.Data.SqlTypes; +using Microsoft.SqlServer.Server; +namespace Apress.Examples +{ + public partial class SampleProc + { + [Microsoft.SqlServer.Server.SqlProcedure()] + public static void GetEnvironmentVars() + { + try + { + SortedList environment_list = new SortedList(); + foreach (DictionaryEntry de in Environment.GetEnvironmentVariables()) + { + environment_list[de.Key] = de.Value; + } + + SqlDataRecord record = new SqlDataRecord ( + new SqlMetaData("VarName", SqlDbType.NVarChar, 1024), + new SqlMetaData("VarValue", SqlDbType.NVarChar, 4000) + ); + SqlContext.Pipe.SendResultsStart(record); + foreach (DictionaryEntry de in environment_list) + { + record.SetValue(0, de.Key); + record.SetValue(1, de.Value); + SqlContext.Pipe.SendResultsRow(record); + } + + SqlContext.Pipe.SendResultsEnd(); + } + catch (Exception ex) + { + SqlContext.Pipe.Send(ex.Message); + } + } + } +}; diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/Median.cs b/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/Median.cs new file mode 100644 index 0000000..ed0d8c3 --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/Median.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.SqlTypes; +using System.Runtime.InteropServices; +using Microsoft.SqlServer.Server; + +namespace Apress.Examples { + [Serializable] + [Microsoft.SqlServer.Server.SqlUserDefinedAggregate ( + Format.UserDefined, + IsNullIfEmpty = true, + MaxByteSize = -1 )] + [StructLayout(LayoutKind.Sequential)] + + public struct Median : IBinarySerialize + { + List temp; // List of numbers + + public void Init() + { + // Create new list of double numbers + this.temp = new List(); + } + + public void Accumulate(SqlDouble number) + { + if (!number.IsNull) // Skip over NULLs + { + this.temp.Add(number.Value); // If number is not NULL, add it to list + } + } + + public void Merge(Median group) + { + // Merge two sets of numbers + this.temp.InsertRange(this.temp.Count, group.temp); + } + + public SqlDouble Terminate() { + SqlDouble result = SqlDouble.Null; // Default result to NULL + this.temp.Sort(); // Sort list of numbers + + int first, second; // Indexes to middle two numbers + + if (this.temp.Count % 2 == 1) + { + // If there is an odd number of values get the middle number twice + first = this.temp.Count / 2; + second = first; + } + else + { + // If there is an even number of values get the middle two numbers + first = this.temp.Count / 2 - 1; + second = first + 1; + } + + if (this.temp.Count > 0) // If there are numbers, calculate median + { + // Calculate median as average of middle number(s) + result = (SqlDouble)( this.temp[first] + this.temp[second] ) / 2.0; + } + + return result; + } + + #region IBinarySerialize Members + + // Custom serialization read method + public void Read(System.IO.BinaryReader r) + { + // Create a new list of double values + this.temp = new List(); + + // Get the number of values that were serialized + int j = r.ReadInt32(); + + // Loop and add each serialized value to the list + for (int i = 0; i < j; i++) + { + this.temp.Add(r.ReadDouble()); + } + } + + // Custom serialization write method + public void Write(System.IO.BinaryWriter w) + { + // Write the number of values in the list + w.Write(this.temp.Count); + + // Write out each value in the list + foreach (double d in this.temp) + { + w.Write(d); + } + } + + #endregion + } +} diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/PostDeployScript.sql b/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/PostDeployScript.sql new file mode 100644 index 0000000..5f28270 --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/PostDeployScript.sql @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/PreDeployScript.sql b/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/PreDeployScript.sql new file mode 100644 index 0000000..5f28270 --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/PreDeployScript.sql @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/Properties/AssemblyInfo.cs b/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..f94ab64 --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/Properties/AssemblyInfo.cs @@ -0,0 +1,31 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Data.Sql; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ApressExamples")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("ApressExamples")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2012")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.*")] + diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/Range.cs b/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/Range.cs new file mode 100644 index 0000000..4608084 --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/Range.cs @@ -0,0 +1,57 @@ +using System; +using System.Data; +using System.Data.SqlClient; +using System.Data.SqlTypes; +using Microsoft.SqlServer.Server; + +namespace Apress.Examples { + [Serializable] + [Microsoft.SqlServer.Server.SqlUserDefinedAggregate(Format.Native)] + + public struct Range + { + SqlDouble min, max; + + public void Init() { + min = SqlDouble.Null; + max = SqlDouble.Null; + } + + public void Accumulate(SqlDouble value) + { + if (!value.IsNull) { + if (min.IsNull || value < min) + { + min = value; + } + + if (max.IsNull || value > max) + { + max = value; + } + } + } + + public void Merge(Range group) + { + if (min.IsNull || (!group.min.IsNull && group.min < min)) + { + min = group.min; + } + if (max.IsNull || (!group.max.IsNull && group.max > max)) + { + max = group.max; + } + } + + public SqlDouble Terminate() { + SqlDouble result = SqlDouble.Null; + if (!min.IsNull && !max.IsNull) + { + result = max - min; + } + + return result; + } + } +} \ No newline at end of file diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/Test Scripts/Test.sql b/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/Test Scripts/Test.sql new file mode 100644 index 0000000..8367fff --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/Test Scripts/Test.sql @@ -0,0 +1,34 @@ +-- Examples for queries that exercise different SQL objects implemented by this assembly + +----------------------------------------------------------------------------------------- +-- Stored procedure +----------------------------------------------------------------------------------------- +-- exec StoredProcedureName + + +----------------------------------------------------------------------------------------- +-- User defined function +----------------------------------------------------------------------------------------- +-- select dbo.FunctionName() + + +----------------------------------------------------------------------------------------- +-- User defined type +----------------------------------------------------------------------------------------- +-- CREATE TABLE test_table (col1 UserType) +-- +-- INSERT INTO test_table VALUES (convert(uri, 'Instantiation String 1')) +-- INSERT INTO test_table VALUES (convert(uri, 'Instantiation String 2')) +-- INSERT INTO test_table VALUES (convert(uri, 'Instantiation String 3')) +-- +-- select col1::method1() from test_table + + + +----------------------------------------------------------------------------------------- +-- User defined type +----------------------------------------------------------------------------------------- +-- select dbo.AggregateName(Column1) from Table1 + + +select 'To run your project, please edit the Test.sql file in your project. This file is located in the Test Scripts folder in the Solution Explorer.' diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/YahooRSS.cs b/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/YahooRSS.cs new file mode 100644 index 0000000..0081dc1 --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch14/ApressExamples/ApressExamples/YahooRSS.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections; +using System.Data.SqlTypes; +using Microsoft.SqlServer.Server; +using System.Xml; + +namespace Apress.Examples +{ + public partial class YahooRSS + { + + [Microsoft.SqlServer.Server.SqlFunction( + IsDeterministic = false, + DataAccess = DataAccessKind.None, + TableDefinition = "title nvarchar(256)," + + "link nvarchar(256), " + + "pubdate datetime, " + + "description nvarchar(max)", + FillRowMethodName = "GetRow") + ] + public static IEnumerable GetYahooNews() + { + XmlTextReader xmlsource = + new XmlTextReader("http://rss.news.yahoo.com/rss/topstories"); + XmlDocument newsxml = new XmlDocument(); + newsxml.Load(xmlsource); + xmlsource.Close(); + return newsxml.SelectNodes("//rss/channel/item"); + } + private static void GetRow( + Object o, + out SqlString title, + out SqlString link, + out SqlDateTime pubdate, + out SqlString description) + { + XmlElement element = (XmlElement)o; + title = element.SelectSingleNode("./title").InnerText; + link = element.SelectSingleNode("./link").InnerText; + pubdate = DateTime.Parse(element.SelectSingleNode("./pubDate").InnerText); + description = element.SelectSingleNode("./description").InnerText; + } + } +} diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch14/ch14.sql b/Pro T-SQL 2012 Programmer's Guide/Ch14/ch14.sql new file mode 100644 index 0000000..6ec5e01 --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch14/ch14.sql @@ -0,0 +1,149 @@ +-------------------------------------------------------------------------- +-- Listing 14-1. Registering a CLR Integration Assembly with SQL Server -- +-------------------------------------------------------------------------- +EXEC sp_configure 'CLR Enabled'; +RECONFIGURE; + +CREATE ASSEMBLY ApressExamples +AUTHORIZATION dbo +FROM N'C:\MyApplication\ Apress.Examples.DLL' +WITH PERMISSION_SET = SAFE; +GO + +--------------------------------------------------------- +-- Listing 14-3. Creating CLR UDF from Assembly Method -- +--------------------------------------------------------- +CREATE FUNCTION dbo.EmailMatch (@input nvarchar(4000)) +RETURNS bit +WITH EXECUTE AS CALLER +AS +EXTERNAL NAME ApressExamples.[Apress.Examples.UDFExample].EmailMatch +GO + +------------------------------------------------------------------------ +-- Listing 14-4. Validating E-mail Addresses with Regular Expressions -- +------------------------------------------------------------------------ +SELECT + 'nospam-123@yahoo.com' AS Email, + dbo.EmailMatch (N'nospam-123@yahoo.com') AS Valid +UNION +SELECT + '123@456789', + dbo.EmailMatch('123@456789') +UNION + SELECT 'BillyG@HOTMAIL.COM', + dbo.EmailMatch('BillyG@HOTMAIL.COM'); +GO + +----------------------------------------------------------------------- +-- Listing 14-6. CREATE ASSEMBLY with EXTERNAL_ACCESS Permission Set -- +----------------------------------------------------------------------- +CREATE ASSEMBLY ApressExample +AUTHORIZATION dbo +FROM N'C:\MyApplication\ Apress.Example.DLL' +WITH PERMISSION_SET = EXTERNAL_ACCESS; +GO + +-------------------------------------------------- +-- Listing 14-7. Querying a CLR Integration TVF -- +-------------------------------------------------- +CREATE FUNCTION dbo.GetYahooNews() +RETURNS TABLE(title nvarchar(256), link nvarchar(256), pubdate datetime, description nvarchar(max)) +AS EXTERNAL NAME ApressExamples.[Apress.Examples.YahooRSS].GetYahooNews +GO + +SELECT + title, + link, + pubdate, + description +FROM dbo.GetYahooNews(); +GO + +------------------------------------------------------------------ +-- Listing 14-9. Executing the GetEnvironmentVars CLR Procedure -- +------------------------------------------------------------------ +CREATE PROCEDURE dbo.GetEnvironmentVars +AS EXTERNAL NAME ApressExamples.[Apress.Examples.SampleProc].GetEnvironmentVars; +GO + +EXEC dbo.GetEnvironmentVars; +GO + +----------------------------------------------------------- +-- Listing 14-11. Retrieving Statistical Ranges with UDA -- +----------------------------------------------------------- +CREATE AGGREGATE Range (@value float) RETURNS float +EXTERNAL NAME ApressExamples.[Apress.Examples.Range]; +GO + +SELECT + ProductID, + dbo.Range(UnitPrice) AS UnitPriceRange +FROM Sales.SalesOrderDetail +WHERE UnitPrice > 0 +GROUP BY ProductID; +GO + +------------------------------------------------------------- +-- Listing 14-13. Calculating Median Unit Price with a UDA -- +------------------------------------------------------------- +CREATE AGGREGATE dbo.Median (@value float) RETURNS float +EXTERNAL NAME ApressExamples.[Apress.Examples.Median]; +GO + +SELECT + ProductID, + dbo.Median(UnitPrice) AS MedianUnitPrice +FROM Sales.SalesOrderDetail +GROUP BY ProductID; +GO + +------------------------------------------------------------------------------ +-- Listing 14-17. Creation of the CLR Trigger to Validate an E-mail Address -- +------------------------------------------------------------------------------ +CREATE TRIGGER atr_Person_EmailAddress_ValidateEmail +ON Person.EmailAddress +AFTER INSERT, UPDATE +AS EXTERNAL NAME ApressExamples.[Apress.Examples.Triggers].EmailAddressTrigger; +GO + +------------------------------------------------------ +-- Listing 14-18. Setting an Invalid E-mail Address -- +------------------------------------------------------ +UPDATE Person.EmailAddress +SET EmailAddress = 'pro%sql@apress@com' +WHERE EmailAddress = 'dylan0@adventure-works.com'; +GO + +------------------------------------------------------------------ +-- Listing 14-19. UPDATE Statement Modified to Handle the Error -- +------------------------------------------------------------------ +BEGIN TRY + UPDATE Person.EmailAddress + SET EmailAddress = 'pro%sql@apress@com' + WHERE EmailAddress = 'dylan0@adventure-works.com'; +END TRY +BEGIN CATCH + IF ERROR_NUMBER() = 3991 + RAISERROR('invalid email address', 16, 10) +END CATCH +GO + +---------------------------------------------------------------- +-- Listing 14-20. T-SQL Trigger to Validate an E-mail Address -- +---------------------------------------------------------------- +CREATE TRIGGER atr_Person_EmailAddress_ValidateEmail +ON Person.EmailAddress +AFTER INSERT, UPDATE +AS BEGIN + IF @@ROWCOUNT = 0 RETURN + + IF EXISTS (SELECT * FROM inserted WHERE dbo.EmailMatch(EmailAddress) = 0) + BEGIN + RAISERROR('an email is invalid', 16, 10) + ROLLBACK TRANSACTION + END + +END; +GO \ No newline at end of file diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-01.cs b/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-01.cs new file mode 100644 index 0000000..fecd021 --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-01.cs @@ -0,0 +1,52 @@ +Listing 15-1. SqlDataReader Sample +using System; +using System.Data.SqlClient; + +namespace Apress.Examples +{ + class Listing15_1 + { + static void Main(string[] args) + { + string sqlconnection = @"DATA SOURCE=SQL2012;" + + "INITIAL CATALOG=AdventureWorks;" + + "INTEGRATED SECURITY=SSPI;"; + + string sqlcommand = "SELECT " + + " DepartmentId, " + + " Name, " + + " GroupName " + + " FROM HumanResources.Department " + + " ORDER BY DepartmentId"; + + try + { + connection = new SqlConnection(sqlconnection); + connection.Open(); + command = new SqlCommand(sqlcommand, connection); + datareader = command.ExecuteReader(); + + while (datareader.Read()) + { + Console.WriteLine + ( + "{0}\t{1}\t{2}", + datareader["DepartmentId"].ToString(), + datareader["Name"].ToString(), + datareader["GroupName"].ToString() + ); + } + } + catch (SqlException ex) + { + Console.WriteLine(ex.Message); + } + finally + { + connection.Close(); + } + Console.Write("Press a Key to Continue..."); + Console.ReadKey(); + } + } +} diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-02.cs b/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-02.cs new file mode 100644 index 0000000..8ee6564 --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-02.cs @@ -0,0 +1,58 @@ +Listing 15-2. Using SqlDataReader to Fill a DataSet +using System; +using System.Data; +using System.Data.SqlClient; + +namespace Apress.Examples +{ + class Listing15_2 + { + static void Main(string[] args) + { + string sqlconnection = @"DATA SOURCE=SQL2012;" + + "INITIAL CATALOG=AdventureWorks;" + + "INTEGRATED SECURITY=SSPI;"; + + string sqlcommand = "SELECT " + + " DepartmentId, " + + " Name, " + + " GroupName " + + " FROM HumanResources.Department " + + " ORDER BY DepartmentId"; + + SqlDataAdapter adapter = null; + DataSet dataset = null; + + try + { + adapter = new SqlDataAdapter(sqlcommand, sqlconnection); + dataset = new DataSet(); + adapter.Fill(dataset); + + foreach (DataRow row in dataset.Tables[0].Rows) + { + Console.WriteLine + ( + "{0}\t{1}\t{2}", + row["DepartmentId"].ToString(), + row["Name"].ToString(), + row["GroupName"].ToString() + ); + } + } + catch (SqlException ex) + { + Console.WriteLine(ex.Message); + } + finally + { + if (dataset != null) + dataset.Dispose(); + if (adapter != null) + adapter.Dispose(); + } + Console.Write("Press a Key to Continue..."); + Console.ReadKey(); + } + } +} diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-03.cs b/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-03.cs new file mode 100644 index 0000000..0769d06 --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-03.cs @@ -0,0 +1,61 @@ +Listing 15-3. Parameterized SQL Query +using System; +using System.Data; +using System.Data.SqlClient; + +namespace Apress.Examples +{ + class Listing15_3 + { + static void Main(string[] args) + { + + string name = "SMITH"; + string sqlconnection = @"SERVER=SQL2012; " + + "INITIAL CATALOG=AdventureWorks; " + + "INTEGRATED SECURITY=SSPI;"; + + string sqlcommand = "SELECT " + + " BusinessEntityID, " + + " FirstName, " + + " MiddleName, " + + " LastName " + + "FROM Person.Person " + + "WHERE LastName = @name"; + + SqlConnection connection = null; + SqlCommand command = null; + SqlDataReader datareader = null; + + try + { + connection = new SqlConnection(sqlconnection); + connection.Open(); + command = new SqlCommand(sqlcommand, connection); + command.Parameters.Add("@name", SqlDbType.NVarChar, 50).Value = name; + datareader = command.ExecuteReader(); + while (datareader.Read()) + { + Console.WriteLine + ( + "{0}\t{1}\t{2}\t{3}", + datareader["BusinessEntityID"].ToString(), + datareader["LastName"].ToString(), + datareader["FirstName"].ToString(), + datareader["MiddleName"].ToString() + ); + } + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + finally + { + connection.Close(); + } + Console.WriteLine("Press any key..."); + Console.ReadKey(); + } + } +} diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-04.cs b/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-04.cs new file mode 100644 index 0000000..b0648e7 --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-04.cs @@ -0,0 +1,9 @@ +SqlCommand command = new SqlCommand + ( + "CREATE TABLE #temp " + + " ( " + + " Id INT NOT NULL PRIMARY KEY, " + + " Name NVARCHAR(50) " + + " );", connection + ); +command.ExecuteNonQuery(); diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-05.cs b/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-05.cs new file mode 100644 index 0000000..770edec --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-05.cs @@ -0,0 +1,6 @@ +SqlCommand command = new SqlCommand + ( + "SELECT COUNT(*) " + + "FROM Person.Person;", sqlconnection + ); +Object count = command.ExecuteScalar(); diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-06.cs b/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-06.cs new file mode 100644 index 0000000..30b9193 --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-06.cs @@ -0,0 +1,68 @@ +using System; +using System.Data; +using System.Data.SqlClient; +using System.Xml; + +namespace Apress.Examples +{ + class Listing15_6 + { + static void Main(string[] args) + { + string name = "SMITH"; + string sqlconnection = @"SERVER=SQL2012; " + + "INITIAL CATALOG=AdventureWorks; " + + "INTEGRATED SECURITY=SSPI;"; + + string sqlcommand = "SELECT " + + " BusinessEntityID, " + + " FirstName, " + + " COALESCE(MiddleName, '') AS MiddleName, " + + " LastName " + + "FROM Person.Person " + + "WHERE LastName = @name " + + "FOR XML AUTO;"; + + SqlConnection connection = null; + SqlCommand command = null; + XmlReader xmlreader = null; + + try + { + connection = new SqlConnection(sqlconnection); + connection.Open(); + command = new SqlCommand(sqlcommand, connection); + SqlParameter par = command.Parameters.Add("@name", SqlDbType.NVarChar, + 50); + par.Value = name; + xmlreader = command.ExecuteXmlReader(); + while (xmlreader.Read()) + { + Console.WriteLine + ( + "{0}\t{1}\t{2}\t{3}", + xmlreader["BusinessEntityID"].ToString(), + xmlreader["LastName"].ToString(), + xmlreader["FirstName"].ToString(), + xmlreader["MiddleName"].ToString() + ); + } + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + finally + { + if (xmlreader != null) + xmlreader.Close(); + if (command != null) + command.Dispose(); + if (connection != null) + connection.Dispose(); + } + Console.WriteLine("Press any key..."); + Console.ReadKey(); + } + } +} diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-07.sql b/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-07.sql new file mode 100644 index 0000000..ecb27ec --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-07.sql @@ -0,0 +1,12 @@ +------------------------------------------------------ +-- Listing 15-7. Creating the ZipCodes Target Table -- +------------------------------------------------------ +CREATE TABLE dbo.ZipCodes +( + ZIP CHAR(5) NOT NULL PRIMARY KEY, + Latitude NUMERIC(8, 4) NOT NULL, + Longitude NUMERIC(8, 4) NOT NULL, + City NVARCHAR(50) NOT NULL, + State CHAR(2) NOT NULL +) +GO diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-08.cs b/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-08.cs new file mode 100644 index 0000000..3ba3acb --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-08.cs @@ -0,0 +1,104 @@ +using System; +using System.Data; +using System.Data.SqlClient; +using System.Data.SqlTypes; +using System.Diagnostics; +using System.IO; +using System.Globalization; + +namespace Apress.Example +{ + class Listing15_8 + { + static string sqlconnection = "DATA SOURCE=SQL2012; " + + "INITIAL CATALOG=AdventureWorks; " + + "INTEGRATED SECURITY=SSPI;"; + + static string sourcefile = "c:\\ZIPCodes.txt"; + + static DataTable loadtable = null; + + static void Main(string[] args) + { + Stopwatch clock = new Stopwatch(); + clock.Start(); + int rowcount = DoImport(); + clock.Stop(); + Console.WriteLine("{0} Rows Imported in {1} Seconds.", + rowcount, (clock.ElapsedMilliseconds / 1000.0)); + Console.WriteLine("Press a Key..."); + Console.ReadKey(); + } + + static int DoImport() + { + using (SqlBulkCopy bulkcopier = new SqlBulkCopy(sqlconnection)) + { + bulkcopier.DestinationTableName = "dbo.ZIPCodes"; + try + { + LoadSourceFile(); + bulkcopier.WriteToServer(loadtable); + } + catch (SqlException ex) + { + Console.WriteLine(ex.Message); + } + } + return loadtable.Rows.Count; + } + + static void LoadSourceFile() + { + loadtable = new DataTable(); + DataColumn loadcolumn = new DataColumn(); + DataRow loadrow = null; + + loadcolumn.DataType = typeof(SqlString); + loadcolumn.ColumnName = "ZIP"; + loadcolumn.Unique = true; + loadtable.Columns.Add(loadcolumn); + + loadcolumn = new DataColumn(); + loadcolumn.DataType = typeof(SqlDecimal); + loadcolumn.ColumnName = "Latitude"; + loadcolumn.Unique = false; + loadtable.Columns.Add(loadcolumn); + + loadcolumn = new DataColumn(); + loadcolumn.DataType = typeof(SqlDecimal); + loadcolumn.ColumnName = "Longitude"; + loadcolumn.Unique = false; + loadtable.Columns.Add(loadcolumn); + + loadcolumn = new DataColumn(); + loadcolumn.DataType = typeof(SqlString); + loadcolumn.ColumnName = "City"; + loadcolumn.Unique = false; + loadtable.Columns.Add(loadcolumn); + + loadcolumn = new DataColumn(); + loadcolumn.DataType = typeof(SqlString); + loadcolumn.ColumnName = "State"; + loadcolumn.Unique = false; + loadtable.Columns.Add(loadcolumn); + + using (StreamReader stream = new StreamReader(sourcefile)) + { + string record = stream.ReadLine(); + while (record != null) + { + string[] cols = record.Split('\t'); + loadrow = loadtable.NewRow(); + loadrow["ZIP"] = cols[0]; + loadrow["Latitude"] = decimal.Parse(cols[1], CultureInfo.InvariantCulture); + loadrow["Longitude"] = decimal.Parse(cols[2], CultureInfo.InvariantCulture); + loadrow["City"] = cols[3]; + loadrow["State"] = cols[4]; + loadtable.Rows.Add(loadrow); + record = stream.ReadLine(); + } + } + } + } +} diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-09.sql b/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-09.sql new file mode 100644 index 0000000..a09eafb --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-09.sql @@ -0,0 +1,10 @@ +----------------------------------------------- +-- Listing 15-9. Verifying Bulk Copy Results -- +----------------------------------------------- +SELECT + ZIP, + Latitude, + Longitude, + City, +State FROM dbo.ZipCodes; + diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-10.cs b/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-10.cs new file mode 100644 index 0000000..53d883d --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-10.cs @@ -0,0 +1,109 @@ +using System; +using System.Data; +using System.Data.SqlClient; + +namespace Apress.Examples +{ + class MARS + { + static string sqlconnection = @"SERVER=SQL2012; " + + "INITIAL CATALOG=AdventureWorks; " + + "INTEGRATED SECURITY=SSPI; " + + "MULTIPLEACTIVERESULTSETS=true; "; + + static string sqlcommand1 = "SELECT " + + " DepartmentID, " + + " Name, " + + " GroupName " + + "FROM HumanResources.Department; "; + + static string sqlcommand2 = "SELECT " + + " ShiftID, " + + " Name, " + + " StartTime, " + + " EndTime " + + "FROM HumanResources.Shift; "; + + static SqlConnection connection = null; + static SqlCommand command1 = null; + static SqlCommand command2 = null; + static SqlDataReader datareader1 = null; + static SqlDataReader datareader2 = null; + + static void Main(string[] args) + { + try + { + connection = new SqlConnection(sqlconnection); + connection.Open(); + command1 = new SqlCommand(sqlcommand1, connection); + command2 = new SqlCommand(sqlcommand2, connection); + datareader1 = command1.ExecuteReader(); + datareader2 = command2.ExecuteReader(); + int i = 0; + + Console.WriteLine("==========="); + Console.WriteLine("Departments"); + Console.WriteLine("==========="); + while (datareader1.Read() && i++ < 3) + { + Console.WriteLine + ( + "{0}\t{1}\t{2}", + datareader1["DepartmentID"].ToString(), + datareader1["Name"].ToString(), + datareader1["GroupName"].ToString() + ); + } + + Console.WriteLine("======"); + Console.WriteLine("Shifts"); + Console.WriteLine("======"); + while (datareader2.Read()) + { + Console.WriteLine + ( + "{0}\t{1}\t{2}\t{3}", + datareader2["ShiftID"].ToString(), + datareader2["Name"].ToString(), + datareader2["StartTime"].ToString(), + datareader2["EndTime"].ToString() + ); + } + + Console.WriteLine("======================"); + Console.WriteLine("Departments, Continued"); + Console.WriteLine("======================"); + while (datareader1.Read()) + { + Console.WriteLine + ( + "{0}\t{1}\t{2}", + datareader1["DepartmentID"].ToString(), + datareader1["Name"].ToString(), + datareader1["GroupName"].ToString() + ); + } + } + catch (SqlException ex) + { + Console.WriteLine(ex.Message); + } + finally + { + if (datareader1 != null) + datareader1.Dispose(); + if (datareader2 != null) + datareader2.Dispose(); + if (command1 != null) + command1.Dispose(); + if (command2 != null) + command2.Dispose(); + if (connection != null) + connection.Dispose(); + } + Console.WriteLine("Press a key to end..."); + Console.ReadKey(); + } + } +} diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-12.cs b/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-12.cs new file mode 100644 index 0000000..02434b9 --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-12.cs @@ -0,0 +1,30 @@ +using System; +using System.Linq; + +namespace Apress.Examples +{ + class Listing15_12 + { + static void Main(string[] args) + { + AdventureWorksDataContext db = new AdventureWorksDataContext(); + db.Log = Console.Out; + + var query = from p in db.Persons + select p; + + foreach (Person p in query) + { + Console.WriteLine + ( + "{0}\t{1}\t{2}", + p.FirstName, + p.MiddleName, + p.LastName + ); + } + Console.WriteLine("Press a key to continue..."); + Console.ReadKey(); + } + } +} diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-18.cs b/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-18.cs new file mode 100644 index 0000000..5d95db9 --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-18.cs @@ -0,0 +1,41 @@ +using System; +using System.Linq; + +namespace Apress.Examples +{ + class Listing15_18 + { + static void Main(string[] args) + { + AdventureWorksDataContext db = new AdventureWorksDataContext(); + db.Log = Console.Out; + + var query = from p in db.Persons + join e in db.EmailAddresses + on p.BusinessEntityID equals e.BusinessEntityID + where p.LastName.Contains("SMI") + orderby p.LastName, p.FirstName + select new + { + LastName = p.LastName, + FirstName = p.FirstName, + MiddleName = p.MiddleName, + EmailAddress = e.EmailAddress1 + }; + + foreach (var q in query) + { + Console.WriteLine + ( + "{0}\t{1}\t{2}\t{3}", + q.FirstName, + q.MiddleName, + q.LastName, + q.EmailAddress + ); + } + Console.WriteLine("Press a key to continue..."); + Console.ReadKey(); + } + } +} diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-20.sql b/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-20.sql new file mode 100644 index 0000000..74bfd43 --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-20.sql @@ -0,0 +1,5 @@ +--------------------------------------------------------------------------------------------- +-- Listing 15-20. Creating a Computed Column to Show Hierarchyid Representation in the EDM -- +--------------------------------------------------------------------------------------------- +ALTER TABLE [HumanResources].[Employee] +ADD OrganizationNodeString AS OrganizationNode.ToString() PERSISTED; diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-21.cs b/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-21.cs new file mode 100644 index 0000000..9f8293a --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-21.cs @@ -0,0 +1,33 @@ +using System; +using System.Linq; +using System.Text; + +namespace EntityFramework +{ + class Program + { + static void Main(string[] args) + { + using (var ctx = new AdventureWorksEntitiesEmployee()) + { + var qry = from e in ctx.Employee + where e.Gender == "F" + select new + { + e.Person.FirstName, + e.Person.LastName, + e.BirthDate + }; + + foreach (var emp in qry.Take(5)) { + Console.WriteLine("{0} {1}, born {2}", + emp.FirstName, + emp.LastName, + emp.BirthDate.ToLongDateString() + ); + } + Console.Read(); + } + } + } +} diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-23.cs b/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-23.cs new file mode 100644 index 0000000..67eed85 --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-23.cs @@ -0,0 +1,25 @@ +using System; +using System.Linq; +using System.Text; + +namespace EntityFramework +{ + class Program + { + static void Main(string[] args) + { + using (var ctx = new AdventureWorksEntitiesEmployee()) + { + foreach (var emp in ctx.Employee.Where(e => e.Gender == "F").Take(5)) + { + Console.WriteLine("{0} {1}, born {2}", + emp.Person.FirstName, + emp.Person.LastName, + emp.BirthDate.ToLongDateString() + ); + } + Console.Read(); + } + } + } +} diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-24.cs b/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-24.cs new file mode 100644 index 0000000..bd05ce7 --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch15/ch15-24.cs @@ -0,0 +1,31 @@ +using System; +using System.Linq; +using System.Text; + +namespace EntityFramework +{ + class Program + { + static void Main(string[] args) + { + using (var ctx = new AdventureWorksEntitiesEmployee()) + { + var newP = new BusinessEntity { + ModifiedDate = DateTime.Now, + rowguid = Guid.NewGuid() + }; + + Console.WriteLine("BusinessEntityID before insert : {0}", + newP.BusinessEntityID); + + ctx.BusinessEntities.AddObject(newP); + ctx.SaveChanges(); + + Console.WriteLine("BusinessEntityID after insert : {0}", + newP.BusinessEntityID); + } + + Console.Read(); + } + } +} diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch16/ch16-01.cs b/Pro T-SQL 2012 Programmer's Guide/Ch16/ch16-01.cs new file mode 100644 index 0000000..c33bc16 --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch16/ch16-01.cs @@ -0,0 +1,45 @@ +using System; +using System.Data.SqlClient; +using System.Text; + +namespace localdbClient +{ + class Program + { + static void Main(string[] args) + { + try + { + SqlConnectionStringBuilder builder = + new SqlConnectionStringBuilder(@"Server=(localdb)\SQLSrvWebApp1;Integrated Security=true"); + + builder.AttachDBFilename = @"C:\Users\Administrator\Documents\AdventureWorksLT2012_Data.mdf"; + + Console.WriteLine("connection string = " + builder.ConnectionString); + + using (SqlConnection cn = new SqlConnection(builder.ConnectionString)) + { + cn.Open(); + SqlCommand cmd = cn.CreateCommand(); + cmd.CommandText = "SELECT Name FROM sys.tables;"; + SqlDataReader rd = cmd.ExecuteReader(); + + while(rd.Read()) + { + Console.WriteLine(rd.GetValue(0)); + } + rd.Close(); + cn.Close(); + } + Console.WriteLine("Press any key to finish."); + Console.ReadLine(); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + Console.WriteLine("Press any key to finish."); + Console.ReadLine(); + } + } + } +} diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch16/ch16-02.cs b/Pro T-SQL 2012 Programmer's Guide/Ch16/ch16-02.cs new file mode 100644 index 0000000..d48b741 --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch16/ch16-02.cs @@ -0,0 +1,13 @@ +private void ExecuteSP() +{ + SqlConnectionStringBuilder cnString = new SqlConnectionStringBuilder(); + cnString.DataSource = @”(localdb)\v11.0”; + cnString.IntegratedSecurity = true; + + using (SqlConnection cn = new SqlConnection(cnString.ConnectionString)) + { + cn.Open(); + SqlCommand cmd = new SqlCommand("EXEC dbo.GetProducts", cn); + cmd.ExecuteReader(); + } +} diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch16/ch16-03.cs b/Pro T-SQL 2012 Programmer's Guide/Ch16/ch16-03.cs new file mode 100644 index 0000000..64261be --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch16/ch16-03.cs @@ -0,0 +1,13 @@ +private async Task ExecuteSP() +{ + SqlConnectionStringBuilder cnString = new SqlConnectionStringBuilder(); + cnString.DataSource = @”(localdb)\v11.0”; + cnString.IntegratedSecurity = true; + + using (SqlConnection cn = new SqlConnection(cnString.ConnectionString)) + { + await cn.OpenAsync(); + SqlCommand cmd = new SqlCommand("EXEC dbo.GetProducts", cn); + await cmd.ExecuteReaderAsync(); + } +} diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch16/ch16-04.java b/Pro T-SQL 2012 Programmer's Guide/Ch16/ch16-04.java new file mode 100644 index 0000000..821e49f --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch16/ch16-04.java @@ -0,0 +1,49 @@ +import java.sql.*; + +public class ApressExample { + + public static void main(String[] args) { + + String connectionUrl = "jdbc:sqlserver://SQL2012;integratedSecurity=true;databaseName=AdventureWorks;failoverPartner=SQL2012B"; + Connection cn = null; + String qry = "SELECT TOP 10 FirstName, LastName FROM Person.Contact"; + + try { + cn = DriverManager.getConnection(connectionUrl); + runQuery(cn, qry); + } catch (SQLException se) { + try { + System.out.println("Connection to principal server failed, trying the mirror server."); + cn = DriverManager.getConnection(connectionUrl); + runQuery(cn, qry); + } catch (Exception e) { + e.printStackTrace(); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (cn != null) try { cn.close(); } catch(Exception e) { } + } + } + + private static void runQuery(Connection cn, String SQL) { + Statement stmt = null; + ResultSet rs = null; + + try { + stmt = cn.createStatement(); + rs = stmt.executeQuery(SQL); + + while (rs.next()) { + System.out.println(rs.getString(0)); + } + rs.close(); + stmt.close(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (rs != null) try { rs.close(); } catch(Exception e) {} + if (stmt != null) try { stmt.close(); } catch(Exception e) {} + } + } +} diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch16/ch16-05.cs b/Pro T-SQL 2012 Programmer's Guide/Ch16/ch16-05.cs new file mode 100644 index 0000000..9fab67d --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch16/ch16-05.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Data.Services; +using System.Data.Services.Common; +using System.Linq; +using System.ServiceModel.Web; +using System.Web; + +namespace WCFDataServicesSample +{ + public class ProductPhotoDataService : DataService + { + // This method is called only once to initialize service-wide policies. + public static void InitializeService(DataServiceConfiguration config) + { + config.SetEntitySetAccessRule("Products", EntitySetRights.AllRead); + config.SetEntitySetAccessRule("ProductPhotoes", EntitySetRights.AllRead); + config.SetEntitySetAccessRule("ProductProductPhotoes", EntitySetRights.AllRead); + config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2; + } + } +} diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch16/ch16-06.cs b/Pro T-SQL 2012 Programmer's Guide/Ch16/ch16-06.cs new file mode 100644 index 0000000..44f631c --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch16/ch16-06.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.UI; +using System.Web.UI.WebControls; +using WCFdsClient.PhotoServiceReference; +using System.Data.Services.Client; + +namespace WCFdsClient +{ + public partial class _Default : System.Web.UI.Page + { + protected void Page_Load(object sender, EventArgs e) + { + PopulateDropDown(); + } + + private void PopulateDropDown() + { + AdventureWorksEntities ctx = new AdventureWorksEntities( + new Uri ("http://localhost:59560/ProductPhotoDataService.svc") + ); + + var qry = from p in ctx.Products + where p.FinishedGoodsFlag + orderby p.Name + select p; + + foreach (Product p in qry) { + ProductDropDown.Items.Add(new ListItem(p.Name, p.ProductID.ToString())); + } + + string id = ProductDropDown.SelectedValue; + UpdateImage(id); + } + + private void UpdateImage(string id) { + ProductImage.ImageUrl = string.Format("GetImage.aspx?id={0}", id); + } + + protected void ProductDropDownlist_SelectedIndexChanged(object sender, EventArgs e) + { + string id = ProductDropDown.SelectedValue; + + AdventureWorksEntities ctx = new AdventureWorksEntities( + new Uri("http://localhost:59560/ProductPhotoDataService.svc") + ); + + var qry = from p in ctx.Products + where p.ProductID == Convert.ToInt32(id) + select p; + + //DataServiceOuery qry = ctx.CreateOuery(string.Format("/Product({0})", id)); + + foreach (Product p in qry) + { + TableProduct.Rows[0].Cells[1].Text = p.Class; + TableProduct.Rows[1].Cells[1].Text = p.Color; + TableProduct.Rows[2].Cells[1].Text = p.Size + " " + p.SizeUnitMeasureCode; + TableProduct.Rows[3].Cells[1].Text = p.Weight + " " + p.WeightUnitMeasureCode; + TableProduct.Rows[4].Cells[1].Text = p.ListPrice.ToString(); + TableProduct.Rows[5].Cells[1].Text = p.ProductNumber; + } + UpdateImage(id); + } + + } + +} diff --git a/Pro T-SQL 2012 Programmer's Guide/Ch18/9781430245964_Natarajan_Ch18_PerformanceTuning.sql b/Pro T-SQL 2012 Programmer's Guide/Ch18/9781430245964_Natarajan_Ch18_PerformanceTuning.sql new file mode 100644 index 0000000..adc6eb6 --- /dev/null +++ b/Pro T-SQL 2012 Programmer's Guide/Ch18/9781430245964_Natarajan_Ch18_PerformanceTuning.sql @@ -0,0 +1,364 @@ +/*Listing 18 - 1. Creating a Narrow Table */ +CREATE TABLE dbo.SmallRows +( +Id int NOT NULL, +LastName nchar(50) NOT NULL, +FirstName nchar(50) NOT NULL, +MiddleName nchar(50) NULL +); +INSERT INTO dbo.SmallRows +( +Id, +LastName, +FirstName, +MiddleName +) +SELECT +BusinessEntityID, +LastName, +FirstName, +MiddleName +FROM Person.Person; + +/*Listing 18 - 2. Looking at Data Allocations for the SmallRows Table */ +SELECT +sys.fn_PhysLocFormatter(%%physloc%%) AS [Row_Locator], +Id +FROM dbo.SmallRows; + +/*Listing 18 - 3. Creating a Table with Wide Rows */ +CREATE TABLE dbo.LargeRows +( +Id int NOT NULL, +LastName nchar(600) NOT NULL, +FirstName nchar(600) NOT NULL, +MiddleName nchar(600) NULL +); +INSERT INTO dbo.LargeRows +( +Id, +LastName, +FirstName, +MiddleName +) +SELECT +BusinessEntityID, +LastName, +FirstName, +MiddleName +FROM Person.Person; +SELECT +sys.fn_PhysLocFormatter(%%physloc%%) AS [Row_Locator], +Id +FROM dbo.LargeRows; + +/*Listing 18 - 4. I/O Comparison of Narrow and Wide Tables */ +SET STATISTICS IO ON; +SELECT +Id, +LastName, +FirstName, +MiddleName +FROM dbo.SmallRows; +SELECT +Id, +LastName, +FirstName, +MiddleName +FROM dbo.LargeRows; + +/*Listing 18 - 5. Estimating Row Compression Space Savings */ +EXEC sp_estimate_data_compression_savings 'Production', +'TransactionHistory', +NULL, +NULL, +'ROW'; + +/*Listing 18 - 5. Estimating Row Compression Space Savings */ +EXEC sp_estimate_data_compression_savings 'Production', +'TransactionHistory', +NULL, +NULL, +'ROW'; + +/*Listing 18 - 5. Estimating Row Compression Space Savings */ +EXEC sp_estimate_data_compression_savings 'Production', +'TransactionHistory', +NULL, +NULL, +'ROW'; + +/*Listing 18 - 8. Estimating Data Compression Savings with Page Compression */ +EXEC sp_estimate_data_compression_savings 'Person', +'Person', +NULL, +NULL, +'PAGE'; + +/*Listing 18 - 9. Applying Page Compression to the Person.Person Table */ +ALTER TABLE Person.Person REBUILD +WITH (DATA_COMPRESSION = PAGE); + +/*Listing 18 - 10. Pivot Query that Generates Columns with Many NULLs */ +SELECT +CustomerID, +[HL Road Frame - Black, 58], +[HL Road Frame - Red, 58], +[HL Road Frame - Red, 62], +[HL Road Frame - Red, 44], +[HL Road Frame - Red, 48], +[HL Road Frame - Red, 52], +[HL Road Frame - Red, 56], +[LL Road Frame - Black, 58] +FROM +( +SELECT soh.CustomerID, p.Name AS ProductName, +COUNT +( +CASE WHEN sod.LineTotal IS NULL THEN NULL +ELSE 1 +END +) AS NumberOfItems +FROM Sales.SalesOrderHeader soh +INNER JOIN Sales.SalesOrderDetail sod +ON soh.SalesOrderID = sod.SalesOrderID +INNER JOIN Production.Product p +ON sod.ProductID = p.ProductID +GROUP BY +soh.CustomerID, +sod.ProductID, +p.Name +) src +PIVOT +( +SUM(NumberOfItems) FOR ProductName +IN +( +"HL Road Frame - Black, 58", +"HL Road Frame - Red, 58", +"HL Road Frame - Red, 62", +"HL Road Frame - Red, 44", +"HL Road Frame - Red, 48", +"HL Road Frame - Red, 52", +"HL Road Frame - Red, 56", +"LL Road Frame - Black, 58" +) +) AS pvt; + +/*Listing 18 - 11. Creating Sparse and Nonsparse Tables */ +CREATE TABLE NonSparseTable +( +CustomerID int NOT NULL PRIMARY KEY, +"HL Road Frame - Black, 58" int NULL, +"HL Road Frame - Red, 58" int NULL, +"HL Road Frame - Red, 62" int NULL, +"HL Road Frame - Red, 44" int NULL, +"HL Road Frame - Red, 48" int NULL, +"HL Road Frame - Red, 52" int NULL, +"HL Road Frame - Red, 56" int NULL, +"LL Road Frame - Black, 58" int NULL +); +CREATE TABLE SparseTable +( +CustomerID int NOT NULL PRIMARY KEY, +"HL Road Frame - Black, 58" int SPARSE NULL, +"HL Road Frame - Red, 58" int SPARSE NULL, +"HL Road Frame - Red, 62" int SPARSE NULL, +"HL Road Frame - Red, 44" int SPARSE NULL, +"HL Road Frame - Red, 48" int SPARSE NULL, +"HL Road Frame - Red, 52" int SPARSE NULL, +"HL Road Frame - Red, 56" int SPARSE NULL, +"LL Road Frame - Black, 58" int SPARSE NULL +); + +/*Listing 18 - 12. Calculating the Space Savings of Sparse Columns */ +EXEC sp_spaceused N'NonSparseTable'; +EXEC sp_spaceused N'SparseTable'; + +/*Listing 18 - 13. Creating and Populating a Table with a Sparse Column Set */ +CREATE TABLE Production.SparseProduct +( +ProductID int NOT NULL PRIMARY KEY, +Name dbo.Name NOT NULL, +ProductNumber nvarchar(25) NOT NULL, +Color nvarchar(15) SPARSE NULL, +Size nvarchar(5) SPARSE NULL, +SizeUnitMeasureCode nchar(3) SPARSE NULL, +WeightUnitMeasureCode nchar(3) SPARSE NULL, +Weight decimal(8, 2) SPARSE NULL, +Class nchar(2) SPARSE NULL, +Style nchar(2) SPARSE NULL, +SellStartDate datetime NOT NULL, +SellEndDate datetime SPARSE NULL, +DiscontinuedDate datetime SPARSE NULL, +SparseColumnSet xml COLUMN_SET FOR ALL_SPARSE_COLUMNS +); +GO +INSERT INTO Production.SparseProduct +( +ProductID, +Name, +ProductNumber, +Color, +Size, +SizeUnitMeasureCode, +WeightUnitMeasureCode, +Weight, +Class, +Style, +SellStartDate, +SellEndDate, +DiscontinuedDate +) +SELECT +ProductID, +Name, +ProductNumber, +Color, +Size, +SizeUnitMeasureCode, +WeightUnitMeasureCode, +Weight, +Class, +Style, +SellStartDate, +SellEndDate, +DiscontinuedDate +FROM Production.Product; +GO + + +/*Listing 18 - 14. Querying XML Sparse Column Set as XML */ +SELECT TOP(7) +ProductID, +SparseColumnSet FROM Production.SparseProduct; + +/*Listing 18 - 15. Querying Sparse Column Sets by Name */ +SELECT +ProductID, +Name, +ProductNumber, +SellStartDate, +Color, +Class +FROM Production.SparseProduct +WHERE ProductID IN (1, 317); + +/*Listing 18 - 16. Query Requiring a Bookmark Lookup */ +SELECT +BusinessEntityID, +LastName, +FirstName, +MiddleName, +Title FROM Person.Person WHERE LastName = N'Duffy'; + +/*Listing 18 - 17. Query Using a Covering Index */ +CREATE NONCLUSTERED INDEX [IX_Covering_Person_LastName_FirstName_MiddleName] ON +[Person].[Person] +( +[LastName] ASC, +[FirstName] ASC, +[MiddleName] ASC +) INCLUDE (Title) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, +ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +GO + +/*Listing 18 - 18. Creating and Testing a Filtered Index on the Production.Product Table */ +CREATE NONCLUSTERED INDEX IX_Product_Size +ON Production.Product +( +Size, +SizeUnitMeasureCode ) +WHERE Size IS NOT NULL; +GO +SELECT +ProductID, +Size, +SizeUnitMeasureCode FROM Production.Product WHERE Size = 'L'; +GO + +/*Listing 18 - 19. Script to Demonstrate Waits */ +use adventureworks +go +CREATE TABLE [dbo].[waitsdemo]( +[Id] [int] NOT NULL, +[LastName] [nchar](600) NOT NULL, +[FirstName] [nchar](600) NOT NULL, +[MiddleName] [nchar](600) NULL +) ON [PRIMARY] +GO +declare @id int = 1 +while (@id < = 50000) +begin +insert into waitsdemo +select @id,'Foo', 'User',NULL +SET @id = @id + 1 +end + +/*Listing 18 - 20. DMV to Query Current Processes and Waiting Tasks */ +--List waiting user requests +SELECT +er.session_id, er.wait_type, er.wait_time, +er.wait_resource, er.last_wait_type, +er.command,et.text,er.blocking_session_id +FROM sys.dm_exec_requests AS er +JOIN sys.dm_exec_sessions AS es +ON es.session_id = er.session_id +AND es.is_user_process = 1 +CROSS APPLY sys.dm_exec_sql_text(er.sql_handle) AS et +GO +--List waiting user tasks +SELECT +wt.waiting_task_address, wt.session_id, wt.wait_type, +wt.wait_duration_ms, wt.resource_description +FROM sys.dm_os_waiting_tasks AS wt +JOIN sys.dm_exec_sessions AS es +ON wt.session_id = es.session_id +AND es.is_user_process = 1 +GO +-- List user tasks +SELECT +t.session_id, t.request_id, t.exec_context_id, +t.scheduler_id, t.task_address, +t.parent_task_address +FROM sys.dm_os_tasks AS t +JOIN sys.dm_exec_sessions AS es +ON t.session_id = es.session_id +AND es.is_user_process = 1 +GO + + +/*Listing 18 - 21. Extended Event Session Script to Troubleshoot Login Timeouts */ +CREATE EVENT SESSION [Troubleshoot page split] ON SERVER +ADD EVENT sqlserver.page_split( +ACTION(sqlserver.client_app_name,sqlserver.database_id,sqlserver.database_name,sqlserver.plan_ +handle,sqlserver.server_instance_name,sqlserver.server_principal_name,sqlserver.server_principal_ +sid,sqlserver.session_id,sqlserver.session_nt_username,sqlserver.sql_text,sqlserver.transaction_ +id,sqlserver.username)), +ADD EVENT sqlserver.rpc_completed( +ACTION(sqlserver.client_app_name,sqlserver.database_id,sqlserver.database_name,sqlserver.plan_ +handle,sqlserver.server_instance_name,sqlserver.server_principal_name,sqlserver.server_principal_ +sid,sqlserver.session_id,sqlserver.session_nt_username,sqlserver.sql_text,sqlserver.transaction_ +id,sqlserver.username)), +ADD EVENT sqlserver.rpc_starting( +ACTION(sqlserver.client_app_name,sqlserver.database_id,sqlserver.database_name,sqlserver.plan_ +handle,sqlserver.server_instance_name,sqlserver.server_principal_name,sqlserver.server_principal_ +sid,sqlserver.session_id,sqlserver.session_nt_username,sqlserver.sql_text,sqlserver.transaction_ +id,sqlserver.username)), +ADD EVENT sqlserver.sp_statement_completed( +ACTION(sqlserver.client_app_name,sqlserver.database_id,sqlserver.database_name,sqlserver.plan_ +handle,sqlserver.server_instance_name,sqlserver.server_principal_name,sqlserver.server_principal_ +sid,sqlserver.session_id,sqlserver.session_nt_username,sqlserver.sql_text,sqlserver.transaction_ +id,sqlserver.username)), +ADD EVENT sqlserver.sp_statement_starting( +ACTION(sqlserver.client_app_name,sqlserver.database_id,sqlserver.database_name,sqlserver.plan_ +handle,sqlserver.server_instance_name,sqlserver.server_principal_name,sqlserver.server_principal_ +sid,sqlserver.session_id,sqlserver.session_nt_username,sqlserver.sql_text,sqlserver.transaction_ +id,sqlserver.username)) +ADD TARGET package0.event_file(SET filename = N'C:\Temp\Troubleshoot page split.xel') +WITH (MAX_MEMORY = 4096 +KB,EVENT_RETENTION_MODE = ALLOW_SINGLE_EVENT_LOSS,MAX_DISPATCH_LATENCY = 30 SECONDS,MAX_EVENT_SIZE = 0 +KB,MEMORY_PARTITION_MODE = NONE,TRACK_CAUSALITY = OFF,STARTUP_STATE = OFF) +GO + diff --git a/README.md b/README.md new file mode 100644 index 0000000..76c4f16 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +#Apress Source Code + +This repository accompanies [*Pro T-SQL 2012 Programmer's Guide*](http://www.apress.com/9781430245964) by Michael Coles, Scott Shaw, Jay Natarajan, and Rudi Bruchez (Apress, 2012). + +![Cover image](9781430245964.jpg) + +Download the files as a zip using the green button, or clone the repository to your machine using Git. + +##Releases + +Release v1.0 corresponds to the code in the published book, without corrections or updates. + +##Contributions + +See the file Contributing.md for more information on how you can contribute to this repository. diff --git a/contributing.md b/contributing.md new file mode 100644 index 0000000..f6005ad --- /dev/null +++ b/contributing.md @@ -0,0 +1,14 @@ +# Contributing to Apress Source Code + +Copyright for Apress source code belongs to the author(s). However, under fair use you are encouraged to fork and contribute minor corrections and updates for the benefit of the author(s) and other readers. + +## How to Contribute + +1. Make sure you have a GitHub account. +2. Fork the repository for the relevant book. +3. Create a new branch on which to make your change, e.g. +`git checkout -b my_code_contribution` +4. Commit your change. Include a commit message describing the correction. Please note that if your commit message is not clear, the correction will not be accepted. +5. Submit a pull request. + +Thank you for your contribution! \ No newline at end of file