2013-06-20 3 views
3

У меня есть таблица, которая содержит, кроме других, такие поля: id integer, status_id integer, add_date date.Как обновить только определенный процент сопоставимых значений

Я хотел бы выполнить запрос, похожее на это:

update table set status_id = new_status_id where status_id = old_status_id

, но один, который будет обновлять только заданный процент значений, скажем, 50%. Более того, распределение обновленных строк для каждой даты должно быть одинаковым; Я хочу половину строк с date = 23.06.2013 обновлен, а половина нет.

ответ

6
update table 
set status_id = new_status_id 
where 
    status_id = old_status_id 
    and random() < 0.5 
+0

Легкий, но не учитывающий даты. И он может обновить всю таблицу (если она достаточно мала) –

+0

@IgorRomanchenko неточное обновление в отношении дат приемлемо, и этот запрос на сегодняшний день является самым простым. Следовательно, принимайте. Я ценю вашу работу, хотя, спасибо. – Dariusz

2

Этот запрос даст вам id строк, вы хотите обновить:

SELECT * 
FROM 
(SELECT id, 
     count(id) OVER (PARTITION BY add_date) cnt, 
     row_num() OVER (PARTITION BY add_date ORDER BY id) rn 
FROM table 
WHERE status_id = old_status_id) sub 
WHERE rn <= cnt * 0.5 -- your percentage 
-- WHERE rn <= cnt * 0.5 + random() -- another (better) version. 
            -- Will update at random if there if only one row 
+1

'ORDER BY id' .. он не в запросе сначала обновить меньшие идентификаторы. Вы можете просто удалить предложение, чтобы получить произвольный выбор (что было бы быстрее, так как обе оконные функции совместно используют одно и то же окно) или заменить его на «ORDER BY random()», чтобы получить по-настоящему случайный выбор. –

+0

@ErwinBrandstetter Да, я знаю. Это просто привычка добавлять 'ORDER BY' к' row_num() 'с любым произвольным столбцом (потому что Oracle будет выдавать ошибку в противном случае). И использование 'random()', когда это может не потребоваться, не является хорошим. (вызов 'random()' не может быть дешевым) –

+0

_if, это достаточно мало, как ваш комментарий к моему ответу, тогда это также не идеально, поскольку ни одна из дат с одной единственной строкой не будет удалена. Также есть небольшая ошибка в том, что 'count' и' row_number' возвращают целые числа. Решение было бы чем-то вроде 'rn :: float/cnt' –

0

При укладке в постели и пытаясь заснуть очень простое решение пришло на ум:

update table 
set status_id = new_status_id 
where 
     status_id = old_status_id 
    and id % 2 = 0; 

Поскольку идентификатор фактически является bigserial, этот запрос будет иметь схожий эффект с Clodoaldo's.