private async Task <IEnumerable <StatisticsResult <BestStatistic> > > ReadBestPlayerAverage(string divideThisField, string byThisField, int multiplier, string sortOrder, bool isFieldingStatistic, string inningsFilter, StatisticsFilter filter) { var clonedFilter = filter.Clone(); clonedFilter.SwapBattingFirstFilter = isFieldingStatistic; var(where, parameters) = _statisticsQueryBuilder.BuildWhereClause(clonedFilter); var sql = $@"SELECT PlayerId, PlayerRoute, TotalMatches, TotalInnings, Average FROM( SELECT PlayerId, PlayerRoute, (CAST(SUM({divideThisField}) AS DECIMAL) / SUM({byThisField}))*{multiplier} AS Average, (SELECT COUNT(DISTINCT MatchId) FROM { Tables.PlayerInMatchStatistics} WHERE PlayerId = s.PlayerId {where}) AS TotalMatches, (SELECT COUNT(PlayerInMatchStatisticsId) FROM { Tables.PlayerInMatchStatistics} WHERE PlayerId = s.PlayerId {inningsFilter} {where}) AS TotalInnings FROM {Tables.PlayerInMatchStatistics} AS s WHERE 1=1 {inningsFilter} {where} GROUP BY PlayerId, PlayerRoute HAVING SUM({byThisField}) > 0 AND (SELECT COUNT(PlayerInMatchStatisticsId) FROM {Tables.PlayerInMatchStatistics} WHERE PlayerId = s.PlayerId {inningsFilter} {where}) >= @MinimumQualifyingInnings ORDER BY CAST(SUM({divideThisField}) AS DECIMAL) / SUM({byThisField}) {sortOrder}, TotalInnings DESC, TotalMatches DESC OFFSET @PageOffset ROWS FETCH NEXT @PageSize ROWS ONLY ) AS BestAverage ORDER BY Average {sortOrder}, TotalInnings DESC, TotalMatches DESC"; parameters.Add("@MinimumQualifyingInnings", filter.MinimumQualifyingInnings ?? 1); parameters.Add("@PageOffset", clonedFilter.Paging.PageSize * (clonedFilter.Paging.PageNumber - 1)); parameters.Add("@PageSize", clonedFilter.Paging.PageSize); using (var connection = _databaseConnectionFactory.CreateDatabaseConnection()) { var results = await connection.QueryAsync <Player, BestStatistic, StatisticsResult <BestStatistic> >(sql, (player, totals) => { totals.Player = player; return(new StatisticsResult <BestStatistic> { Result = totals }); }, parameters, splitOn : $"TotalMatches", commandTimeout : 60).ConfigureAwait(false); var players = await _playerDataSource.ReadPlayers(new PlayerFilter { PlayerIds = results.Select(x => x.Result.Player.PlayerId.Value).ToList() }).ConfigureAwait(false); foreach (var result in results) { result.Result.Player = players.Single(x => x.PlayerId == result.Result.Player.PlayerId); } return(results); } }
private async Task <IEnumerable <StatisticsResult <BestStatistic> > > ReadBestPlayerTotal(string fieldName, bool fieldValueCanBeNegative, bool isFieldingStatistic, string extraSelectFields, string outerQueryIncludingOrderBy, string totalInningsFilter, StatisticsFilter filter) { var clonedFilter = filter.Clone(); clonedFilter.SwapBattingFirstFilter = isFieldingStatistic; var(where, parameters) = _statisticsQueryBuilder.BuildWhereClause(clonedFilter); var group = "GROUP BY PlayerId, PlayerRoute"; var having = fieldValueCanBeNegative ? "HAVING 1=1" : $"HAVING SUM({fieldName}) > 0"; var minimumValue = fieldValueCanBeNegative ? string.Empty : $"AND {fieldName} >= 0"; // The result set can be limited in two mutually-exlusive ways: // 1. Max results (eg top ten) but where results beyond but equal to the max are also included // 2. Paging var preQuery = string.Empty; var offsetWithExtraResults = string.Empty; var offsetPaging = string.Empty; if (clonedFilter.MaxResultsAllowingExtraResultsIfValuesAreEqual.HasValue) { // Get the values from what should be the last row according to the maximum number of results. preQuery = $@"DECLARE @MaxResult int; SELECT @MaxResult = SUM({fieldName}) FROM {Tables.PlayerInMatchStatistics} WHERE {fieldName} IS NOT NULL {minimumValue} {where} {group} {having} ORDER BY SUM({fieldName}) DESC OFFSET {clonedFilter.MaxResultsAllowingExtraResultsIfValuesAreEqual - 1} ROWS FETCH NEXT 1 ROWS ONLY; "; // If @MaxResult IS NULL there are fewer rows than the requested maximum, so just fetch all. // Otherwise look for results that are greater than or equal to the value(s) in the last row retrieved above. offsetWithExtraResults = $"AND (@MaxResult IS NULL OR SUM({fieldName}) >= @MaxResult) "; // Add an ORDER BY clause to sort the results, unless we're relying on an outer query to do that because it's not valid in a sub-query if (string.IsNullOrEmpty(outerQueryIncludingOrderBy)) { offsetWithExtraResults += $"ORDER BY SUM({fieldName}) DESC, TotalInnings ASC, TotalMatches ASC"; } } else { offsetPaging = $"ORDER BY SUM({fieldName}) DESC, TotalInnings ASC, TotalMatches ASC OFFSET @PageOffset ROWS FETCH NEXT @PageSize ROWS ONLY"; parameters.Add("@PageOffset", clonedFilter.Paging.PageSize * (clonedFilter.Paging.PageNumber - 1)); parameters.Add("@PageSize", clonedFilter.Paging.PageSize); } var totalInningsQuery = !string.IsNullOrEmpty(totalInningsFilter) ? $"SELECT COUNT(PlayerInMatchStatisticsId) FROM { Tables.PlayerInMatchStatistics} WHERE PlayerId = s.PlayerId {totalInningsFilter} {where}" : "NULL"; var sql = $@"SELECT PlayerId, PlayerRoute, (SELECT COUNT(DISTINCT MatchId) FROM { Tables.PlayerInMatchStatistics} WHERE PlayerId = s.PlayerId {where}) AS TotalMatches, ({totalInningsQuery}) AS TotalInnings, (SELECT SUM({ fieldName}) FROM { Tables.PlayerInMatchStatistics} WHERE PlayerId = s.PlayerId {where}) AS Total <<SELECT>> FROM {Tables.PlayerInMatchStatistics} AS s WHERE {fieldName} IS NOT NULL {minimumValue} {where} {group} {having} {offsetWithExtraResults} {offsetPaging}"; if (!string.IsNullOrEmpty(extraSelectFields)) { extraSelectFields = extraSelectFields.Replace("<<WHERE>>", where); sql = sql.Replace("<<SELECT>>", extraSelectFields); } else { sql = sql.Replace("<<SELECT>>", string.Empty); } if (!string.IsNullOrEmpty(outerQueryIncludingOrderBy)) { sql = outerQueryIncludingOrderBy.Replace("<<QUERY>>", sql); } using (var connection = _databaseConnectionFactory.CreateDatabaseConnection()) { var results = await connection.QueryAsync <Player, BestStatistic, StatisticsResult <BestStatistic> >($"{preQuery} {sql}", (player, totals) => { totals.Player = player; return(new StatisticsResult <BestStatistic> { Result = totals }); }, parameters, splitOn : $"TotalMatches", commandTimeout : 60).ConfigureAwait(false); var players = await _playerDataSource.ReadPlayers(new PlayerFilter { PlayerIds = results.Select(x => x.Result.Player.PlayerId.Value).ToList() }).ConfigureAwait(false); foreach (var result in results) { result.Result.Player = players.Single(x => x.PlayerId == result.Result.Player.PlayerId); } return(results); } }