Database Optimization

Database Tables

TABLE ResidentChangeLog
 - ID            uniqueidentifier
 - ResidentID    uniqueidentifier
 - ChangeDate    datetime
 - ChangeType    tinyint
 - ChangeDetails nvarchar(MAX)
 - UserName      nvarchar(50)
ID        ResidentID ChangeDate    ChangeType ChangeDetails                            UserName
--------- ---------- ------------- ---------- ---------------------------------------- --------------
001...    X          2016-02-13... 20         Lunch Tray - Cranberry Juice (4 oz)      S-Hampton
008...    A          2014-02-11... 10         NPO to NAS                               E-Marshal
01C...    M          2015-04-02... 10         General to NCS/NAS                       J-Yates
054...    X          2016-02-29... 20         TID - HOUSE SUP 2.0 - 120CC (120CC)      J-DeMouchette
083...    A          2016-02-13... 20         Breakfast Tray - Juice of the Day (4oz)  D-Wojtalik
0FC...    C          2015-02-25... 20         HS SNACK - FRUIT CUP (1/2 CUP)           C-Smith2
126...    D          2014-05-05... 20         2:00PM - Juice, Orange (4 oz)            A-Barron
167...    F          2015-09-23... 30         Regular to Mech. Soft/Ground Meat        C-Palacios
246...    A          2015-12-21... 30         Regular to Pureed                        C-Carraway
248...    W          2015-02-05... 30         Regular to Mech Soft                     D-Kesvormas
...

Scanning For Resident

ID        ResidentID ChangeDate    ChangeType ChangeDetails                            UserName
--------- ---------- ------------- ---------- ---------------------------------------- --------------

001...    X          2016-02-13... 20         Lunch Tray - Cranberry Juice (4 oz)      S-Hampton

008...    A          2014-02-11... 10         NPO to NAS                               E-Marshal

01C...    M          2015-04-02... 10         General to NCS/NAS                       J-Yates

054...    X          2016-02-29... 20         TID - HOUSE SUP 2.0 - 120CC (120CC)      J-DeMouchette

083...    A          2016-02-13... 20         Breakfast Tray - Juice of the Day (4oz)  D-Wojtalik

0FC...    C          2015-02-25... 20         HS SNACK - FRUIT CUP (1/2 CUP)           C-Smith2

126...    D          2014-05-05... 20         2:00PM - Juice, Orange (4 oz)            A-Barron

167...    F          2015-09-23... 30         Regular to Mech. Soft/Ground Meat        C-Palacios

246...    A          2015-12-21... 30         Regular to Pureed                        C-Carraway

248...    W          2015-02-05... 30         Regular to Mech Soft                     D-Kesvormas

Find all changes to Resident A in the past year

SELECT * FROM ResidentChangeLog
WHERE ResidentID='A' AND ChangeDate > '2015-06-16'

+300,000 more records...

We need to read the whole table

(This is called a clustered index scan - we'll get back to that.)

Worse yet, concurrency...

SQL Server uses two-phase read-write locks

  • Before a transaction begins, locks are acquired for needed blocks of data
  • When all locks are acquired, the works begins
  • When work completed, all locks are released
  • When reading data, a read lock is taken out
    • Multiple read locks can be on the same resource simultaneously
  • When writing data, a write lock is taken out
    • Only one write lock can be issued and it must be exclusive (even for read locks)
  • When there is a locking conflict, a deadlock is issued and one transaction is terminated

Solution: Order the data in a way that you don't need to read the whole table.

(The order of the table is defined by its clustered index.)

(By default, the clustered index is the primary key.)

ID        ResidentID ChangeDate    ChangeType ChangeDetails                            UserName
--------- ---------- ------------- ---------- ---------------------------------------- --------------

008...    A          2014-02-11... 10         NPO to NAS                               E-Marshal

246...    A          2015-12-21... 30         Regular to Pureed                        C-Carraway

083...    A          2016-02-13... 20         Breakfast Tray - Juice of the Day (4oz)  D-Wojtalik

0FC...    C          2015-02-25... 20         HS SNACK - FRUIT CUP (1/2 CUP)           C-Smith2

126...    D          2014-05-05... 20         2:00PM - Juice, Orange (4 oz)            A-Barron

167...    F          2015-09-23... 30         Regular to Mech. Soft/Ground Meat        C-Palacios

01C...    M          2015-04-02... 10         General to NCS/NAS                       J-Yates

248...    W          2015-02-05... 30         Regular to Mech Soft                     D-Kesvormas
CREATE CLUSTERED INDEX IX_ResidentChangeLog ON ResidentChangeLog (ResidentID, ChangeDate, ID)

A-W

A-C

A

A-C

D-W

D-F

M-W

Remaining 300,000 rows...

Clustered Index Seek

Limitations

  • Only one clustered index per table
    • (This should be clear because it's literally the order of the table itself)
  • The RowID is the set of clustered index values
    • Plus also a long random number if the PK of the table isn't included in the clustered index

Non-clustered indexes

  • Extra structure that links to the RowID
  • Automatically changed on UPDATE/INSERT/DELETE
  • Unlimited number
  • Included columns

Questions / Comments

DB Optimization

By seanm

DB Optimization

  • 791