2012-02-27 1 views
2

При просмотре кода я наткнулся на что-то странное, кто-то прочитал, что вы можете использовать ABS(CHECKSUM(NewId())) % N, чтобы получить случайные числа от 0 до N-1 (поскольку RAND() не возникает в -строка), но я подозреваю, что они на самом деле не проверить их код (упрощенный вниз теперь с таблицы переменных, а код сделал TOP 1 country неправильно обойти проблему ниже):CHECKSUM (NewId()) выполняется несколько раз в строке

DECLARE @Values TABLE 
(
    id int identity, 
    country VARCHAR(100) 
) 

INSERT INTO @Values (country) VALUES ('UK'), ('USA'), ('China') 

SELECT *, (SELECT country FROM @Values v WHERE v.id = ABS(CHECKSUM(NewId())) % 3 + 1) 
FROM @Values 

при выполнении следующей ошибки происходит:

Подзапрос возвратил более 1 значения. Это недопустимо, когда подзапрос следует =,! =, <, < =,>,> = или когда подзапрос используется как выражение.

Мой первый вопрос: каким образом подзапрос может возвращать более одного значения? и как получается, что он несколько раз не возвращает никакой ценности? (Примечание не вопрос, что я спрашиваю на SO) Испытано:

SELECT country FROM @Values v WHERE v.id = ABS(CHECKSUM(NewId())) % 3 + 1 

Но независимо от того, сколько раз следующий выполняется:

SELECT ABS(CHECKSUM(NewId())) % 3 + 1 

Возвращаемый результат всегда один, 2,3 (это было «тестирование», которое программист использовал при написании своего кода)

Из всего этого я подозреваю, что это происходит потому, что он выполняется повторно для каждого сравнения, а не для каждой строки, может кто-то подтвердить это и обеспечить хорошую ссылку на в таком поведении, чтобы я мог указать ему?

+1

Я не понимаю, что вы говорите здесь. Вы говорите, что выбрали маршрут контрольной суммы, потому что «RAND» не оценивался в строке, и теперь вы спрашиваете, почему он * оценивает каждую строку? –

+3

@ LasseV.Karlsen - Похоже, что им нужно что-то, которое оценивается один раз на внешнюю строку запроса, но стабильно при передаче в подзапрос. 'SELECT *, (SELECT country FROM @Values ​​v WHERE v.id = CA.id) FROM @Values ​​CROSS APPLY (SELECT ABS (CHECKSUM (NewId()))% 3 + 1) CA (id)' делает это, но isn Не гарантировано. –

+0

@MartinSmith, если вы опубликуете это как ответ, я соглашусь с этим. – Seph

ответ

1

Почему, это ожидается.

ABS(CHECKSUM(NewId())) % 3 + 1 оценивается один раз для каждой строки в @Values и поэтому имеет разные значения для каждой строки.

Таким образом, он может возвращать несколько строк или вообще никаких строк.

Это именно то, что вы должны ожидать при произношении. Это выражение оценивается для каждой строки.

Очевидно, что вы действительно хотите, это выражение, которое оценивает один раз за запрос.

0

NewID всегда будет вызываться один раз в строке, если вам нужен уникальный использовать значение что-то вроде:

ROUND(((3) * RAND() + 1), 0)

+0

, который вычисляет только одно значение для выполнения запроса, 'CROSS APPLY (SELECT ABS (CHECKSUM (NewId()))% 3 + 1) CA (id)', как указано в @Martin, даст уникальное значение для каждой строки в родительская таблица, но не вызывается каждый раз во внутреннем запросе – Seph