100% found this document useful (3 votes)
17 views

SQL Server Analytical Toolkit: Using Windowing, Analytical, Ranking, and Aggregate Functions for Data and Statistical Analysis 1st Edition Angelo Bobak - Quickly download the ebook to never miss any content

The document promotes various eBooks available for download on ebookmeta.com, including titles like 'SQL Server Analytical Toolkit' by Angelo Bobak and others covering diverse topics such as history, mathematical analysis, and romance. It provides links to each eBook for instant access and emphasizes the ease of downloading. Additionally, it includes details about the 'SQL Server Analytical Toolkit' book, including its content and structure.

Uploaded by

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

SQL Server Analytical Toolkit: Using Windowing, Analytical, Ranking, and Aggregate Functions for Data and Statistical Analysis 1st Edition Angelo Bobak - Quickly download the ebook to never miss any content

The document promotes various eBooks available for download on ebookmeta.com, including titles like 'SQL Server Analytical Toolkit' by Angelo Bobak and others covering diverse topics such as history, mathematical analysis, and romance. It provides links to each eBook for instant access and emphasizes the ease of downloading. Additionally, it includes details about the 'SQL Server Analytical Toolkit' book, including its content and structure.

Uploaded by

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

Read Anytime Anywhere Easy Ebook Downloads at ebookmeta.

com

SQL Server Analytical Toolkit: Using Windowing,


Analytical, Ranking, and Aggregate Functions for
Data and Statistical Analysis 1st Edition Angelo
Bobak
https://ebookmeta.com/product/sql-server-analytical-toolkit-
using-windowing-analytical-ranking-and-aggregate-functions-
for-data-and-statistical-analysis-1st-edition-angelo-bobak/

OR CLICK HERE

DOWLOAD EBOOK

Visit and Get More Ebook Downloads Instantly at https://ebookmeta.com


Recommended digital products (PDF, EPUB, MOBI) that
you can download immediately if you are interested.

SQL Server Analytical Toolkit: Using Windowing,


Analytical, Ranking, and Aggregate Functions for Data and
Statistical Analysis 1st Edition Angelo Bobak
https://ebookmeta.com/product/sql-server-analytical-toolkit-using-
windowing-analytical-ranking-and-aggregate-functions-for-data-and-
statistical-analysis-1st-edition-angelo-bobak/
ebookmeta.com

Cambridge IGCSE and O Level History Workbook 2C - Depth


Study: the United States, 1919-41 2nd Edition Benjamin
Harrison
https://ebookmeta.com/product/cambridge-igcse-and-o-level-history-
workbook-2c-depth-study-the-united-states-1919-41-2nd-edition-
benjamin-harrison/
ebookmeta.com

Mathematical Analysis and Analytical Modeling 1st Edition


Ivan Stanimirovic■

https://ebookmeta.com/product/mathematical-analysis-and-analytical-
modeling-1st-edition-ivan-stanimirovic/

ebookmeta.com

Good Son is Sad If He Hears the Name of His Father The


Tabooing of Names in China as a Way of Implementing Social
Values 1st Edition Piotr Adamek
https://ebookmeta.com/product/good-son-is-sad-if-he-hears-the-name-of-
his-father-the-tabooing-of-names-in-china-as-a-way-of-implementing-
social-values-1st-edition-piotr-adamek/
ebookmeta.com
Museum Store the Manager s Guide Basic Guidelines for the
New Museum Store Manager 4th Edition Museum Store
Association (U.S.)
https://ebookmeta.com/product/museum-store-the-manager-s-guide-basic-
guidelines-for-the-new-museum-store-manager-4th-edition-museum-store-
association-u-s/
ebookmeta.com

PYRO Sons of Hell MC Sons of Hell M C Book 3 1st Edition


Rebecca Joyce

https://ebookmeta.com/product/pyro-sons-of-hell-mc-sons-of-hell-m-c-
book-3-1st-edition-rebecca-joyce/

ebookmeta.com

Dirty Secret A Dark Enemies to Lovers Mafia Romance 1st


Edition Bianca Cole

https://ebookmeta.com/product/dirty-secret-a-dark-enemies-to-lovers-
mafia-romance-1st-edition-bianca-cole/

ebookmeta.com

A Touch Of Envy Vices Virtues 1st Edition Alana Winters

https://ebookmeta.com/product/a-touch-of-envy-vices-virtues-1st-
edition-alana-winters/

ebookmeta.com

Only Pieces 1st Edition Edd Tello

https://ebookmeta.com/product/only-pieces-1st-edition-edd-tello/

ebookmeta.com
Optimization of Integer/Fractional Order Chaotic Systems
by Metaheuristics and their Electronic Realization 1st
Edition Esteban Tlelo-Cuautle
https://ebookmeta.com/product/optimization-of-integer-fractional-
order-chaotic-systems-by-metaheuristics-and-their-electronic-
realization-1st-edition-esteban-tlelo-cuautle/
ebookmeta.com
SQL Server Analytical
Toolkit
Using Windowing, Analytical,
Ranking, and Aggregate Functions
for Data and Statistical Analysis

Angelo Bobak
SQL Server Analytical Toolkit: Using Windowing, Analytical, Ranking, and Aggregate
Functions for Data and Statistical Analysis
Angelo Bobak
Hastings On Hudson, NY, USA

ISBN-13 (pbk): 978-1-4842-8666-1 ISBN-13 (electronic): 978-1-4842-8667-8


https://doi.org/10.1007/978-1-4842-8667-8

Copyright © 2023 by Angelo Bobak


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: Joan Murray
Development Editor: Laura Berendson
Coordinating Editor: Gryffin Winkler
Cover image designed by Freepik (www.freepik.com)
Distributed to the book trade worldwide by Springer Science+Business Media LLC, 1 New York Plaza,
Suite 4600, New York, NY 10004. 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 [email protected]; for reprint,
paperback, or audio rights, please e-mail [email protected].
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.
Paper in this product is recyclable
I would like to dedicate this book to my wife, Cathy, for all her support
and patience throughout my career and all my book projects. I would
like to thank Apress for this opportunity, specifically Joan Murray, the
acquisition editor, who gave me this chance and Laura Berendson
and Gryffin Winkler for their valuable help and suggestions.
Last but not least, I thank all the technical reviewers for their
suggestions, tips, and corrections. What’s good about this book is
because of them. What’s not so good is entirely due to me!
Table of Contents
About the Author�����������������������������������������������������������������������������������������������������xv

About the Technical Reviewer�������������������������������������������������������������������������������xvii


Introduction������������������������������������������������������������������������������������������������������������xix

Chapter 1: Partitions, Frames, and the OVER( ) Clause��������������������������������������������� 1


What Are Partitions and Window Frames?������������������������������������������������������������������������������������ 1
What Is an OVER( ) Clause?����������������������������������������������������������������������������������������������������������� 2
History of the OVER( ) Clause and Window Functions������������������������������������������������������������������� 2
The Window Functions������������������������������������������������������������������������������������������������������������������ 3
The OVER( ) Clause������������������������������������������������������������������������������������������������������������������������ 4
Syntax������������������������������������������������������������������������������������������������������������������������������������������� 4
Partitions and Frames������������������������������������������������������������������������������������������������������������������� 6
ROWS Frame Definition����������������������������������������������������������������������������������������������������������������� 8
RANGE Frame Definition������������������������������������������������������������������������������������������������������������� 16
Example 1����������������������������������������������������������������������������������������������������������������������������������� 19
ROWS and RANGE Default Behavior�������������������������������������������������������������������������������������������� 23
Scenario 1����������������������������������������������������������������������������������������������������������������������������� 24
Scenario 2����������������������������������������������������������������������������������������������������������������������������� 24
ROWS and RANGE Window Frame Examples������������������������������������������������������������������������������ 25
Data Set��������������������������������������������������������������������������������������������������������������������������������� 25
Example 2������������������������������������������������������������������������������������������������������������������������������ 29
Example 3������������������������������������������������������������������������������������������������������������������������������ 32
Example 4������������������������������������������������������������������������������������������������������������������������������ 34
Example 5������������������������������������������������������������������������������������������������������������������������������ 38
Summary������������������������������������������������������������������������������������������������������������������������������������ 40

v
Table of Contents

Chapter 2: Sales DW Use Case: Aggregate Functions��������������������������������������������� 43


Sales Data Warehouse���������������������������������������������������������������������������������������������������������������� 43
Sales Data Warehouse Conceptual Model����������������������������������������������������������������������������������� 45
A Word About Performance Tuning���������������������������������������������������������������������������������������������� 50
Aggregate Functions������������������������������������������������������������������������������������������������������������������� 60
COUNT( ), MAX( ), MIN( ), AVG( ), and SUM( ) Functions����������������������������������������������������������� 61
With OVER( )��������������������������������������������������������������������������������������������������������������������������� 63
GROUPING( ) Function������������������������������������������������������������������������������������������������������������ 67
GROUPING: Performance Tuning Considerations������������������������������������������������������������������� 71
STRING_AGG Function����������������������������������������������������������������������������������������������������������� 77
STDEV( ) and STDEVP( ) Functions����������������������������������������������������������������������������������������� 80
STDEV: Performance Tuning Considerations�������������������������������������������������������������������������� 85
VAR( ) and VARP( ) Functions�������������������������������������������������������������������������������������������������� 91
SQL Server 2022: Named Window Example�������������������������������������������������������������������������������� 95
Summary���������������������������������������������������������������������������������������������������������������������������������� 103

Chapter 3: Sales Use Case: Analytical Functions������������������������������������������������� 105


Analytical Functions������������������������������������������������������������������������������������������������������������������ 105
CUME_DIST( ) Function�������������������������������������������������������������������������������������������������������� 106
Performance Considerations����������������������������������������������������������������������������������������������� 112
PERCENT_RANK( ) Function������������������������������������������������������������������������������������������������� 117
Performance Considerations����������������������������������������������������������������������������������������������� 124
High-Performance Strategy������������������������������������������������������������������������������������������������� 127
LAST_VALUE( ) and FIRST_VALUE( )������������������������������������������������������������������������������������� 134
Performance Considerations����������������������������������������������������������������������������������������������� 137
LAG( ) and LEAD( )���������������������������������������������������������������������������������������������������������������� 140
Performance Considerations����������������������������������������������������������������������������������������������� 142
PERCENTILE_CONT( ) and PERCENTILE_DISC( )������������������������������������������������������������������� 147
Performance Considerations����������������������������������������������������������������������������������������������� 152
Using a Report Table������������������������������������������������������������������������������������������������������������ 157
Summary���������������������������������������������������������������������������������������������������������������������������������� 163

vi
Table of Contents

Chapter 4: Sales Use Case: Ranking/Window Functions�������������������������������������� 165


Ranking/Window Functions������������������������������������������������������������������������������������������������������ 165
RANK( ) vs. PERCENT_RANK( )��������������������������������������������������������������������������������������������� 171
Performance Considerations����������������������������������������������������������������������������������������������� 175
RANK( ) vs. DENSE_RANK( )������������������������������������������������������������������������������������������������� 179
Performance Considerations����������������������������������������������������������������������������������������������� 183
NTILE( ) Function Revisited�������������������������������������������������������������������������������������������������� 185
Performance Considerations����������������������������������������������������������������������������������������������� 188
ROW_NUMBER( ) Function��������������������������������������������������������������������������������������������������� 190
Performance Considerations����������������������������������������������������������������������������������������������� 194
Islands and Gaps Example��������������������������������������������������������������������������������������������������� 201
Summary���������������������������������������������������������������������������������������������������������������������������������� 209

Chapter 5: Finance Use Case: Aggregate Functions��������������������������������������������� 211


Aggregate Functions����������������������������������������������������������������������������������������������������������������� 211
COUNT( ) and SUM( ) Functions�������������������������������������������������������������������������������������������� 217
Performance Considerations����������������������������������������������������������������������������������������������� 226
SUM( ) Function������������������������������������������������������������������������������������������������������������������� 234
Performance Considerations����������������������������������������������������������������������������������������������� 238
MIN( ) and MAX( ) Functions������������������������������������������������������������������������������������������������� 244
Performance Considerations����������������������������������������������������������������������������������������������� 250
AVG( ) Function�������������������������������������������������������������������������������������������������������������������� 258
Performance Considerations����������������������������������������������������������������������������������������������� 264
GROUPING Function������������������������������������������������������������������������������������������������������������� 267
Performance Considerations����������������������������������������������������������������������������������������������� 272
STRING_AGG( ) Function������������������������������������������������������������������������������������������������������ 277
STDEV( ) and STDEVP( ) Functions��������������������������������������������������������������������������������������� 278
Performance Considerations����������������������������������������������������������������������������������������������� 286
VAR( ) and VARP( ) Functions������������������������������������������������������������������������������������������������ 297
Ticker Analysis�������������������������������������������������������������������������������������������������������������������������� 302

vii
Table of Contents

More Non-statistical Variance��������������������������������������������������������������������������������������������� 305


Even More Statistical Variance�������������������������������������������������������������������������������������������� 308
Summary���������������������������������������������������������������������������������������������������������������������������������� 311

Chapter 6: Finance Use Case: Ranking Functions������������������������������������������������ 313


Ranking Functions�������������������������������������������������������������������������������������������������������������������� 313
RANK( ) Function������������������������������������������������������������������������������������������������������������������ 314
DENSE_RANK( ) Function����������������������������������������������������������������������������������������������������� 341
NTILE( ) Function������������������������������������������������������������������������������������������������������������������ 355
ROW_NUMBER( ) Function��������������������������������������������������������������������������������������������������� 370
The Data Gaps and Islands Problem����������������������������������������������������������������������������������������� 377
Step 1: Create the First CTE������������������������������������������������������������������������������������������������� 383
Step 2: Set Up the Second CTE to Label Gaps��������������������������������������������������������������������� 386
Step 3: Set Up the Third CTE and Identify Start/Stop Dates of Gaps����������������������������������� 388
Step 4: Generate the Report������������������������������������������������������������������������������������������������ 390
Performance Considerations����������������������������������������������������������������������������������������������� 392
Islands Next������������������������������������������������������������������������������������������������������������������������������ 395
Step 1: Create the First CTE Using LAG( ) and LEAD( )��������������������������������������������������������� 395
Step 2: Create the Second CTE That Labels Islands and Gaps�������������������������������������������� 397
Step 3: Identify Island Start/Stop Dates������������������������������������������������������������������������������� 402
Step 4: Create the Final Report�������������������������������������������������������������������������������������������� 404
Summary���������������������������������������������������������������������������������������������������������������������������������� 408

Chapter 7: Finance Use Case: Analytical Functions���������������������������������������������� 409


