Skip to content

Commit 4357e9c

Browse files
authored
fix(schema): use COUNT(*) for EstimateRowCount to drop VIEW DATABASE PERFORMANCE STATE (#98)
Replace sys.dm_db_partition_stats in SourceTableChangeTrackingInfoQuery with a batch that inserts change-tracking metadata into #Base, then for each table runs COUNT(*) with NOLOCK via dynamic SQL and stores result in #Counts. Join #Base and #Counts for the final result. Tables without SELECT permission get NULL EstimateRowCount (TRY/CATCH). Exact row count, no VIEW DATABASE PERFORMANCE STATE; may be slower on very large tables.
1 parent 16ba70c commit 4357e9c

1 file changed

Lines changed: 51 additions & 9 deletions

File tree

src/SqlBulkSyncFunction/Helpers/SchemaExtensions.cs

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,27 +36,69 @@ FROM sys.change_tracking_databases
3636

3737
/// <summary>
3838
/// Query to retrieve change tracking metadata for all tracked tables in the database.
39+
/// Uses COUNT(*) per table so only SELECT on each table is required; no VIEW DATABASE PERFORMANCE STATE.
40+
/// EstimateRowCount is exact but may be slow on very large tables.
3941
/// </summary>
4042
public const string SourceTableChangeTrackingInfoQuery =
4143
"""
44+
CREATE TABLE #Base (
45+
TableObjectId INT NOT NULL,
46+
SchemaName SYSNAME NOT NULL,
47+
TableName SYSNAME NOT NULL,
48+
TrackColumnsUpdated BIT NOT NULL,
49+
MinValidVersion BIGINT NOT NULL,
50+
CurrentDatabaseVersion BIGINT NOT NULL
51+
);
52+
INSERT #Base
4253
SELECT
4354
t.object_id AS TableObjectId,
4455
s.name AS SchemaName,
4556
t.name AS TableName,
46-
CAST(ctt.is_track_columns_updated_on as bit) AS TrackColumnsUpdated,
57+
CAST(ctt.is_track_columns_updated_on AS bit) AS TrackColumnsUpdated,
4758
CHANGE_TRACKING_MIN_VALID_VERSION(t.object_id) AS MinValidVersion,
48-
CHANGE_TRACKING_CURRENT_VERSION() AS CurrentDatabaseVersion,
49-
rc.EstimateRowCount AS EstimateRowCount
59+
CHANGE_TRACKING_CURRENT_VERSION() AS CurrentDatabaseVersion
5060
FROM sys.change_tracking_tables AS ctt
5161
JOIN sys.tables AS t ON t.object_id = ctt.object_id
5262
JOIN sys.schemas AS s ON s.schema_id = t.schema_id
53-
OUTER APPLY (
54-
SELECT SUM(p.row_count) AS EstimateRowCount
55-
FROM sys.dm_db_partition_stats AS p
56-
WHERE p.object_id = t.object_id
57-
AND p.index_id IN (0, 1)
58-
) rc
5963
ORDER BY s.name, t.name;
64+
65+
CREATE TABLE #Counts (TableObjectId INT NOT NULL, EstimateRowCount BIGINT NULL);
66+
67+
DECLARE @object_id INT, @schema SYSNAME, @table SYSNAME, @sql NVARCHAR(MAX);
68+
DECLARE c CURSOR LOCAL FAST_FORWARD FOR
69+
SELECT TableObjectId, SchemaName, TableName FROM #Base;
70+
OPEN c;
71+
FETCH NEXT FROM c INTO @object_id, @schema, @table;
72+
WHILE @@FETCH_STATUS = 0
73+
BEGIN
74+
BEGIN TRY
75+
SET @sql = N'SELECT ' + CAST(@object_id as nvarchar(max)) + N' , COUNT(*)
76+
FROM ' + QUOTENAME(@schema) + N'.' + QUOTENAME(@table) + 'WITH(NOLOCK)';
77+
INSERT INTO #Counts (TableObjectId, EstimateRowCount)
78+
EXEC(@sql)
79+
END TRY
80+
BEGIN CATCH
81+
INSERT #Counts (TableObjectId, EstimateRowCount) VALUES (@object_id, NULL);
82+
END CATCH
83+
FETCH NEXT FROM c INTO @object_id, @schema, @table;
84+
END
85+
CLOSE c;
86+
DEALLOCATE c;
87+
88+
SELECT
89+
b.TableObjectId AS TableObjectId,
90+
b.SchemaName AS SchemaName,
91+
b.TableName AS TableName,
92+
b.TrackColumnsUpdated AS TrackColumnsUpdated,
93+
b.MinValidVersion AS MinValidVersion,
94+
b.CurrentDatabaseVersion AS CurrentDatabaseVersion,
95+
c.EstimateRowCount AS EstimateRowCount
96+
FROM #Base b
97+
LEFT JOIN #Counts c ON c.TableObjectId = b.TableObjectId
98+
ORDER BY b.SchemaName, b.TableName;
99+
100+
DROP TABLE #Counts;
101+
DROP TABLE #Base;
60102
""";
61103

62104
public static void PersistsSourceTargetVersionState(

0 commit comments

Comments
 (0)