100% found this document useful (2 votes)
4 views

Refactoring Legacy T SQL for Improved Performance Modern Practices for SQL Server Applications 1st Edition Lisa Bohm pdf download

The document is a promotional overview of the book 'Refactoring Legacy T-SQL for Improved Performance: Modern Practices for SQL Server Applications' by Lisa Bohm, which focuses on optimizing SQL Server applications. It includes links to download the book and other related SQL Server resources. The content covers various aspects of SQL performance, documentation, database structure, and CRUD operations.

Uploaded by

wuerzokertwn
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
100% found this document useful (2 votes)
4 views

Refactoring Legacy T SQL for Improved Performance Modern Practices for SQL Server Applications 1st Edition Lisa Bohm pdf download

The document is a promotional overview of the book 'Refactoring Legacy T-SQL for Improved Performance: Modern Practices for SQL Server Applications' by Lisa Bohm, which focuses on optimizing SQL Server applications. It includes links to download the book and other related SQL Server resources. The content covers various aspects of SQL performance, documentation, database structure, and CRUD operations.

Uploaded by

wuerzokertwn
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 60

Refactoring Legacy T SQL for Improved

Performance Modern Practices for SQL Server


Applications 1st Edition Lisa Bohm pdf download

https://textbookfull.com/product/refactoring-legacy-t-sql-for-
improved-performance-modern-practices-for-sql-server-
applications-1st-edition-lisa-bohm/

Download more ebook from https://textbookfull.com


We believe these products will be a great fit for you. Click
the link to download now, or visit textbookfull.com
to discover even more!

High Performance SQL Server: Consistent Response for


Mission-Critical Applications 2nd Edition Benjamin
Nevarez

https://textbookfull.com/product/high-performance-sql-server-
consistent-response-for-mission-critical-applications-2nd-
edition-benjamin-nevarez/

High Performance SQL Server Consistent Response for


Mission Critical Applications 2nd Edition Benjamin
Nevarez

https://textbookfull.com/product/high-performance-sql-server-
consistent-response-for-mission-critical-applications-2nd-
edition-benjamin-nevarez-2/

Pro SQL Server Relational Database Design and


Implementation: Best Practices for Scalability and
Performance Louis Davidson

https://textbookfull.com/product/pro-sql-server-relational-
database-design-and-implementation-best-practices-for-
scalability-and-performance-louis-davidson/

SQL Server Execution Plans For SQL Server 2008 through


to 2017 and Azure SQL Database 3rd Edition Grant
Fritchey

https://textbookfull.com/product/sql-server-execution-plans-for-
sql-server-2008-through-to-2017-and-azure-sql-database-3rd-
edition-grant-fritchey/
Expert SQL Server Transactions and Locking: Concurrency
Internals for SQL Server Practitioners 1st Edition
Dmitri Korotkevitch

https://textbookfull.com/product/expert-sql-server-transactions-
and-locking-concurrency-internals-for-sql-server-
practitioners-1st-edition-dmitri-korotkevitch/

Azure SQL Revealed: A Guide to the Cloud for SQL Server


Professionals Bob Ward

https://textbookfull.com/product/azure-sql-revealed-a-guide-to-
the-cloud-for-sql-server-professionals-bob-ward/

Azure SQL Revealed: A Guide to the Cloud for SQL Server


Professionals 1st Edition Bob Ward

https://textbookfull.com/product/azure-sql-revealed-a-guide-to-
the-cloud-for-sql-server-professionals-1st-edition-bob-ward/

Building Custom Tasks for SQL Server Integration


Services: The Power of .NET for ETL for SQL Server 2019
and Beyond 2nd Edition Andy Leonard

https://textbookfull.com/product/building-custom-tasks-for-sql-
server-integration-services-the-power-of-net-for-etl-for-sql-
server-2019-and-beyond-2nd-edition-andy-leonard/

Building Custom Tasks for SQL Server Integration


Services The Power of NET for ETL for SQL Server 2019
and Beyond Second Edition Andy Leonard

https://textbookfull.com/product/building-custom-tasks-for-sql-
server-integration-services-the-power-of-net-for-etl-for-sql-
server-2019-and-beyond-second-edition-andy-leonard/
Refactoring
Legacy T-SQL
for Improved
Performance
Modern Practices for SQL Server
Applications

Lisa Bohm
Refactoring Legacy T-SQL
for Improved Performance
Modern Practices for
SQL Server Applications

Lisa Bohm
Refactoring Legacy T-SQL for Improved Performance: Modern Practices for
SQL Server Applications
Lisa Bohm
Chardon, OH, USA

ISBN-13 (pbk): 978-1-4842-5580-3 ISBN-13 (electronic): 978-1-4842-5581-0


https://doi.org/10.1007/978-1-4842-5581-0

Copyright © 2020 by Lisa Bohm


This work is subject to copyright. All rights are reserved by the Publisher, whether the whole or part of the
material is concerned, specifically the rights of translation, reprinting, reuse of illustrations, recitation,
broadcasting, reproduction on microfilms or in any other physical way, and transmission or information
storage and retrieval, electronic adaptation, computer software, or by similar or dissimilar methodology now
known or hereafter developed.
Trademarked names, logos, and images may appear in this book. Rather than use a trademark symbol with
every occurrence of a trademarked name, logo, or image we use the names, logos, and images only in an
editorial fashion and to the benefit of the trademark owner, with no intention of infringement of the
trademark.
The use in this publication of trade names, trademarks, service marks, and similar terms, even if they are not
identified as such, is not to be taken as an expression of opinion as to whether or not they are subject to
proprietary rights.
While the advice and information in this book are believed to be true and accurate at the date of publication,
neither the authors nor the editors nor the publisher can accept any legal responsibility for any errors or
omissions that may be made. The publisher makes no warranty, express or implied, with respect to the
material contained herein.
Managing Director, Apress Media LLC: Welmoed Spahr
Acquisitions Editor: Jonathan Gennick
Development Editor: Laura Berendson
Coordinating Editor: Jill Balzano
Cover image designed by Freepik (www.freepik.com)
Distributed to the book trade worldwide by Springer Science+Business Media New York, 233 Spring Street,
6th Floor, New York, NY 10013. Phone 1-800-SPRINGER, fax (201) 348-4505, e-mail orders-ny@springer-sbm.com,
or visit www.springeronline.com. Apress Media, LLC is a California LLC and the sole member (owner) is
Springer Science + Business Media Finance Inc (SSBM Finance Inc). SSBM Finance Inc is a Delaware
corporation.
For information on translations, please e-mail rights@apress.com, or visit http://www.apress.com/
rights-permissions.
Apress titles may be purchased in bulk for academic, corporate, or promotional use. eBook versions and
licenses are also available for most titles. For more information, reference our Print and eBook Bulk Sales
web page at http://www.apress.com/bulk-sales.
Any source code or other supplementary material referenced by the author in this book is available to
readers on GitHub via the book’s product page, located at www.apress.com/9781484255803. For more
detailed information, please visit http://www.apress.com/source-code.
Printed on acid-free paper
This book is dedicated to Allen White, who has been my friend as well as
helped me grow into a valued member of the community, and
taught me how important it is to keep giving back and
helping others.
Table of Contents
About the Author����������������������������������������������������������������������������������������������������� xi

About the Technical Reviewer������������������������������������������������������������������������������� xiii


Acknowledgments���������������������������������������������������������������������������������������������������xv

Introduction�����������������������������������������������������������������������������������������������������������xvii

Part I: Everything Is Slow������������������������������������������������������������������������������� 1


Chapter 1: T-SQL Triage�������������������������������������������������������������������������������������������� 3
Severity of the Situation���������������������������������������������������������������������������������������������������������������� 4
Relative Effort������������������������������������������������������������������������������������������������������������������������������� 4
Problem Areas������������������������������������������������������������������������������������������������������������������������������� 4
STATISTICS IO and Time����������������������������������������������������������������������������������������������������������� 5
Query Window Setup��������������������������������������������������������������������������������������������������������������� 5
Code Tests������������������������������������������������������������������������������������������������������������������������������� 6
Execution Plans��������������������������������������������������������������������������������������������������������������������� 11
Perform Triage if Possible����������������������������������������������������������������������������������������������������������� 14
Is There an Index?����������������������������������������������������������������������������������������������������������������� 14
Risk Level of the Change������������������������������������������������������������������������������������������������������� 17
The Answer to Everything…?����������������������������������������������������������������������������������������������������� 18
Triage Outside of Indexing���������������������������������������������������������������������������������������������������������� 18
If Possible, Queries Should Join on Key Values��������������������������������������������������������������������� 19
SARGability���������������������������������������������������������������������������������������������������������������������������� 19
Summary������������������������������������������������������������������������������������������������������������������������������������ 19