Analytical Functions������������������������������������������������������������������������������������������������������������������ 409
CUME_DIST( ) Function�������������������������������������������������������������������������������������������������������� 410
Performance Considerations����������������������������������������������������������������������������������������������� 416
FIRST_VALUE( ) and LAST_VALUE( ) Functions��������������������������������������������������������������������� 420
Performance Considerations����������������������������������������������������������������������������������������������� 424
LAG( ) and LEAD( ) Functions������������������������������������������������������������������������������������������������ 431
LAG( ) Function��������������������������������������������������������������������������������������������������������������������� 431
Performance Considerations����������������������������������������������������������������������������������������������� 435
LEAD( ) Function������������������������������������������������������������������������������������������������������������������ 440

viii
Table of Contents

Performance Considerations����������������������������������������������������������������������������������������������� 442


Memory-Optimized Strategy����������������������������������������������������������������������������������������������� 445
PERCENT_RANK( ) Function������������������������������������������������������������������������������������������������� 450
Performance Considerations����������������������������������������������������������������������������������������������� 453
PERCENTILE_CONT( ) and PERCENTILE_DISC( )������������������������������������������������������������������� 455
PERCENTILE_CONT( )����������������������������������������������������������������������������������������������������������� 455
Performance Considerations����������������������������������������������������������������������������������������������� 459
PERCENTILE_DISC Function������������������������������������������������������������������������������������������������ 462
Performance Considerations����������������������������������������������������������������������������������������������� 465
Multi-memory-enhanced-table Strategy����������������������������������������������������������������������������� 471
Performance Considerations����������������������������������������������������������������������������������������������� 476
Summary���������������������������������������������������������������������������������������������������������������������������������� 482

Chapter 8: Plant Use Case: Aggregate Functions������������������������������������������������� 483


Aggregate Functions����������������������������������������������������������������������������������������������������������������� 484
Data Model�������������������������������������������������������������������������������������������������������������������������������� 485
Data Dictionaries����������������������������������������������������������������������������������������������������������������������� 489
Entity Data Dictionary���������������������������������������������������������������������������������������������������������� 489
Entity Attribute Data Dictionary������������������������������������������������������������������������������������������� 491
Entity Relationship Data Dictionary: Equipment Failure Subject Area��������������������������������� 498
Entity Relationship Data Dictionary: Equipment Status History������������������������������������������� 500
Entity Relationship Data Dictionary: Plant Expense������������������������������������������������������������� 500
COUNT( ) Function��������������������������������������������������������������������������������������������������������������������� 501
AVG( ) Function��������������������������������������������������������������������������������������������������������������������� 512
MIN( ) and MAX( ) Functions������������������������������������������������������������������������������������������������� 514
GROUPING( ) Function���������������������������������������������������������������������������������������������������������� 526
STRING_AGG( ) Function������������������������������������������������������������������������������������������������������ 530
STDEV( ) and STDEVP( ) Functions���������������������������������������������������������������������������������������� 534
VAR( ) and VARP( ) Functions������������������������������������������������������������������������������������������������������ 543
Example 1: Rolling Variance������������������������������������������������������������������������������������������������ 544
Example 2: Variance by Quarter������������������������������������������������������������������������������������������� 547
Example 3: Variance by Year������������������������������������������������������������������������������������������������ 550

ix
Table of Contents

Performance Considerations����������������������������������������������������������������������������������������������������� 553


Memory-Optimized Table Approach������������������������������������������������������������������������������������� 559
Seven-Million-Row Query: Performance Tuning����������������������������������������������������������������������� 570
Summary���������������������������������������������������������������������������������������������������������������������������������� 582

Chapter 9: Plant Use Case: Ranking Functions����������������������������������������������������� 583


Ranking Functions�������������������������������������������������������������������������������������������������������������������� 583
RANK( ) Function������������������������������������������������������������������������������������������������������������������ 584
Performance Considerations����������������������������������������������������������������������������������������������� 588
Performance Considerations����������������������������������������������������������������������������������������������� 594
Performance Considerations����������������������������������������������������������������������������������������������� 600
DENSE_RANK( ) Function����������������������������������������������������������������������������������������������������� 606
Performance Considerations����������������������������������������������������������������������������������������������� 611
NTILE Function��������������������������������������������������������������������������������������������������������������������� 616
Performance Considerations����������������������������������������������������������������������������������������������� 622
ROW_NUMBER( ) Function��������������������������������������������������������������������������������������������������� 626
Performance Considerations����������������������������������������������������������������������������������������������� 630
Summary���������������������������������������������������������������������������������������������������������������������������������� 633

Chapter 10: Plant Use Case: Analytical Functions������������������������������������������������ 635


Analytical Functions������������������������������������������������������������������������������������������������������������������ 635
CUME_DIST( ) Function�������������������������������������������������������������������������������������������������������� 637
Performance Considerations����������������������������������������������������������������������������������������������� 643
FIRST_VALUE( ) and LAST_VALUE( ) Functions��������������������������������������������������������������������� 648
Performance Considerations����������������������������������������������������������������������������������������������� 651
LAG( ) Function��������������������������������������������������������������������������������������������������������������������� 652
Performance Considerations����������������������������������������������������������������������������������������������� 656
LEAD( ) Function������������������������������������������������������������������������������������������������������������������ 665
Performance Considerations����������������������������������������������������������������������������������������������� 667
PERCENT_RANK( ) Function������������������������������������������������������������������������������������������������� 669
Performance Considerations����������������������������������������������������������������������������������������������� 671
PERCENTILE_CONT Function����������������������������������������������������������������������������������������������� 677
Performance Considerations����������������������������������������������������������������������������������������������� 680

x
Table of Contents

PERCENTILE_DISC( ) Function���������������������������������������������������������������������������������������������� 685


Performance Considerations����������������������������������������������������������������������������������������������� 688
Our Usual Report Table Solution������������������������������������������������������������������������������������������ 692
SQL Server Analysis Services��������������������������������������������������������������������������������������������������� 697
Summary���������������������������������������������������������������������������������������������������������������������������������� 714

Chapter 11: Inventory Use Case: Aggregate Functions���������������������������������������� 717


The Inventory Database������������������������������������������������������������������������������������������������������������ 717
The Inventory Data Warehouse������������������������������������������������������������������������������������������������� 719
Loading the Inventory Data Warehouse������������������������������������������������������������������������������������ 721
Aggregate Functions����������������������������������������������������������������������������������������������������������������� 735
COUNT( ), SUM( ), MAX( ), MIN( ), and AVG( ) Functions��������������������������������������������������������� 735
Performance Considerations����������������������������������������������������������������������������������������������� 739
AVG( ) Function�������������������������������������������������������������������������������������������������������������������� 743
Performance Considerations����������������������������������������������������������������������������������������������� 745
Data Warehouse Query�������������������������������������������������������������������������������������������������������� 747
Performance Considerations����������������������������������������������������������������������������������������������� 750
STDEV( ) Function���������������������������������������������������������������������������������������������������������������� 754
Performance Considerations����������������������������������������������������������������������������������������������� 758
Data Warehouse Query�������������������������������������������������������������������������������������������������������� 760
Performance Considerations����������������������������������������������������������������������������������������������� 764
VAR( ) Function��������������������������������������������������������������������������������������������������������������������� 766
Performance Considerations����������������������������������������������������������������������������������������������� 769
Enhancing the SSIS Package���������������������������������������������������������������������������������������������������� 772
Summary���������������������������������������������������������������������������������������������������������������������������������� 783

Chapter 12: Inventory Use Case: Ranking Functions�������������������������������������������� 785


Ranking Functions�������������������������������������������������������������������������������������������������������������������� 786
RANK( ) Function������������������������������������������������������������������������������������������������������������������ 786
Performance Considerations����������������������������������������������������������������������������������������������� 791
Querying the Data Warehouse��������������������������������������������������������������������������������������������� 796
DENSE_RANK( ) Function����������������������������������������������������������������������������������������������������� 802
Performance Considerations����������������������������������������������������������������������������������������������� 805
xi
Table of Contents

NTILE( ) Function������������������������������������������������������������������������������������������������������������������ 809


Performance Considerations����������������������������������������������������������������������������������������������� 813
ROW_NUMBER( ) Function��������������������������������������������������������������������������������������������������� 818
Performance Considerations����������������������������������������������������������������������������������������������� 823
Create an SSRS Report������������������������������������������������������������������������������������������������������������� 824
Report Builder Mini Tutorial������������������������������������������������������������������������������������������������� 828
Create a Power BI Report���������������������������������������������������������������������������������������������������� 855
Summary���������������������������������������������������������������������������������������������������������������������������������� 867

Chapter 13: Inventory Use Case: Analytical Functions����������������������������������������� 869


Analytical Functions������������������������������������������������������������������������������������������������������������������ 869
CUME_DIST( ) Function�������������������������������������������������������������������������������������������������������� 870
Performance Considerations����������������������������������������������������������������������������������������������� 876
FIRST_VALUE( ) and LAST_VALUE( ) Functions��������������������������������������������������������������������� 878
Performance Considerations����������������������������������������������������������������������������������������������� 883
LAG( ) Function��������������������������������������������������������������������������������������������������������������������� 885
Performance Considerations����������������������������������������������������������������������������������������������� 889
LEAD( ) Function������������������������������������������������������������������������������������������������������������������ 894
Performance Considerations����������������������������������������������������������������������������������������������� 900
PERCENT_RANK( ) Function������������������������������������������������������������������������������������������������� 907
Performance Considerations����������������������������������������������������������������������������������������������� 912
PERCENTILE_CONT( ) Function�������������������������������������������������������������������������������������������� 914
Performance Considerations����������������������������������������������������������������������������������������������� 919
PERCENTILE_DISC( ) Function���������������������������������������������������������������������������������������������� 925
Performance Considerations����������������������������������������������������������������������������������������������� 929
Overall Performance Considerations����������������������������������������������������������������������������������� 935
Report Builder Examples����������������������������������������������������������������������������������������������������������� 939
Summary���������������������������������������������������������������������������������������������������������������������������������� 947

Chapter 14: Summary, Conclusions, and Next Steps�������������������������������������������� 949


Summary���������������������������������������������������������������������������������������������������������������������������������� 949
Our Journey������������������������������������������������������������������������������������������������������������������������������ 949
About the Code�������������������������������������������������������������������������������������������������������������������������� 950
xii
Table of Contents

About the Database Folders������������������������������������������������������������������������������������������������������ 952


Data Used in the Examples������������������������������������������������������������������������������������������������������� 952
The Toolkit��������������������������������������������������������������������������������������������������������������������������������� 953
SQL Server�������������������������������������������������������������������������������������������������������������������������������� 954
SSMS���������������������������������������������������������������������������������������������������������������������������������������� 956
The Window Functions�������������������������������������������������������������������������������������������������������������� 959
The Visual Studio Community License�������������������������������������������������������������������������������������� 960
SSAS Projects���������������������������������������������������������������������������������������������������������������������� 962
SSIS Projects����������������������������������������������������������������������������������������������������������������������� 963
Power BI Web Scorecards, Dashboards, and Reports��������������������������������������������������������������� 964
Microsoft Excel Spreadsheets�������������������������������������������������������������������������������������������������� 965
SSAS Server������������������������������������������������������������������������������������������������������������������������������ 966
SSRS Server and Website��������������������������������������������������������������������������������������������������������� 968
Report Builder��������������������������������������������������������������������������������������������������������������������������� 970
Performance Analysis Tools������������������������������������������������������������������������������������������������������ 971
Estimated Query Plans�������������������������������������������������������������������������������������������������������� 972
Live Query Plans������������������������������������������������������������������������������������������������������������������ 973
DBCC������������������������������������������������������������������������������������������������������������������������������������ 974
IO and TIME Statistics���������������������������������������������������������������������������������������������������������� 974
STATISTICS PROFILE������������������������������������������������������������������������������������������������������������ 975
Where to Get the Tools�������������������������������������������������������������������������������������������������������������� 977
SQL Server Developer���������������������������������������������������������������������������������������������������������� 977
Visual Studio Community����������������������������������������������������������������������������������������������������� 978
SQL Server Data Tools��������������������������������������������������������������������������������������������������������� 979
SQL Server SSAS Project Support��������������������������������������������������������������������������������������� 980
SQL Server SSIS Project Support���������������������������������������������������������������������������������������� 981
SQL Server SSRS Project Support��������������������������������������������������������������������������������������� 982
Report Builder���������������������������������������������������������������������������������������������������������������������� 983
Power BI Desktop���������������������������������������������������������������������������������������������������������������� 984
Power BI Server������������������������������������������������������������������������������������������������������������������� 985

xiii
Table of Contents

Microsoft Excel�������������������������������������������������������������������������������������������������������������������� 986


SSMS����������������������������������������������������������������������������������������������������������������������������������� 987
Next Steps��������������������������������������������������������������������������������������������������������������������������������� 988
Thank You!�������������������������������������������������������������������������������������������������������������������������������� 988

Appendix A: Function Syntax, Descriptions���������������������������������������������������������� 989

Appendix B: Statistical Functions����������������������������������������������������������������������� 1009

Index������������������������������������������������������������������������������������������������������������������� 1035

xiv
About the Author
Angelo R. Bobak is a published author with more than three
decades of experience and expertise in the areas of business
intelligence, data architecture, data warehouse design, data
modeling, master data management, and data quality using
the Microsoft BI Stack across several industry sectors such as
finance, telecommunications, engineering, publishing, and
automotive.

xv
About the Technical Reviewer
Alicia Moniz is a leader in Data & AI at Microsoft, an
organizer for Global AI Bootcamp – Houston Edition, and
a #KafkaOnAzure Evangelista and prior was a three-time
Microsoft AI MVP. She is an active supporter of women in
technology and volunteers her time at events that help make
AI technology accessible to the masses. She is a co-author of
the Apress publication Beginning Azure Cognitive Services:
Data-Driven Decision Making Through Artificial Intelligence
along with fellow Microsoft MVPs Matt Gordon, Ida Bergum,
Mia Chang, and Ginger Grant. With over 14 years of experience in data warehousing
and advanced analytics, Alicia is constantly upskilling and holds more than 12 in-
demand IT certifications including AWS, Azure, and Kafka. She is active in the Microsoft
User Group community and enjoys speaking on AI, SQL Server, #KafkaOnAzure, and
personal branding for women in technology topics. Currently, she authors the blog
HybridDataLakes.com, a blog focused on cloud data learning resources, and produces
content for the YouTube channel #KafkaOnAzure.

