SELECT 문의 각 행에 다른 임의의 값을 할당하려면 어떻게해야합니까?

이 코드를보십시오 :

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


답변

하위 쿼리는 가능하면 한 번 평가됩니다. “기능”이 무엇인지 (폴딩?) 미안합니다.

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. 임의의 값은 모든 행의 실제 값과 다릅니다

이것은 단순히 다양한 방법으로 행을 재정렬하는 위에서 제공 한 것과 다릅니다.

그래서 CROSS APPLY를 고려할 것입니다. 행 평가에 의한 WHERE 절 힘 행과 “접이식”문제를 방지 하고 발과 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