このコミットが含まれているのは:
2026-10-27 19:54:31 +09:00
コミット 4f117c7ba3
7個のファイルの変更、366行の追加、127行の削除
+47 -34
ファイルの表示
@@ -98,10 +98,10 @@ class CountsTableQuery
extras << "sort#{ index }.count AS sort#{ index }"
"sort#{ index } IS NULL ASC, sort#{ index } #{ direction.upcase }"
elsif row_keys.include?(key)
"#{ key } IS NULL ASC, #{ key } #{ direction.upcase }"
axis_order_sql(key, direction)
end
end
order_rows = row_sort + row_keys.map { "#{ _1 } IS NULL, #{ _1 }" }
order_rows = row_sort + row_keys.map { axis_order_sql(_1) }
order_rows = ['singleton ASC'] if order_rows.empty?
grouped = "SELECT #{ row_selection } FROM cells #{ row_group }"
selection = (['grouped.*'] + extras).join(', ')
@@ -115,10 +115,10 @@ class CountsTableQuery
order_cols = @sort.filter_map do |key, direction|
next unless key.match?(/\Aa[0-9]+\z/) && !row_keys.include?(key)
"cells.#{ key } #{ direction.upcase }"
axis_order_sql(key, direction, qualifier: 'cells')
end
order = ['page_rows.position'] + order_cols +
axes.all.each_index.map { "cells.a#{ _1 }" }
axes.all.each_index.map { axis_order_sql("a#{ _1 }", qualifier: 'cells') }
records = @connection.select_all(<<~SQL)
#{ prefix }, row_groups AS (#{ group_sql }),
numbered AS (SELECT row_groups.*,
@@ -231,39 +231,41 @@ class CountsTableQuery
# detail pagination continue to use the authorised population exclusively.
source = CountsPopulation.new(
@params.merge('execution_at' => population.at.iso8601(6)), authorisation_flg: false)
records = source.scope.except(:select).optimizer_hints('MAX_EXECUTION_TIME(10000)').count
denied = source.scope.except(:select)
.where(Arel.sql("NOT (#{ HistoryAccess.sql(@definition.model, 'v') })"))
.optimizer_hints('MAX_EXECUTION_TIME(10000)').count
rows = records
if mode == 'summary'
unrestricted_axes = CountsAxes.new(source)
relation = source.scope
joins = unrestricted_axes.joins
relation = relation.joins(joins) unless joins.empty?
expressions = unrestricted_axes.expressions
keys = axes.rows.each_index.map { "a#{ _1 }" }
if keys.empty?
rows = 1
else
selected = expressions.each_with_index.map { |expression, index|
"#{ expression } AS a#{ index }"
}
sql = relation.except(:select).select(Arel.sql(selected.join(', ')))
.distinct.to_sql
prefix = "WITH cells AS (#{ sql })"
groups = @connection.select_value(<<~SQL).to_i
#{ prefix }
SELECT /*+ MAX_EXECUTION_TIME(10000) */ COUNT(*) FROM (
SELECT #{ keys.join(', ') } FROM cells GROUP BY #{ keys.join(', ') }
) output_groups
SQL
rows = output_row_count(prefix, keys, CountsPivotLayout.depths(keys, @sort), groups)
end
end
rows = if mode == 'details'
source.scope.except(:select).optimizer_hints('MAX_EXECUTION_TIME(10000)').count
else
summary_output_count(source)
end
@output_totals = { output_count: rows, excluded_count: denied }
end
def summary_output_count(source)
keys = axes.rows.each_index.map { "a#{ _1 }" }
return 1 if keys.empty?
unrestricted_axes = CountsAxes.new(source)
relation = source.scope
joins = unrestricted_axes.joins
relation = relation.joins(joins) unless joins.empty?
expressions = unrestricted_axes.expressions
selected = expressions.each_with_index.map { |expression, index|
"#{ expression } AS a#{ index }"
}
sql = relation.except(:select).select(Arel.sql(selected.join(', ')))
.distinct.to_sql
prefix = "WITH cells AS (#{ sql })"
groups = @connection.select_value(<<~SQL).to_i
#{ prefix }
SELECT /*+ MAX_EXECUTION_TIME(10000) */ COUNT(*) FROM (
SELECT #{ keys.join(', ') } FROM cells GROUP BY #{ keys.join(', ') }
) output_groups
SQL
output_row_count(prefix, keys, CountsPivotLayout.depths(keys, @sort), groups)
end
def locale = @locale ||= Locale.find_by!(code: @params.fetch('locale', 'ja'))
def limited_columns(prefix)
@@ -272,9 +274,7 @@ class CountsTableQuery
configured = @sort.select { |key, _direction| keys.include?(key) }
directions = (configured + keys.map { [_1, 'asc'] }).uniq { _1.first }
order = directions.map { |key, direction|
"#{ key } IS NULL ASC, #{ key } #{ direction.upcase }"
}.join(', ')
order = directions.map { |key, direction| axis_order_sql(key, direction) }.join(', ')
custom = QueryState.records(@params, 'column_order')
if custom.any?
raise ArgumentError unless custom.map { _1['value'] }.uniq.length == custom.length
@@ -311,6 +311,19 @@ class CountsTableQuery
source.select(Arel.sql(selected.join(', '))).to_sql
end
def axis_order_sql(key, direction = 'asc', qualifier: nil)
column = qualifier ? "#{ qualifier }.#{ key }" : key
expression = column
if axes.all.fetch(key.delete_prefix('a').to_i).fetch('field') == 'category'
# Tag's enum preserves the same category order as TagsController.
cases = Tag.categories.keys.each_with_index.map { |category, index|
"WHEN #{ @connection.quote(category) } THEN #{ index }"
}.join(' ')
expression = "CASE #{ column } #{ cases } ELSE #{ Tag.categories.length } END"
end
"#{ column } IS NULL ASC, #{ expression } #{ direction.upcase }"
end
def measure_values(key)
records = QueryState.records(@params, 'measures').select { _1['key'] == key }
raise ArgumentError if records.empty?