#sql #postgresql #resampling
#sql #postgresql #повторная выборка
Вопрос:
Я пытаюсь проверить распределение чисел в столбце таблицы. Вместо того, чтобы вычислять по всей таблице (которая большая — десятки гигабайт) Я хочу оценить с помощью повторной выборки. Я думаю, что типичный метод Postgres для этого
select COLUMN
from TABLE
order by RANDOM()
limit 1;
но это медленно для повторной выборки, тем более что (я подозреваю) она манипулирует всем столбцом при каждом моем запуске.
Есть ли способ лучше?
РЕДАКТИРОВАТЬ: просто чтобы убедиться, что я выразил это правильно, я хочу сделать следующее:
for(i in 1:numSamples)
draw 500 random rows
end
без необходимости каждый раз переупорядочивать всю массивную таблицу. Возможно, я мог бы получить все идентификаторы строк таблицы и выполнить выборку из нее в R или что-то в этом роде, а затем просто запросить эти строки?
Комментарии:
1. В SQL2003 введено предложение tablesample, где (DB2 реализовал его, не уверен, есть ли другие). Это обсуждалось ( wiki.postgresql.org /… ) для postgres, но я не знаю текущего состояния.
Ответ №1:
Поскольку вам нужна выборка данных, как насчет использования расчетного размера таблицы, а затем вычисления процента от этого в качестве выборки?
В таблице pg_class
хранится оценка количества строк для каждой таблицы (обновляется процессом vacuum, если я не ошибаюсь).
Таким образом, следующее будет выбирать 1% всех строк из этой таблицы:
with estimated_rows as (
select reltuples as num_rows
from pg_class t
join pg_namespace n on n.oid = t.relnamespace
where t.relname = 'some_table'
and n.nspname = 'public'
)
select *
from some_table
limit (select 0.01 * num_rows from estimated_rows)
;
Если вы делаете это очень часто, возможно, вам захочется создать функцию, чтобы вы могли делать что-то вроде этого:
select *
from some_table
limit (select estimate_percent(0.01, 'public', 'some_table'))
;
Комментарии:
1. Но не будет ли так, что в таблице из 100 строк 50%-ная выборка даст мне строки 1: 50, а 20%-ная выборка даст мне строки 1: 20? Моя цель здесь — иметь возможность выполнять несколько независимых отрисовок.
2. @PatrickMcCarthy поскольку строки в таблице не сортируются (особенно если они часто обновляются), это должно дать вам «случайную» выборку. Но вы правы: если вы запустите его дважды, вы, скорее всего, получите одни и те же строки. Но вы можете по-прежнему использовать
order by random()
, но это сделает запрос довольно медленным, поскольку он всегда будет считывать таблицу целиком.
Ответ №2:
Создайте временную таблицу из целевой таблицы, добавив столбец с номером строки
drop table if exists temp_t;
create temporary table temp_t as
select *, (row_number() over())::int as rn
from t
Создайте более легкую временную таблицу, выбрав только те столбцы, которые будут использоваться при выборке и фильтрации по мере необходимости.
Проиндексируйте ее по столбцу номер строки
create index temp_t_rn on temp_t(rn);
analyze temp_t;
Выполняйте этот запрос для каждой выборки
with r as (
select ceiling(random() * (select max(rn) from temp_t))::int as rn
from generate_series(1, 500) s
)
select *
from temp_t
where rn in (select rn from r)