tag:blogger.com,1999:blog-73545462024-03-08T07:49:00.981+05:30TOP 1 WITH TIESRoji's SQL BLOGRoji. P. Thomashttp://www.blogger.com/profile/07837495547296394554noreply@blogger.comBlogger47125tag:blogger.com,1999:blog-7354546.post-77365398219411622102007-04-13T17:36:00.000+05:302007-04-13T17:39:40.172+05:30Ten Ways To Lose Your DBA JobIts always good to know how you can find your way out. <a href="http://www.sqlservercentral.com/columnists/sjones/">Steve Jones</a> has this interesting <a href="http://www.sqlservercentral.com/columnists/sjones/2744.asp">article</a> in <a href="http://www.sqlservercentral.com/">SQLServerCentral.com</a>Roji. P. Thomashttp://www.blogger.com/profile/07837495547296394554noreply@blogger.com7tag:blogger.com,1999:blog-7354546.post-15460676363513067182007-04-02T23:01:00.000+05:302007-04-02T23:08:20.917+05:30Two Minute SQL Server Stumpers Vol. 3Third volume of Two Minute SQL Server Stumpers from <a href="http://www.SQLServerCentral.com/">SQLServerCentral</a> is available for download from <a href="https://www.red-gate.com/Dynamic/Downloads/DownloadForm.aspx?download=ebook2&utm_source=ssp&utm_medium=email&utm_campaign=sqlserverstumpers">here</a>. Thanks to <a href="https://www.red-gate.com/">RedGate</a>Roji. P. Thomashttp://www.blogger.com/profile/07837495547296394554noreply@blogger.com3tag:blogger.com,1999:blog-7354546.post-1170609863220707612007-02-04T22:53:00.000+05:302007-02-04T22:54:25.070+05:30How you can help in the search for Jim Gray<a href="http://blogs.msdn.com/dtjones/archive/2007/02/03/how-you-can-help-in-the-search-for-jim-gray.aspx">http://blogs.msdn.com/dtjones/archive/2007/02/03/how-you-can-help-in-the-search-for-jim-gray.aspx</a>Roji. P. Thomashttp://www.blogger.com/profile/07837495547296394554noreply@blogger.com0tag:blogger.com,1999:blog-7354546.post-1164270230707720202006-11-23T13:52:00.000+05:302006-11-23T15:12:02.356+05:30Which database is more secure? Oracle vs. MicrosoftToday, SQL Server MVP <a href="http://insidesql.de/">Frank Kalis</a> pointed to this interesting comparison by <a href="http://www.ngssoftware.com/">NGSSoftware </a>. As per the study, the reported number of security flaws in Microsoft SQL server is near to zero for the period 0f 2005-2006, whereas the Oracle figures are going higher.<br /><br />Here is the link.<br /><br /><b><a href="http://www.databasesecurity.com/dbsec/comparison.pdf">Which database is more secure?Oracle vs. Microsoft</a></b><br /><br />Update: There is another study emphasizing the fact at the <a href="http://www.enterprisestrategygroup.com/Default.asp?PAName=information">Enterprize Strategy Group</a> website.<br />Also see this <a href="http://download.microsoft.com/download/d/0/e/d0ee6c0a-ffa8-4fa2-9397-0e237d46d728/SQL2005andOracle10gSecurity.doc">comparison</a> from Microsoft.Roji. P. Thomashttp://www.blogger.com/profile/07837495547296394554noreply@blogger.com2tag:blogger.com,1999:blog-7354546.post-1163010508656138842006-11-08T23:57:00.000+05:302006-11-08T23:58:29.710+05:30SQL Server 2005 November CTPSQL Server 2005 November CTP is available for download at<br /><span style="font-family:Verdana, Arial, Helvetica;font-size:85%;color:midnightblue;"><span class="spnMessageText" id="msg"><a href="http://www.microsoft.com/sql/ctp.mspx#E2" target="_blank">http://www.microsoft.com/sql/ctp.mspx#E2</a></span></span>Roji. P. Thomashttp://www.blogger.com/profile/07837495547296394554noreply@blogger.com1tag:blogger.com,1999:blog-7354546.post-1154683225755511812006-08-04T14:50:00.000+05:302006-08-04T16:57:21.166+05:30T-SQL DrawingHere is a humble attempt to draw a landscape using T-SQL ! <br><br>NOTE: Before running this query, you should set the dispaly mode to text and Results font to a fixed width Font (Mine is Courier new) in Query Analyzer<br><br>SET NOCOUNT ON <br><br>DECLARE @ TABLE(id int IDENTITY(1,1), pic CHAR(120))<br><br>INSERT INTO @ <br>SELECT REPLICATE(CHAR(126),120) <br>FROM (SELECT 1 AS N UNION ALL SELECT 2) A, <br>(SELECT 1 AS N UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4) B, <br>(SELECT 1 AS N UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4) C<br><br>--Tree 1<br>UPDATE @<br>SET pic = STUFF(pic, (50 - (-3+ id)) , ((-3+ id) * 2), REPLICATE(CHAR(47),(-3+ id))+ REPLICATE(CHAR(92),(-3+ id)))<br>WHERE id BETWEEN 4 AND 26<br><br>UPDATE @ <br>SET Pic = STUFF(Pic,49, 2, CHAR(43)+CHAR(43))<br>WHERE id >=6<br><br>UPDATE @ <br>SET Pic = STUFF(Pic,48, 4, REPLICATE(CHAR(124),4))<br>WHERE id >=26<br><br>--Tree 2<br>UPDATE @ <br>SET Pic = STUFF(Pic, 100 - ((id - 6)*3), 6 + (((id -6) * 6)), REPLICATE(CHAR(37), 6 + (((id -6) * 6))))<br>WHERE ID BETWEEN 6 AND 10<br><br>UPDATE @ <br>SET Pic = STUFF(Pic, 89 + ((id -11)*2), 28 - ((id -11)*4), REPLICATE(CHAR(37), 28 - ((id-11)*4)))<br>WHERE ID BETWEEN 11 AND 14<br><br>UPDATE @ SET Pic = STUFF(Pic, 102,2,CHAR(64)+CHAR(64))<br>WHERE Id >= 14 AND ID <= 25<br><br>--Add a branch<br>UPDATE @ <br>SET Pic = STUFF(Pic, 90,12,REPLICATE(CHAR(61),12))<br>WHERE Id = 19<br><br>UPDATE @ <br>SET Pic = STUFF(Pic, CASE WHEN id % 2 = 0 THEN 88 ELSE 87 END, 3, REPLICATE(CHAR(37), 3))<br>WHERE ID IN(18,19,20)<br><br>--Add a Bird too :)<br>UPDATE @ <br>SET Pic = STUFF(Pic, 94, 3 , CONVERT(VARCHAR, 0x2F5E5C))<br>WHERE ID = 20<br><br>UPDATE @ <br>SET Pic = STUFF(Pic, 94, 3 , CHAR(43) + CHAR(32) + CHAR(43))<br>WHERE ID = 19<br><br>UPDATE @ <br>SET Pic = STUFF(Pic, 91, 9 , CONVERT(VARCHAR, 0x205C5C2022202F2F20))<br>WHERE ID = 18<br><br>UPDATE @ <br>SET Pic = STUFF(Pic, 91, 9 , CONVERT(VARCHAR, 0x205F7B2A762A7D5F20))<br>WHERE ID = 17<br><br>UPDATE @ <br>SET Pic = STUFF(Pic, 92, 7 , CONVERT(VARCHAR, 0x2020205F202020))<br>WHERE ID = 16<br><br>--Add a house in the hills<br>UPDATE @ <br>SET Pic = STUFF(Pic, 66, 12 , CONVERT(VARCHAR, 0x7C5F5F5F5F5F5F7C5F7C5F7C))WHERE ID = 11<br><br>UPDATE @ <br>SET Pic = STUFF(Pic, 66, 12 , CONVERT(VARCHAR, 0x7C5B5D205B5D207C207C207C))<br>WHERE ID = 10<br><br>UPDATE @ <br>SET Pic = STUFF(Pic, 66, 12 , CONVERT(VARCHAR, 0x2F2F2F2F2F2F2F2F5F5F5F5C))<br>WHERE ID = 9<br><br>UPDATE @ <br>SET Pic = STUFF(Pic, 67, 11 , CONVERT(VARCHAR, 0x2F2F2F2F2F2F2F2F635C))<br>WHERE ID = 8<br><br>UPDATE @ <br>SET Pic = STUFF(Pic, 68, 8 , CONVERT(VARCHAR, 0x5F5F5F5F5F5F5F50))<br>WHERE ID = 7<br><br>--Add fruits to the first tree in random locations.<br>UPDATE @ <br>SET Pic = STUFF(Pic, ((50 - (id - 4))+ (CAST(RAND() * (id -4) AS INT))), 2, CHAR(123) + CHAR(125))<br>WHERE id BETWEEN 10 AND 26<br><br>UPDATE @ <br>SET Pic = STUFF(Pic, 50 + CAST(RAND() * (id -4) AS INT), 2, CHAR(123) + CHAR(125))<br>WHERE id BETWEEN 10 AND 26<br><br>UPDATE @ <br>SET Pic = STUFF(Pic, ((50 - (id - 4))+ (CAST(RAND() * (id -4) AS INT))), 2, CHAR(123) + CHAR(125))<br>WHERE id BETWEEN 10 AND 26<br><br>UPDATE @ <br>SET Pic = STUFF(Pic, 50 + CAST(RAND() * (id -4) AS INT), 2, CHAR(123) + CHAR(125))<br>WHERE id BETWEEN 10 AND 26<br><br>--Sun<br>UPDATE @ <br>SET pic = STUFF(Pic,10,6, REPLICATE(CHAR(32),6))<br>WHERE id IN(2,5)<br><br>UPDATE @ SET pic = STUFF(Pic,9,8, REPLICATE(CHAR(32),8))<br>WHERE id IN(3,4)<br><br>--Add the superCool SQL Guy<br>UPDATE @ SET Pic = STUFF(Pic, 10,7, CONVERT(VARCHAR, 0x285F5F5B5F5F29))<br>WHERE ID = 28<br><br>UPDATE @ <br>SET Pic = STUFF(Pic, 12,5, CONVERT(VARCHAR, 0x5F2F5F5F2F))<br>WHERE ID = 27<br><br>UPDATE @ <br>SET Pic = STUFF(Pic, 12,6, CONVERT(VARCHAR, 0x207C2F20202F))<br>WHERE ID = 26<br><br>UPDATE @ <br>SET Pic = STUFF(Pic, 12,6, CONVERT(VARCHAR, 0x207C5C20205C))<br>WHERE ID = 25<br><br>UPDATE @ <br>SET Pic = STUFF(Pic, 12,6, CONVERT(VARCHAR, 0x5F5F5F285F2F))<br>WHERE ID = 24<br><br>UPDATE @ <br>SET Pic = STUFF(Pic, 16,3, CONVERT(VARCHAR, 0x2F5F2F))<br>WHERE ID = 23<br><br>UPDATE @ <br>SET Pic = STUFF(Pic, 16,3, CONVERT(VARCHAR, 0x5C205C))<br>WHERE ID = 22<br><br>UPDATE @ <br>SET Pic = STUFF(Pic, 10,8, CONVERT(VARCHAR, 0x2F20205C2F20205C))<br>WHERE ID = 21<br><br>UPDATE @ <br>SET Pic = STUFF(Pic, 11,6, CONVERT(VARCHAR, 0x5F5C206F2F5F))<br>WHERE ID = 20<br><br>UPDATE @ <br>SET Pic = STUFF(Pic, 11,6, CONVERT(VARCHAR, 0x5C287E6F6F20))<br>WHERE ID = 19<br><br>UPDATE @ <br>SET Pic = STUFF(Pic, 11,6, CONVERT(VARCHAR, 0x2F20205F5F29))<br>WHERE ID = 18<br><br>UPDATE @ <br>SET Pic = STUFF(Pic, 12,4, REPLICATE(CHAR(95),4))<br>WHERE ID = 17<br><br>UPDATE @ <br>SET Pic = STUFF(Pic, 9,3,CONVERT(VARCHAR, 0x2F202F))<br>WHERE ID = 22<br><br>UPDATE @ <br>SET Pic = STUFF(Pic, 9+ (id -23),2, CONVERT(VARCHAR, 0x5C20))<br>WHERE ID IN(23,24)<br><br>UPDATE @ <br>SET Pic = STUFF(Pic, 11,1, CHAR(124))<br>WHERE ID BETWEEN 23 AND 27<br><br>UPDATE @ <br>SET Pic = STUFF(Pic, 12,4, CASE WHEN id % 2 = 1 THEN CONVERT(VARCHAR, 0x20475559) ELSE CONVERT(VARCHAR, 0x2053514C) END)<br>WHERE ID IN(22,23)<br><br>--Some Bush<br>UPDATE @ <br>SET Pic = REPLICATE(CHAR(92)+CHAR(124)+ CHAR(47),40)<br>WHERE id = 31<br><br>--River (for Veer :)<br>UPDATE @ <br>SET Pic = STUFF(pic, (78 + ((31 - id)*5)), (40 -((31 - id)*5)+2), LEFT(CHAR(63) + REPLICATE(CONVERT(VARCHAR, 0x2D205F207E5E7E2D205F7E207E20),4), (40 -((31 - id)*5)+2) ))<br>WHERE ID BETWEEN 23 AND 31<br><br>--And a couple of Fish too<br>UPDATE @ <br>SET Pic = STUFF(Pic,CASE WHEN ID < 30 THEN 108 ELSE 95 END , 7, CONVERT(VARCHAR, 0x3E3C2828282A3E))<br>WHERE ID IN(27,30)<br><br>UPDATE @ <br>SET Pic = STUFF(Pic,105 , 5, CONVERT(VARCHAR, 0x3C275F3E3C))<br>WHERE ID IN(28,31)<br><br>--Border<br>UPDATE @ <br>SET Pic = REPLICATE(CHAR(95),120)<br>WHERE ID IN(1,32)<br><br>UPDATE @ <br>SET Pic = STUFF(STUFF(Pic,1,1,CHAR(124)),120,1,CHAR(124))<br>WHERE id > 1<br><br>--Signature <br>UPDATE @ <br>SET Pic = STUFF(Pic, 112,6, CONVERT(VARCHAR,0x526F6A697074))<br>WHERE ID = 31<br><br>SELECT Pic [ ] FROM @ ORDER By Id<br> <br>Roji. P. Thomashttp://www.blogger.com/profile/07837495547296394554noreply@blogger.com10tag:blogger.com,1999:blog-7354546.post-1144750862340011422006-04-11T15:47:00.000+05:302006-04-11T15:51:02.690+05:30The future of SQL ServerPaul Flessner, Senior VP of Server Applications at Microsoft, shares his vision about the future of SQL Server in his latest letter <a href="http://www.microsoft.com/sql/letter.mspx">here</a>.Roji. P. Thomashttp://www.blogger.com/profile/07837495547296394554noreply@blogger.com0tag:blogger.com,1999:blog-7354546.post-1144322243324266612006-04-06T16:47:00.000+05:302006-04-06T16:56:06.136+05:30How do you get current Job Name?Today a colleague asked me this question.<br />How do you get current Job Name from a job step?<br /><br />Well, I did'nt know the answer :(<br /><br />It should be simple. If we want to know the current procedure name from inside the procedure, we can use something like OBJECT_NAME(@@PROCID)<br />Do we have something like @@JOBID? Well, NO.<br /><br />I turned to google and learned about SQL Agent Tokens. That was news to me.<br /><br />So SQL Agent Tokens are placeholders that are automatically replaced by the subsystem during runtime.<br />In SQL Server 2000, you can refer to an SQL Agent token like [TOKENNAME]. Token names are case sensitive.<br /><br />Here is an example.<br /><br />SELECT output_file_name<br />FROM msdb..sysjobsteps<br />WHERE job_id = [JOBID]<br />AND step_id = [STEPID]<br /><br />SQL Server Agent in SQL Server 2005 has changed the syntax used in tokens from "[X]" to "$(X)" where X is the token name<br /><br />Here are some relevant links.<br /><br /><a href="http://www.microsoft.com/technet/prodtechnol/sql/2005/newsqlagent.mspx">http://www.microsoft.com/technet/prodtechnol/sql/2005/newsqlagent.mspx</a><br /><br /><a href="http://sqldev.net/sqlagent/SQLAgentStepTokens.htm">http://sqldev.net/sqlagent/SQLAgentStepTokens.htm</a>Roji. P. Thomashttp://www.blogger.com/profile/07837495547296394554noreply@blogger.com1tag:blogger.com,1999:blog-7354546.post-1143710414898659022006-03-30T14:50:00.000+05:302006-03-30T14:51:11.353+05:30Responsibilities of a Development DBARecently, I was asked to define the responsibilities of a Development DBA.<br /><br />The following is the list of items that came to my mind. Feel free to add items you feel appropriate in the comments section.<br /><br /><b>1. Database design and implementation</b><br /><br />Design and implement the database. Normalize and denormalize the database as necessary. This includes the creation of tables, views , common functions and procedures<br /><br /><b>2. Design, Implement and monitor back end jobs</b><br /><br />Define standard notification for all back end jobs. Take corrective action when a job fails. Schedule the job to execute on non-peek hours. Verify job level and step level status on a daily basis.<br /><br /><b>3. Do database tuning and performance monitoring</b><br /><br />Monitor the performance of individual programs and based on the performance results tune/reorganize the tables and indexes.<br /><br /><b>4. Optimize badly performing queries and procedures</b><br /><br />Optimize badly performing programs by modifying/rewriting the programs in statement level.<br /><br /><b>5. Do application tuning and performance monitoring</b><br /><br />Assess the performance of the applications and the load and impact on the database caused by applications. Provide suggestions to concerned developers where database access mechanisms can be optimized.<br /><br /><b>6. Perform troubleshooting on DB related issues</b><br /><br />Help developers to troubleshoot database related issues. Provide trace files containing database access details for a specific database/program/time interval to the developers when required.<br /><br />Make the developers aware about the known issues/bugs in the software and the limitations of the version/service pack in use.<br /><br /><b>7. Participate in design and architecture of new CRs. </b><br /><br />Participate/review the design and architecture of the CRs to ensure that the design/architecture is developed with scalability and performance considerations.<br /><br /><b>8. Code Review</b><br /><br />Review procedures/functions. Evaluate compliance with the defined standards and best practices. Use available tools best practices compliance.<br /><br /><br /><b>9. Training</b><br /><br />Conduct training on database related topics. Introduce new versions of SQL server. Distribute study materials. Assist the developers to start working with the new version.<br /><br /><b>10. Extend support to developers for writing complex programs</b><br /><br />Help the developers write complex queries in an optimized manner. Share common patterns/tips and tricks used in the industry to solve similar problems.<br /><br /><b>11. Setup and maintain standards and best practices.</b><br /><br />Define and enforce organization wide standards and best practices. Update the standards and practices based on the feedback and review experience.<br /><br /><b>12. Recommend Architectural changes.</b><br /><br />Recommend changes to the current design/architecture which can give a database wide performance boost.<br /><br /><b>13. Monitor database activities</b><br /><br />Monitor the activities on the servers and take corrective action when required. Look for issues like connection creeping, blocks, deadlocks, slow response etc..<br /><br /><b>14. Perform database health checking</b><br /><br />Perform regular health checking activities on the database to ensure integrity, stability and robustness of the database. Check fragmentation levels and statistics concurrency. Perform re-indexing and update statistics when required.<br /><br /><b>15. Establish and maintain backup policies</b><br /><br />Verify the backup process and occasionally test the backup. This includes the database backup and script backup.<br /><br /><b>16. Define and Enforce security policies</b><br /><br />Define the minimum security credentials each role/user requires and enforce the defined security policy.<br /><br /><b>17. Capacity Planning</b><br /><br />Monitor the space allocation and free space on the development and QA environments. Shrink the database files on a regular basis to recover free space. Configure/Recommend auto grow settings for each database based on its growth rate.<br /><br /><br /><b>18. Disaster Recovery Planning.</b><br /><br />Document and test a DRP plan for the development and QA environments. This should be in sync with the DR plan for the production environment.<br /><br /><b>19. Installation and configuration of SQL Server Databases.</b><br /><br />Setup SQL server environments for development, QA and performance testing. Configure the servers and databases based on the requirement.<br /><br /><b>20. Evaluate, test and apply service packs</b><br /><br />Test the service packs released for SQL server before applying them in a production environment to make sure they don't break the existing programs and the service pack is bug free.<br /><br /><b>21. Evaluate, test and upgrade to new versions</b><br /><br />Evaluate the new versions of SQL server. Perform performance/scalability/availability/programmability comparisons with the existing version. Use the new versions to run in-house projects for a comfortable time period and recommend for upgrading to the new version if the results are positive.<br /><br /><b>22. Evaluate, test and recommend third party components.</b><br /><br />Evaluate the components available in market that can boost the performance, secure the server, ensure high availability, prevent data loss, perform fast backups etc.., and if essential, recommend using them.<br /><br /><b>23. Coordinating and interfacing with developers, support persons, outside vendors, end users, other department team members, and service providers to resolve issues.</b><br /><br />Interact with other departments and teams to resolve DB related issues. If required get assistance from the product support team.Roji. P. Thomashttp://www.blogger.com/profile/07837495547296394554noreply@blogger.com2tag:blogger.com,1999:blog-7354546.post-1140853844333860072006-02-25T13:20:00.000+05:302006-02-25T13:34:01.176+05:30BUG: Combination of GROUP BY with HAVING Clause and LEFT OUTER JOIN with Derived Table With LEFT Function Produces Incorrect Result<br /><u>Applies To :</u> SQL Server 2000 RTM to SP4<br /><br />This bug is a variant of KB <a href="http://support.microsoft.com/default.aspx/kb/308458/en-us" title="FIX: Combination of GROUP BY with HAVINGClause and LEFT OUTER JOIN with Derived Table Produces Incorrect Result"">308458</a> which is fixed in SP2.<br /><br /><u>Symptoms</u><br /><br />If all of the following conditions are true for a query, the query will return extra rows.<br /><br />• You include GROUP BY and HAVING clauses.<br />• You use a LEFT OUTER JOIN.<br />• You incorporate derived tables.<br />• You use an aggregate. <br />• You use the LEFT function.<br /><br /><u>Cause</u><br />The optimizer inappropriately processes a cross join. <br /> <br /><u>Steps to Reproduce</u><br /><br />Execute the following query in Query Analyzer<br /><font face="'Courier New',Courier,monospace" color="#800000"><br />Use Pubs<br />GO<br /><br />SELECT T.pub_id, T.type, SUM(T.price) AS BasePrice<br />FROM Titles T<br />LEFT JOIN<br />(Select 1) X (pub_id)<br />ON X.pub_id = T.pub_id<br />WHERE LEFT(T.title,3) = 'The'<br />GROUP BY T.pub_id, T.type<br />--HAVING SUM(T.price) > 0<br /></font><br /><br />You will get the following results<br /><br /><Table bgColor="#CCCCCC" border = 1><br /><font color="#400040"><tr><td><b>pub_id</b></td><td><b>type</b></td><td><b>BasePrice</b></td></tr><br /></font><font color="#000040"><tr><td>1389</td><td>business</td><td>19.9900</td></tr><br /><tr><td>0877</td><td>mod_cook </td><td>2.9900</td></tr><br /><tr><td>0877</td><td>UNDECIDED</td><td>NULL</td></tr><br /></font></table><br /><br />Now try executing the above query by uncommenting the the HAVING caluse to exclude the NULL value.<br /><font face="'Courier New',Courier,monospace" color="#800000"><br />Use Pubs<br />GO<br /><br />SELECT T.pub_id, T.type, SUM(T.price) AS BasePrice<br />FROM Titles T<br />LEFT JOIN<br />(Select 1) X (pub_id)<br />ON X.pub_id = T.pub_id<br />WHERE LEFT(T.title,3) = 'The'<br />GROUP BY T.pub_id, T.type<br />HAVING SUM(T.price) > 0<br /></font><br />you will get the following incorrect result. <br /><table bgColor="#CCCCCC" border=1 font color="#400040"><tr><td><b>pub_id</b></td><td><b>type</b></td><td><b>BasePrice</b></td></tr></font><br /><font color="#000040"><tr><td><br />1389</td><td>business</td><td>19.9900</td></tr><tr><td><br />0877</td><td>mod_cook</td><td>19.9900</td></tr><tr><td><br />1389</td><td>business</td><td>2.9900</td></tr><tr><td><br />0877</td><td>mod_cook</td><td>2.9900</td></tr><tr><td><br />1389</td><td>business</td><td>NULL</td></tr><tr><td><br />0877</td><td>mod_cook</td><td>NULL</td></tr></font></table><br /><br />If you look at the execution plan, you can see that the query performs an incorrect cross join.<br /><br /><font color="#0000A0"> |--Nested Loops(Inner Join)<br /> ..... |--Compute Scalar(DEFINE:([Expr1005]=If ([Expr1018]=0) then NULL else [Expr1019]))<br /> ..... |....|--Stream Aggregate(GROUP BY:([T].[type], [T].[pub_id]) DEFINE:([Expr1018]=COUNT_BIG([T].[price]), [Expr1019]=SUM([T].[price])))<br /> ......|........|--Sort(ORDER BY:([T].[type] ASC, [T].[pub_id] ASC))<br /> ......|..............|--Clustered Index Scan(OBJECT:([pubs].[dbo].[titles].[UPKCL_titleidind] AS [T]), WHERE:(substring([T].[title], 1, 3)='The'))<br /> ......|--Table Spool<br /> ...........|--Filter(WHERE:([Expr1002]>0.00))<br /> ................|--Compute Scalar(DEFINE:([Expr1002]=If ([Expr1020]=0) then NULL else [Expr1021]))<br /> .....................|--Stream Aggregate(GROUP BY:([T].[type], [T].[pub_id]) DEFINE:([Expr1020]=COUNT_BIG([T].[price]), [Expr1021]=SUM([T].[price])))<br /> ..........................|--Sort(ORDER BY:([T].[type] ASC, [T].[pub_id] ASC))<br /> ...............................|--Clustered Index Scan(OBJECT:([pubs].[dbo].[titles].[UPKCL_titleidind] AS [T]), WHERE:(substring([T].[title], 1, 3)='The'))<br /></font><br /><br /><u>Workaround</u><br />You can workaround the problem by just replacing the LEFT function with SUBSTRING function.<br /><br /><font face="'Courier New',Courier,monospace" color="#800000">SELECT T.pub_id, T.type, SUM(T.price) AS BasePrice<br />FROM Titles T<br />LEFT JOIN<br />(Select 1) X (pub_id)<br />ON X.pub_id = T.pub_id<br />WHERE SUBSTRING(T.title,1,3) = 'The'<br />GROUP BY T.pub_id, T.type<br />HAVING SUM(T.price) > 0<br /></font><br /><br /><u>Note</u><br />This bug is fixed in SQL server 2005<br /><br /><br /><br /><br />Roji. P. Thomashttp://www.blogger.com/profile/07837495547296394554noreply@blogger.com0tag:blogger.com,1999:blog-7354546.post-1140620179788737732006-02-22T20:26:00.000+05:302006-02-22T20:26:19.826+05:30Named Constraints on Temporary tablesLast week I was investigating a peculiar issue reported by our testing team. They are receving an error like the following occassionally. <br /><font color="#FF0000"><br /> Server: Msg 2714, Level 16, State 4, Line 2<br />There is already an object named 'PK_Temp' in the database</font>.<br /><br />It seems that the error occurs only when multiple users tries to access the same page. A concurrency issue. <br /><br />I looked at the code of the procedure being called from the page. I found that a code segment like the following is causing the problem.<br /><font face="'Courier New',Courier,monospace" color="#800040"><br />CREATE TABLE #Temp(id int NOT NULL)<br />ALTER TABLE #Temp ADD CONSTRAINT PK_Temp PRIMARY KEY(id) <br /></font><br /><br />But why? SQL Server is supposed to make a temp table name unique by adding a unique integer suffix. <br /><br />I opened two seperate connections in Query Analyzer and execute the above code. Same Error!<br /><br />It turns that even though the temprary table names automatically made unique between concurrent sessions, the constraint names still has to be unique across a database. <br /><br />I ended up adding "Do not use named constraints on temporary tables" to the list of best pracices.<br /><br />Alternatively you can define constraints on temporary tables either by specifying a nameless constructor when you create table, like, <br /><br /><font face="'Courier New',Courier,monospace" color="#800040"><br />CREATE TABLE #Temp(id int NOT NULL PRIMARY KEY)<br /></font><br />Or by implicitly adding a constraint with the following syntax<br /><br /><font face="'Courier New',Courier,monospace" color="#800040"><br />ALTER TABLE #Temp ADD PRIMARY KEY(id) <br /></font><br />Roji. P. Thomashttp://www.blogger.com/profile/07837495547296394554noreply@blogger.com1tag:blogger.com,1999:blog-7354546.post-1135147750437559452005-12-21T12:19:00.000+05:302005-12-21T16:07:07.256+05:30SQL Server 2005 System Table map<br />The SQL Server 2005 System Table Map shows the system tables included in SQL Server 2005, and the relationships between them.<br /><br />You can download it <a href="http://www.microsoft.com/downloads/details.aspx?familyid=2ec9e842-40be-4321-9b56-92fd3860fb32&displaylang=en" target="_blank">here</a>Roji. P. Thomashttp://www.blogger.com/profile/07837495547296394554noreply@blogger.com0tag:blogger.com,1999:blog-7354546.post-1130830269887316122005-11-01T13:01:00.000+05:302005-11-01T13:01:11.723+05:30What is your 2005 IQ?Test your SQL Server 2005 knowledge and win <a href="http://www.idera5iq.com/default.asp" target="_blank">grand prizes</a>.Roji. P. Thomashttp://www.blogger.com/profile/07837495547296394554noreply@blogger.com1tag:blogger.com,1999:blog-7354546.post-1130739105163411972005-10-31T11:41:00.000+05:302005-10-31T11:41:45.220+05:30SQL Server 2005 ArticlesA Big collection of SQL Server 2005 related article links are posted at the SQL.RU site.<br /><br />Here is the <a href="http://www.sql.ru/club/YukonArt.shtml" target="_blank">link</a>Roji. P. Thomashttp://www.blogger.com/profile/07837495547296394554noreply@blogger.com0tag:blogger.com,1999:blog-7354546.post-1130652967548526042005-10-30T11:46:00.000+05:302005-10-30T11:46:07.553+05:30T-SQL Coding Challenge<br />Do you know what <a href="http://www.answers.com/main/ntquery?s=quine&gwp=11&ver=1.0.3.109&method=2" target="_blank">quine</a> is?<br /><br />If yes, there is an interesting challenge on Ken Henderson's blog for self replicating T-SQL code.<br /><br />As always, Steve Kass came up with the smartest one.<br /><br />Here are the links<br /><br /><a href="http://blogs.msdn.com/khen1234/archive/2005/10/25/484555.aspx" target="_blank">T-SQL Coding Challenge</a><br /><br /><a href="http://blogs.msdn.com/khen1234/archive/2005/10/26/485477.aspx" target="_blank">More about self-reproducing T-SQL</a><br /><br />Roji. P. Thomashttp://www.blogger.com/profile/07837495547296394554noreply@blogger.com0tag:blogger.com,1999:blog-7354546.post-1130652775224088572005-10-30T11:42:00.000+05:302005-10-30T11:42:55.246+05:30The New Generation of Microsoft Certifications<br />The Microsoft Certifications are getting more specific, and is going to be available by early 2006.<br /><br />Here is an <a href="http://www.microsoft.com/learning/mcp/newgen/" target="_blank">overview</a><br /><br />Roji. P. Thomashttp://www.blogger.com/profile/07837495547296394554noreply@blogger.com0tag:blogger.com,1999:blog-7354546.post-1128930010065226092005-10-10T13:10:00.000+05:302005-10-10T13:10:10.523+05:30Good News!<br />I am <a href="http://toponewithties.blogspot.com/2004/06/why-blog.html" target="_blank">committed</a> that I will be posting only things that are 'SQL', not anything personal, here. <br />But what will I do when I have some great news to tell you guys?<br /><br />Hmmm.. I will tell that in an SQL (read crazy) way.<br /><br />So here you go.<br /><br /><font face="'Courier New',Courier,monospace" color="#800040"><br />SELECT <br /> CHAR(LEFT(P.m,2)) +<br /> CHAR(SUBSTRING(P.m,3,2^1)) +<br /> CHAR(SUBSTRING(P.m,6,~2 * -1)) +<br /> CHAR(RIGHT(P.m,(-3 & 4 -1))) +<br /> SPACE(1)+<br /> STUFF(REPLACE(STUFF(OBJECT_NAME(1), 1, 4, ''),'j',''),4,1,REVERSE('emo'))+<br /> SPACE(1)+ CHAR(ASCII('`')+1)+ SPACE(1)+<br /> LEFT(@@VERSION,20)+<br /> SPACE(1)+ UPPER(LEFT(DB_NAME(1),1))+<br /> CHAR(CAST(LEFT(P.m,2) AS INT)+4)+<br /> CONVERT(CHAR(2), 0x5021) AS [Good News]<br />FROM<br /> (SELECT REVERSE(V.hugo+V.adam+V.david+V.louis) <br /> FROM<br /> (SELECT <br /> CAST(M.a AS VARCHAR) AS thanksto_JCelko, <br /> CAST(M.a + POWER(8^2,DB_ID('tempdb')) AS VARCHAR) AS thanksto_Sommarskog, <br /> REPLICATE(DB_ID('master'),3) As thanksto_ItzikBenGan, <br /> CAST(M.s AS VARCHAR) As thanksto_Tibor<br /> FROM <br /> (SELECT <br /> (DB_ID('msdb') + 1) * POWER(10,DB_ID('tempdb')) + 1 AS thanksto_ASen,<br /> DATEDIFF(yy, '19760509','20051007')-1 AS thanksto_SKass) As M(a,s)<br /> )AS V(hugo, adam, david, louis)) P(m)<br />UNION ALL<br />SELECT CONVERT(VARCHAR, 0x5468616E6B7320746F20796F7520616C6C21)<br /></font><br />-- :)Roji. P. Thomashttp://www.blogger.com/profile/07837495547296394554noreply@blogger.com2tag:blogger.com,1999:blog-7354546.post-1124792306097402022005-08-23T15:48:00.000+05:302005-08-23T15:56:01.683+05:30Sampling Using TABLESAMPLE<br />With due credits to <a href="http://www.users.drew.edu/skass/" target="_blank">Steve Kass</a><br /><br />SQL Server 2005 Implements the TABLESAMPLE clause, which can be used to retrieve a sample (of rows) from a table. <br /><br />Here is the syntax of the TABLESAMPLE command.<br /><br /> <font face="'Courier New',Courier,monospace" color="#800000"><br />TABLESAMPLE [SYSTEM] (sample_number [ PERCENT | ROWS ] ) <br /> [ REPEATABLE (repeat_seed) ] <br /><br />eg.<br /><br /><br />SELECT * FROM Person.Contact<br />TABLESAMPLE SYSTEM(50 ROWS)<br /></font><br /><br />The optional SYSTEM keyword specifies the sampling algorithm to use. In the case of SQL Server 2005 SYSTEM is the only sampling method available and applied by default. Other products like DB2 has implemented sampling methods like BERNOULLI.<br /><br />You can specify the number of rows or percentage of rows you want to get back. But remeber that the sampling method gives an approximate number of rows back. So when you are trying to apply TABLESAMPLE on a small table chances are likely that, you may get zero rows back.<br /><br />WHEN used with the REPEATABLE option, with the same seed value, SQL Server will return the same sample as before. When specified with a different repeat_seed value, SQL Server will likely return a different sample of the rows in the table.<br /><br />TABLESAMPLE does not work with views or inline table-valued functions.<br /><br />When I tried TABLESAMPLE first, I tried<br /><br /> <font face="'Courier New',Courier,monospace" color="#800000"><br />SELECT * FROM Sales.StoreContact <br />TABLESAMPLE SYSTEM(10 ROWS)<br /></font><br /><br />And to my surprize, most of the time I was getting Zero rows back, and sometimes 37 or 179 rows. I had reported this issue in a beta newsgroup and SQL Server MVP Steve Kass help me understand it better. Here is the excerpts from his reply.<br /><br /><font color="#004080"><br /><QUOTE><br />As documented in Books Online ("Limiting Results Sets by Using TABLESAMPLE"),<br />the sampling algorithm can only return full data pages. Each page is<br />selected or skipped with probability [desired number of rows]/[rows in table]<br /><br />The StoreContact table fits on 4 data pages. Three of those pages contain<br />179 rows, and one contains 37 rows. When you sample for 10 rows (1/75<br />of the table), each of the 4 pages is returned with probability 1/75 and<br />skipped with probabiliy 74/75. The chance that no rows are returned is<br />about (74/75)^4, or about 87%. When rows are returned, about 3/4 of the<br />time you will see 179 rows, and about 1/4 of the time you will see 37<br />rows. Very rarely, you will see more rows, if two or more pages are<br />returned, but this is very unlikely.<br /><br />As BOL suggests, SYSTEM sampling (which is the only choice) is not<br />recommended for small tables. I would add that if the table fits on<br />N data pages, you should not try to sample fewer than 1/N-th of the<br />rows, or that you should never try to sample fewer rows than fit on<br />at least 2 or 3 data pages.<br /><br />If you were to sample roughly two data pages worth of rows, say<br />300 rows, the chance of seeing no rows would be about 13%. The<br />larger (more data pages) the table, the smaller the chance of<br />seeing no rows when at least a couple of pages worth are requested.<br />For example, if you request 300 rows from a 1,000,000 row table that<br />fits on 10,000 data pages, only in 5% of trials would you see no<br />rows, even though the request is for far less than 1% of the rows.<br /><br />By choosing the REPEATABLE option, you will get the same sample<br />each time. For most seeds, this will be an empty sample in your<br />case. With other seeds, it will contain 37, 179, 216, 358, 395,<br />537, 574, or 753 rows, depending on which pages were selected,<br />with the larger numbers of rows returned for very few choices<br />of seed.<br /><br />That said, I agree that the consequences of returning only<br />full data pages results in very confusing behavior!<br /></QUOTE><br /></font><br /><br />That makes things clear. For my question about the usability of the feature, Conor Cunningham, Development Lead of SQL Server Query Optimization team, gave the following answer.<br /><br /><font color="#004080"><br /><QUOTE><br />From a usability standpoint, we are following ANSI - we are allowed to <br />arbitrarily over- or under-sample using the SYSTEM keyword. For small <br />tables, we happen to over-sample by sampling all rows.<br /><br />We believe that this provides an effective sampling algorithm for some <br />classes of applications.<br /></QUOTE><br /></font><br /><br />But my concern was mostly about the zero rows case. I gave the following reply to Conor. <br /><br /><font color="#004080"><br /><QUOTE><br /> I agree the point that in the SYSTEM sampling method, the implementor is <br />allowed to arbitrarly over/under sample any given set. And I am OK with <br />that.<br /><br />But my cause of concern is when I try to get a 10% SAMPLE on a table that has <br />over 700 rows, I get zero rows back.<br /><br />To me, Any sampling algorithm, that applied on a set that has one or more <br />rows MUST return one or more rows. I dont think zero rows can be a proper <br />sample.<br /></QUOTE><br /></font><br /><br />I was asked to open a bug, which I did <a href="http://lab.msdn.microsoft.com/ProductFeedback/viewfeedback.aspx?feedbackid=7e289854-3acb-4cdc-b00c-444730b2e4b3" target="_blank">here </a><br /><br />Later the issue was Resolved as By Design by Microsoft.<br /><br />Meanwhile Steve Kass gave me the following T-SQL code to implement Bernoulli Sampling.<br /><br /> <font face="'Courier New',Courier,monospace" color="#800000"><br />select * from Sales.StoreContact<br />where rand(checksum(newid())%1000000000+CustomerID)< 0.1<br />-- 0.1 is the desired probability of choosing a row, change as needed.<br /></font><br /><br />Eventhough TABLESAMPLE can be confusing with small tables, you will still find it useful in some cases.<br /><br /><br />Roji. P. Thomashttp://www.blogger.com/profile/07837495547296394554noreply@blogger.com0tag:blogger.com,1999:blog-7354546.post-1123400909161299352005-08-07T13:18:00.000+05:302005-08-07T13:32:19.146+05:30MAX Sized datatypes<br />You are no longer limited to that 8000 charcter limit for VARCHAR variables. SQL Server 2005 enhances the VARCHAR, NVARCHAR and VARBINARY datatypes by raising the capacity to Approximately 2GB. You can declare the variables with the MAX specifier. Lets quickly try some VARCHAR(MAX) operations.<br /><br /><font face="'Courier New',Courier,monospace" color="#400000"><br />DECLARE @vchTest VARCHAR(MAX) <br />SET @vchTest = REPLICATE('AA', 5000)<br />SELECT LEN(@vchTest) as Length<br /></font><br />Length<br />--------<br />8000<br /><br />Ofcourse, I expected to see 10000 back as result. I refer the REPLICATE command in BOL which clearly says<br /><br />"If character_expression is not of type varchar(max) or nvarchar(max), REPLICATE truncates the return value at 8,000 bytes. To return values greater than 8,000 bytes, character_expression must be explicitly cast to the appropriate large-value data type. "<br /><br />So I tried<br /><br /><font face="'Courier New',Courier,monospace" color="#400000"><br />DECLARE @vchTest VARCHAR(MAX) <br />SET @vchTest = REPLICATE(CAST('AA' AS VARCHAR(MAX)), 5000)<br />SELECT LEN(@vchTest) as Length<br /></font><br /><br />Length<br />--------<br />10000<br /><br />Then I tried Concatenating many VARCHAR(n) values and assign it to VARCHAR(MAX) .<br /><font face="'Courier New',Courier,monospace" color="#400000"><br />DECLARE @vchTest VARCHAR(MAX) <br />SET @vchTest = REPLICATE('AA',4000)+ REPLICATE('AA', 4000)<br />SELECT LEN(@vchTest) as Length<br /></font><br />Length<br />--------<br />8000<br /><br /><br />This time I expected to see 16000 as the result. But I still get 8000 back! <br /><br />I got help from <a href="http://blogs.conchango.com/christianwade/archive/2004/11/06/199.aspx" target="_blank">Christian Wade's blog</a> Which says, <br /><br /><font color="#000080"><QUOTE><br />The problem is that if you concatenate two varchar(8000) variables, you will be left with a varchar(8000) variable. This is the same issue as populating a float with the division of two integers.<br />The reason is that varchar(n) variables are physically stored in the same way - irrespective of length. Whereas an varchar(MAX) is effectively a text under the covers and needs to worry about managing pointers to the relevant data page (although this is abstracted away from the developer).<br /><QUOTE/><br /></font><br />To ensure I just tried <br /><font face="'Courier New',Courier,monospace" color="#400000"><br />DECLARE @S VARCHAR(9000)<br /></font><br />which returned the error.<br /><br /><font color="#FF0000">Msg 131, Level 15, State 3, Line 1<br />The size (9000) given to the type 'varchar' exceeds the maximum allowed for any data type (8000).<br /></font><br /><br />The workaround Christian Wade suggested iscasting the two expressions individually to the large-valued type and then do the assignment.<br /><br />However, I find it more easy to do an Initialisation and Concantenation with the actual variable, like<br /><font face="'Courier New',Courier,monospace" color="#400000"><br />DECLARE @vchTest VARCHAR(MAX) <br />SET @vchTest = 'Start'<br />SET @vchTest = @vchTest + REPLICATE('AbC2',2000) + 'Test'+ REPLICATE('AbC2',2000) + 'End'<br />SELECT LEN(@vchTest) as Length<br /></font><br />Length<br />---------<br />16012<br /><br />Then I tried all String functions on the variable<br /><font face="'Courier New',Courier,monospace" color="#400000"><br />SELECT LEN(LEFT(@vchTest,9000)) --OK<br />SELECT RIGHT(@vchTest,3)<br />SET @vchTest= REPLACE(@vchTest,'End','XXX')<br />SELECT RIGHT(@vchTest,3) <br />SET @vchTest = REVERSE(@vchTest)<br />SELECT RIGHT(@vchTest,3) <br />SET @vchTest = REVERSE(@vchTest)<br />SET @vchTest = SPACE(9000) + @vchTest<br />SELECT LEN(@vchTest)<br />SELECT CHARINDEX('Test',@vchTest) --Correct<br />SET @vchTest = STUFF(@vchTest,24001,4,'SQL')<br />SELECT CHARINDEX('SQL',@vchTest) --Correct<br />SELECT SUBSTRING(@vchTest,32000,3)<br />SET @vchTest = LOWER(UPPER(@vchTest))<br />SELECT LEN(@vchTest)<br />SET @vchTest = LTRIM(@vchTest)<br />SELECT LEN(@vchTest)<br /></font><br /><br />All seems to work fine. <br /><br />The text, ntext and image data types will be deprecated. The varchar(max), nvarchar(max) and varbinary(max) data types are far superior than these old datatypes, because you dont have to use special statements like WRITETEXT and UPDATE text. You can use the standard INSERT and UPDATE statements. . For more information, see the topic Using Large Value Data Types in Books Online.<br /><br /><br /><br /><br />Roji. P. Thomashttp://www.blogger.com/profile/07837495547296394554noreply@blogger.com2tag:blogger.com,1999:blog-7354546.post-1123338319040660822005-08-06T19:55:00.000+05:302005-08-06T20:54:15.306+05:30New APPLY OperatorThe new APPLY Operator lets you reference one or more columns from the LEFT table in the right Derived table or table valued function. There are to variations of the Operator, CROSS APPLY and OUTER APPLY. You can use CROSS APPLY like an INNER JOIN except that you dont have to specify any JOIN condition and you can reference the left table columns in the right hand side of the Operator. OUTER APPLY is like LEFT JOIN, that all rows from the left table is included in the result even when there is no match.<br /><br />The main advantage of the CROSS APPLY operator is that it lets you pass values from the columns of the joined table to a table valued UDF, which was not possible in previous versions of SQL Server.<br /><br />Suppose you have the following table structure.<br /><font face="'Courier New',Courier,monospace" color="#800000"><br />CREATE TABLE #Client(ClientId int, ClientName VARCHAR(35))<br />CREATE TABLE #Account(AccountId int, ClientId int, AccountValue numeric(18,2))<br /><br />INSERT INTO #Client VALUES(1, 'Anil')<br />INSERT INTO #Client VALUES(2, 'Robin')<br /><br />INSERT INTO #Account VALUES(1,1,100)<br />INSERT INTO #Account VALUES(2,1,200)<br />INSERT INTO #Account VALUES(3,1,300)<br />INSERT INTO #Account VALUES(4,2,100)<br />INSERT INTO #Account VALUES(5,2,400)<br />INSERT INTO #Account VALUES(6,2,500)<br /></font><br /><br />And you were asked to get the TOP 2 Accounts based on AccountValue of each client.<br /><br />Is SQL Server 2000 you can achieve it by writing a query like,<br /><br /><font face="'Courier New',Courier,monospace" color="#800000">SELECT C.ClientName, A.AccountValue<br />FROM #Client C<br />INNER JOIN #Account A<br />ON C.ClientId=A.ClientId<br />AND A.AccountID IN(<br /> SELECT TOP 2 AccountId <br /> FROM #Account D<br /> WHERE D.ClientID=A.ClientId<br /> ORDER BY D.AccountValue DESC)<br /></font><br /><br />Which is ofcourse costly. See how CROSS APPLY makes life easy for you.<br /><br /><font face="'Courier New',Courier,monospace" color="#800000">SELECT C.ClientName, A.AccountValue<br />FROM #Client C<br />CROSS APPLY<br />(SELECT TOP 2 AccountId, AccountValue<br /> FROM #Account D<br /> WHERE D.ClientID=C.ClientId<br /> ORDER BY D.AccountValue DESC)A<br /></font><br />Here is the result of the above two queries.<br /><br /><TABLE bgColor=#CCCCCC Border=1><TR><TD>ClientName</TD><TD>AccountValue</TD></TR><TR><TD>Anil</TD><TD>300.00</TD></TR><TR><TD>Anil</TD><TD>200.00</TD></TR><TR><TD>Robin</TD><TD>500.00</TD></TR><TR><TD>Robin</TD><TD>400.00</TD></TR></TABLE><br /><br />To see OUTER APPLY at work, Lets add a client without any accounts and try the above query with OUTER APPLY<br /><font face="'Courier New',Courier,monospace" color="#800000"><br />INSERT INTO #Client VALUES(3, 'Roji')<br /><br />SELECT C.ClientName, A.AccountValue<br />FROM #Client C<br />OUTER APPLY<br />(SELECT TOP 2 AccountId, AccountValue<br /> FROM #Account D<br /> WHERE D.ClientID=C.ClientId<br /> ORDER BY D.AccountValue DESC)A<br /></font><br /><br />And here is the result.<br /><br /><TABLE bgColor=#CCCCCC Border=1><TR><TD>ClientName</TD><TD>AccountValue</TD></TR><TR><TD>Anil</TD><TD>300.00</TD></TR><TR><TD>Anil</TD><TD>200.00</TD></TR><TR><TD>Robin</TD><TD>500.00</TD></TR><TR><TD>Robin</TD><TD>400.00</TD></TR><TR><TD>Roji</TD><TD>NULL</TD></TR></TABLE><br /><br />You feel the real power of OUTER/CROSS APPLY When you use them with the Table Values UDFs. There is a UDF named <b>dbo.ufnGetContactInformation</b> in the new AdventureWorks Sample database, which accepts a ContactID as the input and return all contact details as a table. Lets try using that function to get the contact details of all the contacts in the<b> Sales.ContactCreditCard</b> table.<br /><br /><font face="'Courier New',Courier,monospace" color="#800000"><br />SELECT B.FirstName, B.LastName, B.JobTitle, B.ContactType<br />FROM Sales.ContactCreditCard A<br />CROSS APPLY dbo.ufnGetContactInformation(A.ContactId) B<br /></font><br />And here is the (abridged) result.<br /><br /><br /><TABLE bgColor=#CCCCCC Border=1><TR><TD>FirstName</TD><TD>LastName</TD><TD>JobTitle</TD><TD>ContactType</TD></TR><TR><TD>Catherine</TD><TD>Abel</TD><TD>Owner</TD><TD>Store Contact</TD></TR><TR><TD>Kim</TD><TD>Abercrombie</TD><TD>Owner</TD><TD>Store Contact</TD></TR><TR><TD>Humberto</TD><TD>Acevedo</TD><TD>Owner</TD><TD>Store Contact</TD></TR><TR><TD>Pilar</TD><TD>Ackerman</TD><TD>Owner</TD><TD>Store Contact</TD></TR></TABLE><br /><br /><br />But thats just an introduction. I had seen a lot of smart SQL using CROSS APPLY in NG posting recently. So stay tuned.<br /><br /><br /><br /><br /><br />Roji. P. Thomashttp://www.blogger.com/profile/07837495547296394554noreply@blogger.com1tag:blogger.com,1999:blog-7354546.post-1123145091108020042005-08-04T14:14:00.000+05:302005-08-04T14:23:04.893+05:30DML With OutputSQL Server 2005 introduces a new feature for retrieving rows affected by an INSERT, UPDATE or Delete statement. It is a common requirement to get the affected rows back for further processing, auditing or simply return them back to the client. <br /><br />Those who had worked with triggers are familiar with the special inserted and deleted tables. The new OUTPUT clause also work in the same manner. You can use the OUTPUT keyword to return information about the results of a Transact-SQL statement into a table variable, temporary table, permanent table or just return it back to the client.<br /><br /><b>Using the OUTPUT Clause with INSERT</b><br /><br /><font face="'Courier New',Courier,monospace" color="#800000"><br />CREATE Table #OutputTest(someid int IDENTITY(1,1), sometext varchar(100))<br />--Table variable to collect changes<br /><br />DECLARE @InsertDetails TABLE(someid int,sometext varchar(100))<br />INSERT INTO #OutputTest(sometext)<br />OUTPUT Inserted.SomeId, Inserted.someText INTO @InsertDetails<br />VALUES('Some Text goes Here')<br /><br />SELECT * FROM @InsertDetails<br /></font><br /><br />Here is the result.<br />(1 row(s) affected)<br />someid sometext<br />----------- ----------------------------------<br />1 Some Text goes Here<br /><br />(1 row(s) affected)<br /><br />Note that we are retriving the just inserted IDENTITY value also.<br /><br />Now lets try to insert a batch of rows.<br /><br /><font face="'Courier New',Courier,monospace" color="#800000"><br />--Table variable to collect changes<br />DECLARE @InsertDetails TABLE(someid int,sometext varchar(100))<br /><br />--Inserting a batch<br />INSERT INTO #OutputTest(sometext)<br />OUTPUT Inserted.SomeId, Inserted.someText INTO @InsertDetails<br />SELECT 'Test 1' UNION<br />SELECT 'Test 2' UNION<br />SELECT 'Test 3' <br /><br />SELECT * FROM @InsertDetails<br /><br /></font><br />And here is the OUTPUT.<br />(3 row(s) affected)<br />someid sometext<br />----------- -----------------------------------------------------------------<br />1 Test 1<br />2 Test 2<br />3 Test 3<br /><br />(3 row(s) affected)<br /><br /> <b>Using the OUTPUT Clause with UPDATE</b><br /><br /><font face="'Courier New',Courier,monospace" color="#800000"><br />DECLARE @UpdateDetails TABLE(id int, prevtext VARCHAR(100), newText VARCHAR(100))<br /><br />UPDATE #OutputTest<br />SET sometext = 'XYZ'<br />OUTPUT deleted.someid, deleted.sometext,inserted.sometext INTO @UpdateDetails<br />WHERE SomeID < 3<br /><br />SELECT * FROM @UpdateDetails<br /></font><br /><br />Here is the output.<br /><br />(2 row(s) affected)<br />id prevtext newText<br />----------- ---------- ----------<br />1 Test 1 XYZ<br />2 Test 2 XYZ<br /><br />(2 row(s) affected)<br /><br />Note that here we are capturing both the current and previous values of the affected column.<br /><br /> <b>Using the OUTPUT Clause with DELETE</b><br /><font face="'Courier New',Courier,monospace" color="#800000"><br />DECLARE @DeleteDetails TABLE(id int, DeletedBy sysname)<br />DELETE FROM #OutputTest<br />OUTPUT deleted.someid, SUSER_NAME() INTO @DeleteDetails<br />WHERE SomeID=3<br /><br />SELECT * FROM @DeleteDetails<br /></font><br /><br />Here is the result<br /><br />(1 row(s) affected)<br />id DeletedBy<br />----------- --------------------------------------------------------------------------------------------------------------------------------<br />3 DOMAIN\rojipt<br /><br />(1 row(s) affected)<br /><br /><br />I believe the DML OUTPUTwill be one of the most favourite feature of T-SQL developers.<br /><br />Roji. P. Thomashttp://www.blogger.com/profile/07837495547296394554noreply@blogger.com2tag:blogger.com,1999:blog-7354546.post-1123067162057339882005-08-03T16:24:00.000+05:302005-08-03T16:40:03.400+05:30Common (TOP 10?) T-SQL Programming mistakes<strong>1. TOP Without ORDER BY</strong><br />SQL Server doesn’t guarantee the order of records returned, if an explicit ORDER BY clause is not specified. Records are not returned in the order they were created. Most of the times records are returned in the order of the clustered index, but not necessarily always.<br />Eg.<br /><br /><br /><span style="font-family:courier new;color:#660000;">CREATE TABLE #Client(ClientID int IDENTITY(1,1), ClientName varchar(30), AnnualIncome int)<br /><br />--Add a clustered index on ClientName<br />--CREATE CLUSTERED INDEX idx1 ON #Client(ClientName)<br /><br />--Populate the client table.<br />INSERT INTO #Client(ClientName, AnnualIncome)<br />VALUES('Prasanth', 10000)<br /><br />INSERT INTO #Client(ClientName, AnnualIncome)<br />VALUES('Binu', 20000)<br /><br />INSERT INTO #Client(ClientName, AnnualIncome)<br />VALUES('Shino', 30000)<br /><br />--Populate the account table<br /><br />SELECT TOP 1 * FROM #Client<br />--ORDER BY ClientID<br /><br />DROP TABLE #Client<br /></span><br /><strong>2. TOP Without TIES<br /></strong>If you are expecting TIES in the result and you want all tied records, (like in the case of finding rank) you should use WITH TIES clause with the SELECT statement.<br /><br />Eg.<br /><br /><span style="font-family:courier new;color:#660000;">CREATE TABLE #Client(ClientID int IDENTITY(1,1), ClientName varchar(30), AnnualIncome int)<br /><br />--Populate the client table.<br />INSERT INTO #Client(ClientName, AnnualIncome)<br />VALUES('Prasanth', 10000)<br /><br />INSERT INTO #Client(ClientName, AnnualIncome)<br />VALUES('Binu', 30000)<br /><br />INSERT INTO #Client(ClientName, AnnualIncome)<br />VALUES('Shino', 30000)<br /><br />SELECT TOP 1 * FROM #Client<br />ORDER BY AnnualIncome DESC<br /><br />DROP TABLE #Client<br /></span><br /><br /><strong>3. Not Using an Alias</strong><br /><br />Consider the following query.<br /><br /><br /><span style="font-family:courier new;color:#660000;">CREATE TABLE #Client(ClientID int IDENTITY(1,1), ClientName varchar(30), AnnualIncome int)<br /><br />CREATE TABLE #Account(AccountId int IDENTITY(1,1), ClientId int, AccountValue int)<br /><br /><br />--Populate the client table.<br />INSERT INTO #Client(ClientName, AnnualIncome)<br />VALUES('Prasanth', 10000)<br /><br />INSERT INTO #Client(ClientName, AnnualIncome)<br />VALUES('Binu', 20000)<br /><br />INSERT INTO #Client(ClientName, AnnualIncome)<br />VALUES('Shino', 30000)<br /><br />--Populate the account table<br /><br />INSERT INTO #Account(ClientId, AccountValue)<br />VALUES(1, 500)<br />INSERT INTO #Account(ClientId, AccountValue)<br />VALUES(2, 1500)<br />INSERT INTO #Account(ClientId, AccountValue)<br />VALUES(3, 2000)<br /><br />SELECT * FROM #Account<br />WHERE AccountId IN(SELECT AccountId FROM #Client)<br /><br />DROP TABLE #Client<br />DROP TABLE #Account<br /></span><br />Obviously there is no column called AccountId in the #Client table. Probably you are expecting an error, but the query returns all the records from the #Account table. What happened behind the scenes is that, SQL Server looks for the accountId column in the #Client table and when it fails to find the column there, it looks for the column in the outer table. This behaviour is referred as fancy scoping.<br /><br /><strong>4. Aggreagation And JOINS</strong><br /><br />Be careful when doing aggregation on joined resultset. Chances are there that you may getting the wrong result. Consider the following example.<br /><br /><span style="font-family:courier new;color:#660000;">CREATE TABLE #Client(ClientID int IDENTITY(1,1), ClientName varchar(30), AnnualIncome int)<br /><br />CREATE TABLE #Account(AccountId int IDENTITY(1,1), ClientId int, AccountValue int)<br /><br />--Populate the client table.<br />INSERT INTO #Client(ClientName, AnnualIncome)<br />VALUES('Prasanth', 10000)<br /><br />INSERT INTO #Client(ClientName, AnnualIncome)<br />VALUES('Binu', 20000)<br /><br />INSERT INTO #Client(ClientName, AnnualIncome)<br />VALUES('Shino', 30000)<br /><br />--Populate the account table<br /><br />INSERT INTO #Account(ClientId, AccountValue)<br />VALUES(1, 500)<br />INSERT INTO #Account(ClientId, AccountValue)<br />VALUES(1, 1500)<br />INSERT INTO #Account(ClientId, AccountValue)<br />VALUES(2, 1500)<br />INSERT INTO #Account(ClientId, AccountValue)<br />VALUES(3, 2000)<br /><br />SELECT C.ClientId, C.ClientName,SUM(C.AnnualIncome)<br />FROM #Client C<br />INNER JOIN #Account A<br />ON C.ClientId = A.ClientId<br />GROUP BY C.ClientId, C.ClientName<br />ORDER BY C.ClientId<br /><br />DROP TABLE #Client<br />DROP TABLE #Account<br /></span><br /><strong>5. NOT IN AND NULL<br /></strong>If you are using the NOT IN Opereator with a subquery and the subquery contains any NULL values, the subquery will return NULL!. This can be dangerous, and this is not the case if you use IN.<br /><br />Eg.<br /><br /><span style="font-family:courier new;color:#660000;">CREATE TABLE #Client(ClientID int IDENTITY(1,1), ClientName varchar(30), AnnualIncome int)<br /><br />CREATE TABLE #Account(AccountId int IDENTITY(1,1), ClientId int, AccountValue int)<br /><br /><br />--Populate the client table.<br />INSERT INTO #Client(ClientName, AnnualIncome)<br />VALUES('Prasanth', 10000)<br /><br />INSERT INTO #Client(ClientName, AnnualIncome)<br />VALUES('Binu', 20000)<br /><br />INSERT INTO #Client(ClientName, AnnualIncome)<br />VALUES('Shino', 30000)<br /><br />--Populate the account table<br /><br />INSERT INTO #Account(ClientId, AccountValue)<br />VALUES(1, 500)<br />INSERT INTO #Account(ClientId, AccountValue)<br />VALUES(2, 1500)<br />INSERT INTO #Account(ClientId, AccountValue)<br />VALUES(NULL, 2000)<br /><br />SELECT * FROM #Client<br />WHERE ClientID NOT IN(SELECT ClientId FROM #Account)<br /><br />DROP TABLE #Client<br />DROP TABLE #Account<br /></span><br /><strong>6. Aggregation And NULL<br /></strong><br />If any of the columns you are apllying aggregate functions contains NULL , SQL Server will return a warning, “Warning: Null value is eliminated by an aggregate or other SET operation.”. If you are accessing the recordset using ADO, you might face problems. Either use ISNULL() function or SET ANSI_WARNINGS OFF. Setting ANSI_WARNINGS inside an SP will cause it to recompile everytime.<br /><br />Eg.<br /><br /><span style="font-family:courier new;color:#660000;">CREATE TABLE #Client(ClientID int IDENTITY(1,1), ClientName varchar(30), AnnualIncome int)<br /><br />--Populate the client table.<br />INSERT INTO #Client(ClientName, AnnualIncome)<br />VALUES('Prasanth', 10000)<br /><br />INSERT INTO #Client(ClientName, AnnualIncome)<br />VALUES('Binu', 30000)<br /><br />INSERT INTO #Client(ClientName, AnnualIncome)<br />VALUES('Shino', NULL)<br /><br />SELECT SUM(AnnualIncome) FROM #Client<br /><br />DROP TABLE #Client<br /><br /></span><br /><strong>7. OUTER JOIN AND WHERE Condition<br /></strong><br />Consider the following query.<br /><br /><br /><br /><span style="font-family:courier new;color:#660000;">CREATE TABLE #Client(ClientID int IDENTITY(1,1), ClientName varchar(30), AnnualIncome int)<br /><br />CREATE TABLE #Account(AccountId int IDENTITY(1,1), ClientId int, AccountValue int)<br /><br /><br />--Populate the client table.<br />INSERT INTO #Client(ClientName, AnnualIncome)<br />VALUES('Prasanth', 10000)<br /><br />INSERT INTO #Client(ClientName, AnnualIncome)<br />VALUES('Binu', 20000)<br /><br />INSERT INTO #Client(ClientName, AnnualIncome)<br />VALUES('Shino', 30000)<br /><br />--Populate the account table<br /><br />INSERT INTO #Account(ClientId, AccountValue)<br />VALUES(1, 500)<br />INSERT INTO #Account(ClientId, AccountValue)<br />VALUES(2, 1500)<br /><br />--To find Clients who has an annualIncome > 10000<br />SELECT C.ClientName<br />FROM #Client C<br />LEFT JOIN #Account A<br />ON C.ClientId = A.ClientId<br />AND C.AnnualIncome > 10000<br /><br /><br />DROP TABLE #Client<br />DROP TABLE #Account<br /></span><br /><br />This query returns incorrect result. In case of INNER JOINs putting the filter condition on the WHERE clause or ON Clause has the same effect. But in case of OUTER JOINS, it’s a different story. You specify the JOIN Criteria on the ON clause and filter criteria on the WHERE clause. The condition ON the join criteria doesn’t have an impact on the number of rows returned.<br /><br />A Select statement works in the following way.<br /><br />SELECT<br />JOIN<br />WHERE<br />GROUP BY<br />HAVING<br />DISTINCT<br />ORDER BY<br />TOP<br /><br />(See this <a href="http://tinyurl.com/7523h">link</a> for a detailed description by Joe CELKO)<br /><br /><strong>8. Concatenation And NULL</strong><br /><br />If you are concatenating a field which has a NULL value, the result will be NULL. To avoid this either use ISNULL() function or SET CONCAT_NULL_YIELDS_NULL OFF<br /><br />Eg.<br /><br /><br /><span style="font-family:courier new;color:#660000;">CREATE TABLE #Client(ClientID int IDENTITY(1,1), ClientName varchar(30), AnnualIncome int)<br /><br /><br />-- Populate the client table.<br />INSERT INTO #Client(ClientName, AnnualIncome)<br />VALUES('Prasanth', 10000)<br /><br />INSERT INTO #Client(ClientName, AnnualIncome)<br />VALUES('Binu', 20000)<br /><br />INSERT INTO #Client(ClientName, AnnualIncome)<br />VALUES('Shino', NULL)<br /><br />SELECT * FROM #Client<br /><br />SELECT ClientName + ' - '+ CONVERT(VARCHAR,AnnualIncome) AS [ClientInfo]<br />FROM #Client<br /><br />DROP TABLE #Client<br /></span><br /><strong>9. NULL valued Parameters</strong><br /><br />If the parameter you are passing has a NULL value and even if there is matching records for NULL, the query wont return any rows back.<br /><br /><br /><br /><span style="font-family:courier new;color:#660000;">CREATE TABLE #Client(ClientID int IDENTITY(1,1), ClientName varchar(30), AnnualIncome int)<br /><br />CREATE TABLE #Account(AccountId int IDENTITY(1,1), ClientId int, AccountValue int)<br /><br /><br />--Populate the client table.<br />INSERT INTO #Client(ClientName, AnnualIncome)<br />VALUES('Prasanth', 10000)<br /><br />INSERT INTO #Client(ClientName, AnnualIncome)<br />VALUES('Binu', 20000)<br /><br />INSERT INTO #Client(ClientName, AnnualIncome)<br />VALUES('Shino', 30000)<br /><br />--Populate the account table<br /><br />INSERT INTO #Account(ClientId, AccountValue)<br />VALUES(1, 500)<br />INSERT INTO #Account(ClientId, AccountValue)<br />VALUES(2, 1500)<br />INSERT INTO #Account(ClientId, AccountValue)<br />VALUES(NULL, 2000)<br /><br /><br />DECLARE @intClientId int<br />SET @intClientId = NULL<br /><br />SELECT * FROM #Account<br />WHERE ClientId = @intClientId<br /><br />DROP TABLE #Client<br />DROP TABLE #Account<br /></span><br /><strong>10. Error Handling</strong><br /><br />consider the following example.<br /><br /><span style="font-family:courier new;color:#660000;">CREATE TABLE #Client(ClientID int IDENTITY(1,1), ClientName varchar(30), AnnualIncome int)<br /><br />-- Populate the client table.<br />INSERT INTO #Client(ClientName, AnnualIncome)<br />VALUES('Prasanth', 10000)<br /><br />INSERT INTO #Client(ClientName, AnnualIncome)<br />VALUES('Binu', 20000)<br /><br />INSERT INTO #Client(ClientName, AnnualIncome)<br />VALUES('Shino', 3000)<br /><br />--SELECT * FROM #Client<br /><br />DECLARE @intVar int<br />SET @intVar = 0<br />SELECT AnnualIncome/@intVar AS Something<br />FROM #Client<br /><br />IF @@ERROR <> 0<br />SELECT @@ERROR<br />ELSE<br />SELECT @@ROWCOUNT<br /><br />DROP TABLE #Client<br /></span><br />In both the cases, the Error handling section will not work as expected, because both @@ERROR and @@ROWCOUNT return the status of the last statement executed.Roji. P. Thomashttp://www.blogger.com/profile/07837495547296394554noreply@blogger.com2tag:blogger.com,1999:blog-7354546.post-1122552572013402282005-07-31T14:38:00.000+05:302005-08-23T15:59:56.820+05:30SQL Server 2005 Notes<a href="http://toponewithties.blogspot.com/2005/07/top-enhancements.html">TOP Enhancements</a><br /><a href="http://toponewithties.blogspot.com/2005/07/structured-error-handling.html">Structured error Handling</a><br /><a href="http://toponewithties.blogspot.com/2005/07/except-and-intersect.html">Except And Intersect</a><br /><a href="http://toponewithties.blogspot.com/2005/07/how-many-times-you-want-to-go-today.html"><strike>Where</strike> How many Times you want to GO Today..! </a><br /><a href="http://toponewithties.blogspot.com/2005/07/some-any-all-are-not-new-features.html">SOME, ANY, ALL are NOT new Features</a><br /><a href="http://toponewithties.blogspot.com/2005/07/adventureworks.html">AdventureWorks OLTP Database</a><br /><a href="http://toponewithties.blogspot.com/2005/07/ranking-and-windowing-functions.html">Ranking and Windowing Functions</a><br /><a href="http://toponewithties.blogspot.com/2005/08/dml-with-output.html">DML With OUTPUT</a><br /><a href="http://toponewithties.blogspot.com/2005/08/new-apply-operator.html">New APPLY Operator</a><br /><a href="http://toponewithties.blogspot.com/2005/08/max-sized-datatypes.html">MAX Sized datatypes</a><br /><a href="http://toponewithties.blogspot.com/2005/08/sampling-using-tablesample.html">Sampling Using TABLESAMPLE</a>Roji. P. Thomashttp://www.blogger.com/profile/07837495547296394554noreply@blogger.com1tag:blogger.com,1999:blog-7354546.post-1122552364364464792005-07-31T14:00:00.000+05:302006-03-30T14:48:59.676+05:30SQL Articles<a href="http://toponewithties.blogspot.com/2006/02/bug-combination-of-group-by-with.html">BUG: Combination of GROUP BY with HAVING Clause and LEFT OUTER JOIN with Derived Table With LEFT Function Produces Incorrect Result</a><br /><br /><a href="http://toponewithties.blogspot.com/2006/02/named-constraints-on-temporary-tables.html">Named Constraints on Temporary tables</a><br /><br /><a href="http://toponewithties.blogspot.com/2005/08/common-top-10-t-sql-programming.html">Common (TOP 10?) T-SQL Programming mistakes</a><br /><br /><a href="http://toponewithties.blogspot.com/2005/03/path-enumeration-using-prime-number.html">Path Enumeration Using Prime Number Products</a><br /><br /><a href="http://toponewithties.blogspot.com/2004/08/difference-between-table-variable-and.html">Difference between Table Variable and Temporary Table</a><br /><br /><a href="http://toponewithties.blogspot.com/2004/08/difference-between-set-and-select.html">Difference Between SET and SELECT</a><br /><br /><a href="http://toponewithties.blogspot.com/2004/08/let-us-count-them.html">Let Us Count them!</a><br /><br /><a href="http://toponewithties.blogspot.com/2004/08/differences-between-coalesce-and.html">Differences between COALESCE and ISNULL</a><br /><br /><a href="http://toponewithties.blogspot.com/2004/06/fancy-scoping.html">Fancy Scoping</a>Roji. P. Thomashttp://www.blogger.com/profile/07837495547296394554noreply@blogger.com2tag:blogger.com,1999:blog-7354546.post-1122794960665397522005-07-31T12:59:00.000+05:302005-07-31T13:44:11.236+05:30Ranking and Windowing Functions<br />SQL Server 2005 ships with four functions exclusively for ranking and windowing operations. These functions will make the life of tthe T-SQL programmer easy. The following are the four functions.<br /><br />1. ROW_NUMBER()<br /><br />This function allows you to provide a pseudo column containing sequential integer values to result rows of a query, which makes many tasks like paging easy. Before 2005, to get the same result, you had to write complex queries like,<br /><br /><font face="'Courier New',Courier,monospace" color="#800000"><br />use pubs<br />GO<br /><br />Select job_desc, (Select Count(*) + 1 FROM jobs B<br />WHERE B.job_desc < A.job_desc) AS RecNo<br />FROM jobs A<br />ORDER By job_desc<br /><br /></font><br /><br />Lets see how easy its in 2005<br /><br /><font face="'Courier New',Courier,monospace" color="#800000"><br />CREATE TABLE #Students(StudentName VARCHAR(10), subject VARCHAR(10), Mark int)<br />INSERT INTO #Students VALUES('Jaison','.Net',90)<br />INSERT INTO #Students VALUES('Senthil','.Net',70)<br />INSERT INTO #Students VALUES('Joe','.Net',50)<br />INSERT INTO #Students VALUES('Jaison','SQL',70)<br />INSERT INTO #Students VALUES('Senthil','SQL',90)<br />INSERT INTO #Students VALUES('Joe','SQL',80)<br /><br />--SELECT * FROM #Students<br /><br />SELECT ROW_NUMBER() OVER (ORDER BY Mark DESC) as rownum, * FROM #Students<br /></font><br /><br />And here is the output.<br />rownum StudentName subject Mark<br />-------------------- ----------- ---------- -----------<br />1 Jaison .Net 90<br />2 Senthil SQL 90<br />3 Joe SQL 80<br />4 Senthil .Net 70<br />5 Jaison SQL 70<br />6 Joe .Net 50<br /><br /><br />Note that in the above example we have 2 ties, for marks 90 and 70. The ROW_NUMBER() break ties and assign a unique integer number to each row. If the column specified in the ORDER BY clause is not unique, then the ROW_NUMBER() function is not deterministic. That is the assignment of sequential values for tied rows may vary from execution to execution.<br /><br />2. RANK()<br />The Rank function is similiar to ROW_NUMBER function in the sense that Rank() also produces a ranking column. But the difference is that the Rank function does not break ties. It assigns the same value to the tied rows. Lets see Rank() in action.<br /><br /><font face="'Courier New',Courier,monospace" color="#800000"><br />SELECT RANK() OVER (ORDER BY Mark DESC) as rank, * FROM #Students<br /></font><br /><br />rank StudentName subject Mark<br />-------------------- ----------- ---------- -----------<br />1 Jaison .Net 90<br />1 Senthil SQL 90<br />3 Joe SQL 80<br />3 Ann SQL 80<br />5 Senthil .Net 70<br />5 Jaison SQL 70<br />7 Joe .Net 50<br /><br />Noe that the result has ties and gaps.<br /><br />3. DENSE_RANK()<br />The DENSE_RANK() function is same as RANK() except that it doesnt leave gaps.<br /><br /><font face="'Courier New',Courier,monospace" color="#800000"><br />SELECT DENSE_RANK() OVER (ORDER BY Mark DESC) as rank, * FROM #Students<br /></font><br /><br />rank StudentName subject Mark<br />-------------------- ----------- ---------- -----------<br />1 Jaison .Net 90<br />1 Senthil SQL 90<br />2 Joe SQL 80<br />2 Ann SQL 80<br />3 Senthil .Net 70<br />3 Jaison SQL 70<br />4 Joe .Net 50<br /><br />4.NTILE()<br /><br />NTile(n) will evenly divide all the results into approximately even pieces based on the input argument, and assign each piece by the same number in the result set. For eg, if you want to assign grades to students based on their mark you can use NTILE function like<br /><br /><font face="'Courier New',Courier,monospace" color="#800000"><br />SELECT NTILE(2) OVER (ORDER BY Mark DESC) as grade, * FROM #Students<br /></font><br /><br />grade StudentName subject Mark<br />-------------------- ----------- ---------- -----------<br />1 Jaison .Net 90<br />1 Senthil SQL 90<br />1 Joe SQL 80<br />1 Ann SQL 80<br />2 Senthil .Net 70<br />2 Jaison SQL 70<br />2 Joe .Net 50<br /><br /><br /><b>PARTITION BY</b><br />Ranking functions can be used for windowing with the PARTITION BY Clause. PARTITION BY clause lets you apply ranking functions for subgroups. For eg. if you want to get the ranks in the Subject Level, you canget that by using the PARTITION BY clause along with the ORDER BY Clause.<br /><br /><font face="'Courier New',Courier,monospace" color="#800000"><br />SELECT RANK() OVER (PARTITION BY subject ORDER BY Mark DESC) as rank, * FROM #Students<br /></font><br /><br />Here is the result.<br /><br />rank StudentName subject Mark<br />-------------------- ----------- ---------- -----------<br />1 Jaison .Net 90<br />2 Senthil .Net 70<br />3 Joe .Net 50<br />1 Senthil SQL 90<br />2 Joe SQL 80<br />2 Ann SQL 80<br />4 Jaison SQL 70<br /><br />You can apply the PARTITION BY Clause on all the ranking functions.<br /><br /><font face="'Courier New',Courier,monospace" color="#800000"><br />SELECT <br /> ROW_NUMBER() OVER (PARTITION BY subject ORDER BY Mark DESC) as rownum,<br /> RANK() OVER (PARTITION BY subject ORDER BY Mark DESC) as rank, <br /> DENSE_RANK() OVER (PARTITION BY subject ORDER BY Mark DESC) as dese_rank,<br /> NTILE(3) OVER (PARTITION BY subject ORDER BY Mark DESC) as grade, * FROM #Students<br /><br /></font><br /><br />Here is the result.<br /><br />rownum rank dese_rank grade StudentName subject Mark<br />-------------------- -------------------- -------------------- -------------------- ----------- ---------- -----------<br />1 1 1 1 Jaison .Net 90<br />2 2 2 2 Senthil .Net 70<br />3 3 3 3 Joe .Net 50<br />1 1 1 1 Senthil SQL 90<br />2 2 2 1 Joe SQL 80<br />3 2 2 2 Ann SQL 80<br />4 4 3 3 Jaison SQL 70<br /><br /><br />For more info on ranking functions, see these articles(<a href="http://www.windowsitpro.com/Windows/Articles/ArticleID/42302/pg/2/2.html" target="_blank">1</a>, <a href="http://www.windowsitpro.com/SQLServer/Articles/ArticleID/42646/pg/2/2.html" target="_blank">2</a>) by SQL MVP Itzik Ben Gan.<br /><br /><br /><br />Roji. P. Thomashttp://www.blogger.com/profile/07837495547296394554noreply@blogger.com1