xvii
Introduction
Welcome to my book, SQL Server Analytical Toolkit.
What’s this book about?
This is a book on applying Microsoft SQL Server aggregate, analytical, and ranking
functions across various industries for the purpose of statistical, reporting, analytical,
and historical performance analysis using a series of built-in SQL Server functions
affectionately known as the window functions!
No, not window functions like the ones used in the C# or other Microsoft Windows
application programming. They are called window functions because they implement
windows into the data set generated by a query. These windows allow you to control
where the functions are applied in the data by creating partitions in the query data set.
“What’s a partition?” you might ask. This is a key concept you need to understand to
get the most out of this book. Suppose you have a data set that has six rows for product
category A and six rows for product category B. Each row has a column that stores sales
values that you wish to analyze. The data set can be divided into two sections, one for
each product category. These are the partitions that the window functions use. You can
analyze each partition by applying the window functions (more on this in Chapter 1).
We will see that the window in each partition can be further divided into smaller
windows. The mechanism of a window frame allows you to control which rows in
the partition are submitted to the window function relative to the current row being
processed. For example, apply a function like the SUM() function to the current row being
processed and any prior rows in the partition to calculate running totals by month. Move
to the next row in the partition and it behaves the same.
The book focuses on applying these functions across four key industries: sales,
finance, engineering, and inventory control. I did this so that readers in these industries
can find something they are familiar with in their day-to-day job activities. Even if you
are not working across these industries, you can still benefit by learning the window
functions and seeing how they are applied.
Maybe you want to interview for a developer role in the finance sector? Or maybe
you work in engineering or telecommunications or you are a manufacturer of retail
products. This book will help you acquire some valuable skills that will help you pass the
job interview.
xix
Introduction

Although you could perform these functions with tools like Power BI, performing
these functions at the SQL level precalculates results and improves performance so that
reporting tools use precalculated data.
By the way, there are many books out there on SQL Server and window (or
windowing) functions. What’s so different about this book?

Approach
This book takes a cookbook approach. Not only are you shown how to use the functions,
but you are shown how to apply them across sales, finance, inventory control, and
engineering scenarios.
These functions are grouped into three categories, so for each industry use case we
look at, we will dedicate a chapter to each function category:

• Aggregate functions

• Analytical functions

• Ranking functions

For each function, a query is created and explained. Next, the results are examined
and analyzed.
Where applicable the results are used to generate some interesting graphs with
Microsoft Excel spreadsheets, like creating normal distribution charts for sales data.
Appendix A contains descriptions and syntax for these functions in case you are not
familiar with them, so feel free to examine them before diving into the book.
Key to mastering the concepts in this book is understanding what the OVER() clause
does. Chapter 1 starts off by defining what the OVER() clause is and how it is used with
the window functions.
Several diagrams clearly explain what data sets, partitions, and window frames are
and how they are key to using the window functions.
Each of the industries we identified earlier has three dedicated chapters, one for each
of the window function categories. Each chapter provides a specification for the query to be
written, the code to satisfy the specification, and then one or more figures to show the results.
The book is unique in that it goes beyond just showing how each function works; it
presents use case scenarios related to statistical analysis, data analysis, and BI (BI stands
for business intelligence by the way).

xx
Introduction

The book also makes available all code examples including code to create and load
each of the four databases via the publisher's Google website.
Lastly, just enough theory is included to introduce you to statistical analysis in case
you are not familiar with terms like standard deviation, mean, normal distribution, and
variance. These are important as they will supply you with valuable skills to support your
business users and enhance your skills portfolio. Hey, a little business theory can’t hurt!
Appendix B has a brief primer on statistics, so make sure to check it out in case these
topics are new to you. It discusses standard deviation, variance, normal distribution,
other statistical calculations, and bell curves.
Back to the window functions. These functions generate a lot of numerical data. It’s
great to generate numbers with decimal points but even more interesting to graph them
and understand what they mean. A picture is worth a thousand words. Seeing a graph
that shows sales decreasing month by month is certainly worth looking at and should
raise alarms!
You can also use the Excel spreadsheets to verify your results by using the
spreadsheets’ built-in functions to make sure they match the results of your queries.
Always test your data against a set of results known to be correct (you might just learn
a little bit about Microsoft Excel too!). The spreadsheets used in this book will also be
available on the publisher's Google website.
The book includes tips and discussions that will take you through the process of
learning the SQL Server aggregate, ranking, and analytical functions. These are delivered
in a step-by-step approach so you can easily master the concepts. Data results are
analyzed so that you can understand what the function does and how the windows are
used to analyze the data work.

Expectations
Now that you know what you are in for, what do I expect from you?
Not much really, at a high level.
I expect you to be an intermediate to advanced SQL Server developer or data
architect who needs to learn how to use window functions. You can write ­medium-­
complexity queries that use joins, understand what a CTE (common table expression) is,
and be able to create and load database tables.
You could also be a tech-savvy business analyst who needs to apply sophisticated
data analysis for your business users or clients.

xxi
Introduction

Lastly, you could be a technology manager who wants to understand what your
development team does in their roles as developers. All will benefit from this book.
In conclusion, you need

• A keen interest in learning data analysis skills

• A basic knowledge of relational database technology and SQL skills

• A basic understanding of mathematical calculations like calculating


the average of values and performing basic arithmetic operations like
addition, subtraction, division, and multiplication

• Lastly, a working knowledge of SQL Server Management Studio


(SSMS), the tool we will use to create and execute the queries

In case you do not know how to use SSMS, there are many excellent YouTube videos
and sites that can show you how to use this tool in a short amount of time.
You can also check out my podcast “GRUMPY PODCAST 01 NAVIGATING SSMS” at
www.grumpyolditguy.com under the TSQL Podcasts menu selection in the menu bar.

What’s in It for You?


You, my dear reader, will learn how to use the Microsoft SQL Server analytical, aggregate,
and ranking functions. These are valuable skills that will enhance your capability to
develop solutions for your company role and the business users you support or will
support.

What Else Do You Get?


There are tons of code examples that you can access and play around with to become
familiar with the workings of the window functions. Practice makes perfect. Break the
code, modify it, and experiment. This is the best way to learn.

Note You can download the code from the publisher’s Google website at
https://github.com/Apress/SQL-Server-Analytical-Toolkit.

xxii
Introduction

One Final Note


Besides the queries discussed in the chapters, DDL (data declaration language) code is
included to create the four practice databases and load the tables in each. These scripts
clearly identify the steps and the sequence you need to take to create and load all the
data objects (some will generate millions of rows).
Once you create the databases and load the tables, you can practice by executing
the supplied queries as you read along in each of the chapters. You can also practice
performance tuning by creating indexes and examining the query plans that can be
generated with SSMS.
Lastly, I will also touch upon performance tuning a bit to ensure that the queries
you create run fast and efficient. The functions you will learn are resource intensive, so
we need to write queries that will not tie down server resources, like CPU, memory, and
disk! (Or have your users pull their hair when a query runs for hours or days….)
As added value, you will be shown how to apply good coding standards to improve
code legibility. Code that is easy to read is easy to maintain, fix, and modify. We want
to be good teammates in case your code is handed to another developer once you are
promoted (or get a better job!). For these reasons, we will use the coding standards
suggested by Microsoft Corporation for SQL Server.
These are easy to learn and adopt. All TSQL commands and keywords are in
uppercase, and data objects referenced are named with one or more words that describe
the data object. The first letter of the word that makes up the data object name is in
uppercase, and the remaining letters are lowercase, for example, DeptId for department
identifier.
A sample query that follows this coding standard is

SELECT DeptId, MgrId, MgrLastName


FROM Department
GO

Very simple and easy to read. It is clear by the names used that the query retrieves
departmental information, specifically the department identifier, the manager identifier,
and the manager’s last name. Notice the name of the table. There is no doubt as to what
the table contains.
That’s it! Let’s begin our journey into window functions.

xxiii
CHAPTER 1

Partitions, Frames, and


the OVER( ) Clause
The goal of this chapter is to describe the OVER() clause, its various configurations
in terms of how partitions and window frames are created, and how data is sorted
in the partition so that the window function can operate on the values. (The OVER()
clause together with partitions and window frames is what gives power to the window
functions.)
We will cover window frames and how they are defined to present a set of partition
rows that are processed by the window functions.
Several diagrams illustrate the concepts, and then several examples are shown to
illustrate the various ways partitions and window frames work on real data sets using
window functions. We will discuss ROWS and RANGE clauses and how they operate on data
in the partition.
We will also look at a case where we use subqueries in the ORDER BY clause instead of
columns so as to eliminate expensive sort steps in a query plan.
A final short example is presented to show the new named window feature in SQL
Server 2022.

What Are Partitions and Window Frames?


A TSQL query will generate an initial data result set. The result set can be divided
into sections called partitions, like a partition for each set of rows by specific years.
The window frame is like a smaller window into the partition defined by some row
boundaries or ranges, for example, rows prior to and including the current row being
processed or all rows after and including the current row within the partition. The entire

1
© Angelo Bobak 2023
A. Bobak, SQL Server Analytical Toolkit, https://doi.org/10.1007/978-1-4842-8667-8_1
Chapter 1 Partitions, Frames, and the OVER( ) Clause

data set can also be a partition by itself, and a window can be defined that uses all rows
in the single partition. It all depends on how you include and define the PARTITION BY,
ORDER BY, and ROWS/RANGE window frame clauses.
These conditions can be specified with the OVER() clause. Let’s see how this works.

What Is an OVER( ) Clause?


As stated earlier, the OVER() clause allows you to create partitions and windows into a
data set generated by a query. It allows you to divide the data set into sections called
partitions. The windows allow you to control how the window functions are applied in
terms of the data they will operate on.
Smaller windows called window frames can be created to further carve up the
larger window defined by the PARTITION BY clause using ROWS and RANGE frame clauses.
These windows grow or move (as rows are processed) based on the boundaries defined
by ROWS and RANGE frame specifications included in the OVER() clause. You can also
specify how the rows are sorted via an ORDER BY clause so that the window function can
process them.
This capability allows you to create queries such as three-month rolling averages and
year-to-date, quarter-to-date, and month-to-date totals. Each of the functions we discuss
in this book can utilize the OVER() clause in this manner.
This is why they are called window functions!

History of the OVER( ) Clause and Window Functions


The following is a brief history of the window functions:

• Aggregate/ranking window functions without the ORDER BY clause


support were introduced in 2005.

• Seven years later, aggregate functions with support for the ORDER BY
clause were introduced in 2012.

• Support for window frames (which we will discuss shortly) was also
introduced in 2012.

• Ranking functions and some window capability were introduced


in 2015.

2
Chapter 1 Partitions, Frames, and the OVER( ) Clause

• The batch-mode window aggregate operator was introduced in 2016.

• The STRING_AGG function was introduced in 2017.

• The named WINDOW capability was introduced in SQL Server 2022.

The capability of the window functions has grown over the years and delivers a rich
and powerful set of tools to analyze and solve complex data analysis problems.

The Window Functions


The window functions (which are the focus of this book) that can be used with the
OVER() clause are assigned to three categories and are listed in Table 1-1.

Table 1-1. Aggregate, Analytical, and Ranking Functions


Aggregate Functions Analytical Functions Ranking Functions

COUNT( ) CUME_DIST( ) RANK( )


COUNT_BIG( ) FIRST_VALUE( ) DENSE_RANK( )
SUM( ) LAST_VALUE( ) NTILE( )
MAX( ) LAG( ) ROW_NUMBER( )
MIN( ) LEAD( )
AVG( ) PERCENT_RANK( )
GROUPING( ) PERCENTILE_CONT( )
STRING_AGG( ) PERCENTILE_DISC( )
STDEV( )
STDEVP( )
VAR( )
VARP( )

Each of the subsequent chapters will create and discuss queries for these categories
for four industry-specific databases that are in scope for this book. Please refer to
Appendix A for syntax and descriptions of what each of the preceding functions does if
you are unfamiliar with them or need a refresher on how to use them in a query.

3
Chapter 1 Partitions, Frames, and the OVER( ) Clause

The OVER( ) Clause


The OVER() clause appears right after the window function in the SELECT clause. Our
first example in Listing 1-1 uses the SUM() function to aggregate sales amounts by year
and month.

Listing 1-1. SUM() Function with the OVER() Clause

SELECT OrderYear,OrderMonth,SalesAmount,
      SUM(SalesAmount) OVER(
            PARTITION BY OrderYear
            ORDER BY OrderMonth ASC
            ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
            ) AS AmountTotal
FROM OverExample
ORDER BY OrderYear,OrderMonth
GO

Between a set of parentheses after the OVER keyword, three other clauses can be
included such as PARTITION BY, ORDER BY, and ROWS or RANGE clauses (to define the
window frame that presents the rows to the function for processing).
Even if you have an ORDER BY clause in the OVER() clause, you can also include the
usual ORDER BY clause at the end of the query to sort the final processed result set in any
order you feel is appropriate to the business requirements the query solves.

Syntax
The following are three basic syntax templates that can be used with the window
functions. Reading these syntax templates is easy. Just keep in mind keywords between
square brackets mean they are optional. The following is the first syntax template
available for the OVER() clause:
Syntax 1

<Window Function> OVER (


       [ <PARTITION BY expression> ]
       [ <ORDER BY expression ASC | DESC > ]
       [ <ROWS or RANGE expression > ]
      )
4
Chapter 1 Partitions, Frames, and the OVER( ) Clause

Most of the window functions use this first syntax, and it is composed of three
main clauses, the PARTITION BY clause, the ORDER BY clause, and a ROWS or RANGE
specification. You can include one or more of these clauses or none. These combinations
will affect how the partition is defined. For example, if you do not include a PARTITION
BY clause, the entire data set is considered a one large partition. The expression is usually
one or more columns, but in the case of the PARTITION BY and ORDER BY clauses, it could
also be a subquery (refer to Appendix A).
The Window Function is one of the functions identified in Table 1-1.
This first syntax is pretty much the same for all functions except for the
PERCENTILE_DISC() and PERCENTILE_CONT() functions that use a slight variation:
Syntax 2

PERCENTILE_DISC (numeric literal ) WITHIN GROUP (


                ORDER BY expression [ ASC | DESC ]
      )  OVER ( [ <PARTITION BY expression> ] )

