Як я можу призначити різні випадкові значення кожному рядку в операторі SELECT?


11

Будь ласка, подивіться на цей код:

create table #t1(
  id int identity (1,1),
  val varchar(10)
);


insert into #t1 values ('a');
insert into #t1 values ('b');
insert into #t1 values ('c');
insert into #t1 values ('d');

Тепер, коли ви виконуєте це

select *, 
    ( select top 1 val from #t1 order by NEWID()) rnd 
from #t1 order by 1;

ви отримаєте результат, коли всі рядки мають однакове випадкове значення. напр

id          val        rnd
----------- ---------- ----------
1           a          b
2           b          b
3           c          b
4           d          b

Я знаю спосіб за допомогою курсору для циклу закидання рядків і отримання різних випадкових значень, але це не є виконавським.

Розумне рішення цього питання

select t1.id, t1.val, t2.val
from #t1 t1
    join (select *, ROW_NUMBER() over( order by NEWID()) lfd from #t1) as t2 on  t1.id = t2.lfd 

Але я спростив запит. Справжній запит більше схожий

select *, 
    ( select top 1 val from t2 where t2.x <> t1.y order by NEWID()) rnd 
from t1 order by 1;

і просте рішення не підходить. Я шукаю спосіб примусити повторне оцінювання

( select top 1 val from #t1 order by NEWID()) rnd 

без використання курсорів.

Редагувати: Потрібний вихід:

можливо 1 дзвінок

id          val        rnd
----------- ---------- ----------
1           a          c
2           b          c
3           c          b
4           d          a

і другий дзвінок

id          val        rnd
----------- ---------- ----------
1           a          a
2           b          d
3           c          d
4           d          b

Значення для кожного рядка просто повинно бути випадковим значенням, незалежним від інших рядків

Ось курсова версія коду:

CREATE TABLE #res ( id INT, val VARCHAR(10), rnd VARCHAR(10));

DECLARE @id INT
DECLARE @val VARCHAR(10)
DECLARE c CURSOR FOR
SELECT id, val
FROM #t1
OPEN c
FETCH NEXT FROM c INTO @id, @val
WHILE @@FETCH_STATUS = 0
BEGIN
    INSERT INTO #res
    SELECT @id, @val, ( SELECT TOP 1 val FROM #t1 ORDER BY NEWID()) rnd 
    FETCH NEXT FROM c INTO @id, @val
END
CLOSE c
DEALLOCATE c

SELECT * FROM #res

Яким був би ваш ідеальний вихід, будь ласка? можливо мені чогось не вистачає
gbn

Я готую версію курсору, щоб було зрозуміло
bernd_k

Тож rnd і val завжди різні в кожному ряду? Якби це було "випадковим чином", то періодично вони би те саме. Також у ваших згаданих 2 дзвінках чи має значення те, що rnd не має всіх значень у стовпці?
gbn

Він використовується для генерації невеликої та середньої випадкової демонстрації з великого пулу реальних даних. Так, поповнення дозволено.
bernd_k

Відповіді:


11

Якщо можливо, підзапит оцінюється один раз. Я не можу пригадати, як називається "функція" вибачте.

Те саме стосується функцій GETDATE та RAND. NEWID оцінюється рядком за рядком, тому що він по суті є випадковим значенням і ніколи не повинен генерувати одне і те ж значення двічі.

Звичайними методами є використання NEWID як вхід до CHECKSUM або як насіння для RAND

Для випадкових значень у рядку:

SELECT
   co1l, col2,
   ABS(CHECKSUM(NEWID())) AS Random1,
   RAND(CHECKSUM(NEWID())) AS Random2
FROM
   MyTable

Якщо ви хочете випадкове замовлення:

SELECT
   co1l, col2
FROM
   MyTable
ORDER BY
   NEWID()

Якщо ви хочете довільно замовити і рядок. Тут фактичне замовлення зберігається незалежно від порядку набору результатів

SELECT
   id, val,
   ROWNUMBER() OVER (ORDER BY id) AS id
FROM
   #t1
ORDER BY
   NEWID()

Редагувати:

У цьому випадку ми можемо викласти вимогу як:

  1. повернути будь-яке випадкове значення з набору для кожного рядка в наборі
  2. випадкове значення буде відрізнятися від фактичного значення в будь-якому рядку

Це відрізняється від того, що я запропонував вище, в якому просто переупорядковує рядки різними способами

Отже, я б розглядав КРЕСЛУ ЗАЯВУ. РОЗМОЖЕННЯ ДЕРЖАВНОГО ряду за оцінкою рядків і уникає проблеми "складання" і гарантує, що val і rnd завжди різні. CROSS APPLY теж може бути масштабним

SELECT
   id, val, R.rnd
FROM
   #t1 t1
   CROSS APPLY
   (SELECT TOP 1 val as rnd FROM #t1 t2 WHERE t1.val <> t2.val ORDER BY NEWID()) R
ORDER BY
   id

APPLY - це SQL Server 2005 та верхня
версія

1
@bernd_k: Так, але слід ігнорувати користувачів SQL Server 2000 у 2011 році
реально
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.