<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>James Bryan — posts</title>
    <link href="https://jamesabryan.com/feed.xml" rel="self"/>
    <link href="https://jamesabryan.com/"/>
    <updated>2025-11-16T00:00:00.000Z</updated>
    <id>https://jamesabryan.com/</id>
    <author><name>James A. Bryan</name></author>
    <entry>
        <title>Why Your EZ-CAP Database Is Slow (And It&#39;s Not Because You Need More Indexes)</title>
        <link href="https://jamesabryan.com/posts/ez-cap-database-overindexing/"/>
        <updated>2025-11-16T00:00:00.000Z</updated>
        <id>https://jamesabryan.com/posts/ez-cap-database-overindexing/</id>
        <summary>The counter-intuitive truth about database performance: how removing indexes improved ETL processing by 40%. Real-world strategies for identifying and fixing over-indexing in EZ-CAP systems.</summary>
        <content type="html">&lt;p&gt;Every database analyst has heard this mantra: &amp;quot;Slow query? Add an index.&amp;quot; But what if I told you that removing indexes—not adding them—improved one of my production ETL processes by 40%? Here&#39;s what 15 years of healthcare database work taught me about the hidden costs of over-indexing.&lt;/p&gt;
&lt;h2&gt;The Problem Nobody Talks About&lt;/h2&gt;
&lt;p&gt;When a query runs slowly, the first instinct is always to add an index. And yes, proper indexing is critical for read performance. But here&#39;s what many database analysts miss: &lt;strong&gt;every index you add creates maintenance overhead&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In EZ-CAP systems handling claims data, submissions data, and member information, we&#39;re constantly writing to tables. Every INSERT, UPDATE, and DELETE doesn&#39;t just modify the table—it has to update every index on that table too.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The reality:&lt;/strong&gt; More indexes = faster SELECT queries but slower INSERT/UPDATE/DELETE operations. In ETL-heavy healthcare environments, this trade-off often works against you.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;The Wake-Up Call: A Real Production Example&lt;/h2&gt;
&lt;p&gt;I was working with a health plan&#39;s claims processing database. Their nightly ETL jobs were taking progressively longer—what started as a 2-hour process had stretched to over 3 hours. The data team had been adding indexes every time someone complained about a slow report, and now the main claims table had &lt;strong&gt;47 indexes&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Yes, 47.&lt;/p&gt;
&lt;p&gt;The symptoms were classic over-indexing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ETL processes taking longer despite no increase in data volume&lt;/li&gt;
&lt;li&gt;Transaction log growing faster than expected&lt;/li&gt;
&lt;li&gt;Increased CPU usage during data loads&lt;/li&gt;
&lt;li&gt;Complaints about &amp;quot;the database being slow&amp;quot; from multiple departments&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Finding the Culprits: Identifying Unused Indexes&lt;/h2&gt;
&lt;p&gt;SQL Server tracks index usage through DMVs (Dynamic Management Views). Here&#39;s the query I use to identify indexes that are costing more than they&#39;re worth:&lt;/p&gt;
&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;-- Find indexes with high writes but low reads&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt;
    OBJECT_NAME&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;object_id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;AS&lt;/span&gt; TableName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    i&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token keyword&quot;&gt;AS&lt;/span&gt; IndexName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    i&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type_desc &lt;span class=&quot;token keyword&quot;&gt;AS&lt;/span&gt; IndexType&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;user_seeks&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;user_scans&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;user_lookups&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;user_updates&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;user_updates &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;user_seeks &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;user_scans &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;user_lookups&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;AS&lt;/span&gt; WriteToReadRatio
&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; sys&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dm_db_index_usage_stats s
&lt;span class=&quot;token keyword&quot;&gt;INNER&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;JOIN&lt;/span&gt; sys&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;indexes i
    &lt;span class=&quot;token keyword&quot;&gt;ON&lt;/span&gt; s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;object_id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;object_id
    &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;index_id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;index_id
&lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; OBJECTPROPERTY&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;object_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;IsUserTable&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;database_id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; DB_ID&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type_desc &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HEAP&#39;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;BY&lt;/span&gt; s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;user_updates &lt;span class=&quot;token keyword&quot;&gt;DESC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This query reveals indexes that are being updated constantly but rarely used for queries. In the claims table I mentioned, we found:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;12 indexes that had &lt;strong&gt;never been used&lt;/strong&gt; for any query since the last server restart&lt;/li&gt;
&lt;li&gt;8 indexes with a write-to-read ratio above 100:1 (being updated 100+ times for every query that used them)&lt;/li&gt;
&lt;li&gt;Multiple redundant indexes covering nearly identical columns&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The Fix: Strategic Index Removal&lt;/h2&gt;
&lt;p&gt;I didn&#39;t just drop indexes randomly. Here was my process:&lt;/p&gt;
&lt;h3&gt;Step 1: Document Everything&lt;/h3&gt;
&lt;p&gt;Before touching anything, I scripted out all existing indexes. This gave us a rollback plan if needed.&lt;/p&gt;
&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;-- Script out all indexes before making changes&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&#39;CREATE &#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;CASE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;WHEN&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;is_unique &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;UNIQUE &#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;ELSE&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;
    i&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type_desc &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39; INDEX [&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;] ON [&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;
    SCHEMA_NAME&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;t&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;schema_id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;].[&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; t&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;] (&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;
    STUFF&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;, [&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;]&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;CASE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;WHEN&lt;/span&gt; ic&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;is_descending_key &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39; DESC&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;ELSE&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39; ASC&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;END&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; sys&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;index_columns ic
        &lt;span class=&quot;token keyword&quot;&gt;INNER&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;JOIN&lt;/span&gt; sys&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;columns&lt;/span&gt; c &lt;span class=&quot;token keyword&quot;&gt;ON&lt;/span&gt; ic&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;object_id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;object_id
            &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; ic&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;column_id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;column_id
        &lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; ic&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;object_id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;object_id &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; ic&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;index_id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;index_id
        &lt;span class=&quot;token keyword&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;BY&lt;/span&gt; ic&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;key_ordinal
        &lt;span class=&quot;token keyword&quot;&gt;FOR&lt;/span&gt; XML PATH&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;);&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;AS&lt;/span&gt; CreateIndexScript
&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; sys&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;indexes i
&lt;span class=&quot;token keyword&quot;&gt;INNER&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;JOIN&lt;/span&gt; sys&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;tables&lt;/span&gt; t &lt;span class=&quot;token keyword&quot;&gt;ON&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;object_id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; t&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;object_id
&lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;-- Exclude heaps&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; t&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;YourTableName&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Step 2: Start with Clear Wins&lt;/h3&gt;
&lt;p&gt;We removed indexes that were:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Never used (0 seeks, 0 scans, 0 lookups)&lt;/li&gt;
&lt;li&gt;Completely redundant (exact duplicates)&lt;/li&gt;
&lt;li&gt;Clearly abandoned (old date range filters no longer relevant)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Step 3: Test Before Full Rollout&lt;/h3&gt;
&lt;p&gt;Each removal was tested in our staging environment first, monitoring query performance and ETL execution times.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Critical Note:&lt;/strong&gt; Don&#39;t drop indexes in production without testing. Some indexes might be used by poorly-documented stored procedures or third-party reporting tools. Always check execution plans and monitor for a few days in lower environments first.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;The Results&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;40% faster ETL processing&lt;/strong&gt; — the nightly claims load dropped from 3+ hours back to under 2 hours.&lt;/p&gt;
&lt;p&gt;After removing 23 unused or redundant indexes, here&#39;s what improved:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ETL performance:&lt;/strong&gt; 40% reduction in processing time&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Transaction log:&lt;/strong&gt; 25% less log file growth&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Storage:&lt;/strong&gt; 15GB freed up (indexes consume space too)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CPU usage:&lt;/strong&gt; Noticeably lower during peak data loading times&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And here&#39;s the kicker: &lt;strong&gt;no queries got slower&lt;/strong&gt;. Not one. Because we were removing indexes that weren&#39;t being used anyway.&lt;/p&gt;
&lt;h2&gt;When Over-Indexing Happens&lt;/h2&gt;
&lt;p&gt;How did this health plan end up with 47 indexes? It&#39;s surprisingly common in healthcare IT environments:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&amp;quot;Just in case&amp;quot; indexing:&lt;/strong&gt; Someone creates an index for a one-time report, then never removes it&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Panic indexing:&lt;/strong&gt; A critical report is slow, so an index gets added without analyzing if it&#39;s actually needed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Legacy accumulation:&lt;/strong&gt; EZ-CAP implementations evolve over years, and nobody ever audits old indexes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vendor recommendations:&lt;/strong&gt; Implementation guides suggest indexes that may not fit your specific workload&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Copy-paste solutions:&lt;/strong&gt; Indexes are copied from another environment without understanding the differences in query patterns&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Best Practices for Index Management&lt;/h2&gt;
&lt;p&gt;Here&#39;s how I approach indexing now, particularly for write-heavy EZ-CAP tables:&lt;/p&gt;
&lt;h3&gt;1. Start Conservative&lt;/h3&gt;
&lt;p&gt;Begin with minimal indexes (primary keys and critical foreign keys). Add more only when you have evidence they&#39;re needed.&lt;/p&gt;
&lt;h3&gt;2. Regular Index Audits&lt;/h3&gt;
&lt;p&gt;Schedule quarterly reviews of index usage. I set calendar reminders to run usage queries and identify candidates for removal.&lt;/p&gt;
&lt;h3&gt;3. Document Index Purpose&lt;/h3&gt;
&lt;p&gt;When creating an index, include a comment explaining WHY it exists:&lt;/p&gt;
&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;-- Index created for monthly HEDIS reporting queries&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;-- Used by sp_GetMemberUtilization stored procedure&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;-- Review if report is ever deprecated&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;NONCLUSTERED&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INDEX&lt;/span&gt; IX_Claims_MemberDate
    &lt;span class=&quot;token keyword&quot;&gt;ON&lt;/span&gt; Claims&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;MemberID&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ServiceDate&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4. Monitor Write vs. Read Ratios&lt;/h3&gt;
&lt;p&gt;If an index has a write-to-read ratio above 10:1, it&#39;s worth investigating whether it&#39;s providing enough value.&lt;/p&gt;
&lt;h3&gt;5. Consider Filtered Indexes&lt;/h3&gt;
&lt;p&gt;Instead of indexing an entire table, use filtered indexes for specific subsets of data that are frequently queried:&lt;/p&gt;
&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;-- Only index active claims, not the entire historical table&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;NONCLUSTERED&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INDEX&lt;/span&gt; IX_Claims_ActiveOnly
    &lt;span class=&quot;token keyword&quot;&gt;ON&lt;/span&gt; Claims&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ClaimID&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; MemberID&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; ClaimStatus &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Active&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Red Flags Your Database Might Be Over-Indexed&lt;/h2&gt;
&lt;p&gt;Watch for these warning signs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ETL jobs taking progressively longer despite stable data volumes&lt;/li&gt;
&lt;li&gt;High transaction log growth during data loads&lt;/li&gt;
&lt;li&gt;Increased CPU during INSERT/UPDATE operations&lt;/li&gt;
&lt;li&gt;More than 10-15 indexes on heavily-written tables&lt;/li&gt;
&lt;li&gt;Index names like &amp;quot;IX_temp&amp;quot; or &amp;quot;IX_test&amp;quot; still in production&lt;/li&gt;
&lt;li&gt;Multiple indexes with very similar column combinations&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The Balance&lt;/h2&gt;
&lt;p&gt;To be clear: &lt;strong&gt;I&#39;m not anti-index&lt;/strong&gt;. Proper indexing is essential for query performance. But like any tool, indexes need to be used strategically.&lt;/p&gt;
&lt;p&gt;In healthcare databases like EZ-CAP systems, where you&#39;re constantly processing claims submissions, eligibility updates, and encounter data, the write volume is often higher than in pure reporting databases. Every index you add slows down those write operations.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The principle:&lt;/strong&gt; Index for the workload you have, not the workload you imagine you might have someday. And regularly audit to remove indexes that aren&#39;t earning their keep.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Practical Takeaways&lt;/h2&gt;
&lt;p&gt;If your EZ-CAP database or healthcare data warehouse feels sluggish:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Run the index usage query above to identify unused indexes&lt;/li&gt;
&lt;li&gt;Document what you find—you&#39;ll need this for rollback if necessary&lt;/li&gt;
&lt;li&gt;Start with clear wins: remove indexes with zero usage&lt;/li&gt;
&lt;li&gt;Test in staging before touching production&lt;/li&gt;
&lt;li&gt;Monitor performance after changes&lt;/li&gt;
&lt;li&gt;Schedule regular audits going forward&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In my experience, most production databases have at least 10-20% of their indexes doing more harm than good. Finding and removing them isn&#39;t just about reclaiming storage—it&#39;s about letting your database breathe during those critical ETL windows.&lt;/p&gt;
&lt;p&gt;Sometimes the best optimization isn&#39;t adding something new. It&#39;s removing what you never needed in the first place.&lt;/p&gt;
</content>
    </entry>
    <entry>
        <title>What 15 Years as an EZ-CAP Specialist Taught Me About Healthcare Data Integration</title>
        <link href="https://jamesabryan.com/posts/ez-cap-data-integration-lessons/"/>
        <updated>2025-11-08T00:00:00.000Z</updated>
        <id>https://jamesabryan.com/posts/ez-cap-data-integration-lessons/</id>
        <summary>15 years of EZ-CAP healthcare data integration experience distilled into four critical lessons about technical translation, documentation, HEDIS compliance, and invisible infrastructure.</summary>
        <content type="html">&lt;p&gt;When a Claims Director tells you their monthly submission is &amp;quot;wrong,&amp;quot; they&#39;re not just pointing out a data issue—they&#39;re highlighting a potential compliance risk that could cost the organization millions.&lt;/p&gt;
&lt;p&gt;After 15 years working with EZ-CAP systems across multiple healthcare organizations—from supporting clients who use it, to working directly for healthcare plans, to managing the backend infrastructure—I&#39;ve learned that successful data integration isn&#39;t really about the technology. It&#39;s about understanding what the data means to each stakeholder.&lt;/p&gt;
&lt;p&gt;Here are four lessons that fundamentally changed how I approach healthcare IT:&lt;/p&gt;
&lt;h2&gt;Lesson 1: The Real Integration Challenge Isn&#39;t Technical—It&#39;s Translational&lt;/h2&gt;
&lt;p&gt;The hardest part of integrating EZ-CAP with third-party systems like PCG, DataWing, or Cozeva isn&#39;t writing the SQL queries or mapping the data fields. It&#39;s sitting in a room with Claims, Finance, and UM teams who all use different language to describe the same data.&lt;/p&gt;
&lt;p&gt;Claims talks about submission timelines and encounter data. Finance wants reconciliation and capitation tracking. UM needs utilization patterns and authorization workflows. They&#39;re all looking at the same database, but they need different stories told in different ways.&lt;/p&gt;
&lt;p&gt;I&#39;ve spent countless hours serving as a translator between technical teams and business stakeholders—including offshore development groups who brought their own communication challenges. The ability to break down complex technical concepts into clear, accessible explanations isn&#39;t just a nice-to-have skill. In healthcare IT, it&#39;s essential.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The lesson:&lt;/strong&gt; Your job isn&#39;t just to move data—it&#39;s to ensure everyone understands what that data is telling them, in the language they speak.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Lesson 2: Documentation Is Your Integration Insurance Policy&lt;/h2&gt;
&lt;p&gt;Early in my career, I built plenty of &amp;quot;quick fixes&amp;quot; that worked perfectly... until the person who requested them left the organization. Then suddenly, no one knew why that specific trigger existed, what business rule it was enforcing, or what would break if we removed it.&lt;/p&gt;
&lt;p&gt;Now, I approach every integration with the assumption that I won&#39;t be there to explain it. That means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Detailed process flows&lt;/strong&gt; — I swear by Microsoft Visio for creating visual documentation that both technical and non-technical stakeholders can understand&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Commented SQL code that explains the why, not just the what&lt;/strong&gt; — future developers need to understand the business logic, not just the syntax&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Clear handoff documentation for vendor coordination&lt;/strong&gt; — when you&#39;re managing integrations with multiple third parties, everyone needs to know who owns what&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Standard operating procedures that capture tribal knowledge&lt;/strong&gt; before it walks out the door&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;My graphic design background actually became a competitive advantage here. Good documentation isn&#39;t just accurate—it&#39;s visually clear and easy to follow.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The lesson:&lt;/strong&gt; If you can&#39;t document it clearly enough for someone else to maintain it, you haven&#39;t finished the job.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Lesson 3: HEDIS Measures Taught Me That &amp;quot;Close Enough&amp;quot; Doesn&#39;t Exist&lt;/h2&gt;
&lt;p&gt;There&#39;s no room for &amp;quot;approximately correct&amp;quot; when you&#39;re submitting HEDIS data. A decimal point in the wrong place, a date range off by one day, or a misunderstood exclusion criterion can invalidate months of work and directly impact Star Ratings—which affects funding, reputation, and member access to benefits.&lt;/p&gt;
&lt;p&gt;Working on monthly and quarterly HEDIS submissions taught me to build validation into every step of the process:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Automated checks that flag anomalies before they become problems&lt;/li&gt;
&lt;li&gt;Reconciliation queries that verify data integrity at each stage&lt;/li&gt;
&lt;li&gt;Secondary review processes that catch what automation might miss&lt;/li&gt;
&lt;li&gt;Clear audit trails that document every transformation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&#39;ve learned to treat every data point with the respect it deserves, because in healthcare, data isn&#39;t abstract—it represents real patients, real providers, and real compliance obligations.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The lesson:&lt;/strong&gt; In regulated healthcare environments, precision isn&#39;t perfectionism—it&#39;s professional responsibility.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Lesson 4: The Best Integrations Make Themselves Invisible&lt;/h2&gt;
&lt;p&gt;When I design automated SQL jobs or custom data loads, success means nobody notices they&#39;re running. The Claims team gets their reports on time. Finance sees accurate reconciliation. UM has the utilization data they need for decision-making. Data flows seamlessly from EZ-CAP to third-party applications and back.&lt;/p&gt;
&lt;p&gt;The moment an integration becomes visible is usually when something&#39;s gone wrong—a job fails, data doesn&#39;t match, or a vendor connection drops.&lt;/p&gt;
&lt;p&gt;Over the years, I&#39;ve designed and scheduled countless SQL jobs that automate repetitive tasks, optimize workflows, and enhance data management processes. The goal is always the same: create systems that work reliably in the background, freeing people to focus on using the data rather than chasing it.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The lesson:&lt;/strong&gt; Great healthcare IT infrastructure should fade into the background, reliably supporting the organization&#39;s mission without requiring constant attention.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Looking Forward&lt;/h2&gt;
&lt;p&gt;Healthcare data integration is becoming more complex, not less. More vendors, more regulatory requirements, more data sources to coordinate, and increasing expectations for real-time data access.&lt;/p&gt;
&lt;p&gt;But the fundamentals haven&#39;t changed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Understand your stakeholders and speak their language&lt;/li&gt;
&lt;li&gt;Document everything as if you won&#39;t be there to explain it&lt;/li&gt;
&lt;li&gt;Prioritize accuracy and compliance over speed&lt;/li&gt;
&lt;li&gt;Build systems that work quietly and reliably&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As someone who&#39;s been called the &amp;quot;go-to resource&amp;quot; for EZ-CAP-related queries, technical guidance, and troubleshooting, I&#39;ve seen firsthand how these principles create value. The best technical solutions are the ones that enable healthcare organizations to focus on their real mission: serving patients and members.&lt;/p&gt;
</content>
    </entry>
</feed>