Chapter 2: Documentation�������������������������������������������������������������������������������������� 21
Incorporating Existing Documentation���������������������������������������������������������������������������������������� 21
Functionality Documentation������������������������������������������������������������������������������������������������� 28

v
Table of Contents

Statistics Information and Execution Plans�������������������������������������������������������������������������������� 29


Coding (Anti) Patterns����������������������������������������������������������������������������������������������������������������� 29
Number of Calls to Each Database Table������������������������������������������������������������������������������� 30
Datatype Mismatches������������������������������������������������������������������������������������������������������������ 31
Subqueries and Where They’re Located�������������������������������������������������������������������������������� 38
Calculations��������������������������������������������������������������������������������������������������������������������������� 38
Temporary Tables and/or Table Variables������������������������������������������������������������������������������ 40
Loops and/or Cursors������������������������������������������������������������������������������������������������������������ 41
CTEs��������������������������������������������������������������������������������������������������������������������������������������� 42
Join Types������������������������������������������������������������������������������������������������������������������������������ 42
LIKE, NOT LIKE, and NOT IN���������������������������������������������������������������������������������������������������� 43
DISTINCT, TOP, and Other Sorting Operations������������������������������������������������������������������������� 43
Calls to Other User-Defined SQL Objects������������������������������������������������������������������������������� 44
Native SQL Server Functions in a WHERE Clause������������������������������������������������������������������ 44
Indexes���������������������������������������������������������������������������������������������������������������������������������������� 48
Back to Runtime Data������������������������������������������������������������������������������������������������������������ 48
Summary������������������������������������������������������������������������������������������������������������������������������������ 48
Wrapping Up Our Documentation������������������������������������������������������������������������������������������ 48
DailySummaryReportPerMonth: Documentation������������������������������������������������������������������������� 48
Code as of 2019.06.02����������������������������������������������������������������������������������������������������������� 48
Functional Requirements������������������������������������������������������������������������������������������������������ 56
Data Calls������������������������������������������������������������������������������������������������������������������������������ 56
Coding (Anti) Patterns������������������������������������������������������������������������������������������������������������ 57
Indexes���������������������������������������������������������������������������������������������������������������������������������� 59

Part II: Database Structure��������������������������������������������������������������������������� 61


Chapter 3: Database Tables������������������������������������������������������������������������������������ 63
Computed Columns��������������������������������������������������������������������������������������������������������������������� 63
Denormalized Data���������������������������������������������������������������������������������������������������������������������� 63
Datatypes and Field Size������������������������������������������������������������������������������������������������������������� 65
Deprecated Datatypes����������������������������������������������������������������������������������������������������������������� 66

vi
Table of Contents

How Much Data?������������������������������������������������������������������������������������������������������������������������� 66


Data Skew/Cardinality���������������������������������������������������������������������������������������������������������������� 67
Constraints���������������������������������������������������������������������������������������������������������������������������������� 68
Changing Table Definitions���������������������������������������������������������������������������������������������������������� 69
Summary������������������������������������������������������������������������������������������������������������������������������������ 82

Chapter 4: Database Views������������������������������������������������������������������������������������� 83


Nesting���������������������������������������������������������������������������������������������������������������������������������������� 84
Multiple Table Calls���������������������������������������������������������������������������������������������������������������� 95
Additional Pieces for Documentation������������������������������������������������������������������������������������ 97
Know Your Data��������������������������������������������������������������������������������������������������������������������� 97
Functionality Check��������������������������������������������������������������������������������������������������������������������� 99
What Problems Did We Fix?������������������������������������������������������������������������������������������������������ 103
Filtering������������������������������������������������������������������������������������������������������������������������������������� 104
Updating Data Through a View�������������������������������������������������������������������������������������������������� 104
Additional Limitations��������������������������������������������������������������������������������������������������������������� 105
Why Use a View?����������������������������������������������������������������������������������������������������������������������� 105
Summary���������������������������������������������������������������������������������������������������������������������������������� 105

Part III: CRUD Objects��������������������������������������������������������������������������������� 107


Chapter 5: Triggers����������������������������������������������������������������������������������������������� 109
View-Based Triggers����������������������������������������������������������������������������������������������������������������� 109
When Should We Use Triggers?������������������������������������������������������������������������������������������������ 110
Common Issues with Triggers��������������������������������������������������������������������������������������������������� 110
Trigger Recursion���������������������������������������������������������������������������������������������������������������������� 114
Triggers and Multi-record Changes������������������������������������������������������������������������������������������ 119
Testing for Values Changing������������������������������������������������������������������������������������������������������ 123
Limit the Work Your Database Must Do������������������������������������������������������������������������������������� 129
Summary���������������������������������������������������������������������������������������������������������������������������������� 135

vii
Table of Contents

Chapter 6: Stored Procedures������������������������������������������������������������������������������ 137


Temporary Tables vs. Table Variables���������������������������������������������������������������������������������������� 137
The Temporary Table����������������������������������������������������������������������������������������������������������� 138
The Table Variable���������������������������������������������������������������������������������������������������������������� 138
Loops���������������������������������������������������������������������������������������������������������������������������������������� 139
Functionality Review����������������������������������������������������������������������������������������������������������������� 139
Start with the Simplest Data����������������������������������������������������������������������������������������������������� 139
Add More Data in Small, Easily Testable Increments����������������������������������������������������������� 142
Windowing Functions���������������������������������������������������������������������������������������������������������������� 144
Avoiding Row or Data Duplicates���������������������������������������������������������������������������������������������� 146
How Did We Get Here Again?���������������������������������������������������������������������������������������������������� 151
But What About Performance?�������������������������������������������������������������������������������������������������� 153
No Missing Rows – Tally Tables!����������������������������������������������������������������������������������������������� 171
What Else Should We Watch For?��������������������������������������������������������������������������������������������� 177
GetUserInfoByName: Documentation���������������������������������������������������������������������������������������� 177
Code as of 2019.07.07��������������������������������������������������������������������������������������������������������� 177
Functional Requirements���������������������������������������������������������������������������������������������������� 179
Data Calls���������������������������������������������������������������������������������������������������������������������������� 179
Possible Red Flags�������������������������������������������������������������������������������������������������������������� 180
Run Statistics for This Call�������������������������������������������������������������������������������������������������������� 181
Functions and STATISTICS IO Output����������������������������������������������������������������������������������������� 182
Coming Up with a Plan of Action����������������������������������������������������������������������������������������������� 183
Go Away, Subqueries!���������������������������������������������������������������������������������������������������������� 183
Remove the Function!��������������������������������������������������������������������������������������������������������� 185
Remove the Leading % in Our LIKE Statement�������������������������������������������������������������������� 186
Summary���������������������������������������������������������������������������������������������������������������������������������� 190

Chapter 7: Functions�������������������������������������������������������������������������������������������� 191


Scalar Functions����������������������������������������������������������������������������������������������������������������������� 191
Viewing STATISTICS IO Caused by Functions���������������������������������������������������������������������������� 193
Scalar-Valued Functions in a WHERE Clause���������������������������������������������������������������������������� 199

viii
Table of Contents

Native SQL Server Functions���������������������������������������������������������������������������������������������������� 200


The Dreaded “Massive Business Logic” Function�������������������������������������������������������������������� 203
Using OUTER APPLY to Join Aggregate Data����������������������������������������������������������������������������� 211
Metrics with Increasing Number of Return Rows���������������������������������������������������������������� 213
Table Valued Functions (TVFs)��������������������������������������������������������������������������������������������������� 215
Summary���������������������������������������������������������������������������������������������������������������������������������� 218

Part IV: The Bad and the Ugly��������������������������������������������������������������������� 219