These functions are used to calculate the percentile discrete and percentile
continuous values in a data set column. The numeric literal can be a value like .25, .50,
or .75 that is used to specify the percentile you wish to calculate. Notice that the ORDER
BY clause is inserted between the parentheses of the WITHIN GROUP command and the
OVER() clause just includes the PARTITION BY clause.
Don’t worry about what this does for now. Examples will be given that make the
behavior of this code clear. For now, just understand that there are three basic syntax
templates to be aware of.
In our chapter examples, the expression will usually be a column or columns
separated by commas although you can use other data objects like queries. Please
refer to the Microsoft SQL Server documentation to check out the detailed syntax
specification or Appendix A.
Lastly, our third syntax template applies to SQL Server 2022 (release 16.x). The
window capability has been enhanced that allows you to specify window options in a
named window that appears at the end of the query:
Syntax 3

WINDOW <window name> AS (


       [ <PARTITION BY clause> ]
       [ <ORDER BY clause> ]
       [ <ROWS or RANGE clause> ]
      )
5
Chapter 1 Partitions, Frames, and the OVER( ) Clause

As of this writing, SQL Server 2022 is available for evaluation only. In Listing 1-2 is an
example TSQL query that uses this new feature.

Listing 1-2. SQL Server 2022 Named Window Feature

SELECT OrderYear,OrderMonth,SalesAmount,
      SUM(SalesAmount) OVER SalesWindow AS SQPRangeUPCR
FROM OverExample
WINDOW SalesWindow AS (
            PARTITION BY OrderYear
            ORDER BY OrderMonth
            RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
            );
GO

The name of the window is SalesWindow, and it is used right after the OVER operator
instead of the PARTITION BY, ORDER BY, and RANGE clauses as used in the first syntax
template we discussed.
Probably a good feature in case you have multiple window functions in your SELECT
clause that need to use this partition and window frame configuration. This would avoid
repeating the partition code in each column of the SELECT clause.
The PARTITION BY, ORDER BY, and RANGE clauses are declared at the end of the
query between parentheses after the WINDOW keyword instead of right after the OVER
keyword.
If you want to play around with this, download and install the 2022 evaluation
license and try it out on the example code available with the book or on your own
queries. The setup and download are fast and simple. Make sure you get the latest
version of SSMS. These are available on Microsoft’s download website.

Partitions and Frames


Finally, we get to discuss what a partition looks like and what frames or window
frames are.
Basically, a query will generate a data result set, which you can carve up into sections
called partitions by including the PARTITION BY clause inside the OVER() clause. For
each partition you can further define smaller windows to fine-tune how the window
functions will be applied.
6
Chapter 1 Partitions, Frames, and the OVER( ) Clause

A picture is worth a thousand words, so let’s look at one now. Please refer to
Figure 1-1.

Figure 1-1. A simple data set with three partitions and an example frame

Here we have a simple data set composed of eight rows. There are three example
partitions in this data set. One can include all eight rows of the data set; the other two
include rows identified by the TYPE column. There are only two type values, type A and
type B, so each of these partitions will have four rows. By the way, you cannot include
multiple PARTITION BY clauses in an OVER() clause.
You can define only one partition per OVER() clause although you can have more
than one column in the SELECT clause of the query that uses a partition. You can specify
different column combinations to define the partitions.
Where the power of this architecture comes in is that we can create smaller window
frames against the partition by using the ROWS or RANGE operator. These will allow you to
specify how many rows before and/or after the current row being processed will be used
by the window function.

7
Chapter 1 Partitions, Frames, and the OVER( ) Clause

In our preceding example snapshot, the current row is row 3, and the window frame
is defined so it includes only the prior row, the current row, and the next row relative
to the current row. If we apply the SUM() function to this window frame and add all the
values, we get the result 60 (15 + 20 + 25). (Remember this is within the first partition,
which contains only four rows.)
If processing continues on the next row, row 4, only rows 3 and 4 are available to
the SUM() function, and the result is 45 (20 + 25). I neglected to mention that if we start
at row 1, then only rows 1 and 2 are available to the SUM()function because there is no
prior row. The function returns the value 25 (10 + 15).
How do we control this type of processing? All we need to do is add a ROWS or RANGE
specification to the query if required. We could also include an ORDER BY clause to
specify how to order the rows within the partition so that the window function is applied
as needed. For example, generate rolling totals by month, starting of course at month 1
(January) and ending at month 12 (December).
Sounds easy, but we need to be aware of a few scenarios around default processing
when we leave the ORDER BY clause and/or the PARTITION clause out. We will discuss
these shortly.

ROWS Frame Definition


The ROWS clause operates on the physical set of rows belonging to the partition. Because
the ROWS clause operates on the rows in the partition, it is considered a physical
operation.
The ORDER BY clause allows you to specify the logical order of the rows in the
partition so the window function can evaluate them. For example, if you have sales data
by year and month, you would set the logical order of the rows in the partition by month
so that the SUM() function can generate rolling year-to-date totals for each month. You
can specify optional ascending and descending sort orders by using the keywords ASC
and DESC.
The ROWS clause allows you to define the window frame into the partition. There are
several variations of the ROWS clause that we need to be aware of. The following are two
variations that generate the same window frame:

ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW


ROWS UNBOUNDED PRECEDING

8
Chapter 1 Partitions, Frames, and the OVER( ) Clause

This clause tells the function to operate on the current row and all rows preceding
the current row if there are any in the partition. A simple diagram in Figure 1-2 makes it
all clear.

Figure 1-2. Include the current row and all preceding rows

If we start at row 1, since there are no prior rows in the partition before this row, the
SUM() function returns the value 10.
Moving on to row 2, the SUM() function will include the only available prior row
(row 1), so the result is 25 (10 + 15).
Next (shown in the preceding figure), the current row to be processed is row 3. The
SUM() function will evaluate row 3 plus rows 1 and 2 in order to generate the total. The
result is 45 (10 + 15 + 20).
Lastly, moving to row 4, the function will include the current row and all prior rows
in its calculation and return 70 (10 + 15 + 20 + 25). Processing now concludes for this
partition.
Moving to partition B, the processing repeats itself, and only rows from partition B
are included. All rows in partition A are ignored.

9
Chapter 1 Partitions, Frames, and the OVER( ) Clause

Our next ROWS clause takes us in the opposite direction:

ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING


ROWS UNBOUNDED FOLLOWING (will not work, not supported)

The first clause is legal but the second is not. It is not supported. One would think
that if there is a ROWS UNBOUNDED PRECEDING, there should be a ROWS UNBOUNDED
FOLLOWING, but it is not supported at this time. Go figure!
As stated earlier, this clause takes us in the opposite direction than the prior scenario
we just discussed. It will allow the aggregate or other window functions to include the
current row and all succeeding rows until all rows in the partition are exhausted. Our
next diagram in Figure 1-3 shows us how this works.

Figure 1-3. Process the current row and all succeeding rows

Remember rows are processed one by one in the partition.


If processing starts at row 1, then all four values are included to calculate a sum of 70
(10 + 15 + 20 +25).

10
Chapter 1 Partitions, Frames, and the OVER( ) Clause

Next, if the current row being processed is row 2 as in the preceding example, then
the SUM() function will include rows 2–4 to generate the total value. It will generate a
result of 60 (15 + 20 + 25).
Moving on to row 3, it will only include rows 3 and 4 and generate a total value of 45
(20 + 25).
Once processing gets to row 4, only the current row is used as there are no more rows
available in the partition. The SUM() function calculates a total of 25.
When processing resumes at the next partition, the entire scenario is repeated.
What if we do not want to include all prior or following rows but only a few before or
after? The next window frame clause will accomplish the trick for limiting the number of
following rows:

ROWS BETWEEN CURRENT ROW AND n FOLLOWING

Including this clause in the OVER() clause will allow us to control the number of
rows to include relative to the current row, that is, how many rows after the current row
are going to be used from the available partition rows. Let’s examine another simple
example where we want to include only one row following the current row in the
calculation.
Please refer to Figure 1-4.

11
Chapter 1 Partitions, Frames, and the OVER( ) Clause

Figure 1-4. Current row and one row following

Here is the window frame clause to include in the OVER() clause:

ROWS BETWEEN CURRENT AND 1 FOLLOWING

Processing starts at row 1. I think by now you understand that only rows 1 and 2 are
used and the result is 25 (10 + 15).
Next, in the preceding example, row 2 is the current row. If only the next row is
included, then the SUM() function will return a total of 35 (15 + 20).
Moving to row 3 (the next current row), the SUM() function will return 45 as the sum
(20 + 25).
Finally, when processing gets to the last row in the partition, then only row 4 is used
and the SUM() function returns 25.
When processing continues to the next partition, the sequence repeats itself ignoring
the values from the prior partition.
We can now see how this process creates windows into the partition that change
as the rows are processed one by one by the window function. Remember, the order
in which the rows are processed is controlled by the ORDER BY clause used in the
OVER() clause.

12
Chapter 1 Partitions, Frames, and the OVER( ) Clause

The next example takes us in the opposite direction. We want to include the current
row and the prior two rows (if there are any) in the calculation.
The window frame clause is

ROWS BETWEEN n PRECEDING AND CURRENT ROW

The letter n represents an unsigned integer value specifying the number of rows. The
following example uses 2 as the number of rows.
Please refer to Figure 1-5.

Figure 1-5. Include two prior rows and the current row

The window frame for this scenario is

ROWS BETWEEN 2 PRECEDING AND CURRENT ROW

Starting at row 1 (the current row), the SUM() function will only use row 1 as there are
no prior rows. The value returned by the window function is 10.
Moving to row 2, there is only one prior row so the SUM() function returns 25
(10 + 15).

13
Chapter 1 Partitions, Frames, and the OVER( ) Clause

Next, the current row is row 3, so the SUM() function uses the two prior rows and
returns a value of 45 (10 + 15 +20).
In the preceding figure, the current row being processed is row 4 and the preceding
two rows are included; the SUM() function when applied will return a total value of 60 (15
+ 20 + 25).
Since all rows of the first partition have been processed, we move to partition B, and
this time only row 5 is used as there are no prior rows. We are at the beginning of the
partition. The SUM() function returns a total value of 30.
Processing continues at row 6, so the SUM function processes rows 5 and 6 (the
current and prior rows). The total value calculated is 65 (30 + 35).
Next, the current row is 7 so the window function will include rows 5, 6, and 7. The
SUM() function returns 105 (30 + 35 + 40).
Finally, processing gets and ends at row 8 of the partition. The rows used by the
SUM() function are rows 6, 7, and 8. The SUM() function() returns 120 (35 + 40 + 45).
There are no more partitions so the processing ends.
What if we want to specify several rows before and after the current row? The next
clause does the trick. This was one of the example in the chapter, but let’s review it again
and make one change:

ROWS BETWEEN n PRECEDING AND n FOLLOWING

In this case we want to include the current row, the prior row, and two following
rows. We could well specify two rows preceding and three rows following or any
combination within the number of rows in the partition if we had more rows in the
partition.
Please refer to Figure 1-6.

14
Chapter 1 Partitions, Frames, and the OVER( ) Clause

Figure 1-6. Rows between n = 1 preceding and n = 2 following

Here is the ROWS clause for this scenario:

ROWS BETWEEN 1 PRECEDING AND 2 FOLLOWING

Starting at row 1 of the partition, the SUM() function can only use rows 1, 2, and 3
as there are no rows in the partition prior to row 1. The result calculated by the SUM()
function is 45 (10 + 15 + 20).
Moving to the next row, row 2 of the partition, the SUM() function can use rows 1, 2
(the current row), 3, and 4. The result calculated by the SUM() function is 70 (10 + 15 +
20 + 25).
Next, the current row is row 3. This window frame uses row 2, 3, and only 4 so the
SUM() function will calculate a total sum of 60 (15 + 20 + 25).
Finally, moving to row 4 (last row of the partition), the SUM() function can only use
rows 3 and 4 as there are no more available rows following in the partition. The result is
45 (20 + 25).
Processing at the next partition continues in the same fashion, ignoring all rows from
the prior partition.

15
Chapter 1 Partitions, Frames, and the OVER( ) Clause

We have pretty much examined most of the configurations for the ROWS clause. Next,
we look at window frames defined by the RANGE clause, which works at a logical level. It
considers values instead of physical row position. Duplicate ORDER BY values will yield a
strange behavior.

RANGE Frame Definition


We discussed how the ROWS clause works at the physical level by using values relative
to the row positions they belong to. The RANGE clause operates at the logical level. It
considers the values of the column instead of physical row position within the partition.
It displays a strange (in my opinion) behavior. If it encounters duplicate values in the row
sort column, it will add them all up and display the total in all rows with the duplicate
ORDER BY column. Any values like moving totals will go out the window! (Pardon
the pun.)
An example illustrates this scenario.
Suppose you have a small table with five columns: Row, Year, Month, Amount, and
Running Total. The table is loaded with 12 rows for each year, let’s say 2010 and 2011,
plus a duplicate month row giving us a total of 13 rows. Each row represents a month in
the year, but for the month of March, two rows are inserted for the first year (that’s why
we have 13 rows and not 12 rows). In other words, an extra amount value exists for the
same month, resulting in duplicate month numbers.
The RANGE clause will use all the rows with duplicate current month values to apply
the window function to (in addition to any other rows specified in the RANGE clause).
Check out the partial table, Table 1-2.

Table 1-2. Partial RANGE Running Totals


Row Year Month Amount Running Total

1 2010 1 100.00 100.00


2 2010 2 100.00 200.00
3 2010 3 200.00 600.00
4 2010 3 200.00 600.00

16
Chapter 1 Partitions, Frames, and the OVER( ) Clause

When we get to row 3, the first of two duplicate months, the window function (SUM()
in this case) will include all prior rows and add the values in row 3 and the next row 4
to generate a total value of 600.00. This is displayed for both rows 3 and 4. Weird! One
would expect a running total value of 400.00 for row 3.
Let’s try another one. If we apply the following RANGE clause to calculate
running totals

RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW

or

RANGE UNBOUNDED PRECEDING

we get interesting results. Check out the partial results in Figure 1-7.

Figure 1-7. Sales by year and month

Everything works as expected until we get to the duplicate rows with a value of 5
(May) for the month. The window frame for this partition includes rows 1–4 and the
current row 5, but we also have a duplicate month value in row 6, which calculates the
rolling total value 60. This value is displayed in both rows 5 and 6. Aggregations continue
for the rest of the months (not shown).

17
Chapter 1 Partitions, Frames, and the OVER( ) Clause

Let’s process rows in the opposite direction. Here is our next RANGE clause:

RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING


RANGE UNBOUNDED FOLLOWING (will not work)

By the way, the second RANGE clause is not supported, but I included it so you are
aware that it will not work and generate an error. Please refer to Figure 1-8.

Figure 1-8. RANGE between current row and unbounded following

