Academia.eduAcademia.edu

T-SQL Query tuning

2019, Microsoft

The Microsoft SQL Server community is filled with truly amazing, smart people. They take pride in sharing their great knowledge with others, all for making the community stronger. Anyone in the world can jump on Twitter and ask any question to #sqlhelp, and within seconds one of the smartest experts in the world will be responding. If you are looking for expertise in performance, storage, query optimization, large-scale design, modeling, or any data-related topic, these experts are in the community today sharing their expertise. You will get to know them not only by their expertise but by their unique, friendly personalities as well. We in the SQL Server community world refer to this as our SQL family.

T-SQL Querying Itzik Ben-Gan Dejan Sarka Adam Machanic Kevin Farlee PUBLISHED BY Microsoft Press A Division of Microsoft Corporation One Microsoft Way Redmond, Washington 98052-6399 Copyright © 2015 by Itzik Ben-Gan, Dejan Sarka, Adam Machanic, and Kevin Farlee. All rights reserved. No part of the contents of this book may be reproduced or transmitted in any form or by any means without the written permission of the publisher. Library of Congress Control Number: 2014951866 ISBN: 978-0-7356-8504-8 Printed and bound in the United States of America. First Printing Microsoft Press books are available through booksellers and distributors worldwide. If you need support related to this book, email Microsoft Press Support at mspinput@microsoft.com. Please tell us what you think of this book at http://aka.ms/tellpress. This book is provided “as-is” and expresses the authors’ views and opinions. The views, opinions, and information expressed in this book, including URL and other Internet website references, may change without notice. Some examples depicted herein are provided for illustration only and are fictitious. No real association or connection is intended or should be inferred. Microsoft and the trademarks listed at http://www.microsoft.com on the “Trademarks” webpage are trademarks of the Microsoft group of companies. All other marks are the property of their respective owners. Acquisitions Editor: Devon Musgrave Developmental Editor: Devon Musgrave Project Editor: Carol Dillingham Editorial Production: Curtis Philips, Publishing.com Technical Reviewer: Alejandro Mesa; Technical Review services provided by Content Master, a member of CM Group, Ltd. Copyeditor: Roger LeBlanc Proofreader: Andrea Fox Indexer: William P. Meyers Cover: Twist Creative • Seattle and Joel Panchot To Lilach, for giving meaning to everything that I do. —Itzik Contents at a glance Foreword Introduction xv xvii CHAPTER 1 Logical query processing 1 CHAPTER 2 Query tuning CHAPTER 3 Multi-table queries 187 CHAPTER 4 Grouping, pivoting, and windowing 259 CHAPTER 5 TOP and OFFSET-FETCH 341 CHAPTER 6 Data modification 373 CHAPTER 7 Working with date and time 419 CHAPTER 8 T-SQL for BI practitioners 473 CHAPTER 9 Programmable objects 525 CHAPTER 10 In-Memory OLTP 671 CHAPTER 11 Graphs and recursive queries 707 Index 803 41 Contents Foreword. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xv Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xvii Chapter 1 Logical query processing 1 Logical query-processing phases. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 Logical query-processing phases in brief . . . . . . . . . . . . . . . . . . . . . . . 4 Sample query based on customers/orders scenario. . . . . . . . . . . . . . . . . . . . 6 Logical query-processing phase details. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 Step 1: The FROM phase. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 Step 2: The WHERE phase. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 Step 3: The GROUP BY phase. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 Step 4: The HAVING phase. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 Step 5: The SELECT phase. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 Step 6: The ORDER BY phase. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 Step 7: Apply the TOP or OFFSET-FETCH filter. . . . . . . . . . . . . . . . . . 22 Further aspects of logical query processing . . . . . . . . . . . . . . . . . . . . . . . . . 26 Table operators. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 Window functions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 The UNION, EXCEPT, and INTERSECT operators . . . . . . . . . . . . . . . . 38 Conclusion. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 Chapter 2 Query tuning 41 Internals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 Pages and extents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 Table organization. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 Tools to measure query performance. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 What do you think of this book? We want to hear from you! Microsoft is interested in hearing your feedback so we can continually improve our books and learning resources for you. To participate in a brief online survey, please visit: microsoft.com/learning/booksurvey v Access methods. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 Table scan/unordered clustered index scan . . . . . . . . . . . . . . . . . . . . 57 Unordered covering nonclustered index scan. . . . . . . . . . . . . . . . . . 60 Ordered clustered index scan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 Ordered covering nonclustered index scan . . . . . . . . . . . . . . . . . . . . 63 The storage engine’s treatment of scans. . . . . . . . . . . . . . . . . . . . . . . 65 Nonclustered index seek + range scan + lookups. . . . . . . . . . . . . . . 81 Unordered nonclustered index scan + lookups. . . . . . . . . . . . . . . . . 91 Clustered index seek + range scan. . . . . . . . . . . . . . . . . . . . . . . . . . . . 93 Covering nonclustered index seek + range scan. . . . . . . . . . . . . . . . 94 Cardinality estimates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 Legacy estimator vs. 2014 cardinality estimator. . . . . . . . . . . . . . . . . 98 Implications of underestimations and overestimations . . . . . . . . . . 99 Statistics. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101 Estimates for multiple predicates. . . . . . . . . . . . . . . . . . . . . . . . . . . . 104 Ascending key problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107 Unknowns. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110 Indexing features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115 Descending indexes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115 Included non-key columns. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 Filtered indexes and statistics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120 Columnstore indexes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123 Inline index definition. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130 Prioritizing queries for tuning with extended events. . . . . . . . . . . . . . . . . 131 Index and query information and statistics. . . . . . . . . . . . . . . . . . . . . . . . . 134 Temporary objects. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139 Set-based vs. iterative solutions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149 Query tuning with query revisions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153 Parallel query execution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158 How intraquery parallelism works. . . . . . . . . . . . . . . . . . . . . . . . . . . . 158 Parallelism and query optimization . . . . . . . . . . . . . . . . . . . . . . . . . . 175 The parallel APPLY query pattern. . . . . . . . . . . . . . . . . . . . . . . . . . . . 181 Conclusion. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186 vi Contents Chapter 3 Multi-table queries 187 Subqueries. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187 Self-contained subqueries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187 Correlated subqueries. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189 The EXISTS predicate. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194 Misbehaving subqueries. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201 Table expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204 Derived tables. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205 CTEs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207 Views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211 Inline table-valued functions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215 Generating numbers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215 The APPLY operator. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218 The CROSS APPLY operator. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219 The OUTER APPLY operator. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221 Implicit APPLY. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221 Reuse of column aliases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222 Joins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .224 Cross join. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224 Inner join. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228 Outer join . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229 Self join . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230 Equi and non-equi joins. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230 Multi-join queries. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231 Semi and anti semi joins. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237 Join algorithms. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239 Separating elements. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245 The UNION, EXCEPT, and INTERSECT operators. . . . . . . . . . . . . . . . . . . . . 249 The UNION ALL and UNION operators. . . . . . . . . . . . . . . . . . . . . . . 250 The INTERSECT operator. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253 The EXCEPT operator. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255 Conclusion. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257 Contents vii Chapter 4 Grouping, pivoting, and windowing 259 Window functions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259 Aggregate window functions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260 Ranking window functions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281 Offset window functions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285 Statistical window functions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288 Gaps and islands. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291 Pivoting. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299 One-to-one pivot. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300 Many-to-one pivot. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .304 Unpivoting. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307 Unpivoting with CROSS JOIN and VALUES. . . . . . . . . . . . . . . . . . . . 308 Unpivoting with CROSS APPLY and VALUES. . . . . . . . . . . . . . . . . . . 310 Using the UNPIVOT operator. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 312 Custom aggregations. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313 Using a cursor. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314 Using pivoting. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315 Specialized solutions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316 Grouping sets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327 GROUPING SETS subclause . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328 CUBE and ROLLUP clauses. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331 Grouping sets algebra. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333 Materializing grouping sets. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334 Sorting. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337 Conclusion. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339 Chapter 5 TOP and OFFSET-FETCH 341 The TOP and OFFSET-FETCH filters. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341 The TOP filter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341 The OFFSET-FETCH filter. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345 Optimization of filters demonstrated through paging . . . . . . . . . . . . . . . 346 Optimization of TOP. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346 viii Contents Optimization of OFFSET-FETCH. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 354 Optimization of ROW_NUMBER. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358 Using the TOP option with modifications. . . . . . . . . . . . . . . . . . . . . . . . . . . 360 TOP with modifications. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360 Modifying in chunks. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361 Top N per group. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363 Solution using ROW_NUMBER. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364 Solution using TOP and APPLY . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 365 Solution using concatenation (a carry-along sort). . . . . . . . . . . . . . 366 Median. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368 Solution using PERCENTILE_CONT. . . . . . . . . . . . . . . . . . . . . . . . . . . 369 Solution using ROW_NUMBER. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 369 Solution using OFFSET-FETCH and APPLY. . . . . . . . . . . . . . . . . . . . . 370 Conclusion. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 371 Chapter 6 Data modification 373 Inserting data. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373 SELECT INTO. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373 Bulk import. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376 Measuring the amount of logging. . . . . . . . . . . . . . . . . . . . . . . . . . . 377 BULK rowset provider. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378 Sequences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 381 Characteristics and inflexibilities of the identity property. . . . . . . 381 The sequence object. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 382 Performance considerations. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .387 Summarizing the comparison of identity with sequence. . . . . . . . 394 Deleting data. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395 TRUNCATE TABLE. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395 Deleting duplicates. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399 Updating data. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401 Update using table expressions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 402 Update using variables. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403 Contents ix Merging data. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 404 MERGE examples. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 405 Preventing MERGE conflicts. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 408 ON isn't a filter. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 409 USING is similar to FROM. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 410 The OUTPUT clause . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 411 Example with INSERT and identity. . . . . . . . . . . . . . . . . . . . . . . . . . . 412 Example for archiving deleted data. . . . . . . . . . . . . . . . . . . . . . . . . . 413 Example with the MERGE statement . . . . . . . . . . . . . . . . . . . . . . . . . 414 Composable DML. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 417 Conclusion. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 417 Chapter 7 Working with date and time 419 Date and time data types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 419 Date and time functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 422 Challenges working with date and time. . . . . . . . . . . . . . . . . . . . . . . . . . . . 434 Literals. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 434 Identifying weekdays. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 436 Handling date-only or time-only data with DATETIME and SMALLDATETIME. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 439 First, last, previous, and next date calculations . . . . . . . . . . . . . . . . 440 Search argument . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 445 Rounding issues. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 447 Querying date and time data. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 449 Grouping by the week. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 449 Intervals. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 450 Conclusion. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 471 Chapter 8 T-SQL for BI practitioners 473 Data preparation. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 473 Sales analysis view. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 474 Frequencies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 476 Frequencies without window functions. . . . . . . . . . . . . . . . . . . . . . . 476 x Contents Frequencies with window functions. . . . . . . . . . . . . . . . . . . . . . . . . . 477 Descriptive statistics for continuous variables. . . . . . . . . . . . . . . . . . . . . . . 479 Centers of a distribution. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 479 Spread of a distribution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 482 Higher population moments. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 487 Linear dependencies. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 495 Two continuous variables. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 495 Contingency tables and chi-squared. . . . . . . . . . . . . . . . . . . . . . . . . 501 Analysis of variance. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 505 Definite integration. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 509 Moving averages and entropy. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 512 Moving averages. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 512 Entropy. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 518 Conclusion. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 522 Chapter 9 Programmable objects 525 Dynamic SQL. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 525 Using the EXEC command . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 525 Using the sp_executesql procedure. . . . . . . . . . . . . . . . . . . . . . . . . . . 529 Dynamic pivot. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 530 Dynamic search conditions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .535 Dynamic sorting. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 542 User-defined functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 546 Scalar UDFs. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 546 Multistatement TVFs. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 550 Stored procedures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 553 Compilations, recompilations, and reuse of execution plans. . . . . 554 Table type and table-valued parameters. . . . . . . . . . . . . . . . . . . . . . 571 EXECUTE WITH RESULT SETS. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 573 Triggers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 575 Trigger types and uses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 575 Efficient trigger programming. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 581 Contents xi SQLCLR programming. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 585 SQLCLR architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 586 CLR scalar functions and creating your first assembly . . . . . . . . . . 588 Streaming table-valued functions. . . . . . . . . . . . . . . . . . . . . . . . . . . . 597 SQLCLR stored procedures and triggers. . . . . . . . . . . . . . . . . . . . . . 605 SQLCLR user-defined types. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 617 SQLCLR user-defined aggregates. . . . . . . . . . . . . . . . . . . . . . . . . . . . 628 Transaction and concurrency. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 632 Transactions described. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 633 Locks and blocking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 636 Lock escalation. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 641 Delayed durability. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 643 Isolation levels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 645 Deadlocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 657 Error handling. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 662 The TRY-CATCH construct . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 662 Errors in transactions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 666 Retry logic. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 669 Conclusion. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 670 Chapter 10 In-Memory OLTP 671 In-Memory OLTP overview. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 671 Data is always in memory. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 672 Native compilation. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 673 Lock and latch-free architecture. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 673 SQL Server integration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 674 Creating memory-optimized tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 675 Creating indexes in memory-optimized tables. . . . . . . . . . . . . . . . . . . . . . 676 Clustered vs. nonclustered indexes. . . . . . . . . . . . . . . . . . . . . . . . . . . 677 Nonclustered indexes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 677 Hash indexes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 680 xii Contents Execution environments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 690 Query interop. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 690 Natively compiled procedures. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 699 Surface-area restrictions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 703 Table DDL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 703 DML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 704 Conclusion. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 705 Chapter 11 Graphs and recursive queries 707 Terminology. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 707 Graphs. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 707 Trees. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 708 Hierarchies. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 709 Scenarios. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 709 Employee organizational chart. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 709 Bill of materials (BOM) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 711 Road system. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 715 Iteration/recursion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 718 Subgraph/descendants. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 719 Ancestors/path. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 730 Subgraph/descendants with path enumeration. . . . . . . . . . . . . . . . 733 Sorting. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 736 Cycles. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 740 Materialized path. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 742 Maintaining data. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 743 Querying. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 749 Materialized path with the HIERARCHYID data type. . . . . . . . . . . . . . . . . 754 Maintaining data. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 756 Querying. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 763 Further aspects of working with HIERARCHYID. . . . . . . . . . . . . . . . 767 Nested sets. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 778 Assigning left and right values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 778 Querying. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 784 Contents xiii Transitive closure. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 787 Directed acyclic graph. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 787 Conclusion. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 801 Index 803 What do you think of this book? We want to hear from you! Microsoft is interested in hearing your feedback so we can continually improve our books and learning resources for you. To participate in a brief online survey, please visit: microsoft.com/learning/booksurvey xiv Contents Foreword I have been with Microsoft and working with the Microsoft SQL Server team since 1993. It has been one heck of a ride to watch this product mature into what it is today. It has been exciting to watch how the Microsoft SQL Server customer base uses SQL Server to run their mission-critical businesses. Most of all, it has been an honor to support the most vibrant, passionate technology community I have ever seen. The Microsoft SQL Server community is filled with truly amazing, smart people. They take pride in sharing their great knowledge with others, all for making the community stronger. Anyone in the world can jump on Twitter and ask any question to #sqlhelp, and within seconds one of the smartest experts in the world will be responding. If you are looking for expertise in performance, storage, query optimization, large-scale design, modeling, or any data-related topic, these experts are in the community today sharing their expertise. You will get to know them not only by their expertise but by their unique, friendly personalities as well. We in the SQL Server community world refer to this as our SQL family. Everyone in the community knows the major contributors by their expertise in particular areas. If you ask who the best database performance expert is, people in the community will give you the same four or five names. If you ask for the best storage expert, again people will give you the same four or five storage expert names. You’ll always find a few experts in the community who are the very best for a specific area of database domain expertise. There is only one exception to this that I am aware of, and that is the T-SQL language. There are a lot of talented T-SQL experts, but if you ask for the best everyone will give you one name: Itzik Ben-Gan. Itzik asked me to write this foreword for his new book, and I am honored to do so. His previous books—Inside Microsoft SQL Server: T-SQL Querying (Microsoft Press, 2009), Inside Microsoft SQL Server: T-SQL Programming (Microsoft Press, 2009), and Microsoft SQL Server High-Performance T-SQL Using Window Functions (Microsoft Press, 2012)—are sitting on the shelves of every DBA I know. These books add up to over 2,000 pages of top-notch technical knowledge about Microsoft SQL Server T-SQL, and they set the standard for high-quality database content. I am excited about this new book, T-SQL Querying. Not only does it combine material from his three previous books, but it also adds material from SQL Server 2012 and 2014, including window functions, the new cardinality estimator, sequences, columnstore, In-Memory OLTP, and much more. Itzik has a few exciting co-authors as well: Kevin Farlee, Adam Machanic, and Dejan Sarka. Kevin is part of the Microsoft SQL xv Server engineering team and someone I have been working with for many years. Adam is one of those few names that I refer to above as one of the best database performance experts in the world, and Dejan is well known for his BI and data-modeling expertise. I fully expect this book to be the standard T-SQL guide for the Microsoft SQL Server community. Mark Souza General Manager, Cloud and Enterprise Engineering Microsoft xvi Foreword Introduction U pdating both Inside Microsoft SQL Server 2008: T-SQL Querying (Microsoft Press, 2009) and parts of Inside Microsoft SQL Server 2008: T-SQL Programming (Microsoft Press, 2009), this book gives database developers and administrators a detailed look at the internal architecture of T-SQL and a comprehensive programming reference. It includes coverage of SQL Server 2012 and 2014, but in many cases deals with areas that are not version-specific and will likely be relevant in future versions of SQL Server. Tackle the toughest set-based querying and query-tuning problems—guided by an author team with in-depth, inside knowledge of T-SQL. Deepen your understanding of architecture and internals—and learn practical approaches and advanced techniques to optimize your code’s performance. This book covers many unique techniques that were developed, improved, and polished by the authors over their many years of experience, providing highly efficient solutions for common challenges. There’s a deep focus on the performance and efficiency of the techniques and solutions covered. The book also emphasizes the need to have a correct understanding of the language and its underlying mathematical foundations. Who should read this book This book is designed to help experienced T-SQL practitioners become more knowledgeable and efficient in this field. The book’s target audience is T-SQL developers, DBAs, BI pros, data scientists, and anyone who is serious about T-SQL. Its main purpose is to prepare you for real-life needs, as far as T-SQL is concerned. Its main focus is not to help you pass certification exams. That said, it just so happens that the book covers many of the topics that exams 70-461 and 70-464 test you on. So, even though you shouldn’t consider this book as the only learning tool to prepare for these exams, it is certainly a tool that will help you in this process. Assumptions This book assumes that you have at least a year of solid experience working with SQL Server, writing and tuning T-SQL code. It assumes that you have a good grasp of T-SQL coding and tuning fundamentals, and that you are ready to tackle more advanced challenges. This book could still be relevant to you if you have similar experience with a different database platform and its dialect of SQL, but actual knowledge and experience with SQL Server and T-SQL is preferred. xvii This book might not be for you if… This book might not be for you if you are fairly new to databases and SQL. Organization of this book The book starts with two chapters that lay the foundation of logical and physical query processing required to gain the most from the rest of the chapters. The first chapter covers logical query processing. It describes in detail the logical phases involved in processing queries, the unique aspects of SQL querying, and the special mindset you need to adopt to program in a relational, set-oriented environment. The second chapter covers query tuning and the physical layer. It describes internal data structures, tools to measure query performance, access methods, cardinality estimates, indexing features, prioritizing queries with extended events, columnstore technology, use of temporary tables and table variables, sets versus cursors, query tuning with query revisions, and parallel query execution. (The part about parallel query execution was written by Adam Machanic.) The next five chapters deal with various data manipulation–related topics. The coverage of these topics is extensive; beyond explaining the features, they focus a lot on the performance of the code and the use of the features to solve common tasks. Chapter 3 covers multi-table queries using subqueries, the APPLY operator, joins, and the UNION, INTERSECT, and EXCEPT relational operators. Chapter 4 covers data analysis using grouping, pivoting, and window functions. Chapter 5 covers the TOP and OFFSETFETCH filters, and solving top N per group tasks. Chapter 6 covers data-modification topics like minimally logged operations, using the sequence object efficiently, merging data, and the OUTPUT clause. Chapter 7 covers date and time treatment, including the handling of date and time intervals. Chapter 8 covers T-SQL for BI practitioners and was written by Dejan Sarka. It describes how to prepare data for analysis and how to use T-SQL to handle statistical data analysis tasks. Those include frequencies, descriptive statistics for continuous variables, linear dependencies, and moving averages and entropy. Chapter 9 covers the programmability constructs that T-SQL supports. Those are dynamic SQL, user-defined functions, stored procedures, triggers, SQLCLR programming (written by Adam Machanic), transactions and concurrency, and error handling. xviii Introduction Previously, these topics were covered in the book Inside Microsoft SQL Server: T-SQL Programming. Chapter 10 covers one of the major improvements in SQL Server 2014—the In-­ Memory OLTP engine. This chapter was written by Microsoft’s Kevin Farlee, who was involved in the actual development of this feature. Chapter 11 covers graphs and recursive queries. It shows how to handle graph structures such as employee hierarchies, bill of materials, and maps in SQL Server using T-SQL. It shows how to implement models such as the enumerated path model (using your own custom solution and using the HIERARCHYID data type) and the nested sets model. It also shows how to use recursive queries to manipulate data in graphs. System requirements You will need the following software to run the code samples in this book: ■■ ■■ Microsoft SQL Server 2014: • Edition: 64-bit Enterprise, Developer, or Evaluation; other editions do not support the In-Memory OLTP and columnstore technologies that are covered in the book. You can download a trial version here: http://www.microsoft. com/sql. • For hardware and software requirements see http://msdn.microsoft.com/ en-us/library/ms143506(v=sql.120).aspx. • In the Feature Selection dialog of the SQL Server 2014 setup program, choose the following components: Database Engine Services, Client Tools Connectivity, Documentation Components, Management Tools – Basic, ­Management Tools – Complete. Microsoft Visual Studio 2013 with Microsoft SQL Server Data Tools (SSDT): • You can find the system requirements and platform compatibility for Visual Studio 2013 here: http://www.visualstudio.com/products/ visual-studio-2013-compatibility-vs. • For information about installing SSDT, see http://msdn.microsoft.com/en-us/ data/tools.aspx. Depending on your Windows configuration, you might require Local Administrator rights to install or configure SQL Server 2014 and Visual Studio 2013. Introduction xix Downloads: Code samples This book contains many code samples. You can download the source code for this book from the authors’ site: http://tsql.solidq.com/books/tq3. The source code is organized in a compressed file named T-SQL Querying YYYYMMDD.zip, where YYYYMMDD stands for the last update date of the content. Follow the instructions in the Readme.txt file that is included in the compressed file to install the code samples. Acknowledgments A number of people contributed to making this book a reality, whether directly or indirectly, and deserve thanks and recognition. It’s certainly possible that I omitted some names unintentionally and I apologize for this ahead of time. To Lilach: you’re the one who makes me want to be good at what I do. Besides being my inspiration in life, you had an active unofficial role in this book. You were the book’s first reader! So many hours we spent reading the text together looking for errors before I sent it to the editors. I have a feeling that you know some things about T-SQL better than people who are professionals in the field. To my parents, Mila and Gabi, and to my siblings, Mickey and Ina, for the constant support and for accepting the fact that I’m away. You experienced so much turbulence in the last few years, and I’m hoping that the coming years will be healthy and happy. To the coauthors of the book, Dejan Sarka, Adam Machanic, and Kevin Farlee. It’s a true privilege to be part of such a knowledgeable and experienced group of people. Each of you are such experts in your areas that I felt that your topics would be best served if covered by you: Dejan with the chapter on T-SQL for BI practitioners, Adam with the sections on parallel query execution and SQL CLR programming, and Kevin with the chapter on In-Memory OLTP. Thanks for taking part in this book. To the technical reviewer of the book, Alejandro Mesa: you read and unofficially reviewed my previous books. You’re so passionate about the topic that I’m glad with this book you took a more official reviewer role. I also want to thank the reviewer of the former edition of the book, Steve Kass: you did such thorough and brilliant work that a lot of it echoes in this one. xx Introduction To Mark Souza: you were there pretty much since the inception of the product, being involved in technical, management, and community-related roles. If anyone feels the heartbeat of the SQL Server community, it is you. We’re all grateful for what you do, and it is a great honor to have you write the foreword. Many thanks to the editors at Microsoft Press. To Devon Musgrave, who played both the acquisitions editor and developmental editor roles: you are the one who made this book a reality and handled all the initial stages. I realize that this book is very likely one of many you were responsible for, and I’d like to thank you for dedicating the time and effort that you did. To Carol Dillingham, the book’s project editor: you spent so many hours on this project, and you coordinated it delightfully. It was a pleasure working with you. Also, thanks to Curtis Philips, the project manager from Publishing.com. It was a complex project, and I’m sure it wasn’t a picnic for you. Also, many thanks to the copyeditor, Roger LeBlanc, who worked on my previous books, and to the proofreader, Andrea Fox. It was a pleasure to work with you guys. To SolidQ, my company for over a decade: it’s gratifying to be part of such a great company that evolved into what it is today. The members of this company are much more than colleagues to me; they are partners, friends, and family. Thanks to Fernando G. Guerrero, Douglas McDowell, Herbert Albert, Dejan Sarka, Gianluca Hotz, Antonio Soto, Jeanne Reeves, Glenn McCoin, Fritz Lechnitz, Eric Van Soldt, Berry Walker, Marilyn Templeton, Joelle Budd, Gwen White, Jan Taylor, Judy Dyess, Alberto Martin, Lorena Jimenez, Ron Talmage, Andy Kelly, Rushabh Mehta, Joe Chang, Mark Tabladillo, Eladio Rincón, Miguel Egea, Alejandro J. Rocchi, Daniel A. Seara, Javier Loria, Paco González, Enrique Catalá, Esther Nolasco Andreu, Rocío Guerrero, Javier Torrenteras, Rubén Garrigós, Victor Vale Diaz, Davide Mauri, Danilo Dominici, Erik Veerman, Jay Hackney, Grega Jerkič, Matija Lah, Richard Waymire, Carl Rabeler, Chris Randall, Tony Rogerson, Christian Rise, Raoul Illyés, Johan Åhlén, Peter Larsson, Paul Turley, Bill Haenlin, Blythe Gietz, Nigel Semmi, Paras Doshi, and so many others. To members of the Microsoft SQL Server development team, past and present: Tobias Ternstrom, Lubor Kollar, Umachandar Jayachandran (UC), Boris Baryshnikov, Conor Cunningham, Kevin Farlee, Marc Friedman, Milan Stojic, Craig Freedman, ­Campbell Fraser, Mark Souza, T. K. Rengarajan, Dave Campbell, César Galindo-Legaria, and I’m sure many others. I know it wasn’t a trivial effort to add support for window functions in SQL Server. Thanks for the great effort, and thanks for all the time you spent meeting with me and responding to my emails, addressing my questions, and answering my requests for clarification. Introduction xxi To members of the SQL Server Pro editorial team, past and present: Megan Keller, Lavon Peters, Michele Crockett, Mike Otey, Jayleen Heft, and I’m sure many others. I’ve been writing for the magazine for over a decade and a half, and I am grateful for the opportunity to share my knowledge with the magazine’s readers. To SQL Server MVPs, past and present: Paul White, Alejandro Mesa, Erland ­Sommarskog, Aaron Bertrand, Tibor Karaszi, Benjamin Nevarez, Simon Sabin, Darren Green, Allan Mitchell, Tony Rogerson, and many others—and to the MVP lead, Simon Tien. This is a great program that I’m grateful and proud to be part of. The level of expertise of this group is amazing, and I’m always excited when we all get to meet, both to share ideas and just to catch up at a personal level over beer. I have to extend special thanks to Paul White. I’ve learned so much from you, and I thoroughly enjoy reading your work. I think it’s safe to say that you’re my favorite author. Who knows, maybe one day we’ll get to work on something together. Finally, to my students: teaching about T-SQL is what drives me. It’s my passion. Thanks for allowing me to fulfill my calling and for all the great questions that make me seek more knowledge. —Cheers, Itzik Errata, updates, & book support We’ve made every effort to ensure the accuracy of this book. If you discover an error, please submit it to us via mspinput@microsoft.com. You can also reach the Microsoft Press Book Support team for other assistance via the same email address. Please note that product support for Microsoft software and hardware is not offered through the previous addresses. For help with Microsoft software or hardware, go to http://support.microsoft.com. Free ebooks from Microsoft Press From technical overviews to in-depth information on special topics, the free ebooks from Microsoft Press cover a wide range of topics. These ebooks are available in PDF, EPUB, and Mobi for Kindle formats, ready for you to download at: http://aka.ms/mspressfree Check back often to see what is new! xxii Introduction We want to hear from you At Microsoft Press, your satisfaction is our top priority, and your feedback our most valuable asset. Please tell us what you think of this book at: http://aka.ms/tellpress We know you’re busy, so we’ve kept it short with just a few questions. Your answers go directly to the editors at Microsoft Press. (No personal information will be requested.) Thanks in advance for your input! Stay in touch Let’s keep the conversation going! We’re on Twitter: http://twitter.com/MicrosoftPress. Introduction xxiii This page intentionally left blank This page intentionally left blank CHAPTER 5 TOP and OFFSET-FETCH C lassic filters in SQL like ON, WHERE, and HAVING are based on predicates. TOP and OFFSETFETCH are filters that are based on a different concept: you indicate order and how many rows to filter based on that order. Many filtering tasks are defined based on order and a required number of rows. It’s certainly good to have language support in T-SQL that allows you to phrase the request in a manner that is similar to the way you think about the task. This chapter starts with the logical design aspects of the filters. It then uses a paging scenario to demonstrate their optimization. The chapter also covers the use of TOP with modification statements. Finally, the chapter demonstrates the use of TOP and OFFSET-FETCH in solving practical problems like top N per group and median. The TOP and OFFSET-FETCH filters You use the TOP and OFFSET-FETCH filters to implement filtering requirements in your queries in an intuitive manner. The TOP filter is a proprietary feature in T-SQL, whereas the OFFSET-FETCH filter is a standard feature. T-SQL started supporting OFFSET-FETCH with Microsoft SQL Server 2012. As of SQL Server 2014, the implementation of OFFSET-FETCH in T-SQL is still missing a couple of standard elements—interestingly, ones that are available with TOP. With the current implementation, each of the filters has capabilities that are not supported by the other. I’ll start by describing the logical design aspects of TOP and then cover those of OFFSET-FETCH. The TOP filter The TOP filter is a commonly used construct in T-SQL. Its popularity probably can be attributed to the fact that its design is so well aligned with the way many filtering requirements are expressed—for example, “Return the three most recent orders.” In this request, the order for the filter is based on orderdate, descending, and the number of rows you want to filter based on this order is 3. You specify the TOP option in the SELECT list with an input value typed as BIGINT indicating how many rows you want to filter. You provide the ordering specification in the classic ORDER BY clause. For example, you use the following query to get the three most recent orders. 341 USE TSQLV3; SELECT TOP (3) orderid, orderdate, custid, empid FROM Sales.Orders ORDER BY orderdate DESC; I got the following output from this query: orderid ----------11077 11076 11075 orderdate ---------2015-05-06 2015-05-06 2015-05-06 custid ----------65 9 68 empid ----------1 4 8 Instead of specifying the number of rows you want to filter, you can use TOP to specify the percent (of the total number of rows in the query result). You do so by providing a value in the range 0 through 100 (typed as FLOAT) and add the PERCENT keyword. For example, in the following query you request to filter one percent of the rows: SELECT TOP (1) PERCENT orderid, orderdate, custid, empid FROM Sales.Orders ORDER BY orderdate DESC; SQL Server rounds up the number of rows computed based on the input percent. For example, the result of 1 percent applied to 830 rows in the Orders table is 8.3. Rounding up this number, you get 9. Here’s the output I got for this query: orderid ----------11074 11075 11076 11077 11070 11071 11072 11073 11067 orderdate ---------2015-05-06 2015-05-06 2015-05-06 2015-05-06 2015-05-05 2015-05-05 2015-05-05 2015-05-05 2015-05-04 custid ----------73 68 9 65 44 46 20 58 17 empid ----------7 8 4 1 2 1 4 2 1 Note that to translate the input percent to a number of rows, SQL Server has to first figure out the count of rows in the query result, and this usually requires extra work. Interestingly, ordering specification is optional for the TOP filter. For example, consider the following query: SELECT TOP (3) orderid, orderdate, custid, empid FROM Sales.Orders; 342 Chapter 5 TOP and OFFSET-FETCH I got the following output from this query: orderid ----------10248 10249 10250 orderdate ---------2013-07-04 2013-07-05 2013-07-08 custid ----------85 79 34 empid ----------5 6 4 The selection of which three rows to return is nondeterministic. This means that if you run the query again, without the underlying data changing, theoretically you could get a different set of three rows. In practice, the row selection will depend on physical conditions like optimization choices, storage engine choices, data layout, and other factors. If you actually run the query multiple times, as long as those physical conditions don’t change, there’s some likelihood you will keep getting the same results. But it is critical to understand the “physical data independence” principle from the relational model, and remember that at the logical level you do not have a guarantee for repeatable results. Without ordering specification, you should consider the order as being arbitrary, resulting in a nondeterministic row selection. Even when you do provide ordering specification, it doesn’t mean that the query is deterministic. For example, an earlier TOP query used orderdate, DESC as the ordering specification. The orderdate column is not unique; therefore, the selection between rows with the same order date is nondeterministic. So what do you do in cases where you must guarantee determinism? There are two options: using WITH TIES or unique ordering. The WITH TIES option causes ties to be included in the result. Here’s how you apply it to our example: SELECT TOP (3) WITH TIES orderid, orderdate, custid, empid FROM Sales.Orders ORDER BY orderdate DESC; Here’s the result I got from this query: orderid ----------11077 11076 11075 11074 orderdate ---------2015-05-06 2015-05-06 2015-05-06 2015-05-06 custid ----------65 9 68 73 empid ----------1 4 8 7 SQL Server filters the three rows with the most recent order dates, plus it includes all other rows that have the same order date as in the last row. As a result, you can get more rows than the number you specified. In this query, you specified you wanted to filter three rows but ended up getting four. What’s interesting to note here is that the row selection is now deterministic, but the presentation order between rows with the same order date is nondeterministic. Chapter 5 TOP and OFFSET-FETCH 343 A quick puzzle What is the following query looking for? (Try to figure this out yourself before looking at the answer.) SELECT TOP (1) WITH TIES orderid, orderdate, custid, empid FROM Sales.Orders ORDER BY ROW_NUMBER() OVER(PARTITION BY custid ORDER BY orderdate DESC, orderid DESC); Answer: This query returns the most recent order for each customer. The second method to guarantee a deterministic result is to make the ordering specification unique by adding a tiebreaker. For example, you could add orderid, DESC as the tiebreaker in our example. This means that, in the case of ties in the order date values, a row with a higher order ID value is preferred to a row with a lower one. Here’s our query with the tiebreaker applied: SELECT TOP (3) orderid, orderdate, custid, empid FROM Sales.Orders ORDER BY orderdate DESC, orderid DESC; This query generates the following output: orderid ----------11077 11076 11075 orderdate ---------2015-05-06 2015-05-06 2015-05-06 custid ----------65 9 68 empid ----------1 4 8 Use of unique ordering makes both the row selection and presentation ordering deterministic. The result set as well as the presentation ordering of the rows are guaranteed to be repeatable so long as the underlying data doesn’t change. If you have a case where you need to filter a certain number of rows but truly don’t care about order, it could be a good idea to specify ORDER BY (SELECT NULL), like so: SELECT TOP (3) orderid, orderdate, custid, empid FROM Sales.Orders ORDER BY (SELECT NULL); This way, you let everyone know your choice of arbitrary order is intentional, which helps to avoid confusion and doubt. As a reminder of what I explained in Chapter 1, “Logical query processing,” about the TOP and OFFSET-FETCH filters, presentation order is guaranteed only if the outer query has an ORDER BY clause. For example, in the following query presentation, ordering is not guaranteed: SELECT orderid, orderdate, custid, empid FROM ( SELECT TOP (3) orderid, orderdate, custid, empid FROM Sales.Orders ORDER BY orderdate DESC, orderid DESC ) AS D; 344 Chapter 5 TOP and OFFSET-FETCH To provide a presentation-ordering guarantee, you must specify an ORDER BY clause in the outer query, like so: SELECT orderid, orderdate, custid, empid FROM ( SELECT TOP (3) orderid, orderdate, custid, empid FROM Sales.Orders ORDER BY orderdate DESC, orderid DESC ) AS D ORDER BY orderdate DESC, orderid DESC; The OFFSET-FETCH filter The OFFSET-FETCH filter is a standard feature designed similar to TOP but with an extra element. You can specify how many rows you want to skip before specifying how many rows you want to filter. As you could have guessed, this feature can be handy in implementing paging solutions—that is, returning a result to the user one chunk at a time upon request when the full result set is too long to fit in one screen or web page. The OFFSET-FETCH filter requires an ORDER BY clause to exist, and it is specified right after it. You start by indicating how many rows to skip in an OFFSET clause, followed by how many rows to filter in a FETCH clause. For example, based on the indicated order, the following query skips the first 50 rows and filters the next 25 rows: SELECT orderid, orderdate, custid, empid FROM Sales.Orders ORDER BY orderdate DESC, orderid DESC OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY; In other words, the query filters rows 51 through 75. In paging terms, assuming a page size of 25 rows, this query returns the third page. To allow natural declarative language, you can use the keyword FIRST instead of NEXT if you like, though the meaning is the same. Using FIRST could be more intuitive if you’re not skipping any rows. Even if you don’t want to skip any rows, T-SQL still makes it mandatory to specify the OFFSET clause (with 0 ROWS) to avoid parsing ambiguity. Similarly, instead of using the plural form of the keyword ROWS, you can use the singular form ROW in both the OFFSET and the FETCH clauses. This is more natural if you need to skip or filter only one row. If you’re curious what the purpose of the keyword ONLY is, it means not to include ties. Standard SQL defines the alternative WITH TIES; however, T-SQL doesn’t support it yet. Similarly, standard SQL defines the PERCENT option, but T-SQL doesn’t support it yet either. These two missing options are available with the TOP filter. Chapter 5 TOP and OFFSET-FETCH 345 As mentioned, the OFFSET-FETCH filter requires an ORDER BY clause. If you want to use arbitrary order, like TOP without an ORDER BY clause, you can use the trick with ORDER BY (SELECT NULL), like so: SELECT orderid, orderdate, custid, empid FROM Sales.Orders ORDER BY (SELECT NULL) OFFSET 0 ROWS FETCH NEXT 3 ROWS ONLY; The FETCH clause is optional. If you want to skip a certain number of rows but not limit how many rows to return, simply don’t indicate a FETCH clause. For example, the following query skips 50 rows but doesn’t limit the number of returned rows: SELECT orderid, orderdate, custid, empid FROM Sales.Orders ORDER BY orderdate DESC, orderid DESC OFFSET 50 ROWS; Concerning presentation ordering, the behavior is the same as with the TOP filter; namely, with OFFSET-FETCH also, presentation ordering is guaranteed only if the outermost query has an ORDER BY clause. Optimization of filters demonstrated through paging So far, I described the logical design aspects of the TOP and OFFSET-FETCH filters. In this section, I’m going to cover optimization aspects. I’ll do so by looking at different paging solutions. I’ll describe two paging solutions using the TOP filter, a solution using the OFFSET-FETCH filter, and a solution using the ROW_NUMBER function. In all cases, regardless of which filtering option you use for your paging solution, an index on the ordering elements is crucial for good performance. Often you will get good performance even when the index is not a covering one. Curiously, sometimes you will get better performance when the index isn’t covering. I’ll provide the details in the specific implementations. I’ll use the Orders table from the PerformanceV3 database in my examples. Suppose you need to implement a paging solution returning one page of orders at a time, based on orderid as the sort key. The table has a nonclustered index called PK_Orders defined with orderid as the key. This index is not a covering one with respect to the paging queries I will demonstrate. Optimization of TOP There are a couple of strategies you can use to implement paging solutions with TOP. One is an anchor-based strategy, and the other is TOP over TOP (nested TOP queries). The anchor-based strategy allows the user to visit adjacent pages progressively. You define a stored procedure that when given the sort key of the last row from the previous page, returns the next page. Here’s an implementation of such a procedure: 346 Chapter 5 TOP and OFFSET-FETCH USE PerformanceV3; IF OBJECT_ID(N'dbo.GetPage', N'P') IS NOT NULL DROP PROC dbo.GetPage; GO CREATE PROC dbo.GetPage @orderid AS INT = 0, -- anchor sort key @pagesize AS BIGINT = 25 AS SELECT TOP (@pagesize) orderid, orderdate, custid, empid FROM dbo.Orders WHERE orderid > @orderid ORDER BY orderid; GO Here I’m assuming that only positive order IDs are supported. Of course, you can implement such an integrity rule with a CHECK constraint. The query uses the WHERE clause to filter only orders with order IDs that are greater than the input anchor sort key. From the remaining rows, using TOP, the query filters the first @pagesize rows based on orderid ordering. Use the following code to request the first page of orders: EXEC dbo.GetPage @pagesize = 25; I got the following result (but yours may vary because of the randomization aspects used in the creation of the sample data): orderid ----------1 2 ... 24 25 orderdate ---------2011-01-01 2011-01-01 custid ----------C0000005758 C0000015925 empid ----------205 251 2011-01-01 C0000003541 316 2011-01-01 C0000005636 256 In this example, the last sort key in the first page is 25. Therefore, to request the second page of orders, you pass 25 as the input anchor sort key, like so: EXEC dbo.GetPage @orderid = 25, @pagesize = 25; Of course, in practice the last sort key in the first page could be different than 25, but in my sample data the keys start with 1 and are sequential. I got the following result when running this code: orderid ----------26 27 28 ... 49 50 orderdate ---------2011-01-01 2011-01-01 2011-01-01 custid ----------C0000017397 C0000012629 C0000016429 empid ----------332 27 53 2011-01-01 C0000015415 95 2010-12-06 C0000008667 117 To ask for the third page of orders, you pass 50 as the input sort key in the next page request: EXEC dbo.GetPage @orderid = 50, @pagesize = 25; Chapter 5 TOP and OFFSET-FETCH 347 I got the following output for the execution of this code: orderid ----------51 52 53 ... 74 75 orderdate ---------2011-01-01 2011-01-01 2011-01-01 custid ----------C0000000797 C0000015945 C0000013558 empid ----------438 47 364 2011-01-01 C0000019720 249 2011-01-01 C0000000807 160 The execution plan for the query is shown in Figure 5-1. I’ll assume the inputs represent the last procedure call with the request for the third page. FIGURE 5-1 Plan for TOP with a single anchor sort key. I’ll describe the execution of this plan based on data flow order (right to left). But keep in mind that the API call order is actually left to right, starting with the root node (SELECT). I’ll explain why that’s important to remember shortly. The Index Seek operator performs a seek in the index PK_Orders to the first leaf row that satisfies the Start property of the Seek Predicates property: orderid > @orderid. In the third execution of the procedure, @orderid is 50. Then the Index Seek operator continues with a range scan in the index leaf based on the seek predicate. Absent a Prefix property of the Seek Predicates property, the range scan normally continues until the tail of the index leaf. However, as mentioned, the internal API call order is done from left to right. The Top operator has a property called Top Expression, which is set in the plan to @pagesize (25, in our case). This property tells the Top operator how many rows to request from the Nested Loops operator to its right. In turn, the Nested Loops operator requests the specified number of rows (25, in our case) from the Index Seek operator to its right. For each row returned from the Index Seek Operator, Nested Loops executes the Key Lookup operator to collect the remaining elements from the respective data row. This means that the range scan doesn’t proceed beyond the 25th row, and this also means that the Key Lookup operator is executed 25 times. 348 Chapter 5 TOP and OFFSET-FETCH Not only is the range scan in the Index Seek operator cut short because of TOP’s row goal (Top Expression property), the query optimizer needs to adjust the costs of the affected operators based on that row goal. This aspect of optimization is described in detail in an excellent article by Paul White: “Inside the Optimizer: Row Goals In Depth.” The article can be found here: http://sqlblog.com/ blogs/paul_white/archive/2010/08/18/inside-the-optimiser-row-goals-in-depth.aspx. The I/O costs involved in the execution of the query plan are made of the following: ■■ Seek to the leaf of index: 3 reads (the index has three levels) ■■ Range scan of 25 rows: 0–1 reads (hundreds of rows fit in a page) ■■ ■■ Nested Loops prefetch used to optimize lookups: 9 reads (measured by disabling prefetch with trace flag 8744) 25 key lookups: 75 reads In total, 87 logical reads were reported for the processing of this query. That’s not too bad. Could things be better or worse? Yes on both counts. You could get better performance by creating a covering index. This way, you eliminate the costs of the prefetch and the lookups, resulting in only 3–4 logical reads in total. You could get much worse performance if you don’t have any good index with the sort column as the leading key—not even a noncovering index. This results in a plan that performs a full scan of the data, plus a sort. That’s a very expensive plan, especially considering that you pay for it for every page request by the user. With a single sort key, the WHERE predicate identifying the start of the qualifying range is straightforward: orderid > @orderid. With multiple sort keys, it gets a bit trickier. For example, suppose that the sort vector is (orderdate, orderid), and you get the anchor sort keys @orderdate and @orderid as inputs to the GetPage procedure. Standard SQL has an elegant solution for this in the form of a feature called row constructor (aka vector expression). Had this feature been implemented in T-SQL, you could have phrased the WHERE predicate as follows: (orderdate, orderid) > (@orderdate, @orderid). This also allows good optimization by using a supporting index on the sort keys similar to the optimization of a single sort key. Sadly, T-SQL doesn’t support such a construct yet. You have two options in terms of how to phrase the predicate. One of them (call it the first predicate form) is the following: orderdate >= @orderdate AND (orderdate > @orderdate OR orderid > @orderid). Another one (call it the second predicate form) looks like this: (orderdate = @orderdate AND orderid > @orderid) OR orderdate > @orderdate. Both are logically equivalent, but they do get handled differently by the query optimizer. In our case, there’s a covering index called idx_od_oid_i_ cid_eid defined on the Orders table with the key list (orderdate, orderid) and the include list (custid, empid). Chapter 5 TOP and OFFSET-FETCH 349 Here’s the implementation of the stored procedure with the first predicate form: IF OBJECT_ID(N'dbo.GetPage', N'P') IS NOT NULL DROP PROC dbo.GetPage; GO CREATE PROC dbo.GetPage @orderdate AS DATE = '00010101', -- anchor sort key 1 (orderdate) @orderid AS INT = 0, -- anchor sort key 2 (orderid) @pagesize AS BIGINT = 25 AS SELECT TOP (@pagesize) orderid, orderdate, custid, empid FROM dbo.Orders WHERE orderdate >= @orderdate AND (orderdate > @orderdate OR orderid > @orderid) ORDER BY orderdate, orderid; GO Run the following code to get the first page: EXEC dbo.GetPage @pagesize = 25; I got the following output from this execution: orderid ----------310 330 90 ... 300 410 orderdate ---------2010-12-03 2010-12-03 2010-12-04 custid ----------C0000014672 C0000009594 C0000012937 empid ----------218 10 231 2010-12-07 C0000019961 282 2010-12-07 C0000001585 342 Run the following code to get the second page: EXEC dbo.GetPage @orderdate = '20101207', @orderid = 410, @pagesize = 25; I got the following output from this execution: orderid ----------1190 1270 1760 ... 2470 2830 orderdate ---------2010-12-07 2010-12-07 2010-12-07 custid ----------C0000004678 C0000015067 C0000009532 empid ----------465 376 104 2010-12-09 C0000008664 205 2010-12-09 C0000010497 221 Run the following code to get the third page: EXEC dbo.GetPage @orderdate = '20101209', @orderid = 2830, @pagesize = 25; I got the following output from this execution: 350 Chapter 5 TOP and OFFSET-FETCH orderid ----------3120 3340 3620 ... 2730 3490 orderdate ---------2010-12-09 2010-12-09 2010-12-09 custid ----------C0000015659 C0000008708 C0000009367 empid ----------381 272 312 2010-12-10 C0000015630 317 2010-12-10 C0000002887 306 As for optimization, Figure 5-2 shows the plan I got for the implementation using the first predicate form. FIGURE 5-2 Plan for TOP with multiple anchor sort keys, first predicate form. Observe that the Start property of the Seek Predicates property is based only on the predicate orderdate >= @orderdate. The residual predicate is orderdate > @orderdate OR orderid > @orderid. Such optimization could result in some unnecessary work scanning the pages holding the first part of the range with the first qualifying order date with the nonqualifying order IDs—in other words, the rows where orderdate = @orderdate AND orderid <= @orderid are going to be scanned even though they need not be returned. How many unnecessary page reads will be performed mainly depends on the density of the leading sort key—orderdate, in our case. The denser it is, the more unnecessary work is likely going to happen. In our case, the density of the orderdate column is very low (~1/1500); it is so low that the extra work is negligible. But, when the leading sort key is dense, you could get a noticeable improvement by using the second form of the predicate. Here’s an implementation of the stored procedure with the second predicate form: IF OBJECT_ID(N'dbo.GetPage', N'P') IS NOT NULL DROP PROC dbo.GetPage; GO CREATE PROC dbo.GetPage @orderdate AS DATE = '00010101', -- anchor sort key 1 (orderdate) @orderid AS INT = 0, -- anchor sort key 2 (orderid) @pagesize AS BIGINT = 25 AS SELECT TOP (@pagesize) orderid, orderdate, custid, empid FROM dbo.Orders WHERE (orderdate = @orderdate AND orderid > @orderid) OR orderdate > @orderdate ORDER BY orderdate, orderid; GO Chapter 5 TOP and OFFSET-FETCH 351 Run the following code to get the third page: EXEC dbo.GetPage @orderdate = '20101209', @orderid = 2830, @pagesize = 25; The query plan for the implementation with the second form of the predicate is shown in Figure 5-3. FIGURE 5-3 Plan for TOP with multiple anchor sort keys, second predicate form. Observe that there’s no residual predicate, only Seek Predicates. Curiously, there are two seek predicates. Remember that, generally, the range scan performed by an Index Seek operator starts with the first match for Prefix and Start and ends with the last match for Prefix. In our case, one ­predicate (marked in the plan as [1] Seek Keys…) starts with orderdate = @orderdate AND orderid > @orderid and ends with orderdate = @orderdate. Another predicate (marked in the plan as [2] Seek Keys…) starts with orderdate = @orderdate and has no explicit end. What’s interesting is that during query execution, if Top Expression rows are found by the first seek, the execution of the operator short-circuits before getting to the second. But if the first seek isn’t sufficient, the second will be executed. The fact that in our example the leading sort key (orderdate) has low density could mislead you to think that the first predicate form is more efficient. If you test both implementations and compare the number of logical reads, you might see the first one performing 3 or more reads and the second one performing 6 or more reads (when two seeks are used). But if you test the solutions with a dense leading sort key, you will notice a significant difference in favor of the second solution. There’s another method to using TOP to implement paging. You can think of it as the TOP over TOP, or nested TOP, method. You work with @pagenum and @pagesize as the inputs to the GetPage procedure. There’s no anchor concept here. You use one query with TOP to filter @pagenum * @­pagesize rows based on the desired order. You define a table expression based on this query (call it D1). You use a second query against D1 with TOP to filter @pagesize rows, but in inverse order. You define a table expression based on the second query (call it D2). Finally, you write an outer query against D2 to order the rows in the desired order. Run the following code to implement the GetPage procedure based on this approach: 352 Chapter 5 TOP and OFFSET-FETCH IF OBJECT_ID(N'dbo.GetPage', N'P') IS NOT NULL DROP PROC dbo.GetPage; GO CREATE PROC dbo.GetPage @pagenum AS BIGINT = 1, @pagesize AS BIGINT = 25 AS SELECT orderid, orderdate, custid, empid FROM ( SELECT TOP (@pagesize) * FROM ( SELECT TOP (@pagenum * @pagesize) * FROM dbo.Orders ORDER BY orderid ) AS D1 ORDER BY orderid DESC ) AS D2 ORDER BY orderid; GO Here are three consecutive calls to the procedure requesting the first, second, and third pages: EXEC dbo.GetPage @pagenum = 1, @pagesize = 25; EXEC dbo.GetPage @pagenum = 2, @pagesize = 25; EXEC dbo.GetPage @pagenum = 3, @pagesize = 25; The plan for the third procedure call is shown in Figure 5-4. FIGURE 5-4 Plan for TOP over TOP. The plan is not very expensive, but there are three aspects to it that are not optimal when compared to the implementation based on the anchor concept. First, the plan scans the data in the index from the beginning of the leaf until the last qualifying row. This means that there’s repetition of work—namely, rescanning portions of the data. For the first page requested by the user, the plan will scan 1 * @pagesize rows, for the second page it will scan 2 * @pagesize rows, for the nth page it will scan n * @pagesize rows. Second, notice that the Key Lookup operator is executed 75 times even though only 25 of the lookups are relevant. Third, there are two Sort operators added to the plan: one reversing the original order to get to the last chunk of rows, and the other reversing it back to the original order to present it like this. For the third page request, the execution of this plan performed 241 logical reads. The greater the number of pages you have, the more work there is. The benefit of this approach compared to the anchor-based strategy is that you don’t need to deal with collecting the anchor from the result of the last page request, and the user is not limited to navigating only between adjacent pages. For example, the user can start with page 1, request page 5, and so on. Chapter 5 TOP and OFFSET-FETCH 353 Optimization of OFFSET-FETCH The optimization of the OFFSET-FETCH filter is similar to that of TOP. Instead of reinventing the wheel by creating an entirely new plan operator, Microsoft decided to enhance the existing Top operator. Remember the Top operator has a property called Top Expression that indicates how many rows to request from the operator to the right and pass to the operator to the left. The enhanced Top operator used to process OFFSET-FETCH has a new property called OffsetExpression that indicates how many rows to request from the operator to the right and not pass to the operator to the left. The ­OffsetExpression property is processed before the Top Expression property, as you might have guessed. To show you the optimization of the OFFSET-FETCH filter, I’ll use it in the implementation of the GetPage procedure: IF OBJECT_ID(N'dbo.GetPage', N'P') IS NOT NULL DROP PROC dbo.GetPage; GO CREATE PROC dbo.GetPage @pagenum AS BIGINT = 1, @pagesize AS BIGINT = 25 AS SELECT orderid, orderdate, custid, empid FROM dbo.Orders ORDER BY orderid OFFSET (@pagenum - 1) * @pagesize ROWS FETCH NEXT @pagesize ROWS ONLY; GO As you can see, OFFSET-FETCH allows a simple and flexible solution that uses the @pagenum and @pagesize inputs. Use the following code to request the first three pages: EXEC dbo.GetPage @pagenum = 1, @pagesize = 25; EXEC dbo.GetPage @pagenum = 2, @pagesize = 25; EXEC dbo.GetPage @pagenum = 3, @pagesize = 25; Note Remember that under the default isolation level Read Committed, data changes between procedure calls can affect the results you get, causing you to get the same row in different pages or skip some rows. For example, suppose that at point in time T1, you request page 1. You get the rows that according to the paging sort order are positioned 1 through 25. Before you request the next page, at point in time T2, someone adds a new row with a sort key that makes it the 20th row. At point in time T3, you request page 2. You get the rows that, in T1, were positioned 25 through 49 and not 26 through 50. This behavior could be awkward. If you want the entire sequence of page requests to interact with the same state of the data, you need to submit all requests from the same transaction running under the snapshot isolation level. 354 Chapter 5 TOP and OFFSET-FETCH The plan for the third execution of the procedure is shown in Figure 5-5. FIGURE 5-5 Plan for OFFSET-FETCH. As you can see in the plan, the Top operator first requests OffsetExpression rows (50, in our example) from the operator to the right and doesn’t pass those to the operator to the left. Then it requests Top Expression rows (25, in our example) from the operator to the right and passes those to the operator to the left. You can see two levels of inefficiency in this plan compared to the plan for the anchor solution. One is that the Index Scan operator ends up scanning 75 rows, but only the last 25 are relevant. This is unavoidable without an input anchor to start after. But the Key Lookup operator is executed 75 times even though, theoretically, the first 50 times could have been avoided. Such logic to avoid applying lookups for the first OffsetExpression rows wasn’t added to the optimizer. The number of logical reads required for the third page request is 241. The farther away the page number you request is, the more lookups the plan applies and the more expensive it is. Arguably, in paging sessions users don’t get too far. If users don’t find what they are looking for after the first few pages, they usually give up and refine their search. In such cases, the extra work is probably negligible enough to not be a concern. However, the farther you get with the page number you’re after, the more the inefficiency increases. For example, run the following code to request page 1000: EXEC dbo.GetPage @pagenum = 1000, @pagesize = 25; This time, the plan involves 25,000 lookups, resulting in a total number of logical reads of 76,644. Unfortunately, because the optimizer doesn’t have logic to avoid the unnecessary lookups, you need to figure this out yourself if it’s important for you to eliminate unnecessary costs. Fortunately, there is a simple trick you can use to achieve this. Have the query with the OFFSET-FETCH filter return only the sort keys. Define a table expression based on this query (call it K, for keys). Then in the outer query, Chapter 5 TOP and OFFSET-FETCH 355 join K with the underlying table to return all the remaining attributes you need. Here’s the optimized implementation of GetPage based on this strategy: ALTER PROC dbo.GetPage @pagenum AS BIGINT = 1, @pagesize AS BIGINT = 25 AS WITH K AS ( SELECT orderid FROM dbo.Orders ORDER BY orderid OFFSET (@pagenum - 1) * @pagesize ROWS FETCH NEXT @pagesize ROWS ONLY ) SELECT O.orderid, O.orderdate, O.custid, O.empid FROM dbo.Orders AS O INNER JOIN K ON O.orderid = K.orderid ORDER BY O.orderid; GO Run the following code to get the third page: EXEC dbo.GetPage @pagenum = 3, @pagesize = 25; You will get the plan shown in Figure 5-6. FIGURE 5-6 Plan for OFFSET-FETCH, minimizing lookups. As you can see, the Top operator is used early in the plan to filter the relevant 25 keys. Then only 25 executions of the Index Seek operator are required, plus 25 executions of the Key Lookup operator (because PK_Orders is not a covering index). The total number of logical reads required for the processing of this plan for the third page request was reduced to 153. This doesn’t seem like a dramatic 356 Chapter 5 TOP and OFFSET-FETCH improvement when compared to the 241 logical reads used in the previous implementation. But try running the procedure with a page that’s farther out, like 1000: EXEC dbo.GetPage @pagenum = 1000, @pagesize = 25; The optimized implementation uses only 223 logical reads compared to the 76,644 used in the previous implementation. That’s a big difference! Curiously, a noncovering index created only on the sort keys, like PK_Orders in our case, can be more efficient for the optimized solution than a covering index. That’s because with shorter rows, more rows fit in a page. So, in cases where you need to skip a substantial number of rows, you get to do so by scanning fewer pages than you would with a covering index. With a noncovering index, you do have the extra cost of the lookups, but the optimized solution reduces the number of lookups to the minimum. OFFSET TO | AFTER As food for thought, if you could change or extend the design of the OFFSET-FETCH filter, what would you do? You might find it useful to support an alternative OFFSET option that is based on an input-anchor sort vector. Imagine syntax such as the following (which shows additions to the standard syntax in bold): OFFSET { <offset row count> { ROW | ROWS } | { TO | AFTER ( <sort vector> ) } } FETCH { FIRST | NEXT } [ <fetch first quantity> ] { ROW | ROWS } { ONLY | WITH TIES } [ LAST ROW INTO ( <variables vector> ) ] You would then use a query such as the following in the GetPage procedure (but don’t try it, because it uses unsupported syntax): SELECT orderid, orderdate, custid, empid FROM dbo.Orders ORDER BY orderdate, orderid OFFSET AFTER (@anchor_orderdate, @anchor_orderid) -- input anchor sort keys FETCH NEXT @pagesize ROWS ONLY LAST ROW INTO (@last_orderdate, @last_orderid); -- outputs for next page request The suggested anchor-based offset has a couple of advantages compared to the existing row count–based offset. The former lends itself to good optimization with an index seek directly to the first matching row in the leaf of a supporting index. Also, by using the former, you can see changes in the data gracefully, unlike with the latter. Chapter 5 TOP and OFFSET-FETCH 357 Optimization of ROW_NUMBER Another common solution for paging is using the ROW_NUMBER function to compute row numbers based on the desired sort and then filtering the right range of row numbers based on the input @pagenum and @pagesize. Here’s the implementation of the GetPage procedure based on this strategy: IF OBJECT_ID(N'dbo.GetPage', N'P') IS NOT NULL DROP PROC dbo.GetPage; GO CREATE PROC dbo.GetPage @pagenum AS BIGINT = 1, @pagesize AS BIGINT = 25 AS WITH C AS ( SELECT orderid, orderdate, custid, empid, ROW_NUMBER() OVER(ORDER BY orderid) AS rn FROM dbo.Orders ) SELECT orderid, orderdate, custid, empid FROM C WHERE rn BETWEEN (@pagenum - 1) * @pagesize + 1 AND @pagenum * @pagesize ORDER BY rn; -- if order by orderid get sort in plan GO Run the following code to request the first three pages: EXEC dbo.GetPage @pagenum = 1, @pagesize = 25; EXEC dbo.GetPage @pagenum = 2, @pagesize = 25; EXEC dbo.GetPage @pagenum = 3, @pagesize = 25; The plan for the third page request is shown in Figure 5-7. FIGURE 5-7 Plan for ROW_NUMBER. Interestingly, the optimization of this solution is similar to that of the solution based on the OFFSET-FETCH filter. You will find the same inefficiencies, including the unnecessary lookups. As a result, the costs are virtually the same. For the third page request, the number of logical reads is 241. Run the procedure again asking for page 1000: EXEC dbo.GetPage @pagenum = 1000, @pagesize = 25; 358 Chapter 5 TOP and OFFSET-FETCH The number of logical reads is now 76,644. You can avoid the unnecessary lookups by applying the same optimization principle used in the improved OFFSET-FETCH solution, like so: ALTER PROC dbo.GetPage @pagenum AS BIGINT = 1, @pagesize AS BIGINT = 25 AS WITH C AS ( SELECT orderid, ROW_NUMBER() OVER(ORDER BY orderid) AS rn FROM dbo.Orders ), K AS ( SELECT orderid, rn FROM C WHERE rn BETWEEN (@pagenum - 1) * @pagesize + 1 AND @pagenum * @pagesize ) SELECT O.orderid, O.orderdate, O.custid, O.empid FROM dbo.Orders AS O INNER JOIN K ON O.orderid = K.orderid ORDER BY K.rn; GO Run the procedure again requesting the third page: EXEC dbo.GetPage @pagenum = 3, @pagesize = 25; The plan for the optimized solution is shown in Figure 5-8. FIGURE 5-8 Plan for ROW_NUMBER, minimizing lookups. Observe that the Top operator filters the first 75 rows, and then the Filter operator filters the last 25, before applying the seeks and the lookups. As a result, the seeks and lookups are executed only 25 times. The execution of the plan for the third page request involves 153 logical reads, compared to 241 required by the previous solution. Run the procedure again, this time requesting page 1000: EXEC dbo.GetPage @pagenum = 1000, @pagesize = 25; Chapter 5 TOP and OFFSET-FETCH 359 This execution requires only 223 logical reads, compared to 76,644 required by the previous solution. Using the TOP option with modifications T-SQL supports using the TOP filter with modification statements. This section first describes this capability, and then its limitation and a workaround for the limitation. Then it describes a practical use case for this capability when you need to delete a large number of rows. In my examples, I’ll use a table called MyOrders. Run the following code to create this table as an initial copy of the Orders table in the PerformanceV3 database: USE PerformanceV3; IF OBJECT_ID(N'dbo.MyOrders', N'U') IS NOT NULL DROP TABLE dbo.MyOrders; GO SELECT * INTO dbo.MyOrders FROM dbo.Orders; CREATE UNIQUE CLUSTERED INDEX idx_od_oid ON dbo.MyOrders(orderdate, orderid); TOP with modifications With T-SQL, you can use TOP with modification statements. Those statements are INSERT TOP, DELETE TOP, UPDATE TOP, and MERGE TOP. This means the statement will stop modifying rows once the requested number of rows are affected. For example, the following statement deletes 50 rows from the table MyOrders: DELETE TOP (50) FROM dbo.MyOrders; When you use TOP in a SELECT statement, you can control which rows get chosen using the ORDER BY clause. But modification statements don’t have an ORDER BY clause. This means you can indicate how many rows you want to modify, but not based on what order—at least, not directly. So the preceding statement deletes 50 rows, but you cannot control which 50 rows get deleted. You should consider the order as being arbitrary. In practice, it depends on optimization and data layout. This limitation is a result of the design choice that the TOP ordering is to be defined by the traditional ORDER BY clause. The traditional ORDER BY clause was originally designed to define presentation order, and it is available only to the SELECT statement. Had the design of TOP been different, with its own ordering specification that is not related to presentation ordering, it would have been natural to use also with modification statements. Here’s an example for what such a design might have looked like (but don’t run the code, because this syntax isn’t supported): DELETE TOP (50) OVER(ORDER BY orderdate, orderid) FROM dbo.MyOrders; Fortunately, when you do need to control which rows get chosen, you can use a simple trick as a workaround. Use a SELECT query with a TOP filter and an ORDER BY clause. Define a table expression based on that query. Then issue the modification against the table expression, like so: 360 Chapter 5 TOP and OFFSET-FETCH WITH C AS ( SELECT TOP (50) * FROM dbo.MyOrders ORDER BY orderdate, orderid ) DELETE FROM C; In practice, the rows from the underlying table will be affected. You can think of the modification as being defined through the table expression. The OFFSET-FETCH filter is not supported directly with modification statements, but you can use a similar trick like the one with TOP. Modifying in chunks Having TOP supported by modification statements without the ability to indicate order might seem futile, but there is a practical use case involving modifying large volumes of data. As an example, suppose you need to delete all rows from the MyOrders table where the order date is before 2013. The table in our example is fairly small, having about 1,000,000 rows. But imagine there were 100,000,000 rows, and the number of rows to delete was about 50,000,000. If the table was partitioned (say, by year), things would be easy and highly efficient. You switch a partition out to a staging table and then drop the staging table. However, what if the table is currently not partitioned? The intuitive thing to do is to issue a simple DELETE statement to do the job as a single transaction, like so (but don’t run this statement): DELETE FROM dbo.MyOrders WHERE orderdate < '20130101'; Such an approach can get you into trouble in a number of ways. A DELETE statement is fully logged, unlike DROP TABLE and TRUNCATE TABLE statements. Log writes are sequential; therefore, log-intensive operations tend to be slow and hard to optimize beyond a certain point. For example, deleting 50,000,000 rows can take many minutes to finish. There’s a section in the log considered to be the active portion, starting with the oldest open transaction and ending with the current pointer in the log. The active portion cannot be recycled. So when you have a long-running transaction, it can cause the transaction log to expand, sometimes well beyond its typical size for your database. This can be an issue if you have limited disk space. Modification statements acquire exclusive locks on the modified resources (row or page locks, as decided by SQL Server dynamically), and exclusive locks are held until the transaction finishes. Each lock is represented by a memory structure that is approximately 100 bytes in size. Acquiring a large number of locks has two main drawbacks. For one, it requires large amounts of memory. Second, it takes time to allocate the memory structures, which adds to the time it takes the transaction to complete. To reduce the memory footprint and allow a faster process, SQL Server will attempt to escalate from the initial granularity of locks (row or page) to a table lock (or partition, if configured). The first trigger for SQL Server to attempt escalation is when the same transaction reaches 5,000 locks against the same object. If unsuccessful (for example, another transaction is holding locks that the escalated Chapter 5 TOP and OFFSET-FETCH 361 lock would be in conflict with), SQL Server will keep trying to escalate every additional 1,250 locks. When escalation succeeds, the transaction locks the entire table (or partition) until it finishes. This behavior can cause concurrency problems. If you try to terminate such a large modification that is in progress, you will face the consequences of a rollback. If the transaction was already running for a while, it will take a while for the rollback to finish—typically, more than the original work. To avoid the aforementioned problems, the recommended approach to apply a large modification is to do it in chunks. For our purge process, you can run a loop that executes a DELETE TOP statement repeatedly until all qualifying rows are deleted. You want to make sure that the chunk size is not too small so that the process will not take forever, but you want it to be small enough not to trigger lock escalation. The tricky part is figuring out the chunk size. It takes 5,000 locks before SQL Server attempts escalation, but how does this translate to the number of rows you’re deleting? SQL Server could decide to use row or page locks initially, plus when you delete rows from a table, SQL Server deletes rows from the indexes that are defined on the table. So it’s hard to predict what the ideal number of rows is without testing. A simple solution could be to test different numbers while running a trace or an extended events session with a lock-escalation event. For example, I ran the following extended events session with a Live Data window open, while issuing DELETE TOP statements from a session with session ID 53: CREATE EVENT SESSION [Lock_Escalation] ON SERVER ADD EVENT sqlserver.lock_escalation( WHERE ([sqlserver].[session_id]=(53))); I started with 10,000 rows using the following statement: DELETE TOP (10000) FROM dbo.MyOrders WHERE orderdate < '20130101'; Then I adjusted the number, increasing or decreasing it depending on whether an escalation event took place or not. In my case, the first point where escalation happened was somewhere between 6,050 and 6,100 rows. Once you find it, you don’t want to use that point minus 1. For example, if you add indexes later on, the point will become lower. To be on the safe side, I take the number that I find in my testing and divide it by two. This should leave enough room for the future addition of indexes. Of course, it’s worthwhile to retest from time to time to see if the number needs to be adjusted. Once you have the chunk size determined (say, 3,000), you implement the purge process as a loop that deletes one chunk of rows at a time using a DELETE TOP statement, like so: SET NOCOUNT ON; WHILE 1 = 1 BEGIN DELETE TOP (3000) FROM dbo.MyOrders WHERE orderdate < '20130101'; IF @@ROWCOUNT < 3000 BREAK; END 362 Chapter 5 TOP and OFFSET-FETCH The code uses an infinite loop. Every execution of the DELETE TOP statement deletes up to 3,000 rows and commits. As soon as the number of affected rows is lower than 3,000, you know that you’ve reached the last chunk, so the code breaks from the loop. If this process is running (and during peak hours), you want to abort it, and it’s quite safe to stop it. Only the current chunk will undergo a rollback. You can then run it again in the next window you have for this and the process will simply pick up where it left off. Top N per group The top N per group task is a classic task that appears in many shapes in practice. Examples include the following: “Return the latest price for each security,” “Return the employee who handled the most orders for each region,” "Return the three most recent orders for each customer,” and so on. Interestingly, like with many other examples in T-SQL, it’s not like there’s one solution that is considered the most efficient in all cases. Different solutions work best in different circumstances. For top N per group tasks, two main factors determine which solution is most efficient: the availability of a supporting index and the density of the partitioning (group) column. The task I will use to demonstrate the different solutions is returning the three most recent orders for each customer from the Sales.Orders table in the TSQLV3 database. In any top N per group task, you need to identify the elements involved: partitioning, ordering, and covering. The partitioning element defines the groups. The ordering element defines the order—based on which, you filter the first N rows in each group. The covering element simply represents the rest of the columns you need to return. Here are the elements in our sample task: ■■ Partitioning: custid ■■ Ordering: orderdate DESC, orderid DESC ■■ Covering: empid As mentioned, one of the important factors contributing to the efficiency of solutions is the availability of a supporting index. The recommended index is based on a pattern I like to think of as POC—the acronym for the elements involved (partitioning, ordering, and covering). The PO elements should form the index key list, and the C element should form the index include list. If the index is clustered, only the key list is relevant; all the rest of the columns are included in the leaf row, anyway. Run the following code to create the POC index for our sample task: USE TSQLV3; CREATE UNIQUE INDEX idx_poc ON Sales.Orders(custid, orderdate DESC, orderid DESC) INCLUDE(empid); The other important factor in determining which solution is most efficient is the density of the partitioning element (custid, in our case). The Sales.Orders table in our example is very small, but imagine the same structure with a larger volume of data—say, 10,000,000 rows. The row size in our index is Chapter 5 TOP and OFFSET-FETCH 363 quite small (22 bytes), so over 300 rows fit in a page. This means the index will have about 30,000 pages in its leaf level and will be three levels deep. I’ll discuss two density scenarios in my examples: ■■ Low density: 1,000,000 customers, with 10 orders per customer on average ■■ High density: 10 customers, with 1,000,000 orders per customer on average I’ll start with a solution based on the ROW_NUMBER function that is the most efficient in the lowdensity case. I’ll continue with a solution based on TOP and APPLY that is the most efficient in the high-density case. Finally, I’ll describe a solution based on concatenation that performs better than the others when a POC index is not available, regardless of density. Solution using ROW_NUMBER Two main optimization strategies can be used to carry out our task. One strategy is to perform a seek for each customer in the POC index to the beginning of that customer’s section in the index leaf, and then perform a range scan of the three qualifying rows. Another strategy is to perform a single scan of the index leaf and then filter the interesting rows as part of the scan. The former strategy is not efficient for low density because it involves a large number of seeks. For 1,000,000 customers, it requires 1,000,000 seeks. With three levels in the index, this approach translates to 3,000,000 random reads. Therefore, with low density, the strategy involving a single full scan and a filter is more efficient. From an I/O perspective, it should cost about 30,000 sequential reads. To achieve the more efficient strategy for low density, you use the ROW_NUMBER function. You write a query that computes row numbers that are partitioned by custid and ordered by orderdate DESC, orderid DESC. This query is optimized with a single ordered scan of the POC index, as desired. You then define a CTE based on this query and, in the outer query, filter the rows with a row number that is less than or equal to 3. This part adds a Filter operator to the plan. Here’s the complete solution: WITH C AS ( SELECT ROW_NUMBER() OVER( PARTITION BY custid ORDER BY orderdate DESC, orderid DESC) AS rownum, orderid, orderdate, custid, empid FROM Sales.Orders ) SELECT custid, orderdate, orderid, empid FROM C WHERE rownum <= 3; The execution plan for this solution is shown in Figure 5-9. 364 Chapter 5 TOP and OFFSET-FETCH FIGURE 5-9 Plan for a solution with ROW_NUMBER. As you can see, the majority of the cost of this plan is associated with the ordered scan of the POC index. As mentioned, if the table had 10,000,000 rows, the I/O cost would be about 30,000 sequential reads. Solution using TOP and APPLY If you have high density (10 customers, with 1,000,000 rows each), the strategy with the index scan is not the most efficient. With a small number of partitions (customers), a plan that performs a seek in the POC index for each partition is much more efficient. If only a single customer is involved in the task, you can achieve a plan with a seek by using the TOP filter, like so: SELECT TOP (3) orderid, orderdate, empid FROM Sales.Orders WHERE custid = 1 ORDER BY orderdate DESC, orderid DESC; To apply this logic to each customer, use the APPLY operator with the preceding query against the Customers table, like so: SELECT C.custid, A.orderid, A.orderdate, A.empid FROM Sales.Customers AS C CROSS APPLY ( SELECT TOP (3) orderid, orderdate, empid FROM Sales.Orders AS O WHERE O.custid = C.custid ORDER BY orderdate DESC, orderid DESC ) AS A; The execution plan for this solution is shown in Figure 5-10. FIGURE 5-10 Plan for a solution with TOP and APPLY. You get the desired plan for high density. With only 10 customers, this plan requires about 30 logical reads. That’s big savings compared to the cost of the scan strategy, which is 30,000 reads. Chapter 5 TOP and OFFSET-FETCH 365 TOP OVER Again, as a thought exercise, if you could change or extend the design of the TOP filter, what would you do? In the existing design, the ordering specification for TOP is based on the underlying query’s ORDER BY clause. An alternative design is for TOP to use its own ordering specification that is separate from the underlying query’s ORDER BY clause. This way, it is clear that the TOP ordering doesn’t provide any presentation-ordering guarantees, plus it would allow you to use a different ordering specification for the TOP filter and for presentation purposes. Furthermore, the TOP syntax could benefit from a partitioning element, in that the filter is applied per partition. Because the OVER clause used with window functions already supports partitioning and ordering specifications, there’s no need to reinvent the wheel. A similar syntax can be used with TOP, like so: TOP ( < expression > ) [ PERCENT ] [ WITH TIES ] [ OVER( [ PARTITION BY ( < partition by list > ) ] [ ORDER BY ( <order by list> ) ] ) ] You then use the following query to request the three most recent orders for each customer (but do not run this query, because it relies on unsupported syntax): SELECT TOP (3) OVER ( PARTITION BY custid ORDER BY orderdate, orderid ) orderid, orderdate, custid, empid FROM dbo.Orders ORDER BY custid, orderdate, orderid; You can find a request to Microsoft to improve the TOP filter as described here in the f­ ollowing link: http://connect.microsoft.com/SQLServer/feedback/details/254390/over-clauseenhancement-request-top-over. Implementing such a design in SQL Server shouldn’t cause compatibility issues. SQL Server could assume the original behavior when an OVER clause isn’t present and the new behavior when it is. Solution using concatenation (a carry-along sort) Absent a POC index, both solutions I just described become inefficient and have problems scaling. The solution based on the ROW_NUMBER function will require sorting. Sorting has n log n scaling, becoming more expensive per row as the number of rows increases. The solution with the TOP filter and the APPLY operator might require an Index Spool operator (which involves the creation of an index during the plan) and sorting. Interestingly, when N equals 1 in the top N per group task and a POC index is absent, there’s a third solution that performs and scales better than the other two. Make sure you drop the POC index before proceeding: DROP INDEX idx_poc ON Sales.Orders; The third solution is based on the concatenation of elements. It implements a technique you can think of as a carry-along sort. You start by writing a grouped query that groups the rows by the P 366 Chapter 5 TOP and OFFSET-FETCH element (custid). In the SELECT list, you convert the O (orderdate DESC, orderid DESC) and C (empid) elements to strings and concatenate them into one string. What’s important here is to convert the original values into string forms that preserve the original ordering behavior of the values. For example, use leading zeros for integers, use the form YYYYMMDD for dates, and so on. It’s only important to preserve ordering behavior for the O element to filter the right rows. The C element should be added just to return it in the output. You apply the MAX aggregate to the concatenated string. This results in returning one row per customer, with a concatenated string holding the elements from the most recent order. Finally, you define a CTE based on the grouped query, and in the outer query you extract the individual columns from the concatenated string and convert them back to the original types. Here’s the complete solution query: WITH C AS ( SELECT custid, MAX( (CONVERT(CHAR(8), orderdate, 112) + RIGHT('000000000' + CAST(orderid AS VARCHAR(10)), 10) + CAST(empid AS CHAR(10)) ) COLLATE Latin1_General_BIN2 ) AS s FROM Sales.Orders GROUP BY custid ) SELECT custid, CAST( SUBSTRING(s, 1, 8) AS DATE ) AS orderdate, CAST( SUBSTRING(s, 9, 10) AS INT ) AS orderid, CAST( SUBSTRING(s, 19, 10) AS CHAR(10) ) AS empid FROM C; What’s nice about this solution is that it scales much better than the others. With a small input table, the optimizer usually sorts the data and then uses an order-based aggregate (the Stream Aggregate operator). But with a large input table, the optimizer usually uses parallelism, with a local hash-based aggregate for each thread doing the bulk of the work, and a global aggregate that aggregates the local ones. You can see this approach by running the carry-along-sort solution against the Orders table in the PerformanceV3 database: USE PerformanceV3; WITH C AS ( SELECT custid, MAX( (CONVERT(CHAR(8), orderdate, 112) + RIGHT('000000000' + CAST(orderid AS VARCHAR(10)), 10) + CAST(empid AS CHAR(10)) ) COLLATE Latin1_General_BIN2 ) AS s FROM dbo.Orders GROUP BY custid ) SELECT custid, CAST( SUBSTRING(s, 1, 8) AS DATE ) AS orderdate, CAST( SUBSTRING(s, 9, 10) AS INT ) AS orderid, CAST( SUBSTRING(s, 19, 10) AS CHAR(10) ) AS empid FROM C; Chapter 5 TOP and OFFSET-FETCH 367 The execution plan for this query is shown in Figure 5-11. FIGURE 5-11 Plan for a solution using concatenation. This exercise emphasizes again that there are usually multiple ways to solve any given querying task, and it’s not like one of the solutions is optimal in all cases. In query tuning, different factors are at play, and under different conditions different solutions are optimal. Median Given a set of values, the median is the value below which 50 percent of the values fall. In other words, median is the 50th percentile. Median is such a classic calculation in the statistical analysis of data that many T-SQL solutions were created for it over time. I will focus on three solutions. The first uses the PERCENTILE_CONT window function. The second uses the ROW_NUMBER function. The third uses the OFFSET-FETCH filter and the APPLY operator. Our calculation of median will be based on the continuous-distribution model. What this translates to is that when you have an odd number of elements involved, you should return the middle element. When you have an even number, you should return the average of the two middle elements. The alternative to the continuous model is the discrete model, which requires the returned value to be an existing value in the input set. In my examples, I’ll use a table called T1 with groups represented by a column called grp and values represented by a column called val. You’re supposed to compute the median value for each group. The optimal index for median solutions is one defined on (grp, val) as the key elements. Use the following code to create the table and fill it with a small set of sample data to verify the validity of the solutions: USE tempdb; IF OBJECT_ID(N'dbo.T1', N'U') IS NOT NULL DROP TABLE dbo.T1; GO CREATE TABLE dbo.T1 ( id INT NOT NULL IDENTITY CONSTRAINT PK_T1 PRIMARY KEY, grp INT NOT NULL, val INT NOT NULL ); CREATE INDEX idx_grp_val ON dbo.T1(grp, val); INSERT INTO dbo.T1(grp, val) VALUES(1, 30),(1, 10),(1, 100), (2, 65),(2, 60),(2, 65),(2, 10); 368 Chapter 5 TOP and OFFSET-FETCH Use the following code to populate the table with a large set of sample data (10 groups, with 1,000,000 rows each) to check the performance of the solutions: DECLARE @numgroups AS INT = 10, @rowspergroup AS INT = 1000000; TRUNCATE TABLE dbo.T1; DROP INDEX idx_grp_val ON dbo.T1; INSERT INTO dbo.T1 WITH(TABLOCK) (grp, val) SELECT G.n, ABS(CHECKSUM(NEWID())) % 10000000 FROM TSQLV3.dbo.GetNums(1, @numgroups) AS G CROSS JOIN TSQLV3.dbo.GetNums(1, @rowspergroup) AS R; CREATE INDEX idx_grp_val ON dbo.T1(grp, val); Solution using PERCENTILE_CONT Starting with SQL Server 2012, T-SQL supports window functions to compute percentiles. PERCENTILE_CONT implements the continuous model, and PERCENTILE_DISC implements the discrete model. The functions are not implemented as grouped, ordered set functions; rather, they are implemented as window functions. This means that instead of grouping the rows by the grp column, you will define the window partition based on grp. Consequently, the function will return the same result for all rows in the same partition instead of once per group. To get the result only once per group, you need to apply the DISTINCT option. Here’s the solution to compute median using the continuous model with PERCENTILE_CONT: SELECT DISTINCT grp, PERCENTILE_CONT(0.5) WITHIN GROUP(ORDER BY val) OVER(PARTITION BY grp) AS median FROM dbo.T1; After you overcome the awkwardness in using a window function instead of a grouped one, you might find the solution agreeable because of its simplicity and brevity. That’s until you actually run it and look at its execution plan (which is not for the faint of heart). The plan for the solution is very long and inefficient. It does two rounds of spooling the data in work tables, reading each spool twice— once to get the detail and once to compute aggregates. It took the solution 79 seconds to complete in my system against the big set of sample data. If good performance is important to you, you should consider other solutions. Solution using ROW_NUMBER The second solution defines two CTEs. One called Counts is based on a grouped query that computes the count (column cnt) of rows per group. Another is called RowNums, and it computes row numbers (column n) for the detail rows. The outer query joins Counts with RowNums, and it filters only the relevant values for the median calculation. (Keep in mind that that the relevant values are those where n is (cnt+1)/2 or (cnt+2)/2, using integer division.) Finally, the outer query groups the remaining rows Chapter 5 TOP and OFFSET-FETCH 369 by the grp column and computes the average of the val column as the median. Here’s the complete solution: WITH Counts AS ( SELECT grp, COUNT(*) AS cnt FROM dbo.T1 GROUP BY grp ), RowNums AS ( SELECT grp, val, ROW_NUMBER() OVER(PARTITION BY grp ORDER BY val) AS n FROM dbo.T1 ) SELECT C.grp, AVG(1. * R.val) AS median FROM Counts AS C INNER MERGE JOIN RowNums AS R on C.grp = R.grp WHERE R.n IN ( ( C.cnt + 1 ) / 2, ( C.cnt + 2 ) / 2 ) GROUP BY C.grp; The plan for this solution is shown in Figure 5-12. FIGURE 5-12 Plan for a solution using ROW_NUMBER. SQL Server chose a serial plan that performs two scans of the index, a couple of order-based aggregates, a computation of row numbers, and a merge join. Compared to the previous solution with the PERCENTILE_CONT function, the new solution is quite efficient. It took only 8 seconds to complete in my system. Still, perhaps there’s room for further improvements. For example, you could try to come up with a solution that uses a parallel plan, you could try to reduce the number of pages that need to be scanned, and you could try to eliminate the fairly expensive merge join. Solution using OFFSET-FETCH and APPLY The third solution I’ll present uses the APPLY operator and the OFFSET-FETCH filter. The solution defines a CTE called C, which is based on a grouped query that computes for each group parameters for the OFFSET and FETCH clauses based on the group count. The offset value (call it ov) is computed as (count – 1) / 2, and the fetch value (call it fv) is computed as 2 – count % 2. For example, if you have a group with 11 rows, ov is 5 and fv is 1. For a group with 12 rows, ov is 5 and fv is 2. The outer query applies to each group in C an OFFSET-FETCH query that retrieves the relevant values. Finally, the outer query groups the remaining rows and computes the average of the values as the median. 370 Chapter 5 TOP and OFFSET-FETCH WITH C AS ( SELECT grp, COUNT(*) AS cnt, (COUNT(*) - 1) / 2 AS ov, 2 - COUNT(*) % 2 AS fv FROM dbo.T1 GROUP BY grp ) SELECT grp, AVG(1. * val) AS median FROM C CROSS APPLY ( SELECT O.val FROM dbo.T1 AS O where O.grp = C.grp order by O.val OFFSET C.ov ROWS FETCH NEXT C.fv ROWS ONLY ) AS A GROUP BY grp; The plan for this solution is shown in Figure 5-13. FIGURE 5-13 Plan for a solution using OFFSET-FETCH and APPLY. The plan for the third solution has three advantages over the plan for the second. One is that it uses parallelism efficiently. The second is that the index is scanned in total one and a half times rather than two. The cost of the seeks is negligible here because the data has dense groups. The third advantage is that there’s no merge join in the plan. It took only 1 second for this solution to complete on my system. That’s quite impressive for 10,000,000 rows. Conclusion This chapter covered the TOP and OFFSET-FETCH filters. It started with a logical description of the filters. It then continued with optimization aspects demonstrated through paging solutions. You saw how critical it is to have an index on the sort columns to get good performance. Even with an index, I gave recommendations how to alter the solutions to avoid unnecessary lookups. The chapter also covered modifying data with the TOP filter. Finally, the chapter concluded by demonstrating the use of TOP and OFFSET-FETCH to solve common tasks like top N per group and using a median value. Chapter 5 TOP and OFFSET-FETCH 371 This page intentionally left blank Index Symbols = (equality) operator cardinality estimates for, 110, 113–114, 561 equi joins, 230–231 > (greater than) operator, cardinality estimates for, 561 < (less than) operator, cardinality estimates for, 561 A absolute frequencies, 476–479 absolute percentages, 476–479 absolute values, computing, 323–324 access methods. See also scans; seeks allocation order scan safety issues, 65–76 allocation order scans vs. index order scans, 65 clustered index seek + range scan, 93–94 cost associations of, 57 covering nonclustered index seek + range scan, 94–97 dm_db_index_usage_stats function, 135–136 index order scan issues. See index order scans index seek, 49. See also Index Seek operator logical reads as performance metric for, 57 nonclustered index seek + range scan + lookups, 81–90 ordered clustered index scans, 62–63 ordered covering nonclustered index scans, 63–65 Read Uncommitted isolation level, 68–69, 78, 81 table scan/unordered clustered index scan, 57–60 unordered covering nonclustered index scans, 60–62 unordered nonclustered index scans + lookups, 91–93 acyclic graphs, 708. See also directed acyclic graphs (DAGs) add outer rows logical processing phase, 4–5, 13 ADO.NET context connection strings with SQLCLR, 603–605 Affinity Mask setting, 175, 177 AFTER DDL triggers, 579–581 AFTER DML triggers, 575–578, 581–583 AFTER UPDATE triggers, 575–578 aggregate calculations, custom carry-along-sort solutions, 326–327 cursors for, 314–315 FOR XML string concatenation, 317–319 hierarchical levels of aggregation. See grouping sets modes, 324–327 overview of, 313–314 pivoting for, 315–316, 318–319 products, 322–324 SELECT @local_variable method, 319–322 user-defined. See SQLCLR user-defined aggregates aggregate functions AVG. See averages, AVG window function cardinality overestimations, effects of, 100 cardinality underestimations, effects of, 99 COUNT. See COUNT function cumulative aggregate function plans, 274–275 expected frequency calculations with, 504–505 limitations of, 261–262 parallelism inhibited by certain, 179 scalar aggregates, 179 subquery inputs prohibited, 17 SUM. See SUM function; SUM OVER window function window functions. See aggregate window functions aggregate window functions packing interval solutions, 466–468 similarity to grouped functions, 260 aggregation custom calculations of. See aggregate calculations, custom functions for. See aggregate functions persisting with grouping sets, 334–337 user-defined. See SQLCLR user-defined aggregates aliases, column ensuring uniqueness in queries with, 204 inline vs. external, for derived tables, 205 in ORDER BY clauses, 14, 20 referencing, 8, 17 reuse with APPLY operator, 222–224 ALL variant of UNION operator, 37–38, 250 all-at-once operations, 17–18 803 Allen’s interval algebra Allen’s interval algebra, 452 allocation order scans defined, 44 multiple occurrences of rows problem, 65–67 skipping rows problem, 67–68 storage engine treatment of, 65–76 storage engine’s choice situations, 59 Table Scan operator processing, 58 vs. index order scans, 47, 65 allocation units pages belonging to, 42 types of, 44 ALTER INDEX REBUILD command, 48 ALTER TABLE not supported by In-Memory OLTP, 704 ALTER_TABLE events, 579–580 A-Marks, 10 analysis of variance (ANOVA), 505–508, 523 ancestors GetAncestor method of HIERARCHYID data type, 758, 764–765 materialized path solutions, returning with, 751–753 nested sets graph solution, returning with, 786 parents. See parent nodes returning, 730–733 AND operator intersection calculations with, 452–455 interval determinations with, 456 vs. BETWEEN, 784 ANOVA (analysis of variance), 505–508, 523 ANSI_ set options, 568 ANSI SQL standards, 1–2 anti semi joins, 237–238 APPLY operator CROSS APPLY operator, 219–220 CROSS vs. OUTER, 28 elements of, 27 implicit, 221–222 median calculations with, 370–371 OUTER APPLY operator, 221 Parallel APPLY Pattern, 183–186 phase in logical query processing, 26–29 reuse of column aliases, 222–224 types of, 218 ARITHABORT set option, 568 arithmetic means. See means arrays tables, separating elements of, 245–249 AS, SELECT clauses with, 17 ascending keys B-trees page splits with, 48 cardinality estimate problems, 564–568 statistics problems, 107–110 assemblies, .NET, 587–588 ASSEMBLY keyword, 493–494 assignment SELECT for string concatenation, 319–322 ATOMIC blocks, In-Memory OLTP, 698 804 atomicity property of transactions, 633 authentication with users’ credentials, 614 autocommit default mode for transactions, 633 AUTO_CREATE_STATISTICS property, 93, 102, 111 average fragmentation in percent, 47–48 averages AVG window function, 274–275 means. See means medians. See medians modes. See modes moving. See moving average value computations B backward scans, 116–117, 179 balanced trees, 46 batch execution mode columnstore use of, 128–130 hash algorithm for joins, 243 batches, calling vs. execution, 525 BCL (.NET Base Class Library), 597 bcp.exe utility, 376, 378 BEGIN TRAN command, 633–636 BETWEEN operator cardinality estimates with, 112–113 vs. AND, 784 BI (business intelligence). See statistics for BI (business intelligence) bill of materials (BOM) scenario, 711–714 BINARY function, 326 binary variables, 474 bitmaps, query tuning with, 170–171 blocks. See locks BOM (bill of materials) scenario, 711–714 broadcast parallel distribution scheme, 163 B-trees advantages vs. heaps, 43–44 ascending key pattern, 48 balanced trees, 46 bulk-logging requirements, 376 BW-Tree structure, 677–679 clustered indexes structured as, 46–50 leaf levels, 46–47 nonclustered index seek + range scan + lookups, 83–90 nonclustered indexes structured on, 52–53 uniquifier columns, 46–47 unordered clustered index scans of, 57–60 buffers buffer pools, 42 DROPCLEANBUFFERS, 53 log buffers, flushing of, 634 bulk loads of data 2012 vs. 2014 versions, parallel execution plans, 174 BULK INSERT command, 376, 378 clustered indexes BULK statements, MERGE statements using, 410–411 bulk-import tools, list of, 376 eager writes for, 374–375 measuring logging, 377–378 minimal logging, requirements for, 376 OPENROWSET function, 378–380 SELECT INTO for, 173–174, 373–376 bulk rowset provider tool, 378–380 BULK_LOGGED recovery model for transactions, 634 business intelligence. See statistics for BI (business intelligence) BW-Tree structure, 677–679 C C#. See SQLCLR programming caches cleaning before measuring performance, 53 defaults for, determining, 387–390 identity property with, 382 pages in, 42 sequence creation performance issues from, 387–394 temporary table issues from, 147–148 cardinality estimates cardinality estimator component, 97–98 computed column creation for, 107 disjunction (OR) queries, 106–107 equality operator estimates, 110, 113–114, 561 exponential backoff, 106 filtered index creation for, 107 hardcoded estimates, table of, 561 histogram refresh rate issues, 564–565 histograms, derived from, 103–104 inaccurate, detecting, 98 inaccurate, implications of, 100–101, 557–558 join order issues, 233 legacy vs. 2014 estimators, 98–99, 104–107, 114 LIKE predicate with, 112–115 multiple predicate estimates, 104–107 nonclustered index seek + range scan + lookups, 84–85 operator unknowns, table of estimates for, 110 overestimations, implications of, 100–101 BETWEEN predicate with, 112–113 RECOMPILE option, 110 refresh rate issues, legacy vs. new, 107–110 sniffing, disabling, 110 table variables, 143–146 temporary objects for, 139 trace flag 2389, 109 TVPs, for, 572 underestimations, implications of, 99–100 unknowns in, 110–115 unordered nonclustered index scans + lookups, 92–93 variable sniffing, lack of, 560–564 variables as unknown values, 104, 110 viewing, 98 carry-along-sort solutions, 326–327, 366–368 Cartesian products. See also cross joins logical query-processing phase details, 8–9 as logical query-processing phases, 4 CASE expressions PIVOT implicit use of, 30–31 type-conversion errors from, 542–543 case sensitivity collation of strings issues, 595 of method and property names in SQLCLR userdefined types, 621 .NET SQLCLR issues, 595–597 casting CAST with SELECT INTO, 374 DATE or TIME to DATETIME, 440 CD (coefficient of determination), 498–499 Celko, Joe, 778 centers of distribution, 479–481 CHARINDEX function, 247–248 check constraints CHECK option, views, 212 negative logic with, 201 not supported by In-Memory OLTP, 704 UNKNOWN values with, 11 CHECKIDENT command, 381 CHECKPOINT command, 53 checkpoint process, 42–43 child nodes. See also descendants adding, long paths resulting from, 767 defined, 708 HIERARCHYID children queries, 765 nested sets model of, 778 next level, of, 718 parent-child adjacency lists, converting to HIERARCHYID, 771–773 chi-squared tests, 501–505, 523 chunked modification with TOP filters, 361–363 cloud platform. See SQL Database CLR (Common Language Runtime) enabling in a SQL Server instance, 493 HIERARCHYID. See HIERARCHYID data type SQLCLR. See SQLCLR programming UDFs (user-defined functions). See SQLCLR programming UDFs with DataAccessKind.Read, 179 user-defined aggregates, 490–495 Virtual Studio for developing, 589–591 clustered indexes ALTER INDEX REBUILD command, 48 B-tree structure of, 46 B-trees resulting from, 43–44, 46–50 clustered index keys with nonclustered indexes access, 62 clustered index seek + range scan access method, 93–94 805 COALESCE function clustered indexes, continued CLUSTERED keyword, 43 covering indexes, 93–94 creating, 43 FILLFACTOR option, 48 imports, performance hits with, 44 index order scans, 46–47 leaf levels, 46–47 levels in, determining number of, 49–50 levels of, 48–49 not supported by In-Memory OLTP, 704 ordered clustered index scans, 62–63 root pages, 48 uniquifier columns, 46–47 unordered clustered index scans, 57–60 updateable, columnstore indexes, 128–129 COALESCE function, 316 Codd, Edgar F., 2 coefficient of the variation (CV), 486, 523 coefficient of determination (CD), 498–499, 523 cold caches, 53 collection classes, .NET, 629–630 columns alias processing order issue with SELECT clauses, 14, 20 changes in, recompiles triggered by, 570–571 changing names and types with RESULTS SETS clauses, 574–575 computed columns not supported for In-Memory OLTP, 703 creation order, 21 dense partitioning issue, 150, 154 name requirement in queries against table expressions, 204 referencing, 8, 17 SQL_VARIANT type, 300 subquery correlations, 189 substitution errors in names in subqueries, 201–202 swapping values of, 18 as variables, 473 columnstore indexes 2012 vs. 2014 capabilities, 125, 128 advantages of, 125–127 batch execution, 128–130 clustered, updateable, columnstore indexes, 128–129 column segments, 126 compression, 125, 129 CPU cost benefits, 128 data organization with, 125–126 data type restrictions, 130 data warehousing as target for, 123 delete bitmaps, 129 deltastores, 129 dictionaries, 126 execution plans, 127 hash algorithm for joins, 243 806 I/O cost reduction benefit, 127 rebuild table recommendation, 129 rowgroups, 126 rowstore vs. columnstore efficiencies, 123–127 segment elimination, 127 syntax example, 125 tuple mover process, 129 update handling, 129 user views of, 129 COLUMNS_UPDATED function, 577 commands. See specific command names COMMIT TRAN command, 634–636 commit validation errors, 693–696 Common Language Runtime. See CLR (Common Language Runtime) common table expressions. See CTEs (common table expressions) compilations (recompilations), 568–571 composable DML, 417 computed columns not supported for In-Memory OLTP, 703 CONCAT function, 316 concatenation concatenation with comma separation, 629–632 CONCAT_NULL_YIELDS_NULL set option, 568–570 top N per group task solution using, 366–368 concurrency models pessimistic vs. optimistic concurrency models, 674 vs. isolation levels, table of, 646 consistency property of transactions, 634 console applications, analysis of variations with, 506–508 constraints CHECK. See check constraints DEFAULT with NEXT VALUE FOR, 384 dropping, 385 immediacy of for transactions, 634 kinds not supported by In-Memory OLTP, 704 for memory-optimized tables, 676 unique. See UNIQUE constraints context connection strings, 603–605, 607 CONTEXT_INFO with triggers, 584 contingency tables, 501–505, 523 continuous variables, statistics for centers of distribution, 479–481 defined, 474 descriptive statistics overview, 479 higher population moments, 487–494 kurtosis, 489–495, 523 means, 479, 481–482, 484, 523 medians. See medians modes of, 324–327, 479–480, 523 normal distributions, 487–488, 509–512 population moments, 479 ranges of distributions, 482–483 skewness, 479, 488–489, 490–495, 523 cyclic graphs spread of distribution, 482–486 standard deviations, 486–487, 490, 498, 511, 523 conversion functions CAST function. See casting date and time conversions, 431–432 failures from, 431–432 rounding issues, 447–449 TRY_ versions of, 431–432 CONVERT function for dates and times, 431, 433, 435 coordinator threads, 164–165 correlated subqueries, 187, 189–194 correlated tables, 184 correlation coefficients, 498–499, 523 correlation vs. causation, 499 Cost Threshold for Parallelism setting, 175–176 COUNT function GROUP BY with, 16 HAVING with, 16–17 mode calculations with, 324–327 outer joins issue, 17 phase in logical processing, 36–37 covariance, 495–498, 523 covering indexes benefits of, 93 clustered indexes as, 93–94 memory optimized indexes are always, 677 stored procedures with, 558 covering nonclustered index seek + range scan, 94–97 CPUs costs, viewing, 176–177 parallel query execution dependence on number of logical, 89–90 CREATE ASSEMBLY command, 493–494, 587–588, 591 CREATE CLUSTERED INDEX, 43 CREATE TYPE command, 571–573 CREATE_TABLE events, triggers on, 579–580 CROSS APPLY operator column alias preservation with, 223 expense of, 141–142 logical query-processing phase for, 27–28 MAX OVER window function with, 275–276 multi-table queries with, 219–220 Parallel APPLY Pattern, 183–186 unpivoting with, 310–311 cross joins Cartesian product results of, 224 logical query-processing phases, 4, 8–9 physical-join evaluation order, 232–233 rows, generating large numbers of, 216–217 sample data generation with, 225 subquery optimization with, 226–227 syntax, 224 unpivoting with, 308–310 vs. non-equi joins, 231 cross-container queries, 703 cross-database transactions, not supported by In-Memory OLTP, 705 csc.exe (C# compiler), 589 CTEs (common table expressions) ancestors, returning, 730–733 anchor members, 722 anchor/recursive data type matching requirement, 736 cycle detection in graphs, 740–742 descendants, returning, 722–723 expensive work, avoiding repetition of, 140–143 MAXRECURSION hint, 728–729 mode computations with, 324–327 multiple in WITH statements, 208 multiple references to allowed, 208–209 nesting of, 208 performance issues, 209 persistence of results, 140–142 query requirements for, 204–205 recursive, 209–211, 718–719 recursive graph solutions, 718–719, 722–723, 731–733 recursive, parallelism inhibited by, 179 scopes of, 204 shortest-path solutions vs. loops, 792–801 subgraph solution, 722–723, 725 subgraphs with path enumeration, 736 syntax of, 207 temporary objects of, 139–140 topological sorts with, 736–739 troubleshooting multiple, 208 updating data with, 402–403 vs. derived tables, 207–208 CUBE function, 331–333 CUME_DIST OVER window function frequency calculations with, 477 vs. PERCENT_RANK for ranking, 288–289 cumulative F distributions, 506–508 cumulative frequencies, 476–479 cumulative percentages, 476–479 CURRENT ROW delimiter, 269–270, 274, 276–278 CURRENT_TIMESTAMP function, 422 cursors bad reputation of, 581 custom aggregate calculations with, 314–315 exponential moving average calculations with, 515–518 FAST_FORWARD option, 152 fetching, T-SQL inefficiency, 151–153 iterative solutions with, 151–152 not supported by In-Memory OLTP, 704 returned by ORDER BY clauses, 21–22 string concatenation with, 314–315 syntax example, 151 triggers using, 581–583 CV (coefficient of the variation), 486 CXPacket data structure, 163 cyclic graphs, 708, 715–718, 740–742, 792–801 807 DAGs D DAGs. See directed acyclic graphs (DAGs) data caches. See caches Data Definition Language. See DDL (Data Definition Language) data integrity, filtered indexes to enforce, 122 Data Manipulation Language. See DML (Data Manipulation Language) data structures, internal. See internal data structures data type precedence, 542 data warehousing bitmaps for parallel optimization of star join queries, 171 columnstore technology for. See columnstore indexes ETL processes, isolation level for, 648 grouping sets for persisting aggregates, 328 hash algorithm for joins, 243–244 indexing issues, 125 rowstore technology, inefficiency of, 123–127 slowly changing dimensions type 2, 417 star schema model, 123 data-analysis calculations aggregates. See aggregation defined, 259 filters with, 262–263 grouping sets with. See grouping sets inverse distribution functions, 289–291 limitations without window functions, 261–263 medians, calculating, 289–291 ranking calculations with window functions, 281–285 scalar aggregate subqueries for, 261–262 statistics window functions, 288–291 window functions for. See window functions year-to-date (YTD) calculation, 280–281 databases assembly (.NET) attachment to, 587 compatibility levels, setting, 98–99 cross-database transactions, not supported by In-Memory OLTP, 705 tempdb, 140, 581 DATEFIRST set option, 568 DATEFORMAT set option, 568 dates and times adding or subtracting units, 426 anchor dates, 440–444 BETWEEN predicate errors, 447 character string conversions, 447–449 compensation method for weekdays, 438 CONVERT function for, 431, 433, 435 current date or time, returning, 422 CURRENT_TIMESTAMP function, 422 data type conversion ambiguities, 436 data types for, 419–422 DATE data type, 419–420, 422, 436, 440, 595 DATEADD function, 426–427, 440–445, 449–450 808 DATEDIFF function, 426–430, 438, 440–445 DATEFIRST option, 436–439 DATEFORMAT option, 434, 436 DATEFROMPARTS function, 431, 441 DATENAME function, 415 date-only data with DATETIME, 439–440 DATEPART function, 423–424, 436–438, 449–450 DATETIME data type, 419–420, 436, 439–440, 447–449 DATETIME2 data type, 419–420, 427, 436, 595 DATETIME2FROMPARTS function, 431 DATETIMEFROMPARTS function, 431 DATETIMEOFFSET data type, 419–420, 423, 426, 436, 595 DATETIMEOFFSETFROMPARTS function, 431 DAY function, 425 daylight saving time, extracting state of, 423–425 daylight saving time issues, 420–421 diff and modulo method for weekdays, 437–438 differences between dates, calculating, 426–430 entry formats, table of, 420 EOMONTH function, 431, 441 extracting parts of dates, 423 filtering SARGability, 445–446 first or last day of a period, finding, 440–441 first or last weekday calculations, 443–445 FORMAT function, 432–433 functions returning current date and time, list of, 422 GETDATE function, 422 GETUTCDATE function, 422 grouping by weeks, 449–459 intersecting intervals, 452–456 interval calculations, 450–471 ISDATE function, 425 IsDaylightSavingTime method, 424 island problems with, 295–299 language-dependent results, 425, 434–437 language-neutral formats, 436 last day of the year calculation, 547–549 last modification tracking, 576 leap seconds, 421–422 literals, issues with, 434–436 maximum concurrent interval problems, 456–465 Microsoft Windows time, 421–422 midnight, issues from rounding near, 447 MONTH function, 425 offsets (UTC), extracting, 423 packing intervals, 466–471 PARSE function for, 431–433, 435 performance enhancements for interval calculations, 450–451, 458–465, 470–471 performance issues, 432–433 precision of types, 420 previous or next weekday calculations, 441–443 proleptic Gregorian calendar, use of, 419 query tuning for, 445–446 deviations rounding issues, 447–449 search arguments (SARGs), 445–446 SMALLDATETIME data type, 419–420, 439–440, 448 SMALLDATETIMEFROMPARTS function, 431 SQLCLR type translation issues with, 595 storage requirements, table of, 420 SWITCHOFFSET function, 425–426 SYSDATETIME function, 422, 436–437, 440–441, 448–449 SYSDATETIMEOFFSET function, 421–424 SYSUTCDATETIME function, 422 TIME data type, 419–420, 422, 440, 595 time zone issues, 420–421 TIMEFROMPARTS function, 431 time-only data with DATETIME, 439–440 TODATETIMEOFFSET function, 426 TRY_CAST function, 432 TRY_CONVERT function, 431–432 unambiguous formats, table of, 436 UTC compared to, 420–421 weekdays, 423, 436–439, 441–445 WHERE clauses with, 445–446 Windows Time service, 421–422 YEAR function, 425 YYYYMMDD format, 435–436 DAY function, 425 daylight saving time, 420–425 DBCC commands IND, 70–71 OPTIMIZER_WHATIF, 89–90 SHOW_STATISTICS, 101–102 dbo schema, 620 DDL (Data Definition Language) In-Memory OLTP surface-area restrictions, 703–704 recompilations, 568 triggers on DDL_DATABASE_LEVEL_EVENTS, 579–581 triggers on DDL_TABLE_EVENTS, 579–580 deadlocks choosing victims to terminate, 657 DEADLOCK_PRIORITY option, 657 error messages generated by, 657–659 example of generating, 657–658 graphs generated for, 657 indexes, relation to, 658–659 isolation levels, relation to, 660 lengths of transactions, 660 measures to reduce, 658–660 mechanics of, 657 physical resource access order issue, 659 query interop environment, In-Memory OLTP, 691–693 retry logic after, 669–670 single-table deadlocks, 660–662 SQL Server monitoring of, 657 trace flags for, 657 DECIMAL data type, 592–593 deduplification, 399–401 DEFAULT constraints with NEXT VALUE FOR, 384 definite integration, 509–512, 523 degree of parallelism. See DOP (degree of parallelism) degrees of freedom in analysis of variance, 505–508 chi-squared critical points, 502 defined, 485, 523 delayed durability, 634, 643–645 DELETE clauses AFTER DELETE triggers with, 575–578 DELETE FROM statements, 361, 399–400 DELETE TOP filters, 360–363, 400 INSTEAD OF triggers with, 578 in MERGE statements, 405–406 with OUTPUT, 413–414 deleting data archiving deleted data, 413–414 deduplification, 399–401 DELETE for. See DELETE clauses SELECT INTO another table method, 400–401 TRUNCATE TABLE statements for, 395–399 Demand parallel distribution scheme, 163, 171–173 dense partitioning issue, 150, 154 DENSE_RANK OVER window function, 282–283, 285, 294–296 density vectors, statistics, 102–103 derived tables aliasing computed columns requirement, 205 APPLY operator for column alias preservation, 222–224 disadvantages of, 206 execution plans of, 206 external aliasing, 205–206 inline aliasing, 205–206 multiple references to, issue with, 207 nesting issues, 206 persistence of results, 140–142 query requirements for, 204–205 scopes of, 204 syntax for creating, 205 temporary objects of, 139–140 DESC keyword in index definitions, 115–116 DESC option, TOP filters, 344–345 descendants. See also subgraphs children. See child nodes CTEs (common table expressions), returning with, 722–723 GetDescendant method of HIERARCHYID data type, 757–759 queries on HIERARCHYID data type, 763–764 descending indexes, 63, 115–118 deviations mean absolute (MAD), 484 mean squared (MSD), 484 standard, 486–487, 490, 498, 511, 523 809 dichotomous variables dichotomous variables, 474 digraphs. See directed graphs (digraphs) directed acyclic graphs (DAGs) bill of materials (BOM) scenario, 711–714 defined, 708 hierarchies as, 709 sorting, 736–739 topological sorts, 736–739 transitive closure of, 787–792 two table requirement, 711 directed graphs (digraphs) defined, 707–708 returning subgraphs, 719–729 transitive closure of. See transitive closure dirty pages CHECKPOINT forced writing of, 377 eager writes, 140, 174–175 mechanics of, 42–43 dirty reads defined, 645 Read Uncommitted isolation level, 647 vs. isolation levels, table of, 646 discrete variables, 474 disjunctions not allowed by In-Memory OLTP, 705 DISTINCT clauses improper uses of, 231–232 NULLs, treatment of, 11 ORDER BY clauses with, 20–22 phase in logical processing, 6, 18–20 semi joins with, 237 distinct predicate not supported, 408 distributions centers of distribution, 479–481 contingency tables, 501–505 degrees of freedom, 485, 502, 505–508 deviations, mean absolute (MAD), 484 deviations, mean squared (MSD), 484 distribution functions, 509–512 frequency distributions, 476–478 higher population moments, 487–494 inter-quartile ranges, 483 inverse distribution functions, 289–291 kurtosis, 489–495, 523 means of, 479, 481–482, 484, 523 medians of. See medians modes of, 324–327, 479–480, 523 normal distributions, 487–488, 509–512 ranges of, 482–483 skewness, 479, 488–489, 490–495, 523 spread of distribution, 482–486 standard deviations, 486–487, 490, 498, 511, 523 standard normal distributions, 487–488 variances, 485–486, 505–508 distributor threads, 164–165 divide and conquer algorithms, 733 DLLs in SQLCLR, 587, 591 dm_db_index_operational_stats function, 135 dm_db_index_physical_stats function, 135 810 dm_db_index_usage_stats function, 135–136 dm_db_missing_index_columns function, 136–137 dm_db_missing_index_details view, 136 dm_db_missing_index_group_stats view, 136 dm_exec_procedure_stats, 138 dm_exec_query_profiles view, 138–139 dm_exec_query_stats view, 137–138 dm_exec_trigger_stats, 138 DMFs (dynamic management functions), 134–139 DML (Data Manipulation Language) composable DML, 417 In-Memory OLTP surface-area restrictions, 704–705 DMVs (dynamic management views), 134–139 DOP (degree of parallelism) for costing number, 177 defined, 159 execution plan examples, 161–162, 164 Max Degree of Parallelism setting, 175–177 viewing for a plan, 164 driver tables, 184 DROPCLEANBUFFERS, 53 duplicate row removal. See DISTINCT clauses duplicates, deleting, 399–401 durability property of transactions, 634 dynamic filtering, 535–542 dynamic management functions (DMFs), 134–139 dynamic management views (DMVs), 134–139 dynamic pivoting, 530–535 dynamic schema, 300–301 dynamic SQL batches of code in, 525 dynamic pivoting, 530–535 dynamic search conditions, 535–542 dynamic sorting, 542–546 EXEC AT command, 529 EXEC command, 525–529 interface support, 525–526, 529–530 performance benefits, 539–540 performance issues, 526 security issues, 526–530, 533, 545 sp_executesql procedures, 529–530, 539–540 tools for building and executing, 525 UDF prohibition of, 546 E eager writes, 140, 174–175 EAV (entity, attribute, value) model, 300–301 edges defined, 707 directed vs. undirected, 707–708 transitive closure duplicate edge elimination, 789 elements, separating from arrays, 245–249 EMAs (exponential moving averages), 515–518 employee organizational chart scenario, 709–711 end of year calculations, 547–549 execution plans entropy calculations, 518–521, 523 EOMONTH function, 431, 441 equality operator (=) cardinality estimates, hardcoded, 561 equi joins, 230–231 equality predicate access method issues, 95–97 equi joins, 230–231 @@error function, 662–663 ERROR_ functions, 664–666 error handling commit validation errors, 693–696, 702 ERROR_ functions, list of, 664 RAISERROR command, 664–665 retry logic, 669–670 rollbacks as part of, 668 transactions, errors in, 666–668 TRY-CATCH for. See TRY-CATCH constructs ERROR_NUMBER function, 664–666 escalation of locks, 641–643 estimators, 512 EVENT SESSION clauses, 132 EVENTDATA function, 579–580 events, routines triggered by. See triggers EXCEPT operator EXCEPT ALL implementation, 255–256 for handling NULLs in merges, 407–408 logical query-processing phase for, 38–39 NULLs, treatment of, 11 relational operator characteristics of, 249 returned values of, 255 exception handling for SQLCLR stored procedures, 609–613 Exchange operators, 160–166 exclusive locks (X), 636–637 EXEC AT command, dynamic SQL, 529 EXEC command, dynamic SQL code input to, 525–529 security risks of, 526–529 WITH RESULT SETS clauses, 573–575 EXECUTE AS clauses, 546 execution plans Bitmap operator in, 170–171 cached plans, set options of, 568–570 cardinality estimates in, viewing, 98–100 CLR scalar functions vs. T-SQL scalar functions, 588–589 clustered index seek + range scan, 93–94 columnstore indexes, 127 covering nonclustered index seek + range scan, 96 CPU costs, viewing, 176–177 cursor-based queries, 152 derived table expressions, 206 descending index plans, 116 Distribute Streams variant of Exchange operator, 160–163 dm_exec_query_profiles view, 138–139 Exchange operators, 160–163 expensive part of, identification, 154 few outer rows optimization, 168–170 filter selectivity, example of plan dependence on, 555–556 Gather Streams variant of Exchange operator, 160–163, 165–166 Hash Match operator, 167 Include Actual Execution Plan option, 138–139 index order, reliance on, 543 Index Scan operators, 65, 168–170 I/O costs, viewing, 176 join algorithm identification in, 239 KEEPFIXED PLAN hint, 570–571 Nested Loops operators, 98, 183–186, 240–241 nonclustered index seek + range scan + lookups, 81–90 NonParallelPlanReason node, 180–181 operator interfaces, 162 OPTIMIZE FOR hint, 561–562 ordered clustered index scans, 62–63 ordered covering nonclustered index scans, 63–65 Ordered property in, 65 Parallel APPLY Pattern, 185 parallel query plans, 159–160 parameter sniffing, 555–558 partitioned tables with demand-based row distribution, 173 physical execution plans, 97 Plan Explorer tool, 139 plan optimality, 568, 570–571 plan stability, 568–570 properties window features, 164 query optimizer, SQL, 2 query revisions effects on, 154 recompilations, 568–571 recursive CTE queries, 211 Repartition Streams variant of Exchange operator, 161 requesting, 53–54 reusable vs. one-time issues, 535–542, 561–562 rowstore technology, 124–125 SARG vs. non-SARG, 446 scalar UDFs, of, 548 Seek Predicates property, 96 SELECT INTO parallel plans, 174 stored procedures, example of plan dependence on filter selectivity, 555 stored procedures, preventing reuse for, 558–560 stored procedures, reuse for, 554–558 strategies, testing for desired, 154–158 Stream Aggregate in, 153, 167 Table Scan operators, 65 table scan/unordered clustered index, 57–58 table variables and recompiles, 145 temporary tables, of, 147 trivial plans, 93 unknowns in cardinality estimates, 110–115 811 existing value range identification execution plans, continued unordered covering nonclustered index scans, 60–61 unordered nonclustered index scans + lookups, 91–92 variables, stored procedure, 561–562 existing value range identification. See island problems EXISTS predicate anti semi joins with NOT, 238 identifying gaps problem, 198–200 minimum missing values, finding, 195–200 nested NOT EXISTS, 200–201 NOT EXISTS vs. NOT IN, 204 positive vs. negative logic, 200–201 returns true or false, 194–195 semi joins with, 237 subqueries as inputs to, 194–201 subquery SELECT list index ignored, 195 tuning with, 157 vs. IN predicate, 194–195 EXP function, 322–324 expected frequencies, 501–505 exponential backoff, 106 exponential moving averages (EMAs), 515–518 Extended Events sessions creating, 132 extracting statistics from, 132–134 performance, code for viewing, 54 prioritizing queries for tuning, 131–134 query_hash action, 131–134, 137–138 query_post_execution_showplan event, 138 statement completed events, 131–134 Watch Live Data window statistics, 56 extents, 42–43 F FALSE value, 10 few outer rows optimization, 168–170 filegroup, memory optimized data, 675 FileTable feature with HIERARCHYID type, 754 filtered indexes, 120–122 filters access methods for. See access methods cardinality overestimations, effects of, 100 cardinality underestimations, effects of, 99 with data-analysis calculations, 262–263 date and time SARGability, 445–446 deadlocks resulting from no supporting index, 658–659 dynamic filtering, 535–542 multiple predicate cardinality estimates, 104–107 multiple predicate issue, 95–97 NULL values, treatment of, 11 selectivity, example of execution plan dependence on, 555–556 812 UNKNOWN values, treatment of, 11 unordered nonclustered index scans + lookups, 91–92 user-defined, dynamic, 535–542 WHERE-based. See WHERE filters window functions using, 263–264 first regression line, 500 FIRST_VALUE OVER window function, 285–286, 511 FLOOR function, 468 flow diagram, logical query-processing, 5 fn_dblog function, 377–378 FOLLOWING keyword, 269, 273–274 FOR XML string concatenation, 317–319 FORCE ORDER hint for joins, 233, 236–237, 245 FORCEPLAN set option, 568 foreign-key constraints not supported by In-Memory OLTP, 704 forests, 708. See also graphs; trees FORMAT function, 432–433 fragmentation allocation vs. index order scans, effects of, 65 average page population, 135 code for checking level of, 70–71 defined, 47–48 dm_db_index_physical_stats function, 135 logical scan fragmentation, 135 rebuilding as remedy, 135 frames, window. See window frames frequency calculations defined, 523 expected frequencies, 501–505 frequency distribution analysis, 476–479 for use in chi-squared calculations, 502–505 with window functions, 477–479 without window functions, 476 FROM clauses aliases created in, visibility of, 222–223 derived tables using, 205–207 logical order of, 3–5 logical query-processing phase details, 8–14 multiple operators, processing order of, 14 phase in logical processing, 4–5 sample query for logical phases, 7 table operators in, logical phases of, 26–35 virtual tables generated by, 8 F-tests, 506–508, 523 Full data recovery model durability guarantee from, 634 SELECT INTO operations with, 377–378 FULL OUTER JOIN clauses, 13 full table scans when no order required, 57–60 G GAC (Global Assembly Cache), 587 GAMs (global allocation maps), 42 gaps problems, 291–292 hierarchies Gather Streams operation, 88, 160–166 Gaussian curves, 487–488 GETDATE function, 422 GETUTCDATE function, 422 global allocation maps (GAMs), 42 Global Assembly Cache (GAC), 587 graphical execution plans. See execution plans graphs acyclic, 708. See also directed acyclic graphs (DAGs) ancestors, returning, 730–733 components of, 707–708 CTE-based solutions. See CTEs (common table expressions) cycle detection, 740–742 cyclic, 708, 715–718, 792–801 directed, 707–708, 719–729 directed acyclic. See directed acyclic graphs (DAGs) edges of, 707–708, 789 HIERARCHYID data type for materialized path model. See HIERARCHYID data type iterative solutions. See iterative graph solutions materialized paths for solving. See materialized path model MAXRECURSION hint, 728–729 nested sets solution, 778–786 nodes of. See nodes paths, returning, 730–733 recursive solutions to, 718–719, 722–723 scenarios used to illustrate, 709–718 sorting, 736–739 subgraphs, algorithm for returning, 719–729 subgraphs with path enumeration, 733–736 transitive closure of directed. See transitive closure trees as, 708 types of, 707–708 undirected, 708 undirected cyclic. See undirected cyclic graphs vertexes. See nodes weighted, 714, 718 greater than (>) operator cardinality estimates, hardcoded, 561 Gregorian calendar, use of, 419 GROUP BY clauses COUNT with, 16 dates and times, grouping by weeks, 449–459 grouping sets, 15 GROUPING SETS subclauses, 328–331 logical order of, 3–5 logical phase order sample, 7 NULL value treatment, 11 phase in logical processing, 4–5, 15–16 with PIVOT operators, 30 running total calculation with, 272–273 SELECT with, 16 group functions vs. window functions, 259 grouped query data-analysis calculation limitations, 261 grouping sets addition, 333–334 CUBE function for, 331–333 execution plans for, 330, 333 feature list for, 327 GROUPING function for ordering, 337–338 GROUPING SETS clause, 328–331 GROUPING_ID function, 334–337 indexes to support sorting, 331 merging new aggregates with, 336–337 multiple sets of, advantages of, 328 multiplication, 333 ordering issues, 337–338 parentheses use in defining, 328 persisting aggregates with, 334–337 power sets, 331 querying a single set, 334–337 ROLLUP function for, 331–334 sets algebra with, 333–334 sets as inputs, 333 UNION ALL alternative, 329–330 groups, WHERE filters, not allowed in, 14 H hashes cardinality overestimations, effects of, 100 cardinality underestimations, effects of, 99 hash algorithm for joins, 243–245 Hash Join operators in parallel query execution, 170–171 Hash Match operator, 167 hash parallel distribution scheme, 163–165 HAVING filters COUNT with, 16–17 logical order of, 3–5 logical phase order sample, 7 phase in logical processing, 4–5, 16–17 subquery inputs prohibited, 17 UNKNOWN values, treatment of, 11 headers, page, 42 headers, statistics, 102 heaps bulk-logging requirements, 376 nonclustered index seek + range scan + lookups, 81–83 nonclustered indexes structured on, 50–51 organization of, 43–46 table scans of, 57–60 Hekaton project. See In-Memory OLTP hierarchies defined, 709 employee organizational chart scenario, 709–711 HIERARCHYID for representing. See HIERARCHYID data type 813 HIERARCHYID data type HIERARCHYID data type adding leaf nodes, 756–759 advantages for materialized path model, 754 ancestors queries, 764–765 case sensitivity, 755 children queries, 765 compared to custom graph implementation, 754 conflict prevention, 757–758 data maintenance requirements, 756 DescendantLimit internal method, 763–764 descendants queries, 763–764 execution plans for, 764 GetAncestor method, 758, 765 GetDescendant method, 757–759 GetReparentedValue method, 760–762 GetRoot method, 757 hid attribute, 757–762, 766 index creation for, 755 IP address sorting with, 773–777 IsDescendantOf method, 760, 763 leaf node queries, 765 long paths issue, 767–771 methods provided by, 755 normalizing values of, 767–771 parent-child adjacency lists, converting to HIERARCHYID, 771–773 path calculations, 757–762 path queries, 764–765 portability issue, 754 presentation queries, 766 querying solutions built with, 758–759, 763–766 sibling positioning, 758 sorting separated lists, 773–777 stored procedures using, 756–757 subgraph queries, 763–764 subtrees, moving, 760–762 table creation using, 755 validity of trees, enforcing, 756 higher population moments, 487–494 histograms AUTO_CREATE_STATISTICS option, 102 AVG_RANGE_ROWS, 102–104 cardinality estimates from, 103–104 creation scenarios, 102 DISTINCT_RANGE_ROWS, 102–103 EQ_ROWS, 102–103 filtered index, 120–122 frequency of data, 476–479 of index key values, 85–86 of non-index key columns, 93 not maintained for table variables, 553 RANGE_HI_KEY, 102–104 RANGE_ROWS, 102–103 refresh rate issues, 107–110, 564–565 steps in, 103 trace flag 2389, 109 HOBT (heap or B-tree) organization, 43–44 814 HOLDLOCK hint, 409 Hyper-Threading, 178 I IAMs (index allocation maps), 44–45, 47 identifying gaps problem, 198–200 identity columns, 412–413 identity property aspects and properties of, 394–395 cache performance issues, 387–394 caches with, 382–383 IDENTITY function, 374 limitations of, 381–382 manual value changes, 381 missing values, causes of, 387 performance considerations, 387–394 SELECT INTO copying of, 374 sequence objects, compared to, 390–395 surrogate key generation with, 381–382 trace flag 272, 382, 387 TRUNCATE TABLE, preserving despite, 396–397 identity values, capturing multiple, 412–413 I-Marks, 10 implicit APPLY operator, 221–222 IMPLICIT_TRANSACTIONS option, 633 importing data. See bulk loads of data IN disjunctions not allowed by In-Memory OLTP, 705 IN predicate vs. EXISTS predicate, 194–195 INCLUDE clauses, 119 included non-key columns, 119–120 inconsistent analysis, 645 INDEX <index_name> CLUSTERED, 43 index allocation maps (IAMs), 44–45, 47 index order scans defined, 46 safety issues with, 76–81 storage engine choice situations, 59 vs. allocation order scans, 47, 65 Index Scan operators, 65, 168–170 index seek access method, 49 Index Seek operator. See also seeks forcing as part of a strategy, 154–155 index seek + range scan + lookups access method, 81–82 inefficiency in Max Concurrent Intervals task, 459 optimizing with subqueries, 192–193 parallel scans with, 168 TOP filters with various indexes, optimization of, 348–352 indexes ALTER INDEX REBUILD command, 48 clustered. See clustered indexes columnstore. See columnstore indexes covering. See covering indexes deadlocks from lack of, 658–659 IN_ROW_DATA allocation units descending, 63, 115–118 dm_db_index_operational_stats function, 135 dm_db_index_usage_stats function, 135–136 dm_exec_query_stats view, 137–138 filtered, 120–122 frequency of use, determining, 135–136 imports, performance hits with, 44 INCLUDE clauses in, column order issues, 95–96 included non-key columns, 119–120 INDEXPROPERTY function, 49 inline index definition, 130–131 join nested loops, for, 239–240 key updates as a source of row return errors, 76–81 leaf updates as a source of row return errors, 76–81 levels in, determining number of, 49–50 memory-optimized tables. See indexes for memory-optimized tables missing index information objects, 136 nonclustered. See nonclustered indexes page data structure for, 42 POC (partitioning, ordering, covering) pattern, 192, 271–273, 284–285 REBUILD keyword, 135 REORGANIZE keyword, 135 root pages, 48 statistics created with, 85–86, 101 TOP filters, effects on optimization of, 349 UDF CLR functions, setting on, 590 WHERE clause predicate, 120 indexes for memory-optimized tables BW-Tree structure, 677–679 clustered vs. nonclustered not meaningful, 677 covering nature of, 677, 680 hash indexes, 680–690 key values, 679 latch-free nature of, 679 leaf pages, 679 must be specified in CREATE TABLE statements, 675 nonclustered indexes, 677–680 page-mapping tables, 678 payloads, 679 pointer-based nature of, 676 single-direction nature of, 679–680 syntax for creating nonclustered, 677 information theory, 518–521 inhibitors of parallelism, 178–181 inline index definition, 130–131 inline TVFs (table-valued functions) end of year calculation example, 548–549 input parameter support, 215 integer sequence generator, 215–218 query requirements for, 204–205 scopes of, 204 syntax for, 215 In-Memory OLTP 2014 introduction of, 671 ALTER TABLE not supported by, 704 ATOMIC blocks, 698 blocking, 691–693 check constraints not supported, 704 clustered indexes not supported by, 704 commit validation errors, 693–696, 702 computed columns not supported, 703 constraints not supported, types of, 704 cross-container queries, 703 cross-database query execution bar, 696 cursors not supported, 704 data always in memory, 672–673 deadlocks, 691–693 DURABILITY option, 675 execution engine efficiency, 673 execution environment for, 690 foreign-key constraints not supported, 704 indexes for. See indexes for memory-optimized tables integration with SQL Server, 674 isolation levels in query interop, 691 isolation semantics for transactions, 690 LOB data types not supported, 704 lock and latch-free architecture of, 673–674 memory optimized data filegroup, 675 MEMORY_OPTIMIZED option, 675 memory-optimized tables, 672 modern computing environment for, 671–672 native compilation of stored procedures, 673 natively compiled procedures. See natively compiled procedures environment, In-Memory OLTP optimistic concurrency model, 674 page elimination, 672–673 parallelism not used currently, 696–697 query interop environment, 690–698 schema options, 675 statistics generation for optimization, 697–698 surface-area restrictions, 703–705 tables, memory-optimized, 675–676 timestamps, 672 transaction isolation and consistency, 672 unique constraints not supported, 704 inner joins default type with JOIN keyword, 229 logical steps in, 228 logical-join evaluation order, 234–237 ON clauses mandatory, 228 ON vs. WHERE clauses with, 15 physical-join evaluation order, 232–233 running total calculation with, 272–273 syntax for, 228 WHERE clauses with, 228 inner queries. See subqueries IN_ROW_DATA allocation units, 44 815 INSERT clauses INSERT clauses AFTER INSERT triggers with, 575–578, 581–585 EXEC statements, full logging vs. other bulk methods, 376 INSTEAD OF INSERT triggers with, 578 INTO clauses with, 412–413 in MERGE statements, 405–406 OUTPUT clauses with, 412–413 INSERT SELECT clauses casting column types with, 374 guaranteed identity value order, 374 identity property, as alternative to, 381 minimal logging, requirements for, 376 OUTPUT clauses with, 417 SELECT INTO compared to, 374–375 INSERT TOP filters, 360 inserting data, SELECT INTO command for, 140, 173–174, 373–376 instance-level parallelism setting, 175–176 INSTEAD OF DML triggers, 578 integration, definite, 509–512 intent exclusive locks (IX), 637 intent shared locks (IS), 637–639 intercepts, 499–501 internal data structures allocation units, 44 B-trees as. See B-trees extents, 42–43 headers, page, 42 heaps as. See heaps heaps, nonclustered indexes structured on, 50–51 IAMs (index allocation maps), 44–45, 47 leaf levels, 46–49 pages, 42–48 partitions, 44 row-offset arrays of pages, 42 table organization, 43–53 internal nodes, 708 inter-quartile ranges, 483, 523 INTERSECT operator characteristics of, 249 INTERSECT ALL version implementation, 254 logical query-processing phase for, 38–39 NULLs, treatment of, 11, 249, 253 ORDER BY clauses with, 249 syntax for, 253 intersection interval calculations for dates and times, 452–456 interval calculations for dates and times Allen’s interval algebra, 452 AND operator performance issues, 252–255 CTE for counting active intervals, 457 delimiters, open vs. closed, 256 grouping counted intervals, 458 index optimization for, 252–255 intersections, 452–456 maximum concurrent interval problems, 456–465 packing intervals, 466–471 816 parallel treatment for performance, 464–465, 470–471 performance in max concurrences solutions, 458–465 performance vs. simplicity of queries, 450–451 Relational Interval Tree (RI-tree) model, 456 ROW_NUMBER OVER window function for, 461–465 window aggregate function with frame solution to max concurrences task, 459–461 INTERVAL not supported, 276–277 intraquery parallelism. See parallel query execution inverse distribution window functions, 289–291 I/O costs, viewing, 176 IP address sorting, 773–777 IQRs (inter-quartile ranges), 483, 523 ISDATE function, 425 IsDaylightSavingTime method, 424 island problems date/time based, 295–299 DENSE_RANK queries, 294–296 existing value range identification equivalence, 291 ignoring small gaps variation, 296–299 LAG OVER function for, 296–299 ROW_NUMBER OVER queries, 293–294 SUM OVER function for, 296–299 ISNULL function, 323 ISO SQL standards, 1–2 isolation levels consistent data control purpose of, 645 cross-container queries, for, 703 deadlocks, relationship to, 660 defaults, 646 dirty reads, 645 isolation models, 646 lost updates, 645 NOLOCK hints, 647–648 nonrepeatable reads, 645 phantoms, 645–646 query interop environment, In-Memory OLTP, 691 query level hints, 646 Read Committed, 646, 648–650, 660 Read Committed Snapshot, 646, 652, 655–656, 660 Read Uncommitted, 646, 647–648 READPAST hint, 646 Repeatable Read, 646, 649–651 Serializable, 409, 646, 651–652 SET TRANSACTION ISOLATION LEVEL statements, 646 Snapshot, 646, 652–655 table of available, 646 isolation property of transactions, 634 iterative graph solutions advantages of, 718 ancestors, returning, 730–733 left outer joins CTEs for, 718–719, 722–723 logic encapsulation choices, 718–719 loops in, 718, 721, 723–728, 730–733 next levels, 718 recursion vs. loops, 718 stored procedures for, 718–719 subgraphs, algorithm for returning, 719–729 subgraphs with path enumeration, 733–736 traversing by stored edges, 718 iterative solutions cursor-based solutions, 151–152 inefficiency in T-SQL, 149–150, 152–153 iterative execution model of SQL Server, 149–150 separating elements problem, inefficiency of, 246 strong concatenation using cursors, 314–315 superiority of CLR functions for, 597 UDFs using, 549–550 vs. set-based approaches, 149–153 J joins algorithms for, 239–245 anti semi, 237–238 cardinality overestimations, effects of, 100 cardinality underestimations, effects of, 99 Cartesian product logical processing phase, 8–9 cross. See Cartesian products; cross joins default type with JOIN keyword, 229 equi, 230–231 FORCE ORDER hint, 233, 236–237, 245 forcing algorithm strategies for, 244–245 hash algorithm for, 243–245 index support for, 239–240, 243 inner. See inner joins JOIN operator, elements of, 27 left. See left outer joins left outer. See left outer joins logical phase order sample, 7 logical-join evaluation order, 234–237 LOOP hint for nested loops algorithm, 244–245 merge algorithm, 241–243, 244–245 multi-join queries, 231–237 nested loops join algorithm, 239–241, 244–245 Nested Loops performance issue with, 184 non-equi, 230–231 operator for, 27 OPTION clause hints, 244–245 outer. See outer joins parentheses with multiple, 235–236 physical-join evaluation order, 232–233 prefetches with, 241 self, 230 semi, 237–238 SQL-89 vs. SQL-92 syntax, 224–229 star. See star join queries suboptimal join order issues, 232–233 K KEEPFIXED PLAN hint, 570–571 keys automating creation with sequence objects, 384–385 columns defined as in B-tree clustered indexes, 46 density, effects on optimization, 351–352 foreign-key constraints not supported by In-Memory OLTP, 704 index seek access method for, 49 key columns added to index implicitly, 102 key lists, restrictions on, 119 lookups in nonclustered indexes, 53 lookups, nonclustered index seek + range scan +, 81–90 primary, 43, 408–409 values, histograms of, 85–86 Kriegel, Hans-Peter, 456 kurtosis, 489–495, 523 L LAG OVER window function, 287, 296–299 languages LANGUAGE option, 568 SET LANGUAGE command, 434–435 last day of the year calculations, 547–549 LAST_VALUE OVER window function, 285–286, 287, 511 latches, 673–674 lateral derived tables. See APPLY operator lazywriter process, 42–43 LEAD OVER window function, 287, 291–292 leaf layout, showing with DBCC IND command, 70–71 leaf levels clustered index, rows stored in, 46–47 ordered clustered index scans of, 62–63 rows, clustered index, 93 leaf nodes adding into HIERARCHYID graphs, 756–759 shake effects of adding new, 743, 782 vs. internal nodes, 708 leaf pages BW-Tree structure, 678–679 density, 48–49 rows per, access method dependence, 94–95 splits, 47–48 leap seconds, 421–422 left outer joins. See also outer joins anti semi joins from, 238 followed by other joins, issues with, 234–235 LEFT JOIN, LEFT OUTER JOIN equivalence, 229 logical phase order sample, 7 preserved tables, 13 817 left outer joins left outer joins, continued self join example, 230 subphase of logical processing of, 8–9 vs. right outer joins, 230 less than (<) operator cardinality estimates, 561 LIKE predicate, cardinality estimates with, 112–115 linear dependencies chi-squared tests, 501–505, 523 coefficient of determination (CD), 498–499, 523 correlation coefficients, 498–499, 523 covariance, 495–498, 523 intercepts, 499–501 linear regression, 499–501, 523 slopes, 499–501 two continuous variable dependencies, 495–500 literals, date and time, 434–436 LOB (Large Object) data types LOB_DATA allocation units, 44 not supported by In-Memory OLTP, 704 @local_variable method, 319–322 locks blocking function of, 636 blocking situations, 638–640, 657–658. See also deadlocks deadlocks resulting from. See deadlocks delayed durability, 643–645 dm_exec_connections view for troubleshooting, 639–640 dm_exec_requests view for troubleshooting, 640 dm_exec_sessions view for troubleshooting, 640 dm_os_waiting_tasks view for troubleshooting, 640 dm_tran_locks view for troubleshooting, 639 escalation, 361–363, 641–643 exclusive locks (X), 636–637 In-Memory OLTP vs. traditional, 691–693 intent exclusive locks (IX), 637 intent shared locks (IS), 637–639 isolation function of, 636 isolation levels with. See isolation levels KILL command, 641 LOCK_TIMEOUT option, 638, 641 memory requirements of, 641 NOLOCK hints, 647–648 Read Committed isolation level, 638 Read Uncommitted isolation level, 636–637 READPAST hint, 646 resource hierarchy conflict detection, 637 SELECT INTO, created by, 375 shared locks (S), 636–639, 648–649 shared with intent exclusive locks (SIX), 637 SQL Database alternative model for, 636 TOP modification statement issues from, 361–362 update locks (U), 637 UPDLOCK hints, 637, 650–651 WAIT status, 639 write-ahead logging, 643 818 logarithms, 322–324 Logical Operation property, 239 logical order vs. typed order, 3 logical processing order vs. physical order, 2–3 logical query-processing phases add outer rows phase, 4–5, 13 APPLY operator, 26–29 Cartesian product phase, 4–5, 8–9 descriptions of, brief, 4, 6 DISTINCT phase, 6, 18–20 evaluate expressions phase, 6 EXCEPT operator, 38–39 flow diagram of, 5 FROM phase, 4–5, 8–14 GROUP BY phase, 4–5, 15–16 HAVING phase, 4–5, 16–17 INTERSECT operator, 38–39 logical order vs. typed order, 3 OFFSET-FETCH filter, 6, 22–26 ON predicate phase, 4–5, 9, 11–13 ORDER BY, 6, 20–22 order of phases, 3–4 PIVOT operator, 29–31 sample query, 6–8 SELECT phase, 4–5, 17–20 step numbers, 3 table operators, 26–27 TOP filter, 6, 22–26 UNION operator, 38–39 UNPIVOT operator, 27, 31–35 virtual table generation, 4 WHERE phase, 4–5, 14–15 window functions, 35–37, 268–269 logical reads as performance metric for access methods, 57 STATISTICS IO output data, 55 logs active portion, expansion issues, 361 flushing buffers to complete transactions, 634 log-intensive operations, 361 measuring logging, 377–378 SELECT INTO command logging options, 374 test for statistics during sequence creation, 390–393 write-ahead logging, 643 lookups expense of, minimizing with covering indexes, 93 join nested loops, generated by, 239 key lookups, 53 nonclustered index seek + range scan + lookups, 81–90 RID lookups, 51 unordered nonclustered index scans + lookups, 91–93 loops CTEs shortest-path solutions vs., 792–801 indexes for join nested loops, 239–240 iterative graph solutions with, 718, 721, 723–728, 730–733 moving average value computations LOOP hint for nested loops join algorithm, 244–245 Nested Loops operators, 98, 183–186, 240–241 recursion vs., 718 shortest-path solutions vs. CTEs, 792–801 WHILE loops with TOP on key order, no key index, 581 lost updates, 645–646 LRU-K2 algorithm, 42 M Machanic, Adam, 641 MAD (mean absolute deviation), 484, 523 many-to-one pivots, 304–307 Martin, Laurent, 456 materialized path model adding leaves, stored procedure for, 744–745 advantages of, 743 ancestors, returning, 751–753 defined, 742 execution plans for, 750, 754 HIERARCHYID data type for implementing. See HIERARCHYID data type index creation for, 743–744 index key size limitations, 743 leaf nodes, returning, 751 level and path revisions, 745–748 performance issues, 752 queries, 749–754 shake effects, 743 sorting, 753–754 splitting paths, 752–753 subtrees, moving, 745–748 subtrees, removing, 748–749 subtrees, returning, 751 Max Degree of Parallelism setting, 175–178 MAX function with CASE expressions, 301–302 window function (OVER) version of, 275–276 Max Worker Threads setting, 176–178 max_dop setting, 176 maximum concurrent interval problems, 456–465 MAXRECURSION hint, 728–729 mean absolute deviation (MAD), 484, 523 mean squared deviation (MSD), 484, 523 means arithmetic, 479, 523 of continuous variables, 487–488, 509–512 MAD (mean absolute deviation), 484, 523 MSD (mean squared deviation), 484, 523 medians continuous-distribution model, 368 defined, 479, 523 OFFSET-FETCH with APPLY solution, 370–371 PERCENTILE_CONT solution, 369 PERCENTILE_CONT solutions, 480–481 PERCENTILE_DISC function for, 480–481 ROW_NUMBER solution, 369–370 table for testing, 368–369 window functions for, 289–291 memory leak handling by SQLOS, 586, 588 OLTP in. See In-Memory OLTP memory optimized data filegroup, 675 MEMORY_OPTIMIZED option, 140 memory-optimized tables, parallelism inhibited by, 179 merge algorithm for joins, 241–245 MERGE statements conflict prevention, 408–409 DELETE clauses with, 405–406, 414–416 full logging vs. other bulk methods, 376 INSERT clauses with, 405–406, 414–416 INTO updating persisted sets with, 336–337 MERGE INTO clauses, 405 MERGE ON predicates, 409–410 multiple source rows matched to target errors, 406 NULL handling, 407 ON predicates, 405 OPENROWSET BULK clauses with, 410–411 OUTPUT clause with, 414–416 SERIALIZABLE isolation level for, 409 sources of data allowed for, 410–411 UPDATE clauses with, 405–406, 414–416 USING clauses, 405, 410–411 WHEN MATCHED clauses, 405–407, 415–416 WHEN NOT MATCHED clauses, 404–407 MERGE TOP filters, 360 merging exchange variant of Gather Streams, 165–166 Microsoft Azure SQL Database alternative lock model, 636 Microsoft SQL Server. See SQL Server Microsoft Virtual Academy (MVA), 224 Microsoft Visual Studio for CLR code, 589–591 Microsoft Windows time, 421–422 minimum missing values, finding, 195–199 missing values finding, 195–200, 291–292 treatment of, 10 mixed extents, 43 modes as centers of distributions, 479, 523 custom aggregate calculations of, 324–327 TOP WITH TIES to calculate, 480 MONTH function, 425 moving average value computations advantages over other statistics, 512–513 defined, 523 exponential moving averages, 515–518 simple moving averages, 513–514 weighted moving averages, 514 window functions for, 274 819 MSD MSD (mean squared deviation), 484, 523 multi-join queries DISTINCT clauses in, 231–232 FORCE ORDER option, 236–237 left outer joins followed by other joins, issues with, 234–235 logical-join evaluation order, 234–237 multiple ON clauses in, 236 optimization, bushy plant, 236–237 parentheses with, 235–236 physical-join evaluation order, 232–233 right outer joins for optimization, 235 multiple occurrences of rows allocation order scan sources of, 65–76 index order scan sources of, 76–81 multistatement TVFs (table-valued functions), 550–553 multi-table queries APPLY operator based, 218–223 joins in. See joins relational operators in. See relational operators subqueries as. See subqueries table expressions as. See table expressions multi-valued subqueries, 188–189 MVA (Microsoft Virtual Academy), 224 N name resolution, implied, errors from, 201–202 National Institute of Standards and Technology (NIST), 707 natively compiled procedures environment, In-Memory OLTP advantages of, 690 ATOMIC blocks, 698 cross-container queries, for, 703 cursors not supported, 704 performance vs. interop, 699 retry logic, 702 statistics generation, 697–698 stored procedures (SQL) not callable within, 705 TVPs for, 699–702 validation errors, 702 negative vs. positive logic, 200–201 nested loops join algorithms AND vs. BETWEEN in queries, 784 forcing an optimization strategy, 244–245 index performance issues, 239–241 Nested Loops operator bottlenecks, eliminating with Parallel APPLY Pattern, 183–186 cardinality estimate problems with, 98 parallel scans with, 169–170 nested sets graph solution advantages of, 778 ancestors of nodes, returning, 786 dynamic trees defect, 778, 782 820 indexes for, 779 leaf nodes of roots, returning, 785 performance issues, 782–783 querying, 784–786 shake effect, 782 sort paths for nodes, 781 subordinates of nodes, counting, 786 subtrees of roots, returning, 784–785 value assignments, 778–784 nesting CTEs, 208 derived tables, issues from, 206 loops. See nested loops join algorithms of triggers, 577–578 TRY-CATCH constructs, 664 .NET Base Class Library, 597 .NET Framework, Microsoft. See SQLCLR programming Nevarez, Benjamin, 233 NEWID function, UDF prohibition of, 546 NEXT VALUE FOR function, 382–386 NIST (National Institute of Standards and Technology), 707 NO_BROWSETABLE option, 568 nodes adding, long paths resulting from, 767 ancestor. See ancestors child. See child nodes connections between. See edges defined, 707 descendant. See descendants directed vs. undirected edges with, 707–708 next levels of, 718 parent. See parent nodes subtrees, returning, 719–720 types of, 708 NOLOCK hints, 68, 72, 647–648 nonclustered index seek + range scan + lookups, 81–90 nonclustered indexes clustering keys, 52–53 covering nonclustered index seek + range scan, 96 data structure with, 43–44 filtered, 120–122 full scans of, avoiding, 153–156 included non-key columns in indexes, 119–120 In-Memory OLTP requirement for, 704 key lists, restrictions on, 119 key lookups, 53 levels of, calculating, 49–50 memory-optimized tables with, 677–680 nonclustered index seek + range scan + lookups, 81–90 ordered covering nonclustered index scans, 63–65 row identifiers (RIDs), 51 seek operations in, 52–53 ordered covering nonclustered index scans structured on B-trees, 52–53 structured on heaps, 50–51 uniquifiers, 52–53 unordered covering nonclustered index scans, 60–62 unordered nonclustered index scans + lookups, 91–93 non-equi joins, 230–231 NonParallelPlanReason node, 180–181 nonrepeatable reads, 645–646 normal distributions, 487–488, 509–512 NOT EXISTS clauses. See EXISTS predicate NOT IN clauses, 203–204 NTILE OVER window function, 282–283, 285 null hypotheses, 495 NULLs CONCAT_NULL_YIELDS_NULL set option, 568–570 ISNULL function, 323 IsNull property, SQLCLR, 596 missing value nature of, 10–11 NOT NULL constraints, 203–204 ORDER BY treatment of, 22 relational operators with, 249 subquery errors from, 203–204 UNION, EXCEPT, and INTERSECT operations on, 39 NUMERIC data type, 592–593 NUMERIC_ROUNDABORT option, 569 NVARCHAR type, 593–595 O objects, page membership in, 42 OFFSET BY clauses, 3–5 offset window functions, 285–287 OFFSET-FETCH filters 2012 vs. 2014 versions of, 341 execution plans, 355–356 FETCH clause optional, 346 FIRST keyword, 345 index optimization, 357 isolation level effects on output, 354 median calculations with, 370–371 NEXT keyword, 345 offset required, 345 OffsetExpression property in plan, 354–357 ONLY keyword, 345 optimization of, 354–357 ORDER BY (SELECT NULL), 346 ORDER BY clause required, 345–346 ordering issues, 23–26, 345 paging solutions with, 345, 354–357 phase in logical processing, 6, 22–26 ROW/ROWS keywords, 345 similarity to TOP filters, 345 syntax for, 23, 345 ties not supported, 345 TO | AFTER imagined syntax, 357 Top operator to process, 354–357 OLAP (online analytical processing) Max Degree Of Parallelism setting, 178 OLTP workloads In-Memory feature. See In-Memory OLTP Max Degree Of Parallelism setting for, 178 merging data for, 404 ON predicate inner joins, mandatory for, 228 missing values, treatment of, 10 multiple ON clauses in multi-join queries, 236 outer joins using, 230 phase in logical processing, 4, 9–13 UNKNOWN values, treatment of, 11 WHERE clauses, vs. in outer joins, 14–15 one-to-one pivots, 300–304 online transaction processing. See OLTP workloads open schema, 300–301 OPENROWSET function inserting data with, 378–380 MERGE statements using, 410–411 operators, query plan. See execution plans optimistic concurrency model, 674 optimization of queries. See query tuning OPTIMIZE FOR hint, 561–562 optimizer. See query optimizer OPTIMIZER_WHATIF command (DBCC), 89–90 OPTION(LABEL = 'some label') hint, 556 OR disjunctions not allowed by In-Memory OLTP, 705 ORDER BY clause with SQLCLR table-valued functions, 600 ORDER BY clauses column aliases from SELECT phase, 17 cursors returned by, 21–22 DISTINCT clauses with, 20–22 dynamic sorting with, 542–546 grouping sets with or without, 337–338 logical order of, 3–5 logical phase order sample, 7 NULL value treatment, 11, 22 OFFSET-FETCH filter with, ordering issues, 22–26, 345–346 phase in logical processing, 6, 20–22 prohibited in set operation queries, 38–39 relational operators with, 249 SELECT @local_variable method with, 319–322 SELECT NULL trick, 285, 344 table expression inner query issues with, 204 TOP filter with, 22–26, 341–342 window functions in, 35–37 order of processing logical order, 3–6 physical order, 2–3 typed order, 3 ordered clustered index scans, 62–63 ordered covering nonclustered index scans, 63–65 821 ordered set functions ordered set functions, 290, 326–327 OUTER APPLY operator, 27–28, 221 outer joins FULL keyword with, 229 LEFT keyword with, 229. See also left outer joins logical processing phase, 13 not allowed by In-Memory OLTP, 705 ON clauses, 230 ON vs. WHERE clauses with, 14–15 physical-join evaluation order, 232–233 preserved tables, 13, 229–230 RIGHT keyword with, 229 syntax issues from, 224–225 type specification of, 13 WHERE clauses, 230 outer rows, 13 out-of-order pages, 47 OUTPUT clauses $action function in, 414–416 chunking with, 414 column identification in, 411 DELETE statements with, 413–416 flexibility of, 411 INSERT clauses with, 412–416 INSERT SELECT clauses with, 417 INTO clauses with, 411, 412–413, 414 MERGE statements with, 414–416 UPDATE statements with, 411, 414–416 P packing intervals for dates and times, 466–471 page free space (PFS), 45 pages elimination by In-Memory OLTP, 672–673 extents of, 43 out-of-order, 47 splits, 47–48 structure of, 42 table structure with, 44–45 paging solutions with OFFSET-FETCH filters, 345 optimization of OFFSET-FETCH for, 354–357 optimization of ROW_NUMBER for, 358–360 optimization of TOP for, 346–353 parallel query execution advantages of, 158 Affinity Mask setting, 175, 177 APPLY pattern, 181–186 broadcast distribution scheme, 163 Bulk Copy API, 173 cardinality overestimations, effects of, 100 cardinality underestimations, effects of, 99 configuration guidelines, 177–178 Cost Threshold for Parallelism setting, 175–176 costs, parallel vs. serial executions, 87–90 CPU costs, viewing, 176–177 822 CXPacket data structure, 163 degree of parallelism (DOP), 159, 161–162, 164, 175 Demand distribution scheme, 163, 171–172 dependence on number of logical CPUs, 89–90 Distribute Streams variant of Exchange operator, 160–163 Exchange operators, 160–166 execution plan icons indicating, 159–160 factory line model of, 158 few outer rows optimization, 168–170 forward scans only rule, 116 Gather Streams variant of Exchange operator, 160–163, 165–166 hash distribution scheme, 163–165 Hash Join operators in, 170–171 Hash Match operator, 167 inhibitors of parallelism, 178–181 instance-level settings, 175–176 interval calculations with, 464–465 legacy vs. 2014 versions, 174 manual rewrites as alternative to query optimizer, 181–186 Max Degree of Parallelism setting, 175–178 Max Worker Threads setting, 176–178 max_dop setting, 176 memory-optimized table transactions, not used with, 696–697 merging exchange variant of Gather Streams, 165–166 nested loop solutions, 181–186 nonclustered index seek + range scan + lookups, 87–90 NonParallelPlanReason node, 180–181 optimization process, 176–177 packing tasks with, 470–471 Parallel APPLY Pattern, 181–186 Parallelism (Gather Streams) operator, 88, 160–163 partial aggregation, 166–167 partitioned tables, 171–173 –Pn startup parameter tool, 90 properties window features, 164 query cost metric, 178 query plans with, 159–160 range distribution scheme, 163 Repartition Streams operator, 161, 163, 169–170 Resource Governor settings, 175–176 round robin distribution scheme, 163, 169–170 row-distribution strategies, 163–165 scans, parallel, 168–170 SELECT INTO bulk operations, 173–174, 374 sorting, 165–166 stream-based model used by SQL Server, 159 threads per zone, 159, 161 trace flag 8649 tool, 90, 117 UDF inhibition of, 179–180, 548 XPacket data structure, 163 PRECEDING keyword parameters compiled values, 555–558 passed vs. declared within procedures, 559 preventing sniffing, 564–568 sniffing, 555–558 parent nodes as ancestors. See ancestors defined, 708 nested sets model of, 778 parent-child adjacency lists, converting to HIERARCHYID, 771–773 PARSE function for dates and times, 431–433, 435 partial aggregation, 166–167 partitioning, ordering, covering pattern for indexing. See POC (partitioning, ordering, covering) pattern for indexing partitions allocation unit types used by, 44 changes in, recompiles triggered by, 570–571 creating, 171–172 descending indexes with PARTITION BY, 117–118 parallel query execution with, 171–173 ranking with window functions, 281–285 structure of, 44 window functions with PARTITION BY, 263–266 paths. See also edges ancestors, returning, 730–733 cycles in, determining if, 740–742 cyclic vs. acyclic, 708 HIERARCHYID data type for materialized path model. See HIERARCHYID data type long paths issue, 767–771 materialized algorithms for solving. See materialized path model multiple into a node, 711 subgraphs with paths, 733–736 topological sorts using, 736–739 transitive closure of directed. See transitive closure Pearson chi-squared formula, 501–505 PERCENT input indicator, TOP filters, 342 percentages absolute, 476–479 cumulative, 476–479 frequency calculations with PERCENT_RANK OVER, 477–478 grand totals, of, 268–269 PERCENT_RANK OVER window function, 288–289 percentiles inter-quartile range calculations, 483 inverse distribution functions for calculating, 289–291 PERCENTILE_CONT for finding inter-quartile ranges, 483 PERCENTILE_CONT for finding medians, 369, 480–481 PERCENTILE_CONT window function, 289–291 PERCENTILE_DISC window function, 289–291, 480–481 performance DMVs (dynamic management views), 134–139 query tuning for. See query tuning tools for measuring. See execution plans; statistics, performance related typical workloads, measuring. See Extended Events sessions pessimistic concurrency model, 674 PFS (page free space), 45 phantom reads defined, 645–646 Serializable level for preventing, 651–652 vs. isolation levels, table of, 646 phases, logical query processing. See logical queryprocessing phases phone numbers, cleaning up, 549–550 physical execution plans, 97 physical layer, query tuning dependence on, 41 Physical Operation property, 239 physical processing order, 2–3 physical reads, 55 physical-query execution plans, 2 pivoting. See also unpivoting actual frequencies, calculating with PIVOT operator, 503 CASE expressions, implicit use of, 30–31 custom aggregations with, 315–316, 318–319 dynamic, 530–535 FOR XML with, 318–319 implicit grouping phase, 30 many-to-one pivots, 304–307 one-to-one pivots, 300–304 phase in logical processing, 27, 29–31 PIVOT operator elements, 27 PIVOT operator syntax, 302–304 row counting with PIVOT, 534 spreading attributes, 301–302 use cases for, 299 Plan Explorer tool, 139 plan optimality, 568, 570–571 plan stability, 568–570 plans, parallel. See parallel query execution plans, query execution. See execution plans POC (partitioning, ordering, covering) pattern for indexing correlated subquery optimization, 192 ranking optimization with, 284–285 running totals optimization, 271–273 top N per group tasks, recommended for, 363–365 population moments, 479, 490. See also distributions positive vs. negative logic, 200–201 Pötke, Marco, 456 power sets, CUBE function for creating, 331–333 PRECEDING keyword, 269–274, 277, 280–281 823 predicates, multiple, cardinality estimates for predicates, multiple, cardinality estimates for, 104–107 prefetches joins generating, 241 statistics on, 55 preserved tables of outer joins, 13 primary keys clustered indexes resulting from, 43 merges causing violations, 408–409 procedure statistics, 138 processing order, physical vs. logical, 2–3 processing queries. See logical query-processing phases programmable objects Dynamic SQL. See Dynamic SQL SQLCLR. See SQLCLR programming stored procedures. See stored procedures triggers. See triggers user-defined functions. See UDFs (user-defined functions) proleptic Gregorian calendar, use of, 419 Q query cost metric, 178 query execution plans. See execution plans query interop environment, In-Memory OLTP ATOMIC blocks, 698 blocking, 691–693 cardinality estimates, 697 commit validation errors, 693–696 cross-database query execution bar, 696 deadlocks, 691–693 isolation levels available in, 691 parallelism not used currently, 696–697 similarity to standard SQL Server, 690 statistics generation, 697–698 query optimizer cardinality estimator component of, 97–98 cost breakdowns, 176 Cost Threshold for Parallelism setting, 175–176 flaws in, 154, 156 manual rewrites as alternative, 181–186 ordering issues, 543 parallel optimization process, 176–177 parallel plans, criteria for, 175 plans created by. See execution plans SQL Server, as component of, 2 unnesting of subqueries, 156 query processing. See logical query-processing phases query processor, query optimizer component of, 97 query tuning access method issues. See access methods allocation order scans, 65–76 bitmaps for, 170–171 824 cardinality estimates for. See cardinality estimates clustered index seek + range scan, 93–94 cost breakdowns, 176 covering nonclustered index seek + range scan, 94–97 CPU costs, viewing, 176–177 data structures. See internal data structures date and time calculations, 445–446 DMVs (dynamic management views), 134–139 early row reduction benefit, 166 execution plans for. See execution plans extended events, prioritizing with, 131–134 Extended Events session statistics, 54, 56 filtered indexes, 120–122 graphical execution plans for. See execution plans hash join and bitmap in parallel queries, 170–171 histograms for. See histograms included non-key columns in indexes, 119–120 index order scan safety issues, 76–81 inline index definition, 130–131 join order optimization, 232–233 multiplicity of tasks and optimal solutions, 368 nonclustered index seek + range scan + lookups, 81–90 Parallel APPLY Pattern, 183–186 parallel optimization process, 176–177 partial aggregation, 166–167 PARTITION BY clauses, 117–118 partitioned tables, parallelism for, 171–173 physical layer, importance of, 41 prioritization of queries with Extended Events sessions, 131–134 query cost metric, 178 query revisions for, 153–157 safety issues, allocation order scan, 65–76 SARGs, 445–446 scaling issue, 153 SELECT INTO bulk operations, 173–174 set-based vs. iterative solutions, 149–153 statistical vs. transactional query optimization, 473 statistics for. See statistics, performance related storage engine scan treatment, 65–76 stored procedure tuning, 554–568 strategies, testing for desired execution plans, 154–158 TOP filters to avoid unnesting, 156 unordered nonclustered index scans + lookups, 91–93 query_hash action, 131–134, 137–138 query-processing phases, logical. See logical queryprocessing phases QUOTED_IDENTIFIER option, 569 QUOTENAME function, 318–319 row identifiers R RAISERROR command, 664–665 RAND function, UDF prohibition of, 546 range of a distribution, 482–483, 523 range parallel distribution scheme, 163 range predicate access method issues, 95–97 range scans. See scans RANGE UNBOUNDED PRECEDING phrase, 279–280 RANGE unit for window frames, 276–280 rank, calculating CUME_DIST OVER, 288–289 PERCENT_RANK OVER, 288–289 rank distribution window functions, 288–289 RANK OVER window function, 282–283, 285 window functions for, 281–285 RDBMS (relational database management system), 2 Read Committed isolation level, 646, 648–650, 660 Read Committed Snapshot isolation level, 646, 652, 655–656, 660 Read Uncommitted isolation level compared to other isolation levels, 646 no shared locks, results of, 646–648 NOLOCK resulting in, 68, 81 shared locks not acquired on rows, 78–81 unsafe category allocation order scans, 68–69 reads access method use of. See access methods DataAccessKind.Read, 179 dirty, 645–647 isolation level settings for. See isolation levels logical, 55, 57 phantom, 645–646, 651–652 read-ahead, 55 REBUILD keyword, 135 rebuilding indexes, 135 recompilations of stored procedures, 568–571 RECOMPILE option efficiency benefits from, 537–539 issues with, 559 statement-level vs. procedure-level, 537, 559 stored procedures, preventing plan reuse, 558–560, 562–563 table variables with, 143–145, 148 unknowns, allowing sniffing of, 110 recovery models for database, 376–378 recovery models for transactions, 634 recursion ancestors, returning, 730–733 limiting in an ON clause, 729 MAXRECURSION hint, 728–729 recursive CTEs, 209–211, 718–719, 722–723 triggers, 577–578 reduce and conquer algorithms, 733 references column, table-qualified, 8 correlations, 189 regression, linear, 499–501 relational database management system (RDBMS), 2 relational division, 188 relational engine, SQL Server, 97 Relational Interval Tree (RI-tree) model, 456 relational model of missing values, 10 SQL deviation from for duplicate rows, 18 relational operators EXCEPT, 249, 255–256 input requirements, 249 INTERSECT, 249, 252–254 NULLs, treatment of, 11 ORDER BY clauses with, 249 precedence of, 249 result column names, 249 syntax of, 249 UNION, 249–252 relations, application of operators to, 38 relative standard deviations, 486 REORGANIZE keyword, 135 Repartition Streams operator bottleneck from early use of, 183 redistribution of streams role, 161, 169–170 row-distribution strategies of optimizer, 163 too few rows triggering, 169–170 Repeatable Read isolation level, 646, 649–651, 691 REPLACE function, 597 Resource Governor parallelism settings, 175–176 RESULTS SETS clauses, 573–575 retry logic, 669–670 RIDs (row identifiers) nonclustered index seek + range scan + lookups, 81–90 structure of, 51 right outer joins optimizing multi-join queries with, 235 preserved tables, 13 RIGHT keyword, 229 vs. left outer joins, 230 RI-tree (Relational Interval Tree) model, 456 road system scenario, 715–718 rollbacks ROLLBACK TRAN command, 634–635 sequence object issues from, 386 temporary table and variable behavior, 148–149 ROLLUP function, 331–334, 337–338 root nodes, 708 root pages, 48 round robin parallel distribution scheme, 163, 169–170 rounding dates and times issues, 447–449 ROUND function, 322 row constructor Standard SQL feature, 349 row identifiers (RIDs), 51, 81–90 825 row numbers row numbers calculations of, 477–478 generation, determined vs. random, 283 window function for. See ROW_NUMBER OVER window function @@rowcount, triggers using, 582 row-distribution parallel strategies, 163–165 ROW_NUMBER OVER window function islands task solutions with, 292–294 maximum concurrent intervals task, 461–465 medians, calculating, 369–370 optimization of, 358–360 packing interval solutions, 466, 468–471 paging solutions with, 358–360 ranking function of, 282–285 separating elements with, 248 top N per group solution with, 364–365 topological sorts with, 736–739 row-offset arrays of pages, 42 ROW_OVERFLOW_DATA allocation units, 44 rows errors due to access methods, 65–81. See also scans estimated by histograms, 102–103 estimated from density vectors, 103 estimated in execution plans, 98 generating large numbers of with cross joins, 216–217 outer rows, 13 ranking. See ROW_NUMBER OVER window function ROWS as a window frame unit, 269–276 ROWS UNBOUNDED PRECEDING phrase, 270–271, 273, 281 running total calculations SUM OVER window elements example, 264 window functions for, 271–273, 477 year-to-date (YTD) calculation, 280–281 S sample data generation with cross joins, 225 SARGs (search arguments), 445–446 SAVE TRAN command, 635 scalar subqueries, 187–188 scaling databases, query tuning issue, 153–157 parallel stream-based model, 159 sort parallelism, 165–166 window functions vs. join and grouping for running totals, 273 scans allocation order. See allocation order scans backward, 116–117, 179 clustered index seek + range scan, 93–94 covering nonclustered index seek + range scan, 94–97 826 expensive part of plans, identification of, 154 fragmentation, 47–48 full, of nonclustered indexes, avoiding, 153–156 full scans of leaf level unclustered, expensiveness of, 154 full scans when no order required, 57–60 full table, resulting in deadlocks, 658 index order scans, 46–47, 59, 65, 76–81 ordered clustered index scans, 62–63 ordered covering nonclustered index scans, 63–65 parallel, 168–170 RIDs, using in, 51 scan count statistic, 55 storage engine treatment of, 65–81 table scan/unordered clustered index scan access method, 57–60 unordered covering nonclustered index scans, 60–62 unordered nonclustered index scans + lookups, 91–93 SCHEMABINDING option, views, 212 scope batches, relationship to, 525 SCOPE_IDENTITY function, 412 of table expressions, 204 temporary objects (tables and variables), 140, 148 search arguments, 445–446 second regression line, 500 security issues Dynamic SQL EXEC parameters, 526–528 EXECUTE AS clauses, 546 SQLCLR stored procedure credentials, 614 seeks clustered index seek + range scan access method, 93–94 cost in an index, 154 cost in reads, 49–50, 82 covering nonclustered index seek + range scan access method, 94–97 forcing as part of a strategy, 154–155 generated by POC index strategies, 364 index seek access method, 49 looking for particular key in nonclustered indexes, 52 operators using. See Index Seek operator Segment operator, 64 Seidl, Thomas, 456 SELECT clauses all-at-once operations, 17–18 AS with, 17 column aliases and processing order issue, 14 column references with, 17 Evaluate Expressions phase, 17–18 evaluate expressions phase, 6 INTO with. See SELECT INTO command logical order of, 3–5 ORDER BY clauses with, 20–22 spooling optimization for window functions phase in logical processing, 4–5, 17–20 sample query for logical phases, 7 SELECT @local_variable method, 319–322 window functions in, 35–37 SELECT INTO command bulk operations, 173–174 deduplification with, 400–401 drawbacks of, 375 eager writes, 140, 374–375 execution plans, 374 IDENTITY function with, 381 identity property copying, 374 In-Memory OLTP, not supported by, 705 INSERT SELECT as alternative, 374–375 logging, measuring, 377–378 logging options, 374 materials copied by, 373 metadata access blocking issue, 375–376 parallelism capability of, 374 self joins, 230 self-contained subqueries, 187–189 semi joins, 237–238 separating elements of arrays tables, 245–249 SEQUEL (Structured English QUEry Language), 2–3 sequence objects ALTER SEQUENCE command, 382, 384 aspects and properties of, 394–395 cache performance issues, 387–394 CREATE SEQUENCE command, 382 CYCLE option, 383 data types supported by, 382 default values, 382 identity property, compared to, 384, 390–395 INCREMENT BY property, 383 keys, automating creation of, 384–385 MAXVALUE property, 382–383 MINVALUE property, 382–383 missing values, causes of, 387 NEXT VALUE FOR function, 382–386 NULLs allowed in target columns, 385 overwriting existing keys with, 385 performance considerations, 387–394 rolled back transaction issues, 386 START WITH property, 383 storing values for use, 385 syntax for creating, 382 sys.Sequences view metadata, 384 sequences of numbers, generating identify property for. See identity property performance considerations, 387–394 row numbers. See row numbers sequence objects for. See sequence objects table expressions for, 215–218 Serializable isolation level compared to other levels, 646 implementing, 651–652 merger conflict prevention with, 409 query interop environment with, 691 session contexts, 584 set-based approaches nested sets graph solution, 778–786 separating elements problem, 245–249 sets defined, 149 vs. iterative solutions, 149–153 SGAMs (shared global allocation maps), 42 Shannon, Claude E., 518 shared global allocation maps (SGAMs), 42 shared locks (S), 636–639, 648–649 shared with intent exclusive locks (SIX), 637 shortest-path solutions, 791–801 Showplan Statistics Profile events, 138 Showplan XML Statistics events, 138 SHOW_STATISTICS command, 101–102 sibling nodes defined, 708 sorting within, 736–739 significance, statistical, 498, 502, 509–512 Simple data recovery model cache performance test with, 390–394 SELECT INTO operations with, 378 simple moving averages (SMAs), 513–514 SIMPLE recovery model for transactions, 634 SINGLE_ options, OPENROWSET function, 379–380 skewness, 479, 488–489, 490–495, 523 skipping rows allocation order scan sources of, 65–76 index order scan sources of, 76–81 slopes, 499–501 SMALLDATETIME data type, 419–420 SMALLDATETIMEFROMPARTS function, 431 SMAs (simple moving averages), 513–514. See also moving average value computations Snapshot isolation level, 646, 652–655, 691 sniffing, disabling, 110 Sommarskog, Erland, 249, 542, 573 sorting cardinality overestimations, effects of, 100 cardinality underestimations, effects of, 99 carry-along sorts, 366–368 dynamic, 542–546 graphs, hierarchical, 736–739 IP addresses, 773–777 nonlinear scaling of Sort operation, 183 ORDER BY for. See ORDER BY clauses parallel execution of, 165–166 separated lists of values, 773–777 Sort operator, expense of, 326–327 TOP filter ordering issues, 22–26 sp_executesql procedures, 529–530, 539–540 splits, page allocation order scan errors from, 65–68 causes of, 47–48 spooling optimization for window functions, 278–279 827 spreading attributes spreading attributes, 301–303, 305, 315–316 spreads of distributions inter-quartile ranges, 483 mean absolute deviations, 484 mean squared deviations, 484–485 ranges, 482–483 standard deviations, 486–487, 490, 498, 511, 523 variances, 485–486, 505–508 sp_statement_completed events, 131 SQL, 1–2. See also T-SQL (Transact SQL) SQL Database alternative lock model, 636 default isolation level, 646 SQL injection attacks, 526–530, 533 SQL Sentry, 139 SQL Server Data Tools (SSDT), 589 Management Studio. See SSMS (SQL Server Management Studio) query optimization component of. See query optimizer relationship to T-SQL, 1 SQLOS (SQL operating system), 586, 588 SQLCLR programming advantages of scalar functions, 597 advantages over UDFs, 550 AppDomain management and scope, 587, 589 architecture of, 585–588 assembly hosting, 587–588 AUTHORIZATION options, 588 calling CLR functions, 592, 600 CAS (Code Access Security), 588 case sensitivity issues, 595–597 context connection strings, 603–605, 607 CREATE ASSEMBLY command, 587–588, 591 CREATE FUNCTION statements, 590–591 creating table-valued functions, 598–600 database access by functions, 603–605 DataRow type, 604 date and time types issues, 595 DLLs in, 587, 591 enabling code, 587 EXTERNAL_ACCESS permission set, 587–588, 605 fill row method, 598–599, 604 FillRowMethodName option, 599 function creation, 589–590 HostProtection attribute, 588 IEnumerable values, 598 indexes on functions, setting, 590 in-process hosting model, 586 IsNull property, 596, 599 memory utilization, 600–603 modifying of databases capability. See SQLCLR stored procedures .NET Base Class Library, 597 .NET basis of, 585–586 nullable types, 595 NUMERIC type issues, 592–593 828 offloading to an application server, 597 ORDER BY clause with, 600 ownership of assemblies, 588 PERMISSION_SET options, 587–588 project properties, setting, 591 publishing assemblies, 591–592 query plans of scalar functions, 588–589 read-only nature of access, 604 relation to T-SQL, 585 remote server access, 605 SAFE permission set, 587, 591 sandboxes, memory, 588 scalar function use cases, 597 security privileges of, 586 SqlCompareOptions enumeration, 595–597 SqlFacet attribute, 593, 596 SqlFunction attribute, 590–595, 599 SQLOS integration, 586 SqlString type, 590, 595–597 SqlTypes namespace, 590–595 SSDT projects, 589–590 stored procedures. See SQLCLR stored procedures string splitting, 598–603 string type issues, 593–595, 595–597 stub scripts, custom, 595 TableDefinition, 599 table-valued functions, streaming, 598 TRUSTWORTHY mode, 605 T-SQL stub function creation, 591–592 type compatibility with SQL Server, 590–595 type fixes, making in Visual Studio, 593 UDF CLR scalar functions, 588–596 UNSAFE permission set, 587–588 user-defined aggregates. See SQLCLR userdefined aggregates user-defined types. See SQLCLR user-defined types Value property of SqlTypes, 594 SQLCLR stored procedures compared to other options, 605 context connections, 607 creating, 606–607 @@ERROR function, 613 EventData property, 616 exception handling, 609–613 ExecuteAndSend method, 610–612 executing, 606–607 impersonation, 614 modifying of databases capability, 605 publication of, 606 RAISERROR issues, 611–612 return values, 606 security credentials with, 614 Send methods, 607–609 SqlContext class, 608, 614–616 SqlPipe class, 607–609 templates for, 606 THROW statements, 613 statistics for BI triggers, 615–616 try-catch blocks for exceptions, 612–613 SQLCLR user-defined aggregates Accumulate method, 629–630 binary formatting, 628–629 collection classes in .NET for strings, 629–630 concatenation with comma separation, 629–632 creating, 628–629 duplicate value options, 631 IBinarySerialize interface implementation, 630–631 Init method, 628, 630, 632 IsInvariantToOrder option, 631 life cycles of, 628–629 Merge method, 629–630 methods, mandatory, 628–629 query plan limitation, 631 Read method, 631–632 schema reference requirement, 629 serialization requirement, 629 SqlString default type, 629–630 SqlUserDefinedAggregate settings, 631 template for, 628 Terminate method, 629–630 testing, 632 Write method, 631–632 SQLCLR user-defined types byte ordered option, 625 case sensitivity of method and property names, 621 complex type issues, 621–627 conversion issues, 620 creating, 617–618 deploying, 619–620 factory methods, 624–625 functionality of, 617 IBinarySerialize interface, 625–626 instantiating, 620 INullable interface, 618 Native formatting, 620–621 Null property, 618 Parse method, 618–619, 621, 623–624 public accessor properties, 623–624 publishing, 619–620 Read method, 626–627 required interfaces, attributes and methods, 618 schema of, 620 Serializable attribute, 618 SqlUserDefinedType attribute, 618, 625 street address complex types, 621–625 stubs for, 620 template for, 617–618 ToString method, 618–619, 621, 627 UserDefined byte size, 625 Write method, 626–627 SqlFacet attribute, SQLCLR, 593, 596 SQLOS (SQL operating system), 586, 588 SQLOS schedulers, 175–177 sql_statement_completed events, 131 SqlString type, SQLCLR, 590, 595–597 SqlTypes namespace, SQLCLR, 590–595 SQL_VARIANT type, 542–543 SSDT (SQL Server Data Tools), 589 SSMS (SQL Server Management Studio) Display Estimated Execution Plan, 53 Include Actual Execution Plan option, 138–139, 151–152 performance of queries, measuring, 54 standard deviations, 486–487, 490, 498, 511, 523 standard normal distributions, 487–488 standard SQL, 1 star join queries bitmaps for parallel optimization, 171 columnstore performance statistics, 130 rowstore vs. columnstore efficiencies, 123–127 statement completed events, Extended Events sessions, 131–134 statistics for BI (business intelligence) analysis of variance, 505–508, 523 cases, 473–474 centers of distribution, 479–481 chi-squared tests, 501–505, 523 coefficient of determination (CD), 498–499, 523 coefficient of the variation (CV), 486, 523 contingency tables, 501–505, 523 correlation coefficients, 498–499, 523 covariance, 495–498, 523 CUME_DIST frequency calculations, 477–478 definite integration, 509–512, 523 degrees of freedom, 485, 502, 505–508, 523 descriptive statistics for continuous variables, 479–494 discrete variables, 474 entropy calculations, 518–521, 523 frequency calculations, 476–479, 523 frequency distributions, 477–479 F-tests, 506–508, 523 higher population moments, 487–494 histograms, 476–479 importance of, 473 inter-quartile ranges, 483, 523 kurtosis, 489–495, 523 linear dependencies, 495–511 linear regression, 499–501, 523 mean absolute deviations, 484, 523 mean squared deviations, 484–485, 523 means. See means medians. See medians modes, 324–327, 479–480, 573 moving averages. See moving average value computations normal distributions, 509–512 PERCENT_RANK frequency calculations, 477–478 preparing data for, 473–474 ranges of distributions, 482–483, 523 829 statistics, performance related statistics for BI (business intelligence), continued row number calculations, 477–478 running totals, 477 SalesAnalysis view sample data, 474–475 significance, statistical, 498, 502, 509–512 skewness, 479, 488–489, 490–495, 523 slopes, 499–501 spread of distribution, 482–486 standard deviations, 486–487, 490, 498, 511, 523 variables, 473–474 variances, 485–486, 505–508, 523 window functions for data calculations, 288–291 statistics, performance related ascending key problems, 107–110 AUTO_CREATE_STATISTICS option, 102, 111 cardinality. See cardinality estimates density vectors, 102, 103 dm_db_index_operational_stats function, 135 dm_db_index_physical_stats function, 135 dm_db_index_usage_stats function, 135–136 dm_db_missing_index_columns function, 136–137 dm_db_missing_index_details view, 136 dm_db_missing_index_group_stats view, 136 dm_exec_procedure_stats, 138 dm_exec_query_profiles view, 138–139 dm_exec_query_stats view, 137–138 dm_exec_trigger_stats, 138 DMFs (dynamic management functions), 134–139 DMVs (dynamic management views), 134–139 DROP command, 111 filtered, creating, 120 filtered index, 120–122 headers, 102 histogram. See histograms indexes, creation with, 101 main types created by SQL Server, 101 output interpretation, STATISTICS option, 55 refresh rate issues, 107–110 SHOW_STATISTICS command, 101–102 sniffing, disabling, 110 STATISTICS IO option, 55–56 STATISTICS IO spooling optimization stats, 279 STATISTICS PROFILE option, 138 STATISTICS TIME option, 55–56 STATISTICS tool syntax, 54 STATISTICS XML option, 138 for temporary objects (tables and variables), 143 trace flag 2371, 110 trace flag 2389, 109 unknowns in cardinality estimates, 110–115 variables as unknown values, 104, 110 WHERE clause predicate, 120 STDEV function, 486–487 STDEVP function, 486–487 step numbers, logical query-processing phases, 3 storage engine clustered index seek + range scan, 93–94 830 consistency requirements, 65 covering nonclustered index seek + range scan, 96 NOLOCK effect, 68, 72 Read Uncommitted isolation level, 68–69, 78, 81, 647–648 safe category, 69–70, 72 TABLOCK effect, 69 treatment of scans, 65–81 unordered nonclustered index scans + lookups, 91–92 unsafe category, 68, 72–74 stored procedures adding leaves to graphs, 744–745 advantages of, 553 cardinality estimates, implications of inaccuracy, 557–558 covering indexes for, 558 database designation for execution, 533 deployments of changes to, 553 dynamic filtering example, 535–542 dynamic pivoting example, 531–533 dynamic sorting example, 542–546 execution plan reuse, 554–558 initial compilations of, 554 iterative graph solutions with, 718–719 KEEPFIXED PLAN hint, 570–571 native compilation by In-Memory OLTP, 673 not supported within In-Memory OLTP natively compiled processes, 705 parameter sniffing, 555–558 parameter sniffing, preventing, 564–568 parameterized queries in, 553 parameters passed vs. declared within procedures, 559 preventing execution plan reuse, 558–560 recompilations, 568–571 RECOMPILE option, 558–560, 562–563 set options, plan affecting, 568–569 sp_statement_completed events, 131 SQLCLR. See SQLCLR stored procedures transactions using, 635–636 triggered by events. See triggers tuning, 554–568 TVPs in, 571–573 variable sniffing, lack of, 560–564 Stream Aggregate operator, 153, 167, 272 streaming table-valued functions, SQLCLR, 597–605 street addresses, 621–622 string concatenation COALESCE function, 316 CONCAT function, 316 concatenation with comma separation, 629–632 cursors for, 314–315 FOR XML method, 317–319 pivoting for, 315–316, 318–319 SELECT @local_variable method, 319–322 separators, adding, 318 tables types of custom aggregate calculations of, 313–314 strings cleaning, 549–550 collection classes in .NET for, 629–630 comma separated values SQLCLR examples, 598–603 REPLACE function, String.Replace SQLCLR function performance vs., 597 separating elements of, 245–249 SQLCLR string type issues, 593–597 Structured English QUEry Language (SEQUEL), 2–3 structures, internal data. See internal data structures STUFF function, 318, 549–550 subgraphs. See also descendants algorithm for returning, 719–729 with path enumeration, 733–736 subqueries correlated, 187, 189–194 cross joins for optimization, 226–227 EXISTS predicate with, 194–201 identifying gaps problem, 198–200 In-Memory OLTP, not supported by, 705 multi-valued, 188–189 nesting capabilities of, 187 NULLs, errors from, 203–204 optimizing Index Seek operators, 192–193 POC (partitioning, ordering, covering) pattern for indexing, 192 scalar, 187–188 scalar aggregate, 191 self-contained, 187–189 substitution errors in column names, 201–202 table expressions with, 204–205 TOP filter based, 191–194 troubleshooting self-contained vs. correlated, 187, 189 SUBSTRING function, 246–248 subtrees. See also trees moving, 745–748 removing, 748–749 returning, 719–720 SUM function, 261–262, 265–268 SUM OVER window function advantages over SUM function, 263–264 frame delimiters with, 269–272 GROUP BY with, 267–269 islands solution with, 296–299 maximum concurrent intervals task, 459–461 packing interval solutions with, 466–468 PARTITION BY with, 263–265 performance of, 266–268 window elements supported by, 264 surface-area restrictions, In-Memory OLTP, 703–705 surrogate key generation identity property for, 381–382 sequence object for, 382–386 UPDATE with variables method, 403–404 swapping column values, 18 SWITCHOFFSET function, 425–426 sys.allocation_units view, 42 SYSDATETIME function, 422 SYSDATETIMEOFFSET function, 421–424 sys.partitions view, 42 sys.system_internals_allocation_units view, 44 SYSUTCDATETIME function, 422 T table expressions CTEs. See CTEs (common table expressions) derived tables. See derived tables inline TVFs. See inline TVFs (table-valued functions) kinds of, 204 query requirements for, 204–205 updating data with, 402–403 views. See views table operators Cartesian product logical processing phase, 8–9 evaluation order of, 27 joins. See joins logical query processing of, 8–14 logical query-processing phase for, 26–35 table scans Table Scan operators, 65 table scan/unordered clustered index scan access method, 57–60 table variables advantages over temporary tables, 143 cardinality estimates, 143–146 declaring, 144 declaring with table types, 571–572 inline index definition with, 130–131 INTO clauses directing rows to, 412–413 parallelism inhibited by modification of, 179 RECOMPILE option, 143–145 rollbacks with, 148–149 scopes of, 140, 148 statistics available for, 143 table types for, 571–573 temporary. See temporary table and variable objects trace flag 2453, 146 transactions failures, advantages with, 148–149 as TVF outputs, 550–553 tables allocation units, 44 B-trees, 43–44, 46–50 changes in, recompiles triggered by, 570–571 CTEs. See CTEs (common table expressions) deleting all rows from with TRUNCATE TABLE, 395–399 expressions. See table expressions heap data structure, 43–46 831 table-valued functions, SQLCLR tables, continued HOBT (heap or B-tree) organization, 43–44 IAMs (index allocation maps), 44–45, 47 inline TVFs. See inline TVFs (table-valued functions) internal data structures for, 43–53 memory optimized. See In-Memory OLTP memory optimized, defining as, 140 memory optimized, parallelism inhibited by, 179 MEMORY_OPTIMIZED option, 572 operators. See table operators partitioned. See partitions scans. See table scans temporary. See temporary table and variable objects temporary local, with triggers, 584–585 TVPs (table-valued parameters), 571–573 type definitions, 571–573 variables. See table variables views. See views table-valued functions, SQLCLR, 597–605 table-valued parameters (TVPs), 571–573 TABLOCK hint, 69 tempdb batch spills to, 128–129 cardinality estimate effects, 99–100 tables in not indexed, issues from, 581 temporary object use of, 140 temporary table and variable objects caching mechanism issues, 147–148 cardinality estimates with, 139 cases to use for query tuning, 139 CTE expensive work, avoiding repetition of, 140–143 disk activity from, 140 eager writes, 140 global temporary tables, 148 local temporary table qualities, 139–140 non-tuning usage of, 139 RECOMPILE option, 143–145, 148 rollbacks with, 148–149 scope of, 140, 148 statistics maintained for, 143 table cardinality estimates, 146–147 table execution plans, 147 table expression qualities, 139–140 table variable qualities, 139–140 tables vs. variables, performance, 143 tempdb use, 140 types of temporary objects, 139–140 threads CXPacket data structure, 163 distributor or coordinator threads, 164–165 Max Degree of Parallelism setting, 175–178 Max Worker Threads setting, 176–178 parallel query execution number of, 159, 161, 164 quantum punishment of, 586 three-value logic, 10 832 THROW statements, 613, 665 tile numbers, NTILE for, 282–283 TIME data type, 419–420, 422, 440, 595 TIMEFROMPARTS function, 431 times. See dates and times TODATETIMEOFFSET function, 426 TOP filters anchor-based paging strategy, 346–352 chunked modification with, 361–363 DELETE statements with, 360–363 DESC option, 344–345 elements of, 22 execution plan for TOP over TOP, 353 execution plans for anchor sorts, 348, 351, 352 index choices, effects on optimization, 349 inner ORDER BY clauses do not guarantee order, 344–345 INSERT TOP statements, 360 MERGE TOP statements, 360 minimum missing values, finding with, 196–198 modes, TOP WITH TIES to calculate, 480 modification statements of, 360–363 nested TOP paging strategy, 352–353 optimization of paging solutions, 346–353 ORDER BY clauses with, 341–342 ORDER BY (SELECT NULL), 344 ordering issues, 22–26, 342–343 parallelism inhibited by certain, 179 PERCENT input indicator, 342 phase in logical processing, 6, 22–26 predicate phrasing, effects on performance, 349–352 proprietary to T-SQL, 341 suggestion to add OVER to, 366 syntax of, 341 tie breaking with subqueries, 191–194 Top Expression property of Top operator, 348 Top operator in execution plans, 64, 348 unnesting of subqueries, avoiding with, 156 UPDATE TOP statements, 360 WHILE loops with TOP on key order, no key index, 581 WITH TIES option, 343–344 top N per group tasks APPLY with TOP solutions, 365–366 carry-along sorts for, 366–368 concatenation solutions, 366–368 CROSS APPLY solution to, 219–220 density of partitioning elements, 363–364 elements of, 363 factors influencing efficiency of T-SQL solutions to, 363–364 N=1 solution, 366–368 POC index support for, 192, 363 ROW_NUMBER based solution, 364–365 topological sorts, 736–739 trace flag 272, 382, 387 trace flag 2371, 110 TRY-CATCH constructs trace flag 2389, 109 trace flag 2453, 146 trace flag 8649, 90, 117, 465 Transact SQL (T-SQL). See T-SQL (Transact SQL) transactional replication bulk-logging requirements, 376 transactions ACID properties, list of, 633 AFTER DML triggers with, 575–578 atomicity property, 633 autocommit default mode, 633 BEGIN TRAN command, 633–636 BULK_LOGGED recovery model for, 634 COMMIT TRAN command, 634–636 commit validation errors, 693–696, 702 completion guarantees, 634 consistency property, 634 constraints, immediacy of, 634 deadlocks of. See deadlocks delayed durability, 634 doomed state, 666 durability property, 634 errors in, 666–668 failed state, 666 FULL recovery model for, 634 IMPLICIT_TRANSACTIONS option, 633 In-Memory OLTP of. See In-Memory OLTP isolation level settings. See isolation levels isolation property, 634 locks from. See locks log buffers, flushing of, 634 nesting not supported, 634–635 recovery models, 633–634 redo phase of recovery process, 633 ROLLBACK TRAN command, 634–635 SAVE TRAN command, 635 savepoints, 635–636 SIMPLE recovery model for, 634 stored procedures in, 635–636 @@trancount, 635, 666–667 undo phase of recovery process, 633 UPDATE variable method for gapless sequences, 403–404 XACT_ABORT option, 666–667 XACT_STATE option, 667–668 transitive closure cycle-detection logic for, 793 defined, 787 directed acyclic graphs, of, 787–792 distance calculations, 789–790 duplicate edge elimination, 789 efficiency of solutions, 787 filtering shortest paths, 791–792 materializing result sets, 799 shortest-path solutions, 791–801 undirected cyclic graphs, 792–801 trapezoidal rule, 509–510 trees B-tree table representations. See B-trees components of, 708 defined, 708 employee organizational chart scenario, 709–711 HIERARCHYID for representing. See HIERARCHYID data type materialized path solutions for. See materialized path model nested sets graph solution, 778–786 parent-child adjacency lists, converting to HIERARCHYID, 771–773 subtrees, moving, 745–748 subtrees, removing, 748–749 subtrees, returning, 719–720 triggers AFTER DDL triggers, 579–581 AFTER DML triggers, 575–578, 581–585 AFTER UPDATE triggers, 576–577 CREATE TRIGGER command, 576–578 cursors with, 581–583 defined, 575 disabling, 583–584 INSTEAD OF DML triggers, 578 integrity enforcement for, 583 nesting and recursion of, 577–578 ON ALL SERVER, 580 ON DATABASE, 580 performance issues for, 581–585 rollbacks with, 149 row processing options for, 581 @@rowcount with, 582 session contexts, 584 SQLCLR based, 615–616 temporary local tables with, 584–585 TRUE value, 10 TRUNCATE TABLE statements DML statement nature of, 399 identity property handling, 396–397 keys and views preventing execution, 397–398 partition switching alternative to, 397–399 permissions required for, 395 speed advantage over DELETE statements, 395 syntax of, 395 TRY_CAST function, 432 TRY-CATCH constructs advantages over @@error function, 662–663 ERROR_ functions with, 664 ERROR_NUMBER functions in, 664–666 mechanics of, 663 nesting, 664 non-trappable errors, 664 retry logic, 669–670 syntax, 663–664 THROW statements in, 665–666 transaction errors in TRY blocks, 666–668 trapped errors not reported, 664 UDF prohibition of, 546 833 TRY_CONVERT function TRY_CONVERT function, 431–432 T-SQL (Transact SQL) inefficiency of iterative solutions, 149–150, 152–153 relation to industry standards, 1 tuning queries. See query tuning tuples, 18. See also rows TVFs (table valued functions), 550–553 TVPs (table-valued parameters) for In-Memory OLTP procedures, 699–702 for stored procedures, 571–573 type-conversion errors, 542 typed order vs. logical order, 3 U UDAs (user-defined aggregates) for skewness and kurtosis, 490–495 UDFs (user-defined functions) capabilities of, 546 CLR-based. See SQLCLR programming inline, encapsulating, 719 iterative graph solutions using, 718–719 iterative logic in, 549–550 limitations of, 546–547 multiple statements in, 549–550 parallelism inhibited by, 179–180, 548 performance penalties associated with, 547–549 scalar-valued, 546–550 subgraph solution, 718–724 TDFs as alternatives to, 548–549 UNBOUNDED keyword, 269–272, 277–281, 286 undirected cyclic graphs conversion to directed graphs, 792–793 road system scenario, 715–718 transitive closure of, 792–801 weighted, 718 undirected graphs, 708. See also undirected cyclic graphs uniform extents, 43 UNION operator ALL variant, 37–38, 250 CHECK constraints with, 251–252 costs, 251 DISTINCT implied, 250 execution plans for, 251–252 grouping sets with, 329–330 logical query-processing phase for, 38–39 NULLs, treatment of, 11, 249 ORDER BY with, 249 precedence, 249 unions not allowed by In-Memory OLTP, 705 UNIQUE constraints filtered indexes with, 122 not supported by In-Memory OLTP, 704 NULL value treatment, 11 uniquifier columns, 46–47, 52–53 834 UNKNOWN value, 10–11 unknowns in cardinality estimates, 110–115 unordered clustered index scans, 57–60 unordered covering nonclustered index scans, 60–62 unordered nonclustered index scans + lookups, 91–93 unpivoting. See also pivoting with CROSS APPLY and VALUES, 310–311 with CROSS JOIN and VALUES, 308–310 purpose of, 307–308 with UNPIVOT operator, 312–313 UNPIVOT operator elements, 27 UNPIVOT operator phase in logical processing, 31–35 unrestricted cross joins. See Cartesian products UPDATE statements AFTER UPDATE triggers with, 575–578 INSTEAD OF UPDATE triggers with, 578 in MERGE statements, 405–406 with OPENROWSET function, 380 OUTPUT clauses with, 411 overwriting existing keys with sequence objects, 385 swapping column values, 18 UPDATE TOP filters, 360 updating tables with CTEs, 402–403 variable assignment method, 403–404 updating data Repeatable Read isolation level for preventing conflicts, 650 Snapshot isolation level for preventing conflicts, 652–655 as a source of row return errors, 76–81 table expressions for, 402–403 UPDATE function test for updated columns, 577 update locks (U), 637 variable assignment method for, 403–404 UPDLOCK hints, 650–651, 757–758 user-defined functions. See UDFs (user-defined functions) user-defined types, SQLCLR. See SQLCLR userdefined types USING clauses, 410–411 UTC (Coordinated Universal Time), 420–421 V validation errors, 693–696, 702 .value method, 317–318 variables chi-squared tests of independence of, 501–505 as columns, 473 continuous, 474 correlations between, 495–501 discrete, 474 independence of, assumptions about, 499 relationships between. See linear dependencies window functions sniffing, lack of, 560–564 temporary table. See temporary table and variable objects variances analysis of, 505–508 covariance, 495–498 defined, 523 VAR and VARP functions, 485–486 vector expression Standard SQL feature, 349 vertexes. See nodes views CHECK option, 212 data not stored, just metadata, 212 input parameter support lacking, 215 INSTEAD OF DML triggers, 578 of joined partitioned tables, 212–214 performance issues, 213–214 query requirements for, 204–205 reusable nature of, 211–212 SCHEMABINDING option, 212 scopes of, 204 virtual tables add outer rows results, 13 APPLY operator generation of, 26–29 FROM clauses, generated by, 8–14 generation, 4–6 GROUP BY phase, 15–16 PIVOT operator generation of, 29–31 TOP filters, generated by, 22–26 UNPIVOT operator generation of, 31–35 WHERE phase, 14–15 Visual Studio for CLR code, 589–591 W Waymire, Richard, 528 weekdays, calculating, 423, 436–439, 441–445 weeks, grouping by, 449–450 weighted graphs, 714, 718 weighted moving averages (WMAs), 514 WHEN NOT MATCHED BY SOURCE clauses, 404–407 WHERE filters column IS NOT NULL, 122 dates and times filtered by, 445–446 groups not allowed in, 14 inner joins with, 228 logical order of, 3–5 logical phase order sample, 7 NULLs, subquery errors from, 203–204 ON clause, vs. in outer joins, 14–15 outer joins with, 230 phase in logical processing, 4–5, 14–15 UNKNOWN values, treatment of, 11 window functions using, 263–264 WHILE loops with TOP on key order, no key index, 581 White, Paul, 148, 349, 543 window aggregate functions. See aggregate window functions window frames CURRENT ROW delimiter, 269–270, 274, 276–278 default, 286 delimiters for, 269 as elements of window functions, 264 fast-track optimization cases, 272 FOLLOWING keyword, 269, 273–274 INTERVAL not supported, 276–277 offset functions, for, 286 offsets with RANGE, 276 offsets with ROWS, 273–274 partitions, relationship to, 269 PRECEDING keyword, 269–274, 277, 280–281 RANGE UNBOUNDED PRECEDING phrase, 279–280 RANGE unit with, 276–280 ROWS delimiters, 269–276 ROWS UNBOUNDED PRECEDING phrase, 270–271, 273, 281 spooling optimization, 278–279 ties in ordering value, RANGE vs. ROWS, 278 UNBOUNDED keyword, 269–272, 277–281, 286 year-to-date (YTD) calculation, 280–281 window functions advantages of, 263 aggregates. See aggregate window functions AVG OVER, 274–275 CUME_DIST OVER, 288–289 DENSE_RANK OVER, 282–283, 285, 294–296 elegance for calculations, 259 fast-track optimization plan, 271–272 filters with, 263–264 FIRST_VALUE OVER, 285–286, 511 frames of. See window frames frequency calculations, 476–479 gaps problems, 291–292 GROUP BY with, 267–269 INTERVAL not supported, 276–277 inverse distribution functions, 289–291 island problems, 291–299 LAG OVER, 287, 296–299 LAST_VALUE OVER, 285–287, 511 LEAD OVER, 287, 291–292 legacy vs. 2012 versions, 259 limited to SELECT and ORDER BY clauses, 263 logical query-processing phases, 35–37 MAX OVER, 275–276 medians, finding with PERCENTILE_CONT, 369 moving average value computations with, 274 NTILE OVER, 282–283, 285 offset functions, 285–287 offsets with ROWS, 273–274 optimization of, 266–268 OVER clauses, 259, 263 packing interval solutions, 466–471 parallelism inhibited by certain, 179 835 window sets window functions, continued PARTITION BY with, 263–266 partitions with rankings, 281–285 PERCENTILE_CONT, 289–291, 369 PERCENTILE_DISC, 289–291 PERCENT_RANK OVER, 288–289 percents of grand totals with, 268–269 POC indexes with, 271–273, 284–285 rank distribution, 288–289 RANK OVER, 282–283, 285 ranking calculations with, 281–285 ROW_NUMBER OVER. See ROW_NUMBER OVER window function ROWS delimiters, 269–276 running total calculations with, 271–273 spooling optimization, 278–279 structures for, 264 SUM OVER. See SUM OVER window function underlying results, exposure of, 263 vs. group functions, 259 windows of rows, 259 year-to-date (YTD) calculation, 280–281 window sets, 35–37 Windows Time service, 421–422 WITH RESULT SETS clauses, 573–575 836 WITHIN GROUP clauses, 327 WMAs (weighted moving averages), 514. See also moving average value computations write-ahead logging, 643 X XML features EVENTDATA as XML function, 579–580 EventData property for SQLCLR triggers, 616 FOR XML string concatenation, 317–319 PATH mode, 317, 531 TYPE directive, 317 XPacket data structure, 163 Y YEAR function, 425 year-to-date (YTD) calculation, 280–281 Z Z distribution, 487–488 About the authors IT Z IK BE N- GAN is a mentor for and co-founder of SolidQ. A SQL Server Microsoft MVP (Most Valuable Professional) since 1999, Itzik has delivered numerous training events around the world focused on T-SQL querying, query tuning, and programming. Itzik has authored several T-SQL books as well as articles for SQL Server Pro, SolidQ Journal, and MSDN. Itzik’s speaking activities include TechEd, SQLPASS, SQL Server Connections, SolidQ events, and various user groups around the world. Itzik is the author of SolidQ’s Advanced T-SQL Querying, Programming and Tuning, and T-SQL Fundamentals courses, along with ­being a primary resource within the company for its T-SQL-related activities. DE JAN SARK A , MCT and SQL Server MVP, is an independent consultant, trainer, and developer focusing on database and business intelligence applications. His specialties are advanced topics like data modeling, data mining, and data quality. On these tough topics, he works and researches together with SolidQ and the Data Quality Institute. He is the founder of the Slovenian SQL Server and .NET Users Group. Dejan Sarka is the main author or coauthor of 11 books about databases and SQL Server, with more to come. He also has developed and is continuing to develop many courses and seminars for SolidQ and Microsoft. He has been a regular speaker at many conferences worldwide for more than 15 years, including Microsoft TechEd, PASS Summit, and others. ADAM MACHANIC is a Boston-based SQL Server developer, writer, and speaker. He focuses on large-scale data warehouse performance and development, and he is the author of the award-winning SQL Server monitoring stored procedure sp_WhoIsActive. Adam has written for numerous websites and magazines, including SQLblog, Simple Talk, Search SQL Server, SQL Server Professional, CoDe, and VSJ. He has also contributed to several books on SQL Server, including SQL Server 2008 Internals (Microsoft Press, 2009) and Expert SQL ­Server 2005 Development (Apress, 2007). Adam regularly speaks at conferences and training events on a variety of SQL Server topics. He is a Microsoft Most Valuable Professional (MVP) for SQL Server, a Microsoft Certified IT Professional (MCITP), and an alumnus of the INETA North American Speakers Bureau. KE VIN FARLE E has over 25 years in the industry, in both database and s­ torage-management software. In his current role as a Storage Engine Program Manager on the Microsoft SQL Server team, he brings these threads together. His current projects include the SQL Server Project “Hekaton” InMemory OLTP feature. This page intentionally left blank Free ebooks From technical overviews to drilldowns on special topics, get free ebooks from Microsoft Press at: www.microsoftvirtualacademy.com/ebooks Download your free ebooks in PDF, EPUB, and/or Mobi for Kindle formats. Look for other great resources at Microsoft Virtual Academy, where you can learn new skills and help advance your career with free Microsoft training delivered by experts. Microsoft Press Now that you’ve read the book... Tell us what you think! Was it useful? Did it teach you what you wanted to learn? Was there room for improvement? Let us know at http://aka.ms/tellpress Your feedback goes directly to the staff at Microsoft Press, and we read every one of your responses. Thanks in advance!