DarlingData
CLAUDE.md730 starsChanged 2 months agoMITView on GitHub
This document outlines the T-SQL coding style preferences for Erik Darling (Darling Data, LLC) and must be strictly followed when writing or modifying SQL code.
In this repository2 other files it gives its agents.
- darlingdata-releaseSkill
- review-sqlSkill
The file
Show its rules and text
We split this CLAUDE.md into the rules it gives an agent
44 rules. About 0 are common, 40 are distinctive, and 4 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈40
Distinctive · estimate
This author's own choices. Worth a look.
4
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- could be automatic: a setting could enforce it
- Distinctive: line 7
No Additional Dependencies: Do not create helper functions, procedures, views, or any other database objects as dependencies for stored …
- Distinctive: line 11
Keywords: All SQL keywords in UPPERCASE (SELECT, FROM, WHERE, JOIN, etc.)
- Very specific: line 12
Functions: All SQL functions in UPPERCASE (CONVERT, ISNULL, OBJECT_ID, etc.)
- Distinctive: line 14
Never abbreviate data types (use INTEGER instead of INT)
- Distinctive: line 15
All data types must be lowercase (varchar, nvarchar, datetime2, bigint, etc.)
- Distinctive: line 18
Always use sysname for SQL Server object names (database names, table names, schema names, column names, index names, etc.) rather than …
- Distinctive: line 19
Keywords: Never abbreviate keywords (use EXECUTE instead of EXEC, TRANSACTION instead of TRAN, PROCEDURE instead of PROC)
- Distinctive: line 24
Quotes: Use single quotes for string literals and N-prefix for Unicode strings (N'string')
- Distinctive: line 25
TOP syntax: Always include parentheses, as in TOP (100) not TOP 100
- Distinctive: line 28
Column references: Always qualify columns with their table alias
- Distinctive: line 33
Always use block comments with /* ...
- Distinctive: line 35
Use ASCII art for header blocks to visually distinguish sections
- Distinctive: line 38
Use comments to describe:
- Distinctive: line 51
Objects: Use clear, descriptive names
- Distinctive: line 63
Always terminate queries with a semicolon
- Distinctive: line 66
Always use schema prefixes for all objects except temporary objects
- Distinctive: line 71
Always use the AS keyword with table aliases: table_name AS alias
- Distinctive: line 90
Use modern ANSI JOIN syntax (JOIN table ON condition)
- Distinctive: line 127
Place on new lines with proper indentation
- Distinctive: line 214
CREATE TABLE on first line
- Distinctive: line 218
Always specify NULL or NOT NULL constraint for each column
- Distinctive: line 219
DEFAULT constraints can generally follow other column descriptors on the same line
- Distinctive: line 263
Always use INSERT INTO
- Distinctive: line 281
Use TABLOCK hint with temporary table inserts
- Distinctive: line 293
UPDATE on first line
- Distinctive: line 308
DELETE on first line
- Distinctive: line 321
Use extra parentheses for clarity in complex expressions
- Distinctive: line 333
Place parameters on subsequent lines with proper indentation
- Distinctive: line 352
CREATE/ALTER statements separated with GO
- Distinctive: line 373
IF/ELSE blocks should be formatted with BEGIN/END on their own lines:
- Distinctive: line 465
Always use IS NULL / IS NOT NULL for NULL comparisons, never = NULL or != NULL
- Distinctive: line 466
Use ISNULL() function for value replacement
- Distinctive: line 468
Use RAISERROR with NOWAIT for immediate message display
- Distinctive: line 470
Always validate user inputs before using them
- Distinctive: line 471
Use semicolons at the end of statements (but only at the very end, after any query hints)
- Very specific: line 473
Always use ROWCOUNT_BIG() instead of @@ROWCOUNT
- Very specific: line 474
Always use COUNT_BIG() instead of COUNT() to avoid potential integer overflow
- Very specific: line 477
Always use CONVERT over CAST for data type conversions (except when using TRY_CAST, as TRY_CAST isn't dependent on SQL Server version)
- Distinctive: line 478
Use XML for string splitting and string building (concatenation), as these methods aren't dependent on SQL Server version or database …
- Distinctive: line 479
Always use cursor variables instead of normal cursors, as they don't require explicit CLOSE/DEALLOCATE statements
- Distinctive: line 480
Do not use MERGE statements unless absolutely necessary for functional reasons
- Distinctive: line 481
Prefer temporary tables over table variables for performance reasons, especially when the data will be used in joins
- Distinctive: line 483
Do not drop temporary tables at the end of stored procedures (they're automatically cleaned up when the procedure exits)
- Distinctive: line 484
Prefer + operator for string concatenation as it's not version dependent (though CONCAT is acceptable for SQL Server 2012+)
≈ “Common” is an estimate: at least 1 in 5 of the 26,980 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Erik Darling's T-SQL Coding Style Guide
This document outlines the T-SQL coding style preferences for Erik Darling (Darling Data, LLC) and must be strictly followed when writing or modifying SQL code.
Project Constraints
- No Additional Dependencies: Do not create helper functions, procedures, views, or any other database objects as dependencies for stored procedures. All logic must be self-contained within the procedure itself to avoid deployment and dependency management complexity.
General Formatting
- Keywords: All SQL keywords in UPPERCASE (SELECT, FROM, WHERE, JOIN, etc.)
- Functions: All SQL functions in UPPERCASE (CONVERT, ISNULL, OBJECT_ID, etc.)
- Data types:
- Never abbreviate data types (use INTEGER instead of INT)
- All data types must be lowercase (varchar, nvarchar, datetime2, bigint, etc.)
- Length specifications must also be lowercase: nvarchar(max), not nvarchar(MAX)
- Precision and scale specifications must be lowercase: decimal(38,2), not DECIMAL(38,2)
- Always use sysname for SQL Server object names (database names, table names, schema names, column names, index names, etc.) rather than nvarchar(128)
- Keywords: Never abbreviate keywords (use EXECUTE instead of EXEC, TRANSACTION instead of TRAN, PROCEDURE instead of PROC)
- Indentation: 4 spaces for each level of indentation (NEVER use tabs)
- Line breaks: Each statement on a new line
- Spacing: Consistent spacing around operators (=, <, >, etc.)
- Block separation: Empty line between logical code blocks (maximum of two empty lines between statements)
- Quotes: Use single quotes for string literals and N-prefix for Unicode strings (N'string')
- TOP syntax: Always include parentheses, as in TOP (100) not TOP 100
- Object creation: Generally use CREATE OR ALTER for objects instead of DROP/CREATE
- Table aliases: Tables must always have aliases, even in simple queries
- Column references: Always qualify columns with their table alias
- Commas: Trailing commas always.
Comments
- Always use block comments with /* ... */ for most comments, never use double dash (--)
- Include parameter descriptions as inline comments after parameter definitions
- Use ASCII art for header blocks to visually distinguish sections
- Include copyright and attribution information in header comments
- Prefix code sections with descriptive comments about what the section does
- Use comments to describe:
- New code blocks
- Complex expressions
- Table purposes
- Complex logic
- The logical flow of code
Naming Conventions
- Parameters: Prefixed with @ and use snake_case (@database_name, @debug)
- Variables: Same as parameters (@database_id, @sql)
- Temporary Tables: Prefixed with # and use descriptive snake_case (#filtered_objects)
- Aliases: Short, meaningful lowercase names (ap, o, t)
- Objects: Use clear, descriptive names
Query Structure
- SELECT statements:
- SELECT keyword on first line
- Column list starts on next line, indented four spaces
- Trailing commas for multi-line column lists
- Columns aligned vertically for readability
- FROM clause on new line at same indent level as SELECT
- Column aliases should always use the pattern: column_name = column_expression
- Example: some_date = DATEADD(DAY, 1, GETDATE())
- Always terminate queries with a semicolon
- Table references:
- Always use schema prefixes for all objects except temporary objects
- Examples: FROM dbo.objects, FROM tempdb.dbo.objects
- Temporary tables don't need schema: FROM #temp_table
- Table aliases:
- Always use the AS keyword with table aliases: table_name AS alias
- Example: FROM dbo.sys_objects AS o
- Windowing functions:
- Format with OVER on same line as function
- PARTITION BY and ORDER BY on separate lines indented
- Parentheses on their own lines
SELECT n = ROW_NUMBER() OVER ( PARTITION BY column_name ORDER BY other_column ) - JOIN syntax:
- Use modern ANSI JOIN syntax (JOIN table ON condition)
- JOIN keyword on new line at same indent level as FROM
- ON conditions indented from JOIN
- JOIN conditions with AND should be aligned like this:
FROM dbo.table_a AS a0 JOIN dbo.table_a AS a1 ON a0.col = a1.col AND a0.col = a1.col- For correlated queries and joins, the table most recently referenced should come first in the ON clause:
FROM first_table AS ft0 JOIN dbo.first_table AS ft1 ON ft1.col = ft0.col - Clauses:
- GROUP BY, ORDER BY, and HAVING clauses should always begin on a new line, indented four spaces from the main statement
- WHERE clauses with AND conditions should be formatted with AND aligned:
WHERE a.col = 1 AND b.col = 2- EXISTS and NOT EXISTS should use this format with 1/0 in the SELECT:
WHERE EXISTS ( SELECT 1/0 FROM other_table AS ot WHERE ot.col = t.col ) - Subqueries:
- Subqueries should never be one-liners
- Place on new lines with proper indentation
SELECT column_name = ( SELECT column_name FROM dbo.table_name AS alias WHERE condition ) - APPLY operators:
- Format CROSS APPLY and OUTER APPLY with the query on new lines
FROM dbo.a_table AS y CROSS APPLY ( SELECT columns FROM dbo.table_name AS x WHERE x.col = y.col ) AS x - Set operations:
- UNION, INTERSECT, EXCEPT should have the operator between statements with blank lines
SELECT a.columns FROM dbo.a_table AS a EXCEPT SELECT b.columns FROM dbo.b_table AS b; - Table-valued constructors (VALUES):
- Format with VALUES on its own line, and value rows indented:
FROM ( VALUES (1, 2, 3) ) AS v (named_columns); - CTEs:
- WITH keyword on its own line
- CTE name indented on next line
- Opening parenthesis on same line as CTE name
- Column list indented on subsequent lines
- Closing parenthesis on its own line
- AS keyword on its own line
- Multiple CTEs separated by commas at the end
WITH database_stats ( database_name, recovery_model, log_size_mb ) AS ( SELECT database_name = d.name, recovery_model = d.recovery_model_desc, log_size_mb = SUM(f.size) * 8 / 1024 FROM sys.databases AS d JOIN sys.master_files AS f ON f.database_id = d.database_id GROUP BY d.name, d.recovery_model_desc ), second_cte ( column_list ) AS ( query ) - Table Creation:
- CREATE TABLE on first line
- Schema and table name on next line, indented
- Opening parenthesis on its own line
- Each column on a new line, indented
- Always specify NULL or NOT NULL constraint for each column
- DEFAULT constraints can generally follow other column descriptors on the same line
- Closing parenthesis on its own line
CREATE TABLE dbo.table_name ( column_name bigint NOT NULL, another_column varchar(50) NULL DEFAULT 'value', third_column datetime2(7) NOT NULL DEFAULT SYSDATETIME() ); - Index Creation:
- For multi-column indexes, format with columns on new lines:
CREATE INDEX index_name ON dbo.table_name ( column1, column2 ) INCLUDE ( column3, column4 ) WITH (options);- For single-column indexes, a more compact format is acceptable:
CREATE INDEX index_name ON dbo.table_name (column1) INCLUDE (column3) WITH (options); - INSERT statements:
- INSERT on first line
- Always use INSERT INTO
- Schema and table name on next line, indented
- Column list in parentheses on new lines, indented
INSERT INTO dbo.table_name ( column1, column2 ) VALUES ( value1, value2 ); - Temporary table inserts:
- Use TABLOCK hint with temporary table inserts
INSERT #table_name WITH (TABLOCK) ( column_list ) - UPDATE statements:
- UPDATE on first line
- Table alias on next line, indented
- SET on its own line with same indentation as alias
- FROM clause on its own line
UPDATE alias SET alias.col1 = value1, alias.col2 = value2 FROM dbo.table AS alias WHERE alias.condition; - DELETE statements:
- DELETE on first line
- Table alias on next line, indented
- FROM clause on its own line
DELETE alias FROM dbo.table AS alias WHERE alias.condition; - Parentheses:
- Opening parenthesis on same line as function/procedure name
- Closing parenthesis aligned with starting line or on its own line for long expressions
- Use extra parentheses for clarity in complex expressions
- Function arguments should be indented four spaces and on new lines:
CONVERT ( data_type, value ) - Multi-parameter functions:
- For functions with multiple parameters or complex expressions, format the function name on its own line
- Place parameters on subsequent lines with proper indentation
SELECT formatted_date = DATEFROMPARTS ( YEAR(date_column), MONTH(date_column), 1 )
Code Organization
- SET statements grouped at procedure start
- Validation checks before main logic
- Help/documentation sections clearly separated from main logic
- Version information tracked explicitly
- Parameter validation at beginning of procedures
- CREATE/ALTER statements separated with GO
Code Blocks and Control Structures
- BEGIN/END contents should be indented four spaces:
BEGIN /*logic*/ END; - CASE expression contents should be indented, with each condition on a new line:
CASE WHEN thing AND other_thing THEN stuff ELSE result END - IF/ELSE blocks should be formatted with BEGIN/END on their own lines:
IF condition BEGIN logic END; ELSE BEGIN logic END; - WHILE loops should follow similar formatting:
WHILE condition BEGIN work END; - Error handling should follow this template:
BEGIN BEGIN TRY do stuff END TRY BEGIN CATCH IF @@TRANCOUNT > 0 BEGIN ROLLBACK; END; THROW; END CATCH; END; - DECLARE blocks should put everything on a new line:
DECLARE @t1 integer, @t2 integer; - Variables should be declared and initialized together for static values:
DECLARE @t1 integer = 1, @t2 integer = 2;- Take care when initializing to ensure you don't introduce logical flaws with NULL checks
- Dynamic SQL should follow specific formatting:
- Initial declaration with empty string
- Each string concatenation part on its own line
- Each QUOTENAME or variable reference on its own line
DECLARE @sql nvarchar(max) = N'' SET @sql += N'
SELECT column_name = value ' + QUOTENAME(alias.object_name) + N' FROM table_name ';
EXECUTE sys.sp_executesql @sql, N'@parameters', @input;
- Transaction blocks should use consistent indentation:
```sql
BEGIN TRANSACTION
work
COMMIT TRANSACTION;- XML and JSON output should be formatted with each option on a new line:
FOR XML PATH TYPE
SQL Best Practices
- Always use IS NULL / IS NOT NULL for NULL comparisons, never = NULL or != NULL
- Use ISNULL() function for value replacement
- Include RECOMPILE hints for procedures with variable data distributions
- Use RAISERROR with NOWAIT for immediate message display
- Include thorough error handling with BEGIN TRY/CATCH blocks
- Always validate user inputs before using them
- Use semicolons at the end of statements (but only at the very end, after any query hints)
- Apply query hints consistently (RECOMPILE, MAXDOP, etc.)
- Always use ROWCOUNT_BIG() instead of @@ROWCOUNT
- Always use COUNT_BIG() instead of COUNT() to avoid potential integer overflow
- Example:
COUNT_BIG(i.index_id)notCOUNT(i.index_id) - Even if the result will never be large enough to overflow, use COUNT_BIG() for consistency
- Example:
- Always use CONVERT over CAST for data type conversions (except when using TRY_CAST, as TRY_CAST isn't dependent on SQL Server version)
- Use XML for string splitting and string building (concatenation), as these methods aren't dependent on SQL Server version or database compatibility level
- Always use cursor variables instead of normal cursors, as they don't require explicit CLOSE/DEALLOCATE statements
- Do not use MERGE statements unless absolutely necessary for functional reasons
- Prefer temporary tables over table variables for performance reasons, especially when the data will be used in joins
- Table variables are acceptable for situations where contents are not used relationally or when insert performance is critical
- Do not drop temporary tables at the end of stored procedures (they're automatically cleaned up when the procedure exits)
- Prefer + operator for string concatenation as it's not version dependent (though CONCAT is acceptable for SQL Server 2012+)
- FORMAT is preferred for adding commas to numbers, but complex CONVERT to money with substring operations is also acceptable
- Date literals should always follow yyyymmdd format (e.g., 20250101), with additional precision as needed for the data type
Stored Procedure Structure
- SET configuration statements at the top
- Procedure declaration (CREATE/ALTER)
- Parameter definitions with inline comments
- BEGIN block
- SET NOCOUNT ON and other session settings
- Variable declarations
- Validation checks
- Help section (@help = 1)
- Main processing logic
- Error handling
- Cleanup
Basic stored procedure outline:
CREATE OR ALTER PROCEDURE
dbo.procedure_name
(
@parameter_list
)
AS
BEGIN
SET NOCOUNT, XACT_ABORT ON;
queries...
END;Trigger template:
CREATE OR ALTER TRIGGER
dbo.a_trigger
ON dbo.a_table
AFTER/INSTEAD OF
AS
BEGIN
IF ROWCOUNT_BIG() = 0
BEGIN
RETURN
END;
work
END;View template:
CREATE OR ALTER VIEW
dbo.a_view
AS
SELECT
column1 = t.column1,
column2 = t.column2
FROM dbo.table AS t
WHERE t.condition = 1;Function template:
CREATE OR ALTER FUNCTION
dbo.a_function
(
@parameter1 integer,
@parameter2 varchar(50)
)
RETURNS data_type
AS
BEGIN
RETURN value;
END;Examples
Complex SELECT with Multiple JOINs, GROUP BY, and HAVING
SELECT
database_name = d.name,
index_count = COUNT_BIG(i.index_id),
total_size_mb = SUM(a.total_pages) * 8 / 1024,
read_operations = SUM(ius.user_seeks + ius.user_scans + ius.user_lookups),
write_operations = SUM(ius.user_updates),
avg_fragmentation = AVG(ps.avg_fragmentation_in_percent)
FROM sys.databases AS d
JOIN sys.tables AS t
ON t.database_id = d.database_id
LEFT JOIN sys.indexes AS i
ON i.object_id = t.object_id
AND i.index_id > 0
AND i.is_disabled = 0
LEFT JOIN sys.dm_db_index_usage_stats AS ius
ON ius.database_id = d.database_id
AND ius.object_id = i.object_id
AND ius.index_id = i.index_id
LEFT JOIN sys.dm_db_index_physical_stats
(
DB_ID(),
NULL,
NULL,
NULL,
'LIMITED'
) AS ps
ON ps.object_id = i.object_id
AND ps.index_id = i.index_id
LEFT JOIN sys.allocation_units AS a
ON a.container_id = i.hobt_id
WHERE d.database_id > 4
AND d.is_read_only = 0
AND d.state_desc = N'ONLINE'
GROUP BY
d.name,
d.create_date
HAVING
COUNT(i.index_id) > 10
ORDER BY
total_size_mb DESC,
database_name ASC
OPTION(MAXDOP 1, RECOMPILE);CTE with Multiple Definitions and Nested Queries
WITH
database_stats
(
database_name,
recovery_model,
log_size_mb,
log_used_percent
) AS
(
SELECT
database_name = d.name,
recovery_model = d.recovery_model_desc,
log_size_mb = SUM(CASE WHEN f.type_desc = N'LOG' THEN f.size END) * 8 / 1024,
log_used_percent = SUM(CASE WHEN f.type_desc = N'LOG' THEN CONVERT(decimal(19,2), fileproperty(f.name, 'SpaceUsed')) / f.size * 100 END)
FROM sys.databases AS d
JOIN sys.master_files AS f
ON f.database_id = d.database_id
WHERE d.state_desc = N'ONLINE'
GROUP BY
d.name,
d.recovery_model_desc
),
database_backups AS
(
SELECT
database_name = b.database_name,
last_full_backup = MAX(CASE WHEN b.type = 'D' THEN b.backup_finish_date END),
last_log_backup = MAX(CASE WHEN b.type = 'L' THEN b.backup_finish_date END)
FROM msdb.dbo.backupset AS b
WHERE b.backup_finish_date > DATEADD(DAY, -7, GETDATE())
GROUP BY
b.database_name
)
SELECT
ds.database_name,
ds.recovery_model,
ds.log_size_mb,
ds.log_used_percent,
days_since_full_backup =
CASE
WHEN db.last_full_backup IS NULL
THEN 999
ELSE DATEDIFF(DAY, db.last_full_backup, GETDATE())
END,
days_since_log_backup =
CASE
WHEN db.last_log_backup IS NULL
THEN 999
ELSE DATEDIFF(DAY, db.last_log_backup, GETDATE())
END
FROM database_stats AS ds
LEFT JOIN database_backups AS db
ON db.database_name = ds.database_name
WHERE ds.log_size_mb > 100
ORDER BY
log_size_mb DESC;Dynamic SQL Generation and Execution
DECLARE
@database_name sysname = N'AdventureWorks',
@table_name sysname = N'SalesOrderHeader',
@column_name sysname = N'OrderDate',
@sql nvarchar(max) = N'';
/*
Build query dynamically using proper quoting and formatting
*/
SET @sql = N'
SELECT
order_month =
DATEFROMPARTS
(
YEAR
(t.' +
QUOTENAME(@column_name) +
N'),
MONTH
(t.' +
QUOTENAME(@column_name) +
N'),
1
),
order_count = COUNT_BIG(*),
total_amount = SUM(t.TotalDue),
avg_amount = AVG(t.TotalDue)
FROM ' + QUOTENAME(@database_name) + N'.dbo.' + QUOTENAME(@table_name) + N' AS t
WHERE ' + QUOTENAME(@column_name) + N' >= DATEADD(YEAR, -1, GETDATE())
GROUP BY
DATEFROMPARTS
(
YEAR
(t.' +
QUOTENAME(@column_name) +
N'),
MONTH
(t.' +
QUOTENAME(@column_name) +
N'),
1
)
ORDER BY
order_month;
';
/*
Execute the dynamic SQL with proper parameter passing
*/
EXECUTE sys.sp_executesql
@sql,
N'',
N'';Stored Procedure with Temp Tables and Flow Control
SET ANSI_NULLS ON;
SET QUOTED_IDENTIFIER ON;
GO
IF OBJECT_ID(N'dbo.sp_MyProcedure', N'P') IS NULL
BEGIN
EXECUTE(N'CREATE PROCEDURE dbo.sp_MyProcedure AS RETURN 138;');
END;
GO
ALTER PROCEDURE
dbo.sp_MyProcedure
(
@database_name sysname = NULL, /*the database to analyze*/
@days_back integer = 7, /*how many days of history to analyze*/
@threshold_percent integer = 20, /*minimum percentage change to report*/
@debug bit = 0, /*prints additional diagnostic information*/
@help bit = 0 /*prints help information*/
)
WITH RECOMPILE
AS
BEGIN
SET NOCOUNT ON;
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
BEGIN TRY
/*
Variable declarations
*/
DECLARE
@sql nvarchar(max) = N'',
@database_id integer = NULL,
@start_date datetime2(7) = DATEADD(DAY, -@days_back, GETDATE()),
@error_msg nvarchar(2048) = N'';
/*
Parameter validation
*/
IF @database_name IS NULL
BEGIN
SELECT
@database_name = DB_NAME();
END;
IF @threshold_percent <= 0 OR @threshold_percent > 100
BEGIN
SELECT
@error_msg = N'@threshold_percent must be between 1 and 100.';
RAISERROR(@error_msg, 16, 1);
RETURN;
END;
/*
Help section
*/
IF @help = 1
BEGIN
SELECT
help = N'This procedure analyzes database performance changes';
RETURN;
END;
/*
Create temp tables for analysis
*/
CREATE TABLE
#baseline_metrics
(
object_id bigint NOT NULL,
metric_name varchar(50) NOT NULL,
metric_value decimal(38,2) NOT NULL
);
CREATE TABLE
#current_metrics
(
object_id bigint NOT NULL,
metric_name varchar(50) NOT NULL,
metric_value decimal(38,2) NOT NULL
);
/*
Populate baseline data
*/
INSERT
#baseline_metrics
WITH
(TABLOCK)
(
object_id,
metric_name,
metric_value
)
SELECT
object_id = t.object_id,
metric_name = 'query_cost',
metric_value = AVG(qs.total_elapsed_time / 1000.0)
FROM sys.dm_exec_query_stats AS qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS t
WHERE qs.creation_time < @start_date
AND t.dbid = DB_ID(@database_name)
GROUP BY
t.object_id
OPTION(RECOMPILE);
IF @debug = 1
BEGIN
SELECT
baseline_rows = COUNT(*)
FROM #baseline_metrics;
END;
/*
Main processing logic - analyze changes
*/
SELECT
object_name = o.name,
schema_name = s.name,
b.metric_name,
baseline_value = b.metric_value,
current_value = c.metric_value,
percent_change =
CASE
WHEN b.metric_value = 0
THEN NULL
ELSE (c.metric_value - b.metric_value) / b.metric_value * 100
END
FROM #baseline_metrics AS b
JOIN #current_metrics AS c
ON c.object_id = b.object_id
AND c.metric_name = b.metric_name
JOIN sys.objects AS o
ON o.object_id = b.object_id
JOIN sys.schemas AS s
ON s.schema_id = o.schema_id
WHERE ABS((c.metric_value - b.metric_value) / NULLIF(b.metric_value, 0) * 100) >= @threshold_percent
ORDER BY
ABS((c.metric_value - b.metric_value) / NULLIF(b.metric_value, 0) * 100) DESC;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
BEGIN
ROLLBACK;
END;
THROW;
END CATCH;
END;
GOThis style guide is based on an analysis of Erik Darling's stored procedures from Darling Data, LLC.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