Rows 5 and 6 represent two sales figures for the month of May. Since these are
considered duplicates, row 5 displays a rolling value of 90 and row 6 also shows 90
instead of 80 as one would expect if they did not understand the behavior of this window
frame declaration.
Notice, by the way, how the rolling sum values decrease as the row processing
advances from row 1 onward. We started with 130 and ended with 10 – moving totals in
reverse.
Putting it all together, here is a conceptual view that illustrates the generation of
window frames as rows are processed when the following ROWS clause is used:

ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING

18
Chapter 1 Partitions, Frames, and the OVER( ) Clause

This was one of the example in the chapter. Only the partition for type A rows is
considered. Please refer to Figure 1-9.

Figure 1-9. Window frame and processed rows

Both partitions are shown so you can see how the frame resets when it gets to the
next partition.
The generated window frames, as processing goes row by row, show which values
are included for the window function to process within the partition. The first window
and last window only have two rows to process, but the second and third both have three
rows to process, the prior, current, and next rows. The same pattern repeats itself for the
second partition.
Let’s put our knowledge to the test by looking at a simple query based on the figures
we just examined.

Example 1
We will start by creating a small test temporary table for our analysis.
The following listing shows the test table CREATE TABLE DDL (data declaration
language) statement created as a temporary table and one INSERT statement to load all
the rows we wish to process.

19
Exploring the Variety of Random
Documents with Different Content
kotiin. Miten naurettavaa: kolme yhden kimpussa… Mutta päällikkö
oli voitettu, siinä retken alkusyy.

Oli näet tahtonut lähteä pyyntiin, viettääkseen palattuaan komeat


häät. Polkuhinnasta, velaksi oli saanut laivapahaisen, jolla kyllä oli
kokoa, mutta joka oli kömpelö ja läpimätä. Sellaiseen ruuheen ei
tietenkään ollut helppo hankkia miehistöä. Ja silloin, aivan uhallaan,
yksistään tytön takia, oli Musta-John lupautunut ensimäiseksi;
perässätulijoita oli sitten kyllä ollut. Hän oli tahtonut palata yhtä
rikkaana kuin kilpailijansakin, sillä tokkohan vaan oli oikukas tyttö
sanonut viimeistä sanaansa… Kolmas, hän jäi kotiin, mutta hänestä
ei ollut vaaraa.

Oli tultu suoraa päätä Huippuvuorille, tavattu suuri joukko hylkeitä


ja seurattu niitä yhä pohjoisempaan. Päällikön oli vallannut raivoisa,
tappamishimo, hän oli uhmaillut kuoleman vaaraa ja miehistöä, joka
toisinaan kapinoitsi. Ja Musta-John se häntä yllytti kuin paholainen,
yhä pohjoisempaan ja pohjoisempaan, kunnes laiva oli saatu saalista
täyteen. Hän se oli antanut laivan omistajalle päällikön nimen, aivan
kuin ivatakseen, eikä miehistökään tahtonut sanoa entistä
pyyntitoveriaan kapteeniksi.

Paluumatkalla oli sitten tavattu mursuja. Ja huolimatta siitä, että


Musta-John kielsi ja varoitteli, komensi päällikkö miehensä työhön. Ja
siinä sitä nyt oltiin, pienellä lautalla myrskyn ja pakkasen syleilyssä,
aavan meren ulapalla…

Yhtäkkiä alkoi päällikkö nylkeä mursua ja viittasi toisia avukseen.


Mykkä mies riensi paikalle, mutta Musta-John ei viitsinyt; hän
tuijotteli ulapalle, joka oli yhtä ainoata likaisenharmaata vaahtoa.
Hämärässä erottausi toisinaan jokin jäävuoren tapainen, mutta laiva
oli kadonnut.
Kun mursu oli nyletty, keräsi päällikkö lunta ja jääsohjua; sitten
hän kietoutui nahkaan, asettui mursun taakse tuulensuojaan ja
haparoi lunta päälleen. Vaikka mykkä mies vihasi päällikköä, sukelsi
hän herransa taakse. Molemmat asettuivat niin mukavasti kuin
mahdollista, ja päällikkö sai sytytetyksi sikarinsa.

Musta-John olisi ennemmin paleltunut kuoliaaksi kuin mennyt


heidän suojaansa. Rauhattomana hän käveli edestakaisin lautalla,
josta myrsky tuontuostakin mursi suuria paloja. Hän muisteli
pyyntiretken vaiheita, aikaa, joka oli niin täynnä työtä, vihaa ja
ikävää, hän muisteli pohjolan ikuista päivää, jota lepohetkinään oli
ihaillut, auringon kiertäessä valjuna taivaanrannalla ja tuulen
tuprutellessa Newton-huipun ruusunpunervaa lunta. Nyt olisivat he
jo kotona, ellei päällikkö olisi ollut niin mieletön, ja hän samaten.
Kirousko heitä niin kiinnitti yhteen ihmiseen!…

Hän käveli tuntikausia lautan yhä pienentyessä ja aaltojen


huljuttaessa vettä sen yli. Jalkoja alkoi paleltaa, ja kädet olivat
kontassa. Hän hieroi ja hakkasi niitä ja katseli tovereitaan, jotka
äänettöminä makasivat mursunnahan suojassa.

Yhä enemmän katkeroituen ajatteli Musta-John, mikä oli saanut


mykän miehenkin taipumaan päällikön oikkuihin. Olihan hän yhtä
rohkea ja kylmäverinen kuin päällikkökin, mutta sopimuskirjan
nojalla sai toinen toista komentaa ja vaatia kuoleman kauhuihin.
Itseään ei Musta-John niin suuresti ihmetellyt,
mustasukkaisuudessaan oli hän valmis vaikka mihin, mutta koko
muu miehistö… Siellä se nytkin makasi, päällikkö tuprutellen
sikariaan kuin ei mitään olisi tapahtunut, kirottu mies…

Musta-John tunsi oudon vihan ja kiusanhalun paisuvan


sydämessään. Hän asteli toisten luo, tuijotti ulapalle kuin uneksien ja
puhui hiljaisella, lempeällä äänellä:

— Minusta tuntuu, olenpa miltei varma, että ukon majassa nyt


istuu joku, vaikkapa se kolmas mies, pitäen tyttöä polvellaan. Kaiketi
polttavat tulta takassa, kun myrskyää… Hyväpä onkin, että
lapsikullalla on lohduttaja, sittenpähän ei suotta ikävöi.

Päällikkö nakkasi hehkuvan sikarinpätkän kauas, mutta ei


vastannut.
Musta-John ei saanut tarkatuksi hänen ilmettään.

*****

Yö oli hirveä; uskomattoman hitaasti tunnit kuluivat.

Musta-John käveli ja hyppi, toiset yhä makasivat. Mykkä mies oli


aivan hupussa, leukaa vain näkyi, mutta hänen hampaansa kalisivat.

Tuuli aikoi kuitenkin tyyntyä, mutta lännessä päin oli sankka


sumukerros, joka pelotti ja ihmetytti. Kuinka ei tuuli vienyt sitä
muassaan? Musta-John se ensinnä kiinnitti siihen toisten huomion.

Päällikkö kömpi ylös.

— Jääusvaako? huudahti hän.

Mutta se oli mahdotonta. Ei mitenkään nyt saattanut olla ilmassa


jääpölyä. noita hienoja neulasia, joita toisinaan on koko avaruus
täynnä näillä pohjoisilla seuduilla ja jotka laskeutuvat kuin lasijauho
esineiden pinnalle. Lähemmäksi tultua huomattiinkin, että sumu oli
kosteaa ja lämmintä; siinä tuntui selvästi suolan maku.

— Virta, virta, sanoi päällikkö kauhuissaan.


Tosiaankin. Lautta muutti kulkusuuntaa ja kiiti entistä nopeammin.
Usva ympäröi sen kokonaan, ja kun päällikkö pisti sormensa veteen,
tuntui se lämpimämmältä. Jäätävä kauhu valtasi jokaisen, sillä nyt oli
laivasta mahdoton huomata heitä; virta kuljetti heitä varmaa
kuolemaa kohden.

Vähitellen kävi sumu niin sankaksi, että he tuskin erottivat


toisiaan. Se pisartui iholle, juoksi pitkin ruumista ja teki olon
sanomattoman kurjaksi. Pakkasen vaikutusta se kuitenkin jonkun
verran ehkäisi. Toisinaan, kun tuulenhenki puhalsi, hälveni se hiukan
ja keinui edestakaisin kuin suunnaton, harmaa huntu.

*****

Päällikkö katsoi kelloaan; se oli neljä. He olivat siis jo


kaksikymmentä tuntia olleet lautalla, ja nälkä alkoi vaivata.

Jotain näytti juolahtavan päällikön mieleen. Hän meni mursun luo


ja leikkasi pari viipaletta lihaa. Juuri silloin aalto lohkaisi suuren osan
lautasta, erottaen hänet toisista. Nopeasti railo leveni, mutta
päällikkö teki yhtä nopeasti päätöksensä, otti vauhtia ja hyppäsi.
Toinen jalka hipaisi aaltoa, joka kohosi jäiden väliin pyöreänä ja
tasapintaisena kuin ilmakupla puuropadassa, mutta hän pääsi yli
kuitenkin ja jäi paikalleen valjuna ja vapisten, seuraten silmillään
mursua, joka pian hävisi sumuun.

— Onnestipas! Minä jo pelkäsinkin, että yksin saisin viedä perille


sinun saaliisi. Tietenkin olisi minun täytynyt puhua tytölle monta
kaunista lohdutuksen sanaa sinusta… Mutta paisti meni, puheli
Musta-John hiljaisella, säälivällä äänellä, jonka sävyssä iva kuitenkin
selvästi ilmeni.
Viha leimahti päällikön silmissä. Mikä ihme Mustaan-Johniin nyt oli
mennyt? Ennen ei hän ollut sanaakaan sanonut tytöstä, ja nyt, tässä
kuoleman hädässä, viittaili alinomaa…

— Tässä nälissäni olen muuten jo kauan ajatellut, kuinka häissäsi


mässätään. Jumal'auta! Sielläkös lihat ja liemet, herkut ja viinit.

Kalpeana sipaisi päällikkö sivuaan. Veitsi oli kuitenkin poissa, se oli


mennyt mursun mukana.

— Raukka, sähisi hän.

Musta-John nauroi niin, että ruumis hytki, ja heitti aivan päällikön


jalkojen eteen hyljekeihäänsä joka vavahdellen iskeysi jäähän
pystyyn.

Toinen käänsi hänelle ylpeästi selkänsä.

Yhä usvaa, samaa, loputonta usvaa. Paikoittain se oheni, niin että


saattoi nähdä muutaman metrin eteensä, paikoittain se oli niin
tiheää, että tuskin erotti kämmentään.

Tuntikausia olivat miehet murjottaneet äänettöminä eteensä.


Heidän oli nälkä, mutta se ei ollut pahinta: sumu oli tehnyt vaatteet
kosteiksi, ja heitä paleli kauheasti.

Suuria jäävuoria liukui silloin tällöin ohitse, ja kun irtojäitä putoili


mereen, kuului ryskettä ja veden loisketta. Muuten olikin ihmeen
hiljaista ja aivan tyyntä.

— Jospa pääsisimme pois virrasta. Usvapiirin ulkopuolella on nyt


varmaankin kirkasta, ja laiva saattaisi näkyä, sanoi päällikkö.
— Jospa, jospa!… Jospa meillä olisi kotitanner jalkaimme alla, kesä
ja kärpäset, vastasi Musta-John.

Yö kului kuitenkin, miesten hyppiessä äänettöminä lautalla,


nääntymäisillään kylmästä, nälästä ja väsymyksestä. Lautta kulki yhä
tasaisesti virran mukana, mutta se oli tavattomasti pienentynyt. Ja
herra ties miten kauas se oli joutunut lähtöpaikasta.

Sitten alkoi tuulla heikosti ja sumu hälveni hiukan.

Silloin, yhtäkkiä, mykkä mies päästi eläimellisen mörähdyksen,


syöksyi lautan reunalle ja suoraa päätä veteen. Toiset kääntyivät
katsomaan ja huomasivat harmahtavan kappaleen, jota, kumma
kyllä, virta kuljetti nopeammin kuin lauttaa. Päällikkö ymmärsi heti,
ja toivonsäde valahti hänen pimeään mieleensä.

— Vene! huudahti hän hiljaa.

Musta-Johnkin näytti kuin virkistyvän ja astui lähemmäs. Vene se


siinä tosiaankin hiljaa liukui ohitse.

Kylmästä kankea uimari läheni sitä kuitenkin sangen hitaasti, ja


ennen pitkää peittyivät sekä alus että mies usvaan. Toiset
kuuntelivat jännittyneinä, mutta kesti kauan, ennenkuin mykkä mies
jälleen jotenkuten äännähti.

— Halloo, tännepäin! huusi päällikkö. Sanoja seurasi syvä


hiljaisuus. Sitten kuului jälleen outo, riutuva äännähdys, kuin
vaikerrus, josta lautalla olevat ymmärsivät, että mykkä mies oli
hädässä.

— Tännepäin, tännepäin! kehotti päällikkö uudestaan.


Jälleen pitkä äänettömyys. Sitten kuului hetken aikaa kolinaa, joka
varmaankin johtui siitä, että mela pyörähteli edestakaisin, kun uimari
yritti veneeseen.

— Hän ei pääse sisään, huomautti Musta-John.

Molemmat miehet katsahtivat toisiinsa, ja molempien aivoissa kulki


sama ajatus: mennä apuun. Mutta molemmat käsittivät sen
mahdottomaksi. Vielä kerran kuului heikko kolahdus. Vielä kerran
läikähti toivo miesten sydämissä. Sitten tuli hiljaista, ahdistavan
hiljaista…

*****

Nyt näkyi jo selvästi kirkas taivas. Kuin ihmeen kautta oli lautta
kulkemassa pois usvapiiristä, koska sen toisella puolen sumu oheni
ja meren pintaa alkoi näkyä yhä pitemmältä. Ulappa selkeni yhä, ja
miehet tuijottivat uteliaina etäisyyteen, näkyisikö mahdollisesti
maata, tai laivaa, laivaa…

Kun he kokonaan pääsivät pois usvavyöhykkeestä, kiristyi


pakkanen huomattavasti. Varmaankin oli kylmää yli
kahdenkymmenen asteen. Kelmeä, pikemmin kuuta muistuttava,
mutta suunnattoman suuri ja reunoiltaan epäselvä aurinko näytti
ikäänkuin pyörivän taivaanrannalla. Vesi vivahti paikoittain
punertavalle, vaikka se muuten kiilsi tyynenä ja harmaana kuin
grafiitti.

