Случайный выбор не всегда возвращает одну строку - PullRequest
2 голосов
/ 14 июня 2010

Цель следующего (упрощенного) фрагмента кода - вернуть одну случайную строку .К сожалению, когда мы запускаем этот фрагмент в анализаторе запросов, он возвращает от нуля до трех результатов.

Поскольку наша входная таблица состоит из ровно 5 строк с уникальными идентификаторами, и когда мы выполняем выборку для этой таблицы, где идентификатор равен случайному числу, мы озадачены тем, что когда-либо будет больше, чемвернулся один ряд.

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

DECLARE @Table TABLE (
  ID INTEGER IDENTITY (1, 1)
  , FK1 INTEGER
)

INSERT INTO @Table
SELECT 1
UNION ALL SELECT 2
UNION ALL SELECT 3
UNION ALL SELECT 4
UNION ALL SELECT 5

SELECT  *
FROM    @Table 
WHERE   ID = ABS(CHECKSUM(NEWID())) % 5 + 1

Редактировать

Наше использованиеСценарий таков (пожалуйста, не комментируйте, правильно ли это делать или нет. Это решающие силы)

В конечном итоге мы должны создать результат с реалистичным значений, при которых комбинация производителя и весов равна обфусцированному путем случайного выбора существующих весов из самой таблицы.
Тогда запрос станет примерно таким (также причина, по которой RAND не можетбыть использованы)

SELECT  t.ID
        , FK1 = (SELECT FK1 FROM @Table WHERE ID=ABS(CHECKSUM(NEWID())) % 5 + 1)
FROM    @Table t

Поскольку внутреннее выделение может возвращать ноль результатов, оно будет возвращать значение NULL, которое снова неприемлемо.Это вопрос о том, почему внутренний отбор возвращает результаты от нуля до x, что этот вопрос возник (это даже английский?).

Ответ

То, что зажгло свет для меня, былопростое наблюдение, что ABS(CHECKSUM(NEWID())) % 5 + 1) был переоценен для каждой строки .У меня сложилось впечатление, что ABS(CHECKSUM(NEWID())) % 5 + 1) будет оцениваться один раз , а затем совпадать.

Спасибо всем за ответы и медленно, но верно ведущие меня к лучшему пониманию.

Ответы [ 6 ]

8 голосов
/ 14 июня 2010

Причина, по которой это происходит, заключается в том, что NEWID () выдает различное значение для каждой строки в таблице.Для каждого ряда, независимо от других , существует шанс возврата одного из пяти.Следовательно, в действительности у вас есть шанс 1 на 3125, что все 5 строк будут возвращены!

Чтобы увидеть это, выполните следующий запрос.Вы увидите, что каждая строка получает свой идентификатор.

SELECT  * , NEWID()
FROM    @Table  

Это исправит ваш код:

DECLARE @Id int
SET @Id = ABS(CHECKSUM(NEWID())) % 5 + 1

SELECT  * 
FROM    @Table  
WHERE   ID = @Id

Однако я не уверен, что это самый эффективный методвыбирая одну случайную строку из таблицы.

Эта статья MSDN может оказаться полезной: http://msdn.microsoft.com/en-us/library/Aa175776 (Случайная выборка в T-SQL)

РЕДАКТИРОВАНИЕ 1 : теперь я думаю об этом, это, вероятно, самый эффективный способ сделать это, при условии, что количество строк остается фиксированным и идентификаторы гарантированно будут смежными .

РЕДАКТИРОВАТЬ 2 : чтобы достичь желаемого результата при использовании в качестве подзапроса, используйте TOP 1 следующим образом:

SELECT  t.ID 
        , FK1 = (SELECT TOP 1 FK1 FROM @Table ORDER BY NEWID()) 
FROM    @Table t
2 голосов
/ 14 июня 2010

Немного догадка, и вы не уверены, что SQL работает таким образом, но разве SQL не оценит "ABS (CHECKSUM (NEWID ()))% 5 + 1" для каждой строки в таблице? Если это так, то каждая оценка может возвращать или не возвращать значение идентификатора текущей строки.

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

declare @targetRandom int
set @targetRandom = ABS(CHECKSUM(NEWID())) % 5 + 1

select * from @table where ID = @targetRandom
1 голос
/ 14 июня 2010

Это может помочь вам понять причины.Запустите запрос несколько раз.Сколько раз MY_FILTER = ID?

SELECT  *, ABS(CHECKSUM(NEWID())) % 5 + 1 AS MY_FILTER
FROM    @Table

SELECT  *, ABS(CHECKSUM(NEWID())) % 5 + 1 AS MY_FILTER
FROM    @Table

SELECT  *, ABS(CHECKSUM(NEWID())) % 5 + 1 AS MY_FILTER
FROM    @Table
1 голос
/ 14 июня 2010

Или вы можете использовать RAND () вместо NEWID (), который оценивается только один раз для запроса в MS SQL

Если вы хотите использовать CHECKSUM для получения случайной строки, это способ сделатьэто.

SELECT TOP 1 *
FROM @Table
ORDER BY CHECKSUM(NEWID())

а как же?

SELECT  t.ID 
        , FK1 = (SELECT TOP 1 FK1 FROM @Table ORDER BY NEWID()) 
FROM    @Table t 
1 голос
/ 14 июня 2010

Попробуйте следующее, чтобы увидеть, что происходит:

SELECT  ABS(CHECKSUM(NEWID())) % 5 + 1 AS Number, @Table.*
FROM    @Table 
WHERE   ID = Number
0 голосов
/ 14 июня 2010

Я не знаю, насколько это будет полезно для вас, но попробуйте это ... Все, что я понял, это то, что вы хотите одну случайную строку каждый раз, когда вы выполняете запрос ..

select top 1 newid() as row,ID from @Table order by row

ВотлогикаКаждый раз, когда вы выполняете запрос, каждой строке присваивается newid, и все они уникальны, и вы просто упорядочиваете их с новым уникальным сгенерированным rowid.Тогда все, что вам нужно сделать, это выбрать самый верх или все, что вы хотите ..

...