diff --git a/src/Strata.SqlTools.PostgreSql/Breakdowns/QueryBreakdownCollection.cs b/src/Strata.SqlTools.PostgreSql/Breakdowns/QueryBreakdownCollection.cs
index d0a15f2..c370704 100644
--- a/src/Strata.SqlTools.PostgreSql/Breakdowns/QueryBreakdownCollection.cs
+++ b/src/Strata.SqlTools.PostgreSql/Breakdowns/QueryBreakdownCollection.cs
@@ -163,35 +163,14 @@ public class QueryBreakdownCollection : SqlBreakdownCollection
///
/// Summary information for each query.
public IEnumerable GetQuerySummaries()
- {
- return _queryBreakdowns.Select((q, index) => new SqlServer.QuerySummary
- {
- Index = index,
- HasSelectClause = !string.IsNullOrWhiteSpace(q.SelectClause?.Clause),
- HasFromClause = !string.IsNullOrWhiteSpace(q.FromClause?.Clause),
- HasWhereClause = !string.IsNullOrWhiteSpace(q.WhereClause?.Clause),
- HasGroupByClause = !string.IsNullOrWhiteSpace(q.GroupByClause?.Clause),
- HasHavingClause = !string.IsNullOrWhiteSpace(q.HavingClause?.Clause),
- HasOrderByClause = !string.IsNullOrWhiteSpace(q.OrderByClause?.Clause),
- HasJoins = false,
- HasCTE = q.WithClauses.Count > 0,
- ColumnCount = !string.IsNullOrWhiteSpace(q.SelectClause?.Clause) ? q.SelectClause.Clause.Split(',').Length : 0,
- ParameterCount = q.ParameterList.Count(),
- JoinCount = 0
- });
- }
+ => SqlServer.QueryCollectionAnalysisHelper.GetQuerySummaries(_queryBreakdowns);
///
/// Gets the total number of selected columns across all queries.
///
/// Total column count.
public int GetTotalSelectedColumns()
- {
- return _queryBreakdowns.Sum(q =>
- !string.IsNullOrWhiteSpace(q.SelectClause?.Clause)
- ? q.SelectClause.Clause.Split(',').Length
- : 0);
- }
+ => SqlServer.QueryCollectionAnalysisHelper.GetTotalSelectedColumns(_queryBreakdowns);
///
/// Gets all unique table names referenced across all queries.
@@ -203,20 +182,7 @@ public class QueryBreakdownCollection : SqlBreakdownCollection
///
/// List of unique table names.
public IEnumerable GetUniqueTableReferences()
- {
- var tables = new HashSet(StringComparer.OrdinalIgnoreCase);
-
- var tableNames = _queryBreakdowns
- .Where(q => !string.IsNullOrWhiteSpace(q.FromClause?.Clause))
- .SelectMany(q => ExtractTableNames(q.FromClause!.Clause!));
-
- foreach (var table in tableNames)
- {
- tables.Add(table);
- }
-
- return tables;
- }
+ => SqlServer.QueryCollectionAnalysisHelper.GetUniqueTableReferences(_queryBreakdowns);
///
/// Gets parameter usage information across all queries.
@@ -224,83 +190,14 @@ public class QueryBreakdownCollection : SqlBreakdownCollection
/// Parameter usage information.
public IEnumerable GetParameterUsageReport()
{
- // Collect all unique parameter names from both ParameterList and Parameters dictionary
- var allParamNames = new HashSet(StringComparer.OrdinalIgnoreCase);
-
- foreach (var query in _queryBreakdowns)
- {
- // Add from ParameterList (parsed parameters)
- foreach (var param in query.ParameterList)
+ return SqlServer.QueryCollectionAnalysisHelper.GetParameterUsage(_queryBreakdowns)
+ .Select(usage => new ParameterUsageReport
{
- allParamNames.Add(param.Name);
- }
-
- // Add from Parameters dictionary (manually added parameters)
- foreach (var paramName in query.Parameters.Keys)
- {
- allParamNames.Add(paramName);
- }
- }
-
- foreach (var paramName in allParamNames)
- {
- var queriesUsing = 0;
- object? lastValue = null;
-
- foreach (var query in _queryBreakdowns)
- {
- // Check ParameterList first (parsed)
- var param = query.ParameterList.FirstOrDefault(p => p.Name.Equals(paramName, StringComparison.OrdinalIgnoreCase));
- if (param != null)
- {
- queriesUsing++;
- lastValue = param.Value;
- }
- // Also check Parameters dictionary (manually added)
- else if (query.Parameters.TryGetValue(paramName, out var dictValue))
- {
- queriesUsing++;
- lastValue = dictValue;
- }
- }
-
- yield return new ParameterUsageReport
- {
- ParameterName = paramName,
- Value = lastValue,
- UsedInQueryCount = queriesUsing,
- TotalQueries = _queryBreakdowns.Count
- };
- }
- }
-
- ///
- /// Helper method to extract table names from a FROM clause.
- ///
- private static IEnumerable ExtractTableNames(string fromClause)
- {
- if (string.IsNullOrWhiteSpace(fromClause))
- {
- yield break;
- }
-
- // Simple extraction: split by comma and clean up aliases
- var parts = fromClause.Split(',');
- foreach (var part in parts)
- {
- var trimmed = part.Trim();
-
- // Remove alias (assuming format: table AS alias or table alias)
- var tokens = trimmed.Split(new[] { " AS ", " " }, StringSplitOptions.RemoveEmptyEntries);
- if (tokens.Length > 0)
- {
- var tableName = tokens[0].Trim();
- if (!string.IsNullOrWhiteSpace(tableName))
- {
- yield return tableName;
- }
- }
- }
+ ParameterName = usage.Name,
+ Value = usage.Value,
+ UsedInQueryCount = usage.UsedInQueryCount,
+ TotalQueries = usage.TotalQueries
+ });
}
}
diff --git a/src/Strata.SqlTools.Snowflake/Breakdowns/QueryBreakdownCollection.cs b/src/Strata.SqlTools.Snowflake/Breakdowns/QueryBreakdownCollection.cs
index 9a033a2..74d32a0 100644
--- a/src/Strata.SqlTools.Snowflake/Breakdowns/QueryBreakdownCollection.cs
+++ b/src/Strata.SqlTools.Snowflake/Breakdowns/QueryBreakdownCollection.cs
@@ -474,44 +474,7 @@ public class QueryBreakdownCollection : SqlBreakdownCollection
/// Ensures that if a parameter with the same name exists in multiple queries, they all have the same value.
///
public void SynchronizeParameters()
- {
- // Get all unique parameter names across all queries
- var allParameterNames = new HashSet(StringComparer.OrdinalIgnoreCase);
- foreach (var query in _queryBreakdowns)
- {
- foreach (var paramName in query.Parameters.Keys)
- {
- allParameterNames.Add(paramName);
- }
- }
-
- // For each parameter, use the last query's value and sync to all queries that have it
- foreach (var paramName in allParameterNames)
- {
- object? lastValue = null;
- bool parameterFound = false;
-
- // Find the last query that has this parameter and get its value
- for (int i = _queryBreakdowns.Count - 1; i >= 0; i--)
- {
- if (_queryBreakdowns[i].Parameters.ContainsKey(paramName))
- {
- lastValue = _queryBreakdowns[i].Parameters[paramName];
- parameterFound = true;
- break;
- }
- }
-
- // Synchronize the parameter value to all queries that have it
- if (parameterFound)
- {
- foreach (var query in _queryBreakdowns.Where(q => q.Parameters.ContainsKey(paramName)))
- {
- query.Parameters[paramName] = lastValue!;
- }
- }
- }
- }
+ => SqlServer.QueryCollectionAnalysisHelper.SynchronizeParameters(_queryBreakdowns);
///
/// Adds a parameter with a specific value to all queries in the collection.
@@ -536,26 +499,7 @@ public class QueryBreakdownCollection : SqlBreakdownCollection
///
/// A dictionary containing all unique parameters across all queries.
protected Dictionary GetCombinedParameterDictionary()
- {
- var combinedParams = new Dictionary(StringComparer.OrdinalIgnoreCase);
-
- foreach (var query in _queryBreakdowns)
- {
- // Add from ParameterList (parsed parameters)
- foreach (var param in query.ParameterList)
- {
- combinedParams[param.Name] = param.Value;
- }
-
- // Add/override from Parameters dictionary (manually added parameters)
- foreach (var param in query.Parameters)
- {
- combinedParams[param.Key] = param.Value;
- }
- }
-
- return combinedParams;
- }
+ => SqlServer.QueryCollectionAnalysisHelper.GetCombinedParameters(_queryBreakdowns);
///
/// Gets a formatted string representation of all unique parameters with Snowflake-specific syntax.
@@ -607,54 +551,14 @@ public class QueryBreakdownCollection : SqlBreakdownCollection
/// Parameter usage information.
public IEnumerable GetParameterUsageReport()
{
- // Collect all unique parameter names from both ParameterList and Parameters dictionary
- var allParamNames = new HashSet(StringComparer.OrdinalIgnoreCase);
-
- foreach (var query in _queryBreakdowns)
- {
- // Add from ParameterList (parsed parameters)
- foreach (var param in query.ParameterList)
+ return SqlServer.QueryCollectionAnalysisHelper.GetParameterUsage(_queryBreakdowns)
+ .Select(usage => new ParameterUsageReport
{
- allParamNames.Add(param.Name);
- }
-
- // Add from Parameters dictionary (manually added parameters)
- foreach (var paramName in query.Parameters.Keys)
- {
- allParamNames.Add(paramName);
- }
- }
-
- foreach (var paramName in allParamNames)
- {
- var queriesUsing = 0;
- object? lastValue = null;
-
- foreach (var query in _queryBreakdowns)
- {
- // Check ParameterList first (parsed)
- var param = query.ParameterList.FirstOrDefault(p => p.Name.Equals(paramName, StringComparison.OrdinalIgnoreCase));
- if (param != null)
- {
- queriesUsing++;
- lastValue = param.Value;
- }
- // Also check Parameters dictionary (manually added)
- else if (query.Parameters.TryGetValue(paramName, out var dictValue))
- {
- queriesUsing++;
- lastValue = dictValue;
- }
- }
-
- yield return new ParameterUsageReport
- {
- ParameterName = paramName,
- Value = lastValue,
- UsedInQueryCount = queriesUsing,
- TotalQueries = _queryBreakdowns.Count
- };
- }
+ ParameterName = usage.Name,
+ Value = usage.Value,
+ UsedInQueryCount = usage.UsedInQueryCount,
+ TotalQueries = usage.TotalQueries
+ });
}
///
@@ -662,12 +566,7 @@ public class QueryBreakdownCollection : SqlBreakdownCollection
///
/// Total column count.
public int GetTotalSelectedColumns()
- {
- return _queryBreakdowns.Sum(q =>
- !string.IsNullOrWhiteSpace(q.SelectClause?.Clause)
- ? q.SelectClause.Clause.Split(',').Length
- : 0);
- }
+ => SqlServer.QueryCollectionAnalysisHelper.GetTotalSelectedColumns(_queryBreakdowns);
///
/// Gets all unique table names referenced across all queries.
@@ -679,20 +578,7 @@ public class QueryBreakdownCollection : SqlBreakdownCollection
///
/// List of unique table names.
public IEnumerable GetUniqueTableReferences()
- {
- var tables = new HashSet(StringComparer.OrdinalIgnoreCase);
-
- var tableNames = _queryBreakdowns
- .Where(q => !string.IsNullOrWhiteSpace(q.FromClause?.Clause))
- .SelectMany(q => ExtractTableNames(q.FromClause!.Clause!));
-
- foreach (var table in tableNames)
- {
- tables.Add(table);
- }
-
- return tables;
- }
+ => SqlServer.QueryCollectionAnalysisHelper.GetUniqueTableReferences(_queryBreakdowns);
///
/// Gets a summary of all queries including their types and basic composition.
@@ -722,32 +608,6 @@ public class QueryBreakdownCollection : SqlBreakdownCollection
});
}
- ///
- /// Helper method to extract table names from a FROM clause.
- ///
- private static IEnumerable ExtractTableNames(string fromClause)
- {
- if (string.IsNullOrWhiteSpace(fromClause))
- {
- yield break;
- }
-
- var parts = fromClause.Split(',');
- foreach (var part in parts)
- {
- var trimmed = part.Trim();
- var tokens = trimmed.Split(new[] { " AS ", " " }, StringSplitOptions.RemoveEmptyEntries);
- if (tokens.Length > 0)
- {
- var tableName = tokens[0].Trim();
- if (!string.IsNullOrWhiteSpace(tableName))
- {
- yield return tableName;
- }
- }
- }
- }
-
///
/// Helper method to convert a .NET object to its corresponding Snowflake data type string.
///
diff --git a/src/Strata.SqlTools.SqlServer/Breakdowns/QueryBreakdownCollection.cs b/src/Strata.SqlTools.SqlServer/Breakdowns/QueryBreakdownCollection.cs
index 4fbf08c..d6a22af 100644
--- a/src/Strata.SqlTools.SqlServer/Breakdowns/QueryBreakdownCollection.cs
+++ b/src/Strata.SqlTools.SqlServer/Breakdowns/QueryBreakdownCollection.cs
@@ -257,12 +257,7 @@ public class QueryBreakdownCollection : SqlBreakdownCollection
///
/// Total column count.
public int GetTotalSelectedColumns()
- {
- return _queryBreakdowns.Sum(q =>
- !string.IsNullOrWhiteSpace(q.SelectClause?.Clause)
- ? q.SelectClause.Clause.Split(',').Length
- : 0);
- }
+ => QueryCollectionAnalysisHelper.GetTotalSelectedColumns(_queryBreakdowns);
///
/// Gets all unique table names referenced across all queries.
@@ -274,72 +269,14 @@ public class QueryBreakdownCollection : SqlBreakdownCollection
///
/// List of unique table names.
public IEnumerable GetUniqueTableReferences()
- {
- var tables = new HashSet(StringComparer.OrdinalIgnoreCase);
-
- var tableNames = _queryBreakdowns
- .Where(q => !string.IsNullOrWhiteSpace(q.FromClause?.Clause))
- .SelectMany(q => ExtractTableNames(q.FromClause!.Clause!));
-
- foreach (var table in tableNames)
- {
- tables.Add(table);
- }
-
- return tables;
- }
+ => QueryCollectionAnalysisHelper.GetUniqueTableReferences(_queryBreakdowns);
///
/// Gets a summary of all queries including their types and basic composition.
///
/// Summary information for each query.
public IEnumerable GetQuerySummaries()
- {
- return _queryBreakdowns.Select((q, index) => new QuerySummary
- {
- Index = index,
- HasSelectClause = !string.IsNullOrWhiteSpace(q.SelectClause?.Clause),
- HasFromClause = !string.IsNullOrWhiteSpace(q.FromClause?.Clause),
- HasWhereClause = !string.IsNullOrWhiteSpace(q.WhereClause?.Clause),
- HasGroupByClause = !string.IsNullOrWhiteSpace(q.GroupByClause?.Clause),
- HasHavingClause = !string.IsNullOrWhiteSpace(q.HavingClause?.Clause),
- HasOrderByClause = !string.IsNullOrWhiteSpace(q.OrderByClause?.Clause),
- HasJoins = false,
- HasCTE = q.WithClauses.Count > 0,
- ColumnCount = !string.IsNullOrWhiteSpace(q.SelectClause?.Clause) ? q.SelectClause.Clause.Split(',').Length : 0,
- ParameterCount = q.ParameterList.Count(),
- JoinCount = 0
- });
- }
-
- ///
- /// Helper method to extract table names from a FROM clause.
- ///
- private static IEnumerable ExtractTableNames(string fromClause)
- {
- if (string.IsNullOrWhiteSpace(fromClause))
- {
- yield break;
- }
-
- // Simple extraction: split by comma and clean up aliases
- var parts = fromClause.Split(',');
- foreach (var part in parts)
- {
- var trimmed = part.Trim();
-
- // Remove alias (assuming format: table AS alias or table alias)
- var tokens = trimmed.Split(new[] { " AS ", " " }, StringSplitOptions.RemoveEmptyEntries);
- if (tokens.Length > 0)
- {
- var tableName = tokens[0].Trim();
- if (!string.IsNullOrWhiteSpace(tableName))
- {
- yield return tableName;
- }
- }
- }
- }
+ => QueryCollectionAnalysisHelper.GetQuerySummaries(_queryBreakdowns);
///
/// Synchronizes parameters across all queries in the collection.
@@ -350,44 +287,7 @@ public class QueryBreakdownCollection : SqlBreakdownCollection
/// Only synchronizes parameters that the query already defines to avoid adding unused parameters.
///
public void SynchronizeParameters()
- {
- // Get all unique parameter names across all queries
- var allParameterNames = new HashSet(StringComparer.OrdinalIgnoreCase);
- foreach (var query in _queryBreakdowns)
- {
- foreach (var paramName in query.Parameters.Keys)
- {
- allParameterNames.Add(paramName);
- }
- }
-
- // For each parameter, use the last query's value and sync to all queries that have it
- foreach (var paramName in allParameterNames)
- {
- object? lastValue = null;
- bool parameterFound = false;
-
- // Find the last query that has this parameter and get its value
- for (int i = _queryBreakdowns.Count - 1; i >= 0; i--)
- {
- if (_queryBreakdowns[i].Parameters.ContainsKey(paramName))
- {
- lastValue = _queryBreakdowns[i].Parameters[paramName];
- parameterFound = true;
- break;
- }
- }
-
- // Synchronize the parameter value to all queries that have it
- if (parameterFound)
- {
- foreach (var query in _queryBreakdowns.Where(q => q.Parameters.ContainsKey(paramName)))
- {
- query.Parameters[paramName] = lastValue!;
- }
- }
- }
- }
+ => QueryCollectionAnalysisHelper.SynchronizeParameters(_queryBreakdowns);
///
/// Adds a parameter to all queries in the collection.
@@ -411,27 +311,8 @@ public class QueryBreakdownCollection : SqlBreakdownCollection
/// Gets all unique parameters from all queries in the collection as a combined dictionary.
///
/// A dictionary containing all unique parameters across all queries.
- protected Dictionary GetCombinedParameterDictionary()
- {
- var combinedParams = new Dictionary(StringComparer.OrdinalIgnoreCase);
-
- foreach (var query in _queryBreakdowns)
- {
- // Add from ParameterList (parsed parameters)
- foreach (var param in query.ParameterList)
- {
- combinedParams[param.Name] = param.Value;
- }
-
- // Add/override from Parameters dictionary (manually added parameters)
- foreach (var param in query.Parameters)
- {
- combinedParams[param.Key] = param.Value;
- }
- }
-
- return combinedParams;
- }
+ protected Dictionary GetCombinedParameterDictionary()
+ => QueryCollectionAnalysisHelper.GetCombinedParameters(_queryBreakdowns);
///
/// Gets all unique parameters from all queries in the collection.
@@ -482,54 +363,14 @@ public class QueryBreakdownCollection : SqlBreakdownCollection
/// Parameter usage information.
public IEnumerable GetParameterUsageReport()
{
- // Collect all unique parameter names from both ParameterList and Parameters dictionary
- var allParamNames = new HashSet(StringComparer.OrdinalIgnoreCase);
-
- foreach (var query in _queryBreakdowns)
- {
- // Add from ParameterList (parsed parameters)
- foreach (var param in query.ParameterList)
+ return QueryCollectionAnalysisHelper.GetParameterUsage(_queryBreakdowns)
+ .Select(usage => new ParameterUsageReport
{
- allParamNames.Add(param.Name);
- }
-
- // Add from Parameters dictionary (manually added parameters)
- foreach (var paramName in query.Parameters.Keys)
- {
- allParamNames.Add(paramName);
- }
- }
-
- foreach (var paramName in allParamNames)
- {
- var queriesUsing = 0;
- object? lastValue = null;
-
- foreach (var query in _queryBreakdowns)
- {
- // Check ParameterList first (parsed)
- var param = query.ParameterList.FirstOrDefault(p => p.Name.Equals(paramName, StringComparison.OrdinalIgnoreCase));
- if (param != null)
- {
- queriesUsing++;
- lastValue = param.Value;
- }
- // Also check Parameters dictionary (manually added)
- else if (query.Parameters.TryGetValue(paramName, out var dictValue))
- {
- queriesUsing++;
- lastValue = dictValue;
- }
- }
-
- yield return new ParameterUsageReport
- {
- ParameterName = paramName,
- Value = lastValue,
- UsedInQueryCount = queriesUsing,
- TotalQueries = _queryBreakdowns.Count
- };
- }
+ ParameterName = usage.Name,
+ Value = usage.Value,
+ UsedInQueryCount = usage.UsedInQueryCount,
+ TotalQueries = usage.TotalQueries
+ });
}
///
diff --git a/src/Strata.SqlTools.SqlServer/Breakdowns/QueryCollectionAnalysisHelper.cs b/src/Strata.SqlTools.SqlServer/Breakdowns/QueryCollectionAnalysisHelper.cs
new file mode 100644
index 0000000..651f24f
--- /dev/null
+++ b/src/Strata.SqlTools.SqlServer/Breakdowns/QueryCollectionAnalysisHelper.cs
@@ -0,0 +1,246 @@
+namespace Strata.SqlTools.Breakdowns.SqlServer;
+
+///
+/// Provides dialect-agnostic analysis routines shared by the query breakdown collections.
+///
+///
+/// These operations depend only on members exposed by . Because the
+/// PostgreSQL and Snowflake query types derive from , their collections
+/// reuse this logic through covariance instead of duplicating it.
+///
+public static class QueryCollectionAnalysisHelper
+{
+ ///
+ /// Gets the total number of columns selected across all queries.
+ ///
+ /// The queries to inspect.
+ /// Total column count.
+ public static int GetTotalSelectedColumns(IEnumerable queries)
+ {
+ return queries.Sum(q =>
+ !string.IsNullOrWhiteSpace(q.SelectClause?.Clause)
+ ? q.SelectClause.Clause.Split(',').Length
+ : 0);
+ }
+
+ ///
+ /// Gets all unique table names referenced across all queries.
+ ///
+ ///
+ /// This provides a quick overview of which tables are being queried.
+ /// Note: This is a best-effort extraction and may not capture all table references,
+ /// especially in complex subqueries or with aliasing.
+ ///
+ /// The queries to inspect.
+ /// List of unique table names.
+ public static IEnumerable GetUniqueTableReferences(IEnumerable queries)
+ {
+ var tables = new HashSet(StringComparer.OrdinalIgnoreCase);
+
+ var tableNames = queries
+ .Where(q => !string.IsNullOrWhiteSpace(q.FromClause?.Clause))
+ .SelectMany(q => ExtractTableNames(q.FromClause!.Clause!));
+
+ foreach (var table in tableNames)
+ {
+ tables.Add(table);
+ }
+
+ return tables;
+ }
+
+ ///
+ /// Gets a summary of all queries including their types and basic composition.
+ ///
+ /// The queries to summarize.
+ /// Summary information for each query.
+ public static IEnumerable GetQuerySummaries(IEnumerable queries)
+ {
+ return queries.Select((q, index) => new QuerySummary
+ {
+ Index = index,
+ HasSelectClause = !string.IsNullOrWhiteSpace(q.SelectClause?.Clause),
+ HasFromClause = !string.IsNullOrWhiteSpace(q.FromClause?.Clause),
+ HasWhereClause = !string.IsNullOrWhiteSpace(q.WhereClause?.Clause),
+ HasGroupByClause = !string.IsNullOrWhiteSpace(q.GroupByClause?.Clause),
+ HasHavingClause = !string.IsNullOrWhiteSpace(q.HavingClause?.Clause),
+ HasOrderByClause = !string.IsNullOrWhiteSpace(q.OrderByClause?.Clause),
+ HasJoins = false,
+ HasCTE = q.WithClauses.Count > 0,
+ ColumnCount = !string.IsNullOrWhiteSpace(q.SelectClause?.Clause) ? q.SelectClause.Clause.Split(',').Length : 0,
+ ParameterCount = q.ParameterList.Count(),
+ JoinCount = 0
+ });
+ }
+
+ ///
+ /// Synchronizes parameter values across all queries in the collection.
+ ///
+ ///
+ /// Ensures all queries share the same parameter value based on parameter name. Later parameter
+ /// values override earlier ones if there are conflicts. Only parameters a query already defines
+ /// are synchronized, to avoid adding unused parameters.
+ ///
+ /// The queries whose parameters should be synchronized.
+ public static void SynchronizeParameters(IEnumerable queries)
+ {
+ var queryList = queries as IReadOnlyList ?? queries.ToList();
+
+ // Get all unique parameter names across all queries
+ var allParameterNames = new HashSet(StringComparer.OrdinalIgnoreCase);
+ foreach (var query in queryList)
+ {
+ foreach (var paramName in query.Parameters.Keys)
+ {
+ allParameterNames.Add(paramName);
+ }
+ }
+
+ // For each parameter, use the last query's value and sync to all queries that have it
+ foreach (var paramName in allParameterNames)
+ {
+ object? lastValue = null;
+ bool parameterFound = false;
+
+ // Find the last query that has this parameter and get its value
+ for (int i = queryList.Count - 1; i >= 0; i--)
+ {
+ if (queryList[i].Parameters.ContainsKey(paramName))
+ {
+ lastValue = queryList[i].Parameters[paramName];
+ parameterFound = true;
+ break;
+ }
+ }
+
+ // Synchronize the parameter value to all queries that have it
+ if (parameterFound)
+ {
+ foreach (var query in queryList.Where(q => q.Parameters.ContainsKey(paramName)))
+ {
+ query.Parameters[paramName] = lastValue!;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Gets all unique parameters from all queries in the collection as a combined dictionary.
+ ///
+ ///
+ /// Parameters parsed into ParameterList are added first; values from the manually managed
+ /// Parameters dictionary override them when names collide.
+ ///
+ /// The queries to inspect.
+ /// A dictionary containing all unique parameters across all queries.
+ public static Dictionary GetCombinedParameters(IEnumerable queries)
+ {
+ var combinedParams = new Dictionary(StringComparer.OrdinalIgnoreCase);
+
+ foreach (var query in queries)
+ {
+ // Add from ParameterList (parsed parameters)
+ foreach (var param in query.ParameterList)
+ {
+ combinedParams[param.Name] = param.Value;
+ }
+
+ // Add/override from Parameters dictionary (manually added parameters)
+ foreach (var param in query.Parameters)
+ {
+ combinedParams[param.Key] = param.Value;
+ }
+ }
+
+ return combinedParams;
+ }
+
+ ///
+ /// Computes parameter usage statistics across all queries in the collection.
+ ///
+ ///
+ /// Callers project each tuple into their dialect-specific parameter usage report type so that
+ /// dialect-specific formatting (for example, @ versus : parameter prefixes) is preserved.
+ ///
+ /// The queries to inspect.
+ ///
+ /// One entry per unique parameter, carrying the parameter name, its last-seen value, the number of
+ /// queries using it, and the total query count.
+ ///
+ public static IEnumerable<(string Name, object? Value, int UsedInQueryCount, int TotalQueries)> GetParameterUsage(
+ IEnumerable queries)
+ {
+ var queryList = queries as IReadOnlyList ?? queries.ToList();
+
+ // Collect all unique parameter names from both ParameterList and Parameters dictionary
+ var allParamNames = new HashSet(StringComparer.OrdinalIgnoreCase);
+ foreach (var query in queryList)
+ {
+ foreach (var param in query.ParameterList)
+ {
+ allParamNames.Add(param.Name);
+ }
+
+ foreach (var paramName in query.Parameters.Keys)
+ {
+ allParamNames.Add(paramName);
+ }
+ }
+
+ foreach (var paramName in allParamNames)
+ {
+ var queriesUsing = 0;
+ object? lastValue = null;
+
+ foreach (var query in queryList)
+ {
+ // Check ParameterList first (parsed)
+ var param = query.ParameterList.FirstOrDefault(p => p.Name.Equals(paramName, StringComparison.OrdinalIgnoreCase));
+ if (param != null)
+ {
+ queriesUsing++;
+ lastValue = param.Value;
+ }
+ // Also check Parameters dictionary (manually added)
+ else if (query.Parameters.TryGetValue(paramName, out var dictValue))
+ {
+ queriesUsing++;
+ lastValue = dictValue;
+ }
+ }
+
+ yield return (paramName, lastValue, queriesUsing, queryList.Count);
+ }
+ }
+
+ ///
+ /// Extracts table names from a FROM clause on a best-effort basis.
+ ///
+ /// The FROM clause text.
+ /// The table names referenced by the clause.
+ private static IEnumerable ExtractTableNames(string fromClause)
+ {
+ if (string.IsNullOrWhiteSpace(fromClause))
+ {
+ yield break;
+ }
+
+ // Simple extraction: split by comma and clean up aliases
+ var parts = fromClause.Split(',');
+ foreach (var part in parts)
+ {
+ var trimmed = part.Trim();
+
+ // Remove alias (assuming format: table AS alias or table alias)
+ var tokens = trimmed.Split(new[] { " AS ", " " }, StringSplitOptions.RemoveEmptyEntries);
+ if (tokens.Length > 0)
+ {
+ var tableName = tokens[0].Trim();
+ if (!string.IsNullOrWhiteSpace(tableName))
+ {
+ yield return tableName;
+ }
+ }
+ }
+ }
+}