Lautta pysyi nyt aivan paikallaan. Virran kohdalla liehui sumu kuin
suunnaton, harmahtava huntu, rajottaen näköalaa. Mataloita,
hohtavia jäävuoria oli siellä täällä, mutta muuta ei mitään, vain
toivoton, ääretön ulappa.
Päällikkö oli jo kauan istunut erään lohkareen päällä. Sitkeä Musta-
John hyppi yhä kuin mieletön, hammasta purren, horjahdellen
väsymyksestä. Hän tajusi, että liikunto oli ainoa tie pelastukseen,
ainoa keino välttää pakkasen hyökkäyksiä.

Päällikkökin kyllä käsitti, että toverinsa oli oikeassa, mutta ei


jaksanut enää, vaan tuijotteli alakuloisena eteensä. Hyvä oli
kuitenkin, että jalkoja oli lakannut kirveltämästä. Niillä ei tuntunut
enää olevan mitään hätää. Hajamielisenä koetti hän niitä käsillään,
mutta säpsähti ja kauhistui. Kummassakaan ei ollut tuntoa.

Ponnistaen kaikkensa alkoi hän hieroa niitä, mutta hänen


voimansa pettivät. Yhä uudestaan ja uudestaan hän yritti, mutta
vapisevat kädet eivät enää totelleet. Silloin hän jätti kaiken, kyynel
kiertyi hänen silmäänsä, mutta hän ei valittanut eikä virkkanut
sanaakaan toverilleen.

Niin kului pitkiä hetkiä. Nälkä alkoi yhä tuntuvammin kiusata


miehiä.

Silloin laskeusi suuri parvi kömpelöitä lintuja lautalle.


Otaksuttavasti ne eivät olleet huomanneet miehiä, koska ne tuskin
ennättivät hupaistakaan jäätä, ennenkuin päästivät kirkuvan
hätähuudon ja kiitivät pois samaa tietä. — Musta-John oli kuitenkin
saanut yhden tapetuksi hyljekeihäällään.

Hän avasi veitsellään linnun ja ahmi raakaa lihaa, päällikön


seuratessa häntä himokkain silmin. Saatuaan kyllikseen hän leikkasi
pari viipaletta, pisti ne puukkonsa kärkeen ja toi toverilleen.

— Tuoss' on, sai hän vaivoin sanotuksi puolijäätyneillä huulillaan,


joille tummaa linnunverta oli hyytynyt.
Kun päällikkö koetti nostaa kättään, huomasi hän kauhukseen,
ettei voinut sitä liikuttaa. Veri pakeni hänen kasvoiltaan, ja hän
tuijotti kuin mieletön toveriaan kasvoihin, voimatta sanoa mitään.

Musta-John ymmärsi hänet väärin, luuli hänen ylpeilevän.

— Eikö kelpaa, piru vieköön. Syö! Luuletko sinä luurankona tyttöä


miellyttäväsi?

Päällikkö, jota Mustan-Johnin katkeruus ei enää loukannut, koetti


hymyillä, mutta hänen hymynsä oli jäätynyttä, sanan oikeassa
merkityksessä. Hän nyökkäsi päällään käsiänsä kohti, ja rinta alkoi
kiihkeämmin kohota ja laskea, kuin tuskan vaikutuksesta.

Silloin Musta-John käsitti, ja kaikki ilkeys katosi tuossa tuokiossa


hänen sydämestään. Nopeasti hän alkoi voimainsa takaa hieroa
toverinsa käsivarsia, mutta tämä antoi päänsä retkahtaa rinnalle ja
mutisi tuskin kuuluvasti:

— Hyödytöntä.

Jonkinlainen liikutus kuvastui Mustan-Johnin tummilla kasvoilla.


Hän lakkasi, tuijotti sanattomana toveriaan, joka näytti vaipuvan
välinpitämättömään, tylsään tilaan, kuin horrokseen…

Musta-John alkoi jälleen kävellä ja hyppiä, ponnistaen viimeiset


voimansa. Outo kammo esti häntä edes vilkaisemasta päällikköön,
joka yhä istui vaieten, kuin torkuksissa. Hän tuijotteli ulapalle, joka
avartui yhtä rannattomana, yhtä tyhjänä ja kalseana kuin tähänkin
asti, herättämättä pienintäkään toivoa…

Illemmällä kuuli Musta-John oudon kahahduksen takanaan, siltä


kohdalta, missä päällikkö istui. Kun hän kääntyi katsomaan, näki hän
päällikön makaavan kaatuneena, ruumis käppyrässä, kasvot
kellertävän valjuina ja silmät ummessa.

Musta-John kumartui koettamaan sydäntä, mutta ruumis oli jo


kankea.

Järkensä viimeisillä voimilla tajusi Musta-John mitä nyt oli tehtävä.


Kohmettuneilla käsillään hän riisui vaivalloisesti ja hitaasti kuolleen
aivan alasti, kietoen ympärilleen vainajan vaatteet niin hyvin kuin
taisi. Miten ihmeellistä, häntä alkoi nukuttaa, jalat eivät tahtoneet
mitenkään kantaa. Hän lysähti istumaan aivan ruumiin viereen,
vieläpä suorastaan nojasi kuolleeseen, ja nuokkuessaan siinä, pää
hervottomasti retkallaan, muistutti hän enemmän ryysykasaa kuin
ihmistä.

*****

Alkoi tuulla hieman, ja lautta kulki hiljalleen sumua kohden.

Musta-John ei tiennyt enää mitään ajan kulusta, mutta


vaistomaisesti hän ymmärsi taistella unta vastaan. Kun hän oli
saapunut jo aivan sumuvyöhykkeen rajalle, missä mereen painuvan
auringon valjut säteet taittuivat värikkäinä, näki hän merkillisen
näyn, joka hetkeksi herätti hänet horrostilasta.

Aivan hänen edessään, ehkä kymmenen metrin päässä, kaareutui


suuri kehä, joka sisäreunaltaan oli vihertävä, sitten kauniin
oranssinvärinen, sitten karmosiinipunainen, heiketen vähitellen
epämääräiseksi. Se oli sateenkaaren tapainen, muistuttaen Ulloan
ympyrää tai sitä ihmeellistä ilmiötä, jonka Tissandier oli nähnyt Pic
du Mindellä. Tuon suuren kehän keskellä, aivan kuin peilistä, tunsi
Musta-John itsensä. Siellä oli toinen lautta, jonka päällä makasi
ryysyihin hautaantunut ihminen, nojaten sinertävään,
käpristyneeseen, alastomaan ruumiiseen…

Vielä silloin, kun aurinko alkoi hieman kohota merenpinnalta, jonka


päällä se oli seisonut kuin levähtäen, oli Musta-John tajuissaan. Hän
katseli raukein silmin ulapalle, joka aaltoili hiljaa. Selvästi hän erotti
laineiden loiskeen lauttaa vasten. Sumuun hän ei ollutkaan joutunut,
vaan oli kulkeutunut kauas siitä.

Hänessä heräsi outoja mielikuvia ja unelmia. Hän muisteli entisiä


iloisia päiviä, viiniä ja naisia… Sitten pieni, ruskeasilmäinen
kalastajatyttö hymyili hänelle. Kotiinjäänyt mies se nyt silitteli tytön
kiharoita…

Äkkiä pieni, valkea kohta ulapalla sai hänen raukeat silmänsä


terävöitymään. Sydän sykähti, mutta hänellä ei ollut voimaa kohottaa
edes päätään.

— Purjeita, mutisi hän.

Ruskeasilmäinen tyttö hymyili jälleen hänelle. Hänen katseessaan


kuvastui syvä tuska, kun se jäykkänä seurasi valkean esineen liikettä
ulapalla.

— Purjeita, purjeita, yritti hän taaskin kuiskata, mutta ääntä ei


enää syntynyt, silmät vain painuivat vähitellen umpeen.

Laiva siellä todellakin viiletti täysin purjein kotiin päin. Meri avautui
sen ympärillä toivottomana ja harmaana epämääräisessä kalseassa
valossa. Ja kaukana, kaukana siitä keinui aalloilla pieni jäälautta,
jonka päällä näkyi jotakin tummaa, kuin nukkuva hylje.
Suru.

Lääkäri kumarsi lähtiessään myötätuntoisena ja vakavana.


Sydänhalvaus oli sattunut. Kuolema oli ottanut omansa, hän ei
voinut muuta kuin todeta, että henki oli lähtenyt.

Maisteri Törmä jäi seisomaan keskelle huonetta seuraten lääkärin


menoa aivan erikoisella, mutta itsetiedottomalla mielenkiinnolla.
Näytti kuin hän olisi tahtonut huutaa, pidättää poistuvaa, ties mistä
syystä, ehkäpä järjettömässä toivossa, jonka alkava epätoivo
synnytti, uskossa, että apu vielä voisi olla mahdollinen. Mutta ovi
painui hiljaa kiinni ja hän jäi yksin rakastettunsa kanssa, joka lepäsi
vuoteella kauniina kuin eläissäänkin, mutta kylmänä ja kankeana,
silmät ummessa, kasvoilla kuoleman valju jäykkyys.

Kuinka saattoikaan olla mahdollista, että hänen vaimonsa, joka


vastikään oli liikkunut kodin sulona, säteillyt lämpöä ja iloa, nyt
makasi tuossa elottomana? Heidän avioliittonsa oli kestänyt vasta
pari kuukautta, miten voi kylmä, puistattava sini peittää huulet, jotka
äsken punersivat elämänhalua, rakkautta, onnea? Maisteri Törmä ei
sitä todeksi käsittänyt, ei voinut käsittää. Loistihan peili yhtä
kirkkaana kuin ennenkin, kuvastaessaan rakastetun piirteitä,
tuoksuivathan kukat sen edessä kuihtumattomina, hänen kätensä
järjestäminä, kuinka saattoi siis…

Törmä ei osannut selvästi ajatella. Hän näki kuin ahdistavaa unta.


Ei hänen ollut kovin tuskallista, mutta sydämeen patoutui jotakin
tylyä ja raskasta. Kummallinen tyyneys valtasi hänet, ja hän ymmärsi
vaistomaisesti pelätä sitä.
Ilta pimeni. Tuuli vinkui ulkona, kiidätti suuria sadepisaroita rajulla
voimalla ruutuja vasten, hiljeni vähäksi aikaa valittavasti
humisemaan ja sitten jälleen yltyi. Raskaita pilvenmöykkyjä vaelsi
juhlallisesti yli synkkenevän taivaan; kadulla riensi kiireesti muuan
yksinäinen kulkija, sateenvarjonsa suojaan kumartuneena.

Törmä seisoi kauan aikaa liikkumattomana, sitten hän astui jykevin


askelin eteiseen ja veti palttoon ylleen. Hätääntyneen näköinen
palvelustyttö pisti päänsä ovenraosta tuijottaen tuskaisin silmin
isäntäänsä. Törmä ei saanut katsotuksi häneen, vaan kiiruhti ulos.

Mitä katuja hän kulki? Kulkiko kauankin? Sitä hän ei tiennyt. Märät
asfalttikäytävät heijastivat hänen kuvansa, kuin hän olisi kävellyt
jäällä. Ihmisiä riensi hänen ohitseen, taisipa joku tuttava nyökätäkin,
mutta ei se kiinnittänyt hänen huomiotaan. "Tokkohan se olen
todellakin minä, joka tässä astuskelen?" ajatteli hän itsekseen. Oli
niin keveätä. Aivanhan ilma kantoi häntä. Tuskaa ei hän tuntenut
ollenkaan, kurkkua vain kirvelsi ja kuivasi. — Hän tarkasteli
näyteikkunoita, joissa hänen mielestään oli kovin hauskoja esineitä.
Hän kastui likomäräksi, mutta ei tiennyt siitä mitään.

Eräästä kahvilasta kuului soittoa. Hän pysähtyi oven eteen, epäröi,


astui kuitenkin sisään ja tilasi teetä. Uteliaana hän silmäili
ympärilleen, kaikki näytti hänestä niin vieraalta. "Minäköhän tässä
nyt istun, minä poika?" ajatteli hän taas, koettaen teeskennellä tuon
itselleen tekemänsä kysymyksen iloiseksi ja huolettomaksi. Turhia,
eihän hän unissaan ollut, sen hän kyllä tiesi… Ennen pitkää tylsä
mielentila valtasi hänet; hän sytytti sikarin, vaikkei itse sitä
huomannut.

Orkesteri alkoi soittaa surullista venäläistä ballaadia. Tavallisella


tuulella ollessaan olisi Törmä pitänyt tuota kappaletta aivan liian
haikeana ja hempeämielisenä, ensi viulun soittajakin tarpeettomasti
nau'utti soitintaan, mutta nyt, nyt se tuotti hänelle ihan ruumiillista
tuskaa. Hänen sydäntään viilsi, kummallisia väreitä kiiti kuin veren
mukana ympäri ruumista. Yhtämittaa täytyi hänen niellä jotakin, joka
pyrki kurkkuun; hän painoi päänsä alas ja tuijotti pöytään
sumentunein silmin… Äkkiä hän tunsi sormissaan kipua.
Huomaamattaan oli hän polttanut sikarinsa loppuun, ja sormiin oli
palanut rakkoja. Heti hänen mielentilansa muuttui. Hän tarkasteli
kättään erinomaisella mielenkiinnolla. "Jopahan nyt jotakin, sormeni
poltin, voi kuitenkin", mutisi hän kyynelet silmissä, vaikka oli
sisimmässään lohdutettu tapahtumasta, sillä kirvely teki hyvää.
Sangen pian kaikki tämä kuitenkin unohtui ja hän vaipui jälleen
tylsään tilaansa ajattelematta mitään…

Yhtäkkiä hän uudestaan halusi kuulla äskeistä ballaadia. Sen


synnyttämä tuska, joka taannoin oli kohota yli hänen voimiensa,
tuntui nyt suloiselta hänen mielestään, ja hän himoitsi sitä jälleen.
Kun hän nousi lausuakseen toivomuksensa orkesterin johtajalle, ei
kahvilassa ollut enää ketään, muuan tarjoilijatar vain seisoi hänen
vieressään kasvoilla kärsimätön ihmettely. Valoja jo sammuteltiin.
Törmä huomasi jaloissaan suuren lätäkön, joka oli valunut hänen
märistä vaatteistaan. Häntä hävetti ja hän joutui hämilleen; teekin oli
vielä lasissa. Hetkisen hän epäröi, joi sitten yhdellä siemauksella
kylmän teen ja poistui kiireesti.

Satoi yhä, kadut olivat autioina; hän harhaili päämäärää vailla.