Chapter 8: Agent Jobs������������������������������������������������������������������������������������������ 221
What Agent Jobs Should Be Doing�������������������������������������������������������������������������������������������� 221
Good Job(s)!������������������������������������������������������������������������������������������������������������������������ 221
When Should I Run Jobs?���������������������������������������������������������������������������������������������������� 222
Common Issues with Agent Jobs���������������������������������������������������������������������������������������������� 224
Overlap�������������������������������������������������������������������������������������������������������������������������������� 224
Summary���������������������������������������������������������������������������������������������������������������������������������� 226

Chapter 9: External Influences����������������������������������������������������������������������������� 227


Linked Servers�������������������������������������������������������������������������������������������������������������������������� 227
Security������������������������������������������������������������������������������������������������������������������������������� 227
Queries Joining Tables from Different Databases��������������������������������������������������������������� 228
Network IO��������������������������������������������������������������������������������������������������������������������������� 228
How Can We Fix This?��������������������������������������������������������������������������������������������������������� 229
SQL Code Calls in the Application��������������������������������������������������������������������������������������������� 229
So What’s the Problem?������������������������������������������������������������������������������������������������������ 229
Dynamic SQL����������������������������������������������������������������������������������������������������������������������� 231
Summary���������������������������������������������������������������������������������������������������������������������������������� 231

Index��������������������������������������������������������������������������������������������������������������������� 233

ix
About the Author
Lisa Bohm leads a team of database administrators (DBAs)
for a software development company. Her history with
legacy database code began early in her career with a
summer project to rewrite the chemical inventory database
for the research division of a local VA hospital. From
there, she went on to building front-end web applications.
When the web calls timed out, Lisa dug in to learn what
databases can do. She has since transitioned into database
administration, inheriting and improving legacy applications
along the way. Her personal focus remains on solid database
architecture and writing well-performing T-SQL.  

xi
About the Technical Reviewer
Kathi Kellenberger is a data platform MVP and the editor of Simple Talk at Redgate
Software. She has worked with SQL Server for over 20 years. She is also coleader of the
PASS Women in Technology Virtual Group and an instructor at LaunchCode. In her
spare time, Kathi enjoys spending time with family and friends, singing, and cycling.

xiii
Acknowledgments
I would like to thank all of the people who believed in me, encouraged, and pushed me
to continue to grow and learn. Special thanks go to Mindy Curnutt, Eric Blinn, and Tim
Tarbet, who showed me how amazing someone can be at the job they choose to do, and
believed that I could be that good too.
I cannot leave out the people who work for me. I lead a wonderful team of involved
people who are active in their continued learning, and continue to inspire me every day
by finding solutions to really difficult problems.
Also thank you to my family (including my #sqlfamily) who have always been
supportive, loving, and unstinting of hugs and moral support when needed!

xv
Introduction
What is legacy code? There are a few definitions floating around out there, but as a
working definition, we’re going to use the following:
Legacy code is code that is no longer being actively supported by the people who
wrote it.
Why are we going to use that? In software development, good documentation goes a
long way. Developers should understand what code is trying to accomplish and how it’s
trying to do so. When documentation either doesn’t exist or isn’t as thorough as required
and the original programmers aren’t available if you need to know why something was
written a particular way, it can be a nightmare to fix. In some cases, it may not even
be clear whether code ever worked as intended, or if the functionality of the change
someone is requesting is within the original intent of the programmer(s).

A Tale of Woe
How does legacy code start? Let’s look at this story. Code is written to solve a problem –
for example, someone is copying data into Excel every day and doing some hand
manipulation to generate a graph to add to a larger report. A developer sets up a quick
application to pull the data from the database and export it into Excel automatically for
the user, also performing the calculations the user was doing by hand.
This user then trains their successor and another person in the department on how
to view this report. One of them says, “Hey, this is great! Can you also make it pull data
for this other report and we can show how these numbers reflect against each other?”
Someone else loves the additional functionality but needs the code to work in a different
way, or do different statistical calculations, or needs to add an additional field on the
report. That person’s manager is intrigued by the functionality and wants a weekly
summary report to review. Code structure starts to resemble something that is cobbled
together, as multiple developers add bits of functionality over time. Oftentimes, there
is little to no documentation on the functionality or the choice of code – everyone just
adds a bunch of lines at the end of the code to handle the small part they were asked to
develop.

xvii
Introduction

Many times, front-end developers don’t specialize in T-SQL, so do not usually have
a deep understanding of the SQL Server optimizer. Especially in the case of “let’s just
add lines of code to the bottom of this to handle additional functionality,” calls to the
database can increase exponentially; in many cases, calls grab the same data over and
over. And, oh, by now, over half the company is using this app in one way or another – or
perhaps three ways. The vast majority of these uses, by the way, were never intended by
anyone who had ever touched the code.
Users complain about slowness and performance. Even more frustrating, all of the
other business-critical applications that use the same database(s) become slower and
slower as they fight for resources and locks with the application and its chatty data calls.
Also, of course, every developer that has ever touched this application has moved on or
has been promoted and hasn’t looked at code for years, so has no recollection of ever
manipulating any code even remotely similar to this patched-together behemoth that is
rampaging through the company infrastructure.

Congratulations!
You have inherited one of these types of applications, or you probably wouldn’t be
here reading this book. Although there will be (possibly many) times that you may
want to cry, yell, or swear, this will also give you some unparalleled opportunities to be
a hero and pull off some very spectacular-seeming fixes. Just remember, though, that
when you really fix something amazing, most people will be completely oblivious to
that fact. Then, when you do something you think is so obvious that a worm out there
on the sidewalk could probably manage it, you may get so many congratulations and
thanks that you’ll wonder if you really did something magical. That is probably more
of a general life/job observation and not related specifically to legacy code, but it’s also
prevalent here.

What This Book Is


This book is meant to help you, the reader, step in and identify potential issues in a
specific database object relatively quickly. There are a few assumptions that go with this:

1. Hardware/hardware configuration/VM configuration has been


ruled out as the performance problem.

xviii
Introduction

2. External sources have been ruled out as the problem (although we


all know it’s really the network…).

3. The database objects that are causing concern have been


identified.

We are going to continue on from the point of “Okay, this has been identified as an
issue. Now what do I do with it?” Most of what we’ll be doing is actually looking at the
code with the help of a few performance measures and learning about best practices to
help identify problem areas. You should be familiar with basic T-SQL coding syntax and
techniques and how to do a bit more advanced querying.
We will cover most of the issues commonly seen by object type, as well as a couple
of less common problems just for fun. Once these problem areas within the object are
identified, you can then mitigate the performance issues with relatively low effort and
cost. Some objects may require a much deeper dive. Once we’ve done some triage to
help alleviate the immediate pain an object is causing, we will cover what is involved in
effectively performing the deeper dive.
We also will talk about how to quickly tell if you’re on the right track in terms of the
fixes you want to apply. We’ll go over some simple (and free) tools that can be used to
measure performance, so you can document the before/after metrics to go along with
the rest of the documentation you’re going to be sure to add so the next poor sod
(I mean the next person) who has to maintain this system will have an easier time of it!

What This Book Is Not


Magic. This book is not magic, although at times it might seem like it helps you perform
some. Seriously though, there are times when things are so bad that you cannot fix them.
This is pretty rare, and either myself or one of the members of my various teams has
almost always managed to “just fix it,” but just be aware that it is possible that something
is unsalvageable. Even better, your boss should also be aware of this and have your back
should you run into that situation. If you are not that fortunate, you may want to consider
finding another job, but that is probably outside the scope of this book.

xix
Introduction

This is not a deep dive into indexes, statistics, or database maintenance. I’d hope
that you have all of those things set up. If you do not, please go find a book that discusses
those items and make sure you are doing them! If you are not in charge of the database
administration specifically, talk to whoever is and make sure THEY have those bases
covered. We will definitely mention indexes and statistics, but there is much more
complete information on them in other places, so discussion will be more of a flyby than
exhaustive information.
This is also not a way to identify problematic hardware, external forces, or database objects.
We mentioned in the preceding text what the assumptions are. If you skipped those, please
go back and at least read those points so we’re all on the same page when we start our triage.

T he Tools
We will be using a data dump from Stack Overflow. Brent Ozar kindly provided it on
his web site at www.brentozar.com/archive/2015/10/how-to-download-the-stack-
overflow-database-via-bittorrent/ which he in turn got from the awesome Stack
Overflow folks.
I will also include a backup with the files for the book so everyone can start on the
same foot. If you want to follow along with the examples, please make sure to run the
database setup script (included in the files for the book) after restoring the database
backup to add the additional objects we’ll be using throughout the book.
SQL Server Management Studio (SSMS) will be the code-running application of
choice. It is a free download from Microsoft: https://docs.microsoft.com/en-us/sql/
ssms/download-sql-server-management-studio-ssms
SentryOne Plan Explorer is also a free download. We will be using it to look at
execution plans and performance metrics related to the plan:

www.sentryone.com/plan-explorer
The last tool is a web site. You can paste in your exhaustively long statistics IO/time
output, and it will parse it into nice handy tables. It is worth its weight in… um… bitcoin?
If you’ve ever spent time trying to wade through output from a cursor or loop, you’ll
completely understand. Please be forewarned though that there is a limit to how much
you can dump in and have it still actually regurgitate output:

http://statisticsparser.com/

xx
Introduction

The Inconsequential (Mostly) Details


I ran examples using SSMS 18 on a Dell Latitude E7470 laptop. I set up a VM using Oracle
VirtualBox. The VM had a single vCPU and 8 GB RAM. I was using SQL Server 2016 SP2
running on Server 2016 Core.

Let’s Go!
Now that we’ve gotten that out of the way, let’s pull up the code for our first painful
object and go do some first aid!

xxi
PART I

Everything Is Slow
CHAPTER 1

T-SQL Triage
The most dreaded call a DBA can get is this: “Everything in the application is slow! FIX IT!”
In many cases, the database is one of the culprits of the poor performance of the legacy
code. When you are approached to deal with some of these issues, what do you do? Well,
first you identify painful code. We’re going to assume that you (or someone else) have
already identified the concerning areas. Once that happens, we need to assess the situation
by answering these questions:

1. Determine how critical the situation is:

a. Are users down?

b. Are all users down?

c. Is this causing the business to lose money?

2. Determine the relative effort involved:

a. How many lines of code are involved?

b. How many areas of the application call this code?

c. How much of the code needs to be changed?

3. Identify problem areas in the code:

a. What areas are causing pain?

4. Perform triage if possible.

3
© Lisa Bohm 2020
L. Bohm, Refactoring Legacy T-SQL for Improved Performance, https://doi.org/10.1007/978-1-4842-5581-0_1
Chapter 1 T-SQL Triage

Severity of the Situation


This is something you can only determine by gathering information either from users or
user managers. I know, I know, it means you have to actually talk to people. However, it
is a great indicator of whether you should immediately drop everything else and work
on this immediately, or if it can wait until the task you’re working on is completed. If you
don’t already, you may want to consider having a written SLA (Service-Level Agreement)
that states how quickly you or your team is expected (or required) to react to certain
events. Severity should be a key piece of that document.

Relative Effort
If this is a several-thousand-line-of-code object, just shake your head sadly and walk
away. Just kidding! However, the effort to just understand what is going on will be
significant, let alone the effort to actually fix the performance issues. This is where great
documentation comes into play, but it’s unlikely that if you’re reading this book, you
have that kind of help at your disposal.
Additionally, you need to think about QA (quality assurance) effort. The best
scenario is to never EVER let untested code go into production. By untested, I mean code
that hasn’t passed a rigorous QA process. Running something once on your laptop seems
like a great idea, but there are all sorts of weird scenarios that you may not be aware of,
that a QA professional will have a much better grasp on.
How many precise pain points are in this code? This will be the biggest
determination of what kind of triage we can perform to put smiles on the users’ faces (or
at least make them stop complaining to your boss). Regardless of any of these answers
though, if code has been identified as a real problem, triage is only your first step.
Even if you “fix” it with an index or other smooth wizardry, DO NOT STOP there! Fully
document the code and rewrite if necessary.

Problem Areas
“Hello? Sam DBA? Every time a user tries to change their display name, the application
hangs for seconds, and it’s really frustrating everyone.”
The best way to see what’s going on with code is to go run some of the code that is
causing problems. Sometimes, settings outside of the actual SQL code can cause issues
4
Chapter 1 T-SQL Triage

as well. Application connection strings can sometimes set ANSI settings, and this can
make SQL code run very differently from SQL Server Management Studio (SSMS),
for example. We’re going to assume that this has already been ruled out and isn’t the
cause of what we’re seeing in this book, but I wanted you to be aware that if you haven’t
checked into those possibilities, you probably should.
This is really useful information that you just received from the caller. Be aware,
however, that people like to throw around terms like “always” and “never” pretty lightly.
Try to get details: “When you say every time, is it 100% of the time? Is it 90% of the time?
Is it any time of day or limited to certain times?” Document the answers to go along with
the ticket/request/complaint documentation.
Let’s go look at what’s happening when a user tries to change their display name.
We’re going to try to change the following users’ display names in the sections to follow
as we look for where the issues are. We’ll use Jon Skeet who has an Id of 22656 and stic
who has an Id of 31996. The query will be run against the dbo.Users table.

STATISTICS IO and Time


STATISTICS IO is a measure of the amount of IO resources a query uses, and STATISTICS
TIME measures CPU and elapsed time a query runs, as well as query compile time. If you
are counting on that number at the bottom right of SSMS to measure query time, STOP
IT! It is not accurate – or certainly not accurate ENOUGH.

Query Window Setup


First, open up SSMS. Connect to the database and open a new query window and then
turn STATISTICS IO and STATISTICS TIME on. To do this through the UI, choose the
menu option for Query, then Query Options, and then Advanced. Figure 1-1 shows the
Advanced window for the Query Options.

5
Chapter 1 T-SQL Triage

Figure 1-1. Query Options Advanced window in SSMS

Make sure you check BOTH the SET STATISTICS TIME and the SET STATISTICS IO
boxes and then click OK.
If you want to go all scripting, you simply type in the query window the code
shown in Listing 1-1.

Listing 1-1. Command to set STATISTICS TIME and IO on

SET STATISTICS TIME, IO ON;

and click Execute (or hit Ctrl-E). Please note that either way you set STATISTICS
TIME and IO on, it will only be set for that specific query window or connection (SPID).
If the connection gets reset (e.g., restarting SSMS) or if you open another query window,
you will need to turn STATISTICS TIME and IO on again for the new query window.

C
 ode Tests
Next, we want to update a user’s name. So, first, I went and found a user by querying the
Users table and came up with a quick query to change a name. I ran it twice, because the
first run usually includes compile time. This user’s original name was “stic”, by the way.
We’ll change it back later.

6
Chapter 1 T-SQL Triage

Listing 1-2. Code to update the display name of user “stic”

UPDATE Users
SET DisplayName = 'stic in mud'
WHERE Id = 31996;

Listing 1-3 shows what the STATISTICS IO and TIME output looks like for the query in
Listing 1-2.

Listing 1-3. SSMS STATISTICS TIME and IO output

SQL Server parse and compile time:


   CPU time = 0 ms, elapsed time = 0 ms.
SQL Server parse and compile time:
   CPU time = 0 ms, elapsed time = 0 ms.
Table 'Users'. Scan count 0, logical reads 3, physical reads 0, read-ahead
reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server parse and compile time:
   CPU time = 0 ms, elapsed time = 0 ms.
SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.
Table 'WidePosts'. Scan count 1, logical reads 61083, physical reads 0,
read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-
ahead reads 0.
SQL Server Execution Times:
   CPU time = 110 ms,  elapsed time = 116 ms.
Table 'WidePosts'. Scan count 1, logical reads 305110, physical reads 0,
read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-
ahead reads 0.
SQL Server Execution Times:
   CPU time = 1141 ms,  elapsed time = 1142 ms.
SQL Server parse and compile time:
   CPU time = 1141 ms,  elapsed time = 1142 ms.

SQL Server Execution Times:


   CPU time = 0 ms,  elapsed time = 0 ms.
SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.

7
Chapter 1 T-SQL Triage

SQL Server Execution Times:


   CPU time = 1266 ms,  elapsed time = 1270 ms.

There is a much nicer way to look at the output in Listing 1-3. Go to the web site
http://statisticsparser.com/, and paste that output into the big window. Click the
Parse button, and scroll to the bottom of the page. You’ll get a nice summary table that
will give you what you need to know at a glance. This is the section labeled “Totals” on
the web page. Table 1-1 shows the read columns from the “Totals” section after running
the output shown in Listing 1-3 through the Statistics Parser web site.

Table 1-1. The “Totals” section read columns of the Statistics Parser site output for
Listing 1-3
Table Scan Count Logical Reads Physical Reads Read-Ahead Reads

Total 2 366,195 0 0
Users 0 3 0 0
WidePosts 2 366,192 0 0

CPU Elapsed

SQL Server parse and 00:00:01.141 00:00:01.142


compile time:
SQL Server Execution Times: 00:00:02.532 00:00:02.539
Total 00:00:03.673 00:00:03.681

So what are we looking at with Table 1-1? When we’re using STATISTICS output
to help with query tuning, we’re mostly going to focus on logical reads. The first time
you run a query, if your data isn’t in memory, you will see higher physical reads. When
testing queries, I generally ignore the first run and focus on the second. This is more
accurate to what is usually seen in production, where the data being called frequently
will already be in memory. Also, by always using this method, we can make sure we’re
comparing apples to apples.

8
Chapter 1 T-SQL Triage

We are seeing a LOT of page reads in Table 1-1, especially for the update of a single
row. But what is this WidePosts table? We weren’t updating that table… were we? No,
we were updating the dbo.Users table. Somehow, this WidePosts table is related to
the Users table. In SSMS, are there any objects around the Users table? A foreign key
constraint? A… wait, a trigger? Hmm, let’s look at the definition of the trigger, which
is shown in Listing 1-4.

Listing 1-4. ALTER statement for the ut_Users_WidePosts trigger

/*****************************************************************
  Object Description: Pushes user changes to the WidePosts table.
  Revision History:
  Date         Name             Label/PTS    Description
  -----------  --------------   ----------   -------------------
  2019.05.12   LBohm                         Initial Release
*****************************************************************/

ALTER TRIGGER [dbo].[ut_Users_WidePosts] ON [dbo].[Users]


FOR UPDATE
AS
SET NOCOUNT ON;

IF EXISTS
(
      SELECT 1
      FROM INSERTED i
      INNER JOIN dbo.WidePosts wp ON i.id = wp.OwnerUserId
)
BEGIN
      IF EXISTS
      (
            SELECT 1
            FROM INSERTED i
            INNER JOIN dbo.WidePosts wp ON i.Id = wp.OwnerUserId
            WHERE i.Age <> wp.Age

9
Chapter 1 T-SQL Triage

                  OR i.CreationDate <> wp.UserCreationDate


                  OR i.DisplayName <> wp.DisplayName
                  OR i.DownVotes <> wp.DownVotes
                  OR i.EmailHash <> wp.EmailHash
                  OR i.[Location] <> wp.[Location]
                  OR i.Reputation <> wp.Reputation
                  OR i.UpVotes <> wp.UpVotes
                  OR i.[Views] <> wp.[Views]
                  OR i.WebsiteUrl <> wp.WebsiteUrl
      )
      BEGIN
            UPDATE wp
            SET
            wp.[AboutMe] = LEFT(i.AboutMe, 2000)
                  , wp.[Age] = i.Age
                  , wp.[UserCreationDate] = i.CreationDate
                  , wp.[DisplayName] = i.DisplayName
                  , wp.[DownVotes] = i.DownVotes
                  , wp.[EmailHash] = i.EmailHash
                  , wp.[LastAccessDate] = i.LastAccessDate
                  , wp.[Location] = i.[Location]
                  , wp.[Reputation] = i.Reputation
                  , wp.[UpVotes] = i.UpVotes
                  , wp.[Views] = i.[Views]
                  , wp.[WebsiteUrl] = i.WebsiteUrl
                  , wp.AccountID = i.AccountID
            FROM dbo.WidePosts wp
            INNER JOIN INSERTED i ON wp.OwnerUserId = i.Id
            WHERE i.Age <> wp.Age
                  OR i.CreationDate <> wp.UserCreationDate
                  OR i.DisplayName <> wp.DisplayName
                  OR i.DownVotes <> wp.DownVotes
                  OR i.EmailHash <> wp.EmailHash
                  OR i.[Location] <> wp.[Location]
                  OR i.Reputation <> wp.Reputation

10
Chapter 1 T-SQL Triage

                  OR i.UpVotes <> wp.UpVotes


                  OR i.[Views] <> wp.[Views]
                  OR i.WebsiteUrl <> wp.WebsiteUrl;
      END;
END;
GO

Huh. Every time we are updating the Users table, it sends the update to any records
in this WidePosts table where the OwnerUserID is equal to the Users table id. It’s doing a
LOT of reads to perform this task. Let’s run another example, and this time we’ll grab an
execution plan.

E xecution Plans
An execution plan shows how SQL Server decided to run your query or statement. It
shows the operators that the optimizer chose. If you have never looked at one, it can be
confusing. The execution plan is XML data that can be parsed into a diagram of what
operators the SQL Server engine uses to fulfill your query request. (Well, that’s clear as
mud, right?) When a query is run, SQL Server uses the Query Optimizer to figure out
the best way to return data or perform the request. For each table that we’re getting data
from or performing an operation against, SQL Server will use one or more operators to
access that table. For example, if we need data from a large portion of the Posts table,
SQL Server will perform a “table scan” – that is, it will read all of the data pages for the
table – to find the data to return. The plan will also show how many rows SQL Server is
expecting to push along to the next operator.
The first things I look for at a quick glance are big fat lines (indicating a LOT of data,
or number of rows being pushed to the next operator). Also, I review which operators
show the most work being done. I use Plan Explorer to help sort operations by the work
being done per operation, which allows us to easily find the most expensive culprits. So
how do we get this information?
In SSMS, there is a button you can click above the query window. When you hover
over it, it will show you text reading “Get Actual Execution Plan”. This icon is shown in
Figure 1-2, surrounded by a box.

11
Chapter 1 T-SQL Triage

Figure 1-2. Turning on an actual execution plan in SSMS

S
 tatistics
An actual execution plan is simply an estimated execution plan including statistics
information, so generally they’re not very different. Statistics are metadata that
SQL Server keeps against columns in tables to indicate the distribution of data. In a
dictionary, for example, there are lots of words starting with the letters “st”. There are less
words starting with “zy”. This skew of data is tracked through statistics, which helps the
Query Optimizer find a better plan.
I usually use an actual execution plan because it’s also an easy way to see if statistics
might be out of date and require maintenance. If we get back a single row and the Query
Optimizer thinks we’re returning 3,000 rows, statistics maintenance might be needed.
Let’s run a query updating a different display name now.

Listing 1-5. Updating the display name for user ID 22656

UPDATE Users
SET DisplayName = 'Rita Skeeter'
WHERE Id = 22656;

What can our execution plan tell us? Let’s take a look at the plan generated by
running the code in Listing 1-5. If you have SentryOne Plan Explorer installed, you
can right-click the execution plan in SSMS (look for the tab near your Results tab) and
choose to open the plan in Plan Explorer from the menu that appears.

12
Chapter 1 T-SQL Triage

Once you look at the execution plan in Plan Explorer, you’ll see something similar to
what we see in Figure 1-3. Please make sure you have clicked the third row of the Results
tab Statement output – we want to be looking at the query plan for the most expensive
(highest estimated cost) part of the query we ran.

Figure 1-3. Initial execution plan for the query in Listing 1-5

We see that the most effort was spent on a clustered index scan for WidePosts, since
the trigger updates the WidePosts table when the Users table is updated.
What is a clustered index scan? Well, a clustered index is the index where all of the
table’s data is stored, in the order specified by the index. There can only be a single
clustered index per table. If a table does not have a clustered index, it is considered a
heap. So a scan of the clustered index is comparable to a scan of the entire table.
Also, there are a lot of rows being read in that operation. Well, if we decided to
look in the WidePosts table, we’d find that there were 4,394 rows of data that included
the OwnerUserID corresponding to user ID 22656. That’s all well and good, but we are
reading over 850,000 rows. (We can see that 850,000 rows in Figure 1-3; it’s the number
right under the wide arrow leading from the Clustered Index Scan.) This is a problem
that we will need to address.

13
Another Random Document on
Scribd Without Any Related Topics
The Project Gutenberg eBook of John Marvel,
Assistant
This ebook is for the use of anyone anywhere in the United States
and most other parts of the world at no cost and with almost no
restrictions whatsoever. You may copy it, give it away or re-use it
under the terms of the Project Gutenberg License included with this
ebook or online at www.gutenberg.org. If you are not located in the
United States, you will have to check the laws of the country where
you are located before using this eBook.

Title: John Marvel, Assistant

Author: Thomas Nelson Page

Illustrator: James Montgomery Flagg

Release date: January 10, 2013 [eBook #41817]


Most recently updated: October 23, 2024

Language: English

Credits: Produced by D Alexander, Mary Meehan and the Online


Distributed Proofreading Team at http://www.pgdp.net

*** START OF THE PROJECT GUTENBERG EBOOK JOHN MARVEL,


ASSISTANT ***
JOHN MARVEL
ASSISTANT
BY THOMAS NELSON PAGE
ILLUSTRATED BY
JAMES MONTGOMERY FLAGG
NEW YORK
CHARLES SCRIBNER'S SONS
1909
Copyright, 1909, by
CHARLES SCRIBNER'S SONS
Published October, 1909

TO THOSE LOVED ONES


WHOSE NEVER FAILING SYMPATHY HAS
LED ME ALL THESE YEARS
"To ply your old trade?" I asked.
CONTENTS
CHAPTER PAGE
I. My First Failure 1
II. The Jew and the Christian 5
III. The Fight 16
IV. Delilah 26
V. The Hare and the Tortoise 36
VI. The Meteor 44
VII. The Hegira 55
VIII. Padan-Aram 67
IX. I Pitch My Tent 84
X. A New Girl 103
XI. Eleanor Leigh 114
XII. John Marvel 138
XIII. Mr. Leigh 147
XIV. Miss Leigh Seeks Work 154
XV. The Lady of the Violets 172
XVI. The Shadow of Sham 186
XVII. The Gulf 198
XVIII. The Drummer 215
XIX. Re-enter Peck 227
XX. My First Client 245
XXI. The Resurrection of Dix 259
XXII. The Preacher 275
XXIII. Mrs. Argand 286
XXIV. Wolffert's Mission 305
XXV. Fate Leads 319
XXVI. Coll McSheen's Methods 339
XXVII. The Shadow 354
XXVIII. The Walking Delegate 361
XXIX. My Confession 381
XXX. Seeking One That Was Lost 398
XXXI. John Marvel's Raid 416
XXXII. Doctor Caiaphas 430
XXXIII. The Peace-maker 453
XXXIV. The Flag of Truce 465
XXXV. Mr. Leigh has a Proposal of Marriage Made Him 493
XXXVI. The Riot and Its Victim 507
XXXVII. Wolffert's Neighbors 517
XXXVIII. Wolffert's Philosophy 527
XXXIX. The Conflict 539
XL. The Curtain 563
ILLUSTRATIONS
"To ply your old trade?" I asked Frontispiece
Wolffert ... was cursing me with all the eloquence of a
20
rich vocabulary
"Hi! What you doin'?" he stammered 60
"But you must not come in" 140
"Perhaps, you are the man yourself?" she added
302
insolently
"Speak her soft, Galley" 412
"I suppose it is necessary that we should at least
468
appear to be exchanging the ordinary inanities"
I am sure it was on that stream that Halcyone found
556
retreat
JOHN MARVEL, ASSISTANT
I
MY FIRST FAILURE
I shall feel at liberty to tell my story in my own way; rambling along
at my own gait; now going from point to point; now tearing ahead;
now stopping to rest or to ruminate, and even straying from the
path whenever I think a digression will be for my own enjoyment.
I shall begin with my college career, a period to which I look back
now with a pleasure wholly incommensurate with what I achieved in
it; which I find due to the friends I made and to the memories I
garnered there in a time when I possessed the unprized treasures of
youth: spirits, hope, and abounding conceit. As these memories,
with the courage (to use a mild term) that a college background
gives, are about all that I got out of my life there, I shall dwell on
them only enough to introduce two or three friends and one enemy,
who played later a very considerable part in my life.
My family was an old and distinguished one; that is, it could be
traced back about two hundred years, and several of my ancestors
had accomplished enough to be known in the history of the State—a
fact of which I was so proud that I was quite satisfied at college to
rest on their achievements, and felt no need to add to its distinction
by any labors of my own.
We had formerly been well off; we had, indeed, at one time prior to
the Revolutionary War, owned large estates—a time to which I was
so fond of referring when I first went to college that one of my
acquaintances, named Peck, an envious fellow, observed one day
that I thought I had inherited all the kingdoms of the earth and the
glory of them. My childhood was spent on an old plantation, so far
removed from anything that I have since known that it might almost
have been in another planet.
It happened that I was the only child of my parents who survived,
the others having been carried off in early childhood by a scourge of
scarlet fever, to which circumstance, as I look back, I now know was
due my mother's sadness of expression when my father was not
present. I was thus subjected to the perils and great misfortune of
being an only child, among them that of thinking the sun rises and
sets for his especial benefit. I must say that both my father and
mother tried faithfully to do their part to counteract this danger, and
they not only believed firmly in, but acted consistently on, the
Solomonic doctrine that to spare the rod is to spoil the child. My
father, I must say, was more lenient, and I think gladly evaded the
obligation as interpreted by my mother, declaring that Solomon, like
a good many other persons, was much wiser in speech than in
practice. He was fond of quoting the custom of the ancient
Scythians, who trained their youth to ride, to shoot, and to speak
the truth. And in this last particular he was inexorable.
Among my chief intimates as a small boy was a little darkey named
"Jeams." Jeams was the grandson of one of our old servants—Uncle
Ralph Woodson. Jeams, who was a few years my senior, was a
sharp-witted boy, as black as a piece of old mahogany, and had a
head so hard that he could butt a plank off a fence. Naturally he and
I became cronies, and he picked up information on various subjects
so readily that I found him equally agreeable and useful.
My father was admirably adapted to the conditions that had created
such a character, but as unsuited to the new conditions that
succeeded the collapse of the old life as a shorn lamb would be to
the untempered wind of winter. He was a Whig and an aristocrat of
the strongest type, and though in practice he was the kindest and
most liberal of men, he always maintained that a gentleman was the
choicest fruit of civilization; a standard, I may say, in which the
personal element counted with him far more than family connection.
"A king can make a nobleman, sir," he used to say; "but it takes
Jehovah to make a gentleman." When the war came, though he was
opposed to "Locofocoism" as he termed it, he enlisted as a private
as soon as the State seceded, and fought through the war, rising to
be a major and surrendering at Appomattox. When the war closed,
he shut himself up on his estate, accepting the situation without
moroseness, and consoling himself with a philosophy much more
misanthropic in expression than in practice.
My father's slender patrimony had been swept away by the war, but,
being a scholar himself, and having a high idea of classical learning
and a good estimate of my abilities—in which latter view I entirely
agreed with him—he managed by much stinting to send me to
college out of the fragments of his establishment. I admired greatly
certain principles which were stamped in him as firmly as a fossil is
embedded in the solid rock; but I fear I had a certain contempt for
what appeared to me his inadequacy to the new state of things, and
I secretly plumed myself on my superiority to him in all practical
affairs. Without the least appreciation of the sacrifices he was
making to send me to college, I was an idle dog and plunged into
the amusements of the gay set—that set whose powers begin below
their foreheads—in which I became a member and aspired to be a
leader.
My first episode at college brought me some éclat.
II
THE JEW AND THE CHRISTIAN
I arrived rather late and the term had already begun, so that all the
desirable rooms had been taken. I was told that I would either have
to room out of college or take quarters with a young man by the
name of Wolffert—like myself, a freshman. I naturally chose the
latter. On reaching my quarters, I found my new comrade to be an
affable, gentlemanly fellow, and very nice looking. Indeed, his broad
brow, with curling brown hair above it; his dark eyes, deep and
luminous; a nose the least bit too large and inclining to be aquiline;
a well-cut mouth with mobile, sensitive lips, and a finely chiselled
jaw, gave him an unusual face, if not one of distinction. He was
evidently bent on making himself agreeable to me, and as he had
read an extraordinary amount for a lad of his age and I, who had
also read some, was lonely, we had passed a pleasant evening when
he mentioned casually a fact which sent my heart down into my
boots. He was a Jew. This, then, accounted for the ridge of his well-
carved nose, and the curl of his soft brown hair. I tried to be as frank
and easy as I had been before, but it was a failure. He saw my
surprise as I saw his disappointment—a coolness took the place of
the warmth that had been growing up between us for several hours,
and we passed a stiff evening. He had already had one room-mate.
Next day, I found a former acquaintance who offered to take me into
his apartment, and that afternoon, having watched for my
opportunity, I took advantage of my room-mate's absence and
moved out, leaving a short note saying that I had discovered an old
friend who was very desirous that I should share his quarters. When
I next met Wolffert, he was so stiff, that although I felt sorry for him
and was ready to be as civil as I might, our acquaintance thereafter
became merely nominal. I saw in fact, little of him during the next
months, for he soon forged far ahead of me. There was, indeed, no
one in his class who possessed his acquirements or his ability. I used
to see him for a while standing in his doorway looking wistfully out
at the groups of students gathered under the trees, or walking
alone, like Isaac in the fields, and until I formed my own set, I would
have gone and joined him or have asked him to join us but for his
rebuff. I knew that he was lonely; for I soon discovered that the cold
shoulder was being given to him by most of the students. I could
not, however, but feel that it served him right for the "airs" he put
on with me. That he made a brilliant exhibition in his classes and
was easily the cleverest man in the class did not affect our attitude
toward him; perhaps, it only aggravated the case. Why should he be
able to make easily a demonstration at the blackboard that the
cleverest of us only bungled through? One day, however, we learned
that the Jew had a room-mate. Bets were freely taken that he would
not stick, but he stuck—for it was John Marvel. Not that any of us
knew what John Marvel was; for even I, who, except Wolffert, came
to know him best, did not divine until many years later what a
nugget of unwrought gold that homely, shy, awkward John Marvel
was!
It appeared that Wolffert had a harder time than any of us dreamed
of.
He had come to the institution against the advice of his father, and
for a singular reason: he thought it the most liberal institution of
learning in the country! Little he knew of the narrowness of youth!
His mind was so receptive that all that passed through it was
instantly appropriated. Like a plant, he drew sustenance from the
atmosphere about him and transmuted what was impalpable to us to
forms of beauty. He was even then a man of independent thought; a
dreamer who peopled the earth with ideals, and saw beneath the
stony surface of the commonplace the ideals and principles that
were to reconstruct and resurrect the world. An admirer of the Law
in its ideal conception, he reprobated, with the fury of the Baptist,
the generation that had belittled and cramped it to an instrument of
torture of the human mind, and looked to the millenial coming of
universal brotherhood and freedom.
His father was a leading man in his city; one who, by his native
ability and the dynamic force that seems to be a characteristic of the
race, had risen from poverty to the position of chief merchant and
capitalist of the town in which he lived. He had been elected mayor
in a time of stress; but his popularity among the citizens generally
had cost him, as I learned later, something among his own people.
The breadth of his views had not been approved by them.
The abilities that in the father had taken this direction of the
mingling of the practical and the theoretical had, in the son, taken
the form I have stated. He was an idealist: a poet and a dreamer.
The boy from the first had discovered powers that had given his
father the keenest delight, not unmingled with a little misgiving. As
he grew up among the best class of boys in his town, and became
conscious that he was not one of them, his inquiring and aspiring
mind began early to seek the reasons for the difference. Why should
he be held a little apart from them? He was a Jew. Yes, but why
should a Jew be held apart? They talked about their families. Why,
his family could trace back for two thousand and more years to
princes and kings. They had a different religion. But he saw other
boys with different religions going and playing together. They were
Christians, and believed in Christ, while the Jew, etc. This puzzled
him till he found that some of them—a few—did not hold the same
views of Christ with the others. Then he began to study for himself,
boy as he was, the history of Christ, and out of it came questions
that his father could not answer and was angry that he should put to
him. He went to a young Rabbi who told him that Christ was a good
man, but mistaken in His claims.
So, the boy drifted a little apart from his own people, and more and
more he studied the questions that arose in his mind, and more and
more he suffered; but more and more he grew strong.
The father, too proud of his son's independence to coerce him by an
order which might have been a law to him, had, nevertheless,
thrown him on his own resources and cut him down to the lowest
figure on which he could live, confident that his own opinions would
be justified and his son return home.
Wolffert's first experience very nearly justified this conviction. The
fact that a Jew had come and taken one of the old apartments
spread through the college with amazing rapidity and created a
sensation. Not that there had not been Jews there before, for there
had been a number there at one time or another. But they were
members of families of distinction, who had been known for
generations as bearing their part in all the appointments of life, and
had consorted with other folk on an absolute equality; so that there
was little or nothing to distinguish them as Israelites except their
name. If they were Israelites, it was an accident and played no
larger part in their views than if they had been Scotch or French. But
here was a man who proclaimed himself a Jew; who proposed that it
should be known, and evidently meant to assert his rights and
peculiarities on all occasions. The result was that he was subjected
to a species of persecution which only the young Anglo-Saxon, the
most brutal of all animals, could have devised.
As college filled rapidly, it soon became necessary to double up, that
is, put two men in one apartment. The first student assigned to live
with Wolffert was Peck, a sedate and cool young man—like myself,
from the country, and like myself, very short of funds. Peck would
not have minded rooming with a Jew, or, for that matter, with the
Devil, if he had thought he could get anything out of him; for he had
few prejudices, and when it came to calculation, he was the
multiplication-table. But Peck had his way to make, and he coolly
decided that a Jew was likely to make him bear his full part of the
expenses—which he never had any mind to do. So he looked
around, and within forty-eight hours moved to a place out of college
where he got reduced board on the ground of belonging to some
peculiar set of religionists, of which I am convinced he had never
heard till he learned of the landlady's idiosyncrasy.
I had incurred Peck's lasting enmity—though I did not know it at the
time—by a witticism at his expense. We had never taken to each
other from the first, and one evening, when someone was talking
about Wolffert, Peck joined in and said that that institution was no
place for any Jew. I said, "Listen to Peck sniff. Peck, how did you get
in?" This raised a laugh. Peck, I am sure, had never read "Martin
Chuzzlewit"; but I am equally sure he read it afterward, for he never
forgave me.
Then came my turn and desertion which I have described. And then,
after that interval of loneliness, appeared John Marvel.
Wolffert, who was one of the most social men I ever knew, was
sitting in his room meditating on the strange fate that had made him
an outcast among the men whom he had come there to study and
know. This was my interpretation of his thoughts: he would probably
have said he was thinking of the strange prejudices of the human
race—prejudices to which he had been in some sort a victim all his
life, as his race had been all through the ages. He was steeped in
loneliness, and as, in the mellow October afternoon, the sound of
good-fellowship floated in at his window from the lawn outside, he
grew more and more dejected. One evening it culminated. He even
thought of writing to his father that he would come home and go
into his office and accept the position that meant wealth and luxury
and power. Just then there was a step outside, and someone
stopped and after a moment, knocked at the door. Wolffert rose and
opened it and stood facing a new student—a florid, round-faced,
round-bodied, bow-legged, blue-eyed, awkward lad of about his own
age.
"Is this number ——?" demanded the newcomer, peering curiously at
the dingy door and half shyly looking up at the occupant.
"It is. Why?" Wolffert spoke abruptly.
"Well, I have been assigned to this apartment by the Proctor. I am a
new student and have just come. My name is Marvel—John Marvel."
Wolffert put his arms across the doorway and stood in the middle of
it.
"Well, I want to tell you before you come in that I am a Jew. You are
welcome not to come, but if you come I want you to stay." Perhaps
the other's astonishment contained a query, for he went on hotly:
"I have had two men come here already and both of them left after
one day. The first said he got cheaper board, which was a legitimate
excuse—if true—the other said he had found an old friend who
wanted him. I am convinced that he lied and that the only reason he
left was that I am a Jew. And now you can come in or not, as you
please, but if you come you must stay." He was looking down in
John Marvel's eyes with a gaze that had the concentrated bitterness
of generations in it, and the latter met it with a gravity that
deepened into pity.
"I will come in and I will stay; Jesus was a Jew," said the man on
the lower step.
"I do not know him," said the other bitterly.
"But you will. I know Him."
Wolffert's arms fell and John Marvel entered and stayed.
That evening the two men went to the supper hall together. Their
table was near mine and they were the observed of all observers.
The one curious thing was that John Marvel was studying for the
ministry. It lent zest to the jokes that were made on this incongruous
pairing, and jests, more or less insipid, were made on the Law and
the Prophets; the lying down together of the lion and the lamb, etc.
It was a curious mating—the light-haired, moon-faced, slow-witted
Saxon, and the dark, keen Jew with his intellectual face and his
deep-burning eyes in which glowed the misery and mystery of the
ages.
John Marvel soon became well known; for he was one of the slowest
men in the college. With his amusing awkwardness, he would have
become a butt except for his imperturbable good-humor. As it was,
he was for a time a sort of object of ridicule to many of us—myself
among the number—and we had many laughs at him. He would
disappear on Saturday night and not turn up again till Monday
morning, dusty and disheveled. And many jests were made at his
expense. One said that Marvel was practising preaching in the
mountains with a view to becoming a second Demosthenes; another
suggested that, if so, the mountains would probably get up and run
into the sea.
When, however, it was discovered later that he had a Sunday-school
in the mountains, and walked twelve miles out and twelve miles
back, most of the gibers, except the inveterate humorists like myself,
were silent.
This fact came out by chance. Marvel disappeared from college one
day and remained away for two or three weeks. Wolffert either could
not or would not give any account of him. When Marvel returned, he
looked worn and ill, as if he had been starving, and almost
immediately he was taken ill and went to the infirmary with a case of
fever. Here he was so ill that the doctors quarantined him and no
one saw him except the nurse—old Mrs. Denny, a wrinkled and bald-
headed, old, fat woman, something between a lightwood knot and
an angel—and Wolffert.
Wolffert moved down and took up his quarters in the infirmary—it
was suggested, with a view to converting Marvel to Judaism—and
here he stayed. The nursing never appeared to make any difference
in Wolffert's preparation for his classes; for when he came back he
still stood easily first. But poor Marvel never caught up again, and
was even more hopelessly lost in the befogged region at the bottom
of the class than ever before. When called on to recite, his brow
would pucker and he would perspire and stammer until the class
would be in ill-suppressed convulsions, all the more enjoyable
because of Leo Wolffert's agonizing over his wretchedness. Then
Marvel, excused by the professor, would sit down and mop his brow
and beam quite as if he had made a wonderful performance (which
indeed, he had), while Wolffert's thin face would grow whiter, his
nostrils quiver, and his deep eyes burn like coals.
One day a spare, rusty man with a frowzy beard, and a lank,
stooping woman strolled into the college grounds, and after
wandering around aimlessly for a time, asked for Mr. Marvel. Each of
them carried a basket. They were directed to his room and remained
with him some time, and when they left, he walked some distance
with them.
It was at first rumored and then generally reported that they were
Marvel's father and mother. It became known later that they were a
couple of poor mountaineers named Shiflett, whose child John
Marvel had nursed when it had the fever. They had just learned of
his illness and had come down to bring him some chickens and other
things which they thought he might need.
This incident, with the knowledge of Marvel's devotion, made some
impression on us, and gained for Marvel, and incidentally for
Wolffert, some sort of respect.
III
THE FIGHT
All this time I was about as far aloof from Marvel and Wolffert as I
was from any one in the college.
I rather liked Marvel, partly because he appeared to like me and I
helped him in his Latin, and partly because Peck sniffed at him, and
Peck I cordially disliked for his cold-blooded selfishness and his
plodding way.
I was strong and active and fairly good-looking, though by no means
so handsome as I fancied myself when I passed the large plate-glass
windows in the stores; I was conceited, but not arrogant except to
my family and those I esteemed my inferiors; was a good poker-
player; was open-handed enough, for it cost me nothing; and was
inclined to be kind by nature.
I had, moreover, several accomplishments which led to a certain
measure of popularity. I had a retentive memory, and could get up a
recitation with little trouble; though I forgot about as quickly as I
learned. I could pick a little on a banjo; could spout fluently what
sounded like a good speech if one did not listen to me; could write,
what someone has said, looked at a distance like poetry and, thanks
to my father, could both fence and read Latin. These
accomplishments served to bring me into the best set in college and,
in time, to undo me. For there is nothing more dangerous to a young
man than an exceptional social accomplishment. A tenor voice is
almost as perilous as a taste for drink; and to play the guitar, about
as seductive as to play poker.
I was soon to know Wolffert better. He and Marvel, after their work
became known, had been admitted rather more within the circle,
though they were still kept near the perimeter. And thus, as the
spring came on, when we all assembled on pleasant afternoons
under the big trees that shaded the green slopes above the athletic
field, even Wolffert and Marvel were apt to join us. I would long ago
have made friends with Wolffert, as some others had done since he
distinguished himself; for I had been ashamed of my poltroonery in
leaving him; but, though he was affable enough with others, he
always treated me with such marked reserve that I had finally
abandoned my charitable effort to be on easy terms with him.
One spring afternoon we were all loafing under the trees, many of
us stretched out on the grass. I had just saved a game of baseball
by driving a ball that brought in three men from the bases, and I
was surrounded by quite a group. Marvel, who was as strong as an
ox, was second-baseman on the other nine and had missed the ball
as the center-fielder threw it wildly. Something was said—I do not
recall what—and I raised a laugh at Marvel's expense, in which he
joined heartily. Then a discussion began on the merits in which
Wolffert joined. I started it, but as Wolffert appeared excited, I drew
out and left it to my friends.
Presently, at something Wolffert said, I turned to a friend, Sam
Pleasants, and said in a half-aside, with a sneer: "He did not see it;
Sam, you—" I nodded my head, meaning, "You explain it."
Suddenly, Wolffert rose to his feet and, without a word of warning,
poured out on me such a torrent of abuse as I never heard before or
since. His least epithet was a deadly insult. It was out of a clear sky,
and for a moment my breath was quite taken away. I sprang to my
feet and, with a roar of rage, made a rush for him. But he was
ready, and with a step to one side, planted a straight blow on my
jaw that, catching me unprepared, sent me full length on my back. I
was up in a second and made another rush for him, only to be
caught in the same way and sent down again.
When I rose the second time, I was cooler. I knew then that I was in
for it. Those blows were a boxer's. They came straight from the
shoulder and were as quick as lightning, with every ounce of the
giver's weight behind them. By this time, however, the crowd had
interfered. This was no place for a fight, they said. The professors
would come on us. Several were holding me and as many more had
Wolffert; among them, John Marvel, who could have lifted him in his
strong arms and held him as a baby. Marvel was pleading with him
with tears in his eyes. Wolffert was cool enough now, but he took no
heed of his friend's entreaties. Standing quite still, with the blaze in
his eyes all the more vivid because of the pallor of his face, he was
looking over his friend's head and was cursing me with all the
eloquence of a rich vocabulary. So far as he was concerned, there
might not have been another man but myself within a mile.
Welcome to our website – the ideal destination for book lovers and
knowledge seekers. With a mission to inspire endlessly, we offer a
vast collection of books, ranging from classic literary works to
specialized publications, self-development books, and children's
literature. Each book is a new journey of discovery, expanding
knowledge and enriching the soul of the reade

Our website is not just a platform for buying books, but a bridge
connecting readers to the timeless values of culture and wisdom. With
an elegant, user-friendly interface and an intelligent search system,
we are committed to providing a quick and convenient shopping
experience. Additionally, our special promotions and home delivery
services ensure that you save time and fully enjoy the joy of reading.

Let us accompany you on the journey of exploring knowledge and


personal growth!

textbookfull.com

You might also like