Eräässä kulmauksessa kiintyi hänen huomionsa suureen,
kellertävään ilmotukseen. Tanssiaiset, suuret juhlat, luki hän. Hän
nauroi äänettömästi, suu veltosti puoliavoimia, ja lähti nopeasti
astumaan. Oven lähettyvillä hän säpsähti ja pysähtyi. "Tokkohan
pelkään jotakin?" mutisi hän, hämärästi tajuten pyrkivänsä piiloon
itseltään, tuntiessaan sydämessään myrskyn enteitä. Hänen
raskaitten aivojensa läpi kulki heikko aavistus siitä, kuinka
ulkopuolella omaa itseään hän oli. Mutta ei hän kuitenkaan
väkinäisesti astunut sisään.

Kun hän näki ihmisiä, kahlehti unissakulkijan tylsyys hänet


uudelleen. Aivan tiedottomasti hän meni ravintolan puolelle ja kutsui
edeskäypää, eikä tuo tiedottomuus ollut petosta. Vasta kun
edeskäypä kumartaen kysyi mitä hän tarvitsi, hän ikäänkuin heräsi,
huomasi olevansa sopimattomassa puvussa, tunsi vilua ja tilasi
neuvottomana viskyä. "Olenhan aivan sekaisin. Mitäs minä täältä,
mieletön?" ajatteli hän.

Viereisessä pöydässä istui lukuisia herrasmiehiä. Muuan seisoi selin


Törmään, puhuen parrattomalle, kalpealle nuorukaiselle:

— Vai teatteriin. Puhallat poskesi pullolleen, panet tyynyjä vatsasi


päälle ja ihmettelet Falstaffina kunnian hyödyttömyyttä. Tai otat
mustan viitan harteillesi, murheen miinin kasvoillesi ja pohdit
Hamletina ollako vai ei olla…

Törmä tuijotti kauan puhujan hartioihin; ne näyttivät hänestä


tutuilta… Jälleen kuuli hän seisovan miehen sanovan:

— Onnetonko? Tyhjiä. Minä olen rakastunut naiseen, joka on


yhden miehen leski, toisen vaimo ja kolmannen rakastajatar. Mitä te
siitä sanotte? Olen aivan toivoton, sillä mitä voisikaan hän enää olla
minulle. Mutta on minulla onnellinenkin lempi. Tulinen madame
Remy-Martin Frapin Sayer Meukow suo minulle suuteloita, vaikka
tosin lasista. Näkemiin, hyvät herrat, lähden hänen luokseen.
"Sehän on Raunio", sanoi Törmä itsekseen. Juuri sillä hetkellä
Raunio, lakimies ja Törmän entinen kilpakosija, kääntyi, huomasi
Törmän ja tuli hänen luokseen. Hän oli korkeaotsainen,
teräväsilmäinen, ruma mies, jonka suun ympärillä oli veltto ilme ja
jonka vartalo oli käyrä.

— Tekö täällä! Terve tuloa. Entäs rouvanne? huudahti hän


ojentaen kätensä.

Törmä säikähti hirveästi. Hän hapuili sanoja, mutta väriseviltä


huulilta ei kuulunut kuiskettakaan. Hän ei voinut katsoa Raunioon,
jonka silmät tuijottivat häneen kuin tikarit koko pitkän
äänettömyyden ajan.

Visky tuotiin. Törmää inhotti, ja hän sysäsi lasinsa syrjään. Ei hän


olisi mitenkään voinut juoda. Raunion läsnäolo ahdisti häntä.

— Täynnäkö olette? kysyi lakimies ilkeästi.

Törmään koski kipeästi. Hän tunsi punan kohoavan kasvoilleen.

— En ole humalassa, hyvä herra, vastasi hän hillitysti.

— Olettepa! intti Raunio painostaen. Hän kumartui hiukan, tuijotti


Törmään hetken, silmät hehkuvina kuin hiilet. Sitten hän miltei
kuiskasi: — Vastako te nyt tiedätte, että ihminen humaltuu
muustakin kuin viinasta… tuskasta esimerkiksi, kuten te.

Ja lakimies kääntyi lähteäkseen. Törmä tunsi outoa liikutusta,


vaikka
Raunion katse kiusasi häntä. Merkillisellä tavalla hän hätääntyi.

— Älkäähän… Miksi te lähdette? Istukaa toki, sai hän sanotuksi.


— No, juokaa! kehotti Raunio ystävällisesti, muuttunein äänin.

Törmä pudisti päätään; häntä inhotti alkoholin hajukin.

Lakimies silmäili häntä hetken vaieten.

— Tulkaa sitten tänne, sanoi hän miltei käskevästi, tarttuen


Törmää käsipuolesta ja puoliväkisin taluttaen hänet tanssisaliin.

Musiikki pauhasi huumaavasti. Seinillä kierteli vihreitä köynnöksiä,


ja erivärisillä paperikoristeilla sälytettyjä nauhoja kulki yli salin.
Lattialla pyöri pareja; ne näyttivät Törmän silmissä häipyvän kuin
sumuun.

— Maisteri Törmä, rouva X, kuuli hän kuin unessa Raunion


esittävän, ja ennenkuin hän oikein sai selville mitä tapahtui, huomasi
hän tanssivansa valssia. Kaikki humisi hänen ympärillään. Hänestä
tuntui, kuin hänen jalkansa eivät olisi ollenkaan koskettaneet
permantoa, olleet hänen omansakaan. Ihmisiä vilisi kuin varjokuvia
hänen ympärillään. Häntä horjutti, eikä hän voinut puhua.

Rouva tuijotti häneen oudoksuen, vetääntyen hänestä


mahdollisimman loitolle.

— Lopettakaamme, olen uuvuksissa, sanoi hän.

Juuri silloin Törmän katse osui Raunion kasvoihin, joilla näytti


karehtivan ilkeä hymy. Rouvan sanat loukkasivat häntä.

— Arvoisa neiti, Yksi ympärys vielä, tehkää mielikseni. En tunne


ketään muita, ja tanssi huvittaa minua.

Rouva rypisti kulmiaan.


— Mutta esittihän tuomari Raunio minut ja näettehän sormukseni.
Olette humalassa! tiuskaisi hän suuttuneena.

— No rouva sitten, suokaa anteeksi.

Melkein väkisin riistäytyi rouva irti ja lehahti pois. Törmä hoippui


Raunion ohi ravintolan puolelle ja istui pöytänsä ääreen. Hänestä oli
kaikki yhdentekevää.

Lakimies tuli takaapäin ja laski kätensä hänen olalleen.

— Lähtekäähän, tulen teitä saattamaan, sanoi hän tummalla


äänellään. Hän oli kovin kalpea, ja hänen kasvoillaan oli vakava,
myötätuntoinen ilme.

Törmä nousi tahdottomasti, aivan kuin lapsi; hänen päähänsä ei


pälkähtänyt vastustamisen mahdollisuuskaan. Viime tingassa hän
muisti maksaa.

He kulkivat pitkän aikaa aivan ääneti sateen piestessä heidän


kasvojaan. Erään lyhdyn luona Raunio äkkiä pysäytti toverinsa,
katsoi häntä terävästi ja sanoi:

— Margit on teitä pettänyt.

Törmä oli luhistua maahan. Hän tuijotti vavisten lakimiestä silmiin;


kauhu kuvastui hänen verettömillä kasvoillaan.

— Hän kuoli, vastasi hän soinnuttomalla äänellä.

Raunio kumartui äkkiä nostaen käden suulleen, kuin olisi häntä


yskittänyt. Sitten hän kääntyi ympäri kuin tuulenkela ja lähti sanaa
sanomatta.
Hetken Törmä seisoi alallaan painaen kädellään sydäntään. Hänen
rintansa kohoili kuin maininki. Hän oli menehtyä tuskaan, joka
yhtäkkiä oli päässyt valloilleen. Kyyneliä kihosi hänen silmiinsä, eikä
hän nähnyt eteensä. Horjuvin askelin hän lähti kulkemaan.

— Margit Margit Margit Margit, valitti hän ääneen, nyyhkyttäen ja


tavantakaa niellen esiinpyrkivää itkua. Muuan vastaantulija kuuli
hänen valituksensa ja pysähtyi. Hän huomasi sen, häpesi, sai vähän
hillityksi itseään ja kiihdytti käyntiään. Ponnistaen kaiken tahtonsa
koetti hän kiinnittää huomionsa ulkonaisiin seikkoihin. Hän tarkasteli
talojen numeroita, lyhtyjä, kadunnimiä. Pitkän aikaa hän laski
askeleitaan, mutta sekaantui aina. Vähitellen hän tyyntyi; häntä alkoi
vilustaa…

Tuossa asui hänen vaimonsa ystävätär; sisällä valvottiin vielä,


ikkunasta näkyi valoa. Hän soitti kelloa, tietämättä, että puoliyö oli jo
ohitse; voimakas halu oli hänet vallannut. Neiti tuli itse avaamaan,
peloissaan ja hämmästyneenä. Törmä ei toivottanut edes hyvää
iltaa, hän tuijotti neitoa kasvoihin hämärästi tajuten tulleensa suotta
ja häveten. Vettä tippui lattialle hänen likomärän palttoonsa liepeistä.

— Hyvä Jumala! Mitä sinä ajattelet, äiti nukkuu jo… Mutta mikä
sinun on? Sinähän olet humalassa!

Tytön silmissä oli tuskainen ja hätääntynyt ilme.

— Minäkö? En.

— Mistä sinä tulet? Kasvosihan ovat ihan muuttuneet ja kätesi


sinertävät.
— En tiedä… Niin no, tanssiaisista, jos haluat… Mutta en minä
humalassa ole. Milloin sinä olet nähnyt minut humalassa?

— Entä Margit? Missä Margit on?

Törmän kasvot vääristyivät, hän oli tyrskähtämäisillään itkuun.


Sitten hän alkoi puhua kummallisen kiihkeästi, sysäyksittäin,
särkyneellä äänellä:

— Mitä varten sinä minua kuulustelet? Älä ole sydämetön. Anna


minun istua luonasi vähän aikaa, minun on niin vaikea olla. Tuolla
lailla katsotkin… Minkätähden olet vihoissasi?

Tyttö auttoi palttoon hänen päältään ja vei hänet sisään,


ymmärtämättä syytä hänen käytökseensä, mutta sekavasti
aavistaen, että jotakin oli hullusti.

— Tahdotko mitään, teetä? Sinunhan on vilu.

— Anna olla, vastasi Törmä hiljaa, tehden kädellään ykskaikkisen


liikkeen. Pitkän aikaa hän istui tylsästi tuijottaen eteensä, sanomatta
sanaakaan. Sitten hän kaivoi paperossin taskustaan, pisti sen
suuhunsa, mutta unohti sytyttämisen. Hetken kuluttua hän otti sen
jälleen käteensä ja pudotti sormiensa välistä lattialle itse sitä
ollenkaan huomaamatta.

Tyttö katseli häntä neuvottomana, tietämättä mitä sanoa. Selvästi


oli hänen vieraansa sairas. Hän oli jo aikeessa herättää äitinsä, kun
Törmä kavahti tuolilta kuin heräten.

— Minähän häiritsen sinua. Olet väsyksissä ja menisit


nukkumaan…
Hyvästi nyt, älä pahastu, sanoi hän, katse arkana.
— Ei, istu vaan… Sinä olet niin kummallinen tänään, et puhukaan
mitään. Tokkohan olet oikein terve?

— Kyllä minä nyt lähden.

Syntyi hetken vaitiolo.

— Menethän sinä kotia, suoraa päätä, eikö totta?

— Tietysti… Turhia sinä epäilet.

— No hyvästi sitten. Sano Margitille terveisiä. Tulen huomenna


häntä tapaamaan.

Törmä loi tyttöön sameat silmänsä. Yhtäkkiä hän puristi


pitelemäänsä kättä niin lujasti, että toinen oli huudahtaa.

— Margit kuoli tänään, sanoi hän kylmästi ja selkeästi. Ja


ennenkuin tyttö ehti toipua hämmästyksestään, oli hän jo ovesta
ulkona. —

Törmä riensi kotia kiireesti, kuin jotakin paeten. Kadulla oli ihmeen
hiljaista ja autiota. Häntä värisytti. Päästyään asuntonsa luo hän
katsahti miltei peloissaan ylös ikkunoihin. Kaikki oli pimeää,
palvelustyttö nukkui tai oli kenties mennyt pois kuollutta peläten.

Törmä avasi varovasti oven ja nousi portaat vaivalloisesti kuin


vanhus. Eteiseen päästyään hän väänsi nopeasti valoa ja riisui
vaatteensa. Ahdistus ja pelko valtasi hänet kokonaan. Hän epäröi,
seisoi alallaan pitkän aikaa, hänestä tuntui mahdottomalta mennä
vaimonsa luo. Hän aikoi jo työhuoneeseensa, mutta peräytyi
kuitenkin ja sammutti valon eteisestä. Hän hapuili pimeässä kuolleen
kammion ovelle, avasi sen vapisevin käsin, sydämensä sykkiessä
kiihkeästi. Hänen haparoivat sormensa löysivät sähkönappulan,
mutta hän ei sitä kiertänyt, vaan jäi kuuntelemaan. Hikihelmiä kihoili
hänen otsallaan. Häntä kammotti.

Tuuli tohisi haikeasti ja pitkäveteisesti. Katolta kuului sadeveden


lorina ja peltisen tuuliviirin vihlova vingahtelu. Oli pilkkopimeä.
Yhtäkkiä rupesi hänen korvissaan soimaan sama venäläinen ballaadi,
jonka hän oli kahvilassa kuullut. Hirveä tuska puristi häntä. Hänen
sydäntään poltti kuin tulinen rauta, hän väänsi valoa ja tuijotti
kauhuissaan vuoteelle. Sitten hän syöksyi mielipuolisessa vimmassa
kuolleen luo, vaipui polvilleen hänen viereensä, syleili häntä
käsillään, jotka nytkähtelivät kuin kouristuksessa, suuteli kuoleman
tuskassa hänen huuliaan, silmiään, hiuksiaan.

— Rakastettuni, rakastettuni! Ota minutkin mukaasi. En voi elää


ilman sinua. Vasta kadottaessani sinut minä tunnen, mitä sinussa
omistin… Rakastettuni, rakastettuni.

Hän itki, vääntelehti, valitti ääneen, haparoiden käsillään ilmaa


kuin sokea. Hän kasteli runsailla kyynelillään kuolleen kasvot, suuteli
häntä lukemattomia kertoja, odottaen ihmettä, mielettömyydessään
toivoen sulkeutuneiden silmien vielä aukenevan, kylmien huulten
vielä hymyilevän…

Vähitellen tyyntyi myrsky hänen rinnassaan. Pohjaton väsymys


valtasi hänet. Vihdoin hän nukahti raskaaseen uneen, pää samalla
tyynyllä, millä hänen vaimonsa elottomat kasvot lepäsivät
harmahtavina ja kylminä.

Viettelijä.
Ei minua vaivaa mikään tilinteon halu. Mutta tunnen eräänlaista
synkkää riemua ja tyytyväisyyttä sen johdosta, että näin kevein
mielin astun elämästä pois. Tosinhan voisi ajatella minussa kaiken
olevan jo niin tylsynyttä, ettei minkäänlainen hennompi tunto ole
minulle edes mahdollinen. Kenties todellakin olen konna, syvintä
olemustani myöten, vähät se minua liikuttaa. Minulle riittää, kun
tiedän järkeni olevan entisellään. Kunnon mies tai konna, kuka siitä
välittää! Kunhan vain otsallani on ylpeä uhma, kunhan vain nöyrä
katumus ja hempeä anteeksisaannin kaipuu pysyvät poissa. Tekoni
tarkotus ei ole sovittaa mitä olen rikkonut. Tahdon vain hankkia
huolettoman toimeentulon sille ihmiselle, joka on tauttani joutunut
kärsimään. Teen kaiken vapaasta tahdosta; ulkonainen hätä ei minua
ahdista, eikä elämänväsymykseni ole niin perinpohjainen, että se
minua pakottaisi.

En voi sietää sitä ajatusta, että herra Kaarlo Örn, liikemies ja


keikari, olisi ollut aseena kohtaloni kädessä, vaikka elämäni
luisuminen alamäkeen oikeastaan alkoi hänen kauttaan. Oman
luonteeni alhaisuus ja ylpeä heikkous, ne ne ovat päätekijöinä
kaikessa. Vaikka kiihkeä viha Örniä kohtaan kiehuu sydämessäni, on
se kumminkin vakavaa aihetta vailla. Johan hänen henkilökohtainen
vähäpätöisyytensäkin velvottaisi anteeksiantoon tai ainakin
ylenkatseeseen.

Vääryyttä hän minulle kyllä on tehnyt, sitä ei käy kieltäminen.


Hänhän tunsi minut tarkoin ylioppilasvuosinamme. Ja jos hänellä
olikin jonkinlainen oikeus nimittää minua "raffineeratuksi
satanistiksi", niin eihän hän itsekään ollut moitteeton, eipä pysynyt
edes poissa seurastani, vaan päinvastoin etsi sitä.
Hurjasteluhimossaan tarvitsi hän minua ja jälestäpäin tietysti syytti
minua siitä, että autoin häntä. Sitäpaitsi ei hän ollenkaan tiennyt
syitä mielettömyyksiini. Kaikki se ylenkatse, joka köyhyyteni tautta
oli tullut osakseni, kaikki se verinen vääryys, joka jo silloin oli
rinnassani herättänyt pohjattoman katkeruuden, oli hänelle
tuntematonta. — Jos runsaslahjainen, arkatuntoinen ja
herkkäsydäminen ihminen, joka kokonaan on jäänyt kotikasvatusta
vaille, yhtäkkiä joutuu ylhäisten henkilöitten kanssa tekemisiin; jos
hänen köyhyytensä lasketaan hänelle viaksi; jos jokainen katkera
pisto, jokainen loukkaus synnyttää vain sisällepäin vuotavia haavoja,
niin mikä muu voikaan tulla kysymykseen kuin joko täydellinen
masentuminen tai kylmäverinen, mistään piittaamaton uhma. Kun
huomaa itseään halveksittavan, niin todella heittäytyy
halveksittavaksi; kun keskustelutaidosta puuttuu hienous, niin
höystää sen myrkyllä. Mitä kummaa köyhä tekeekään tunteilla,
nehän ovat luodut vain poljettaviksi, sitä täydellisemmin, kuta
lahjakkaampi hän on. — Itse olin juuri sellainen. Arkailematta voin
sanoa olleeni lahjakas, sillä tiedän sen. Omanarvontuntoni oli herkkä
kuin ruhtinaan. Ja kun pistävinkään loukkaus ei voinut saada minua
kuohuksiin, synnyttää äkillistä vihaa, joka purkautumisen kautta olisi
haihtunut, patoutui sydämeeni valtava katkeruus, joka johti minut
mielettömyyksiin ja juoppouteen. Muuten en usko, että tuo kaikki oli
niin vaarallista. Se oli ohimenevää kiihkoilua, sillä oikeastaan rakastin
työtä enkä täydellisesti lyönyt laimin tehtäviäni pitkällisintenkään
hurjastelujen aikana. Olen kutakuinkin varma, että minusta olisi
tullut säännöllinen, kunhan vain olisin päässyt omille jaloilleni.
Muistanhan minä kuinka hyvää minulle teki se huomio, että
merkillisellä tavalla saatoin vaikuttaa naisiin — seikka, joka osottaa,
että kunnianhimoinen luonteeni olisi kaivannut ymmärtämystä,
ihailua ja arvonantoa voidakseen suorittaa jotakin kunnollista.

Miten tahansa, tässä ei ole paikka siihen syventyä! Mutta, kuten


sanottu, Kaarlo Örn tunsi minut täydelleen, miksi hän siis jätti niin
suuren vallan käsiini ja niin luottavana uskoi sisarensa seuraani!

Isänsä kuoltua hän nimittäin kirjotti minulle kirjeen, joka


sivumennen sanoen sisälsi imarteluja ja harvinaista uskoa kykyyni ja
jossa hän kutsui minua kotikaupunkiinsa auttamaan itseään liikkeen
järjestämisessä. Tyhjää lorua, sen huomasin ennen pitkää. Ukko oli
pitänyt asiat mitä parhaimmassa kunnossa, ei niissä ollut mitään
järjestelemistä. Eikä minusta suinkaan tullut mikään apulainen, vaan
liikkeen pää, sillä Kaarlo Örn, salonkimies ja keikari, oli aivan
kykenemätön johtoon, jopa suorastaan haitaksi minulle. Käsitin
saaneeni pysyvän paikan, ja vaikka palkkani oli verrattain alhainen,
ahersin tyytyväisenä kuin myyrä, sillä rakastuin melkein heti neiti
Anna Örniin.

Eikä toisin voinut käydäkään. Kaarlo oli miltei aina poissa. Hän oli
pienen kaupunkinsa seuraelämän keskeisin henkilö, ja tietäessään
voivansa täysin luottaa minuun heittäytyi hän vapauteensa. Ja niin
jouduin minä yhä useammin olemaan hänen sisarensa parissa, joka
enemmän rakasti rauhaa. Me teimme kävelyjä, me kävimme
konserteissa; ja tämän Kaarlo tiesi varsin hyvin, mutta en ollenkaan
ymmärtänyt, että hän katseli minua nurjin silmin. Sen hyödyn tautta,
joka minusta hänelle oli, ei hän sanonut mitään; nyt vasta sen
selvästi käsitän.

Mitä minä sanoisin neiti Annasta? Että hän lumosi minut kuin
minkäkin maitoparran. Että hän tuskin oli mikään kaunotar, mutta
kumminkin ainoa nainen, jota olen rakastanut, mikäli rakkaudella
ymmärretään kohottavaa ja puhdistavaa, mutta samalla
voimakkaasti intohimoista tunnetta. Hänen ylpeä, hillitysti tulinen
luonteensa ja kauniiden silmien loiste muuttivat koko sisäisen
maailmani.
Jos tosiaan on elämässäni ollut aika, jolloin elin säännöllisesti,
jolloin jonkinlainen pyrkimys hyvään ja jaloon asui sydämessäni, niin
käsittää se ne kuusi kuukautta, jotka väsymättömästi ponnistellen
uhrasin Örnin liikkeen hoitoon. Lempeni teki minut pehmeäksi ja
sokeaksi, se pimitti järkeni siinä määrin, että uskoin naiseen, uskoin
pelkkää hyvää rakastetustani, uskoin hänen tunteeseensa, josta hän
osasi niin kauniisti puhua. En ihmettelisi sitä, eikä niin suuri
katkeruus täyttäisi nyt mieltäni, jos silloin olisin ollut kokematon.
Mutta kaiken sen perästä, mitä olin nähnyt! Olinhan jo aikoja sitten
oppinut tuntemaan sen julkean kavaluuden, jonka naisen
enkelikauneus ja silmien kylmäverinen viattomuus kätkevät. Ja
kuitenkin menin ansaan! Itseeni takaisin kilpistyy jokainen syytös,
jonka katkeruuteni synnyttää.

Ehkä olen liian ankara. Kenties ensimäisessä uskossani


rakastettuuni sittenkin on jotain, oikeata. Mahdollisesti myöhemmät
tapahtumat ovat valaneet sieluuni niin paljon myrkkyä, etten enää
osaa erottaa sairautta pahuudesta. — Minun asemassani se on
tarkemmin ajatellen yhdentekevää.

Ratkaisu tapahtui yhtäkkiä ja niin typerällä tavalla, että se nytkin


minua liettää.

Eräänä aamuna istuin konttorihuoneessa, tapani mukaan


ahkerassa hommassa. Kaarlo herra astui sisään, hienoilla kasvoillaan
jonkinlainen ikävystynyt ilme. Hän toivotti hyvää huomenta,
pureskeli paperossiaan tietämättä mitä sanoa minulle, otti
silkkinenäliinansa ja huolellisesti poisti tuhkahiukkasen housuiltaan,
jotka näyttivät juuri päässeen silitysraudan alta. Minulla ei hänelle
ollut mitään asiaa, jatkoin työtäni kuin ei häntä olisi ollutkaan
lähettyvillä.
Yhtäkkiä hän sanoi naismaisella äänellään:

— Kaunis sormus sormessasi. Naisvoittoja varmaankin. Minä jo


muuten luulinkin, että sinusta on täällä tullut täydellinen pyhimys.

— Sisaresi sormus, vastasin nostamatta katsettani papereista.

Hän kumartui lähemmäksi, arvattavasti hämmästyneenä.

— Niinpä tosiaan! Tunnenkin. Mutta mitenkä se on mahdollista?

Mitä hän tarkotti teeskentelyllään? En sitä ymmärtänyt. Mitä


merkillistä siinä oli, että minulla sattui olemaan Annan sormus,
koskapa niin usein olin hänen seurassaan.

— Mahdollistako? Miksei? Kysyisit ennemmin kuinka se mahtuu


pikkusormeeni.

Hän muuttui totiseksi.

— Oikeastaan, niin mitä varten sinä kannat sisareni sormusta?

— No yksinkertaisesti siitä syystä, että hän korkeimman


omakätisesti on suvainnut pistää sen sormeeni… Sinä aavistelet,
huomaan. No niin. Mutta mitä sinulla oikeastaan on
vastaansanomista, jos menen naimisiin siskosi kanssa?

— Älä laske leikkiä. Eihän sinulla ole varojakaan, vastasi hän


vaaleten.

— Ei. Mutta Annalla on. Itseni minä taas kyllä elätän, kun korotat
palkkani puolella. Vai enkö ansaitse?
— Julkeutta sinulla kyllä näkyy olevan siinä määrin, että sekin jo
miehensä elättää, sanoi hän väkinäisesti nauraen.

— Ei, pois leikki, Kaarlo. Rakastan häntä ihan todella.

— Sinä!

— Myöskin hän.

Suunniltaan raivosta hän syöksyi kimppuuni. Pelkästä


hämmästyksestä ja halveksumisesta en yrittänytkään häntä
vastustaa. Annoin hänen heittää itseni permannolle ja vetää
sormuksen sormestani, hänen, jonka olisin voinut kiertää köydeksi.

— Konna, konna! kähisi hän hampaittensa välistä. — Kaiken sen


jälkeen, mitä sinusta tiedän… Tokko terveytesikään on enää
kunnollinen?

Ilkeästi nauraen kuuntelin hänen änkytystään. Johan minua huvitti


sekin, että salonkirnies noin unohtui kiihkoilemaan ja väkivaltaa
käyttämään; nyt oli hänen pukunsa epäjärjestyksessä. Vihan
vimmassa hän heitti sormuksen nurkkaan ja lähti pois.

Pidin häntä siksi vähäpätöisenä, etten suuriakaan välittänyt koko


tapauksesta. Toivottomaksi en suinkaan tullut, sillä hän oli minusta
enkä minä hänestä riippuvainen. Ilmeisesti hän oli kohdellut minua
väärin, mutta hänen tekonsa oli niin liioteltu, etten osannut siitä
syvemmin loukkaantua. Toimeni hänen liikkeessään katsoin kuitenkin
päättyneeksi.

Aivan toisenlaisen iskun sain Annalta. En tavannut häntä sinä


päivänä. Vasta seuraavana iltana menin hakemaan häntä kävelylle.
Hän tuskin vastasi tervehdykseeni eikä ojentanut minulle kättään. Jo
silloin aavistin pahaa, mutta pyysin kuitenkin häntä ulos.

— Mitä te tarkotatte? kysyi hän vaivoin hilliten ääntään, kasvot


kylminä ja vieraina.

Sain aivan kuin piston. "Te", oli hän sanonut. Hetkeen en osannut
vastata.

— Huomaan, että veljesi on kertonut…

— Miksi sitten enää tulette?

— Luulin, että te luottaisitte minuun. Ajattelin, ettei mikään


parjaus voisi minua niin täydellisesti alentaa, ainakaan teidän
silmissänne. Vakuutittehan te niin minua rakastavanne.

— Uskotteko te naisen vakuutuksiin, te, jolla on meistä sellaiset


käsitykset, joka olette sanonut… — Hän hillitsi liikutuksensa,
rypistäen kulmiaan. — Ei, en ole koskaan teitä rakastanut. On
sangen merkillistä, että olette antanut pettää itsenne. — Hän nauroi
kimakasti, ja hänen kasvonsa olivat melkein iloiset.

— Te minun käsitykseni naisista varmennatte, neiti, sanoin


katkerana, hiljaisella äänellä, joka pyrki vavahtelemaan. — Sallikaa
minun onnitella. Olette täydellisesti onnistunut pettäessänne minua.
Ei epäilyksen varjoakaan ole liikkunut aivoissani.

Kova hymy ilmaantui hänen kasvoilleen. Näin kyllä, että hän teki
tahdottomia eleitä, mutta hänen ilmeensä oli pilkallinen. Tulin
kokonaan toivottomaksi enkä enää tietänyt mitä sanoin.

You might also like