powered by simpleCommunicator - 2.0.60     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / Случайное число
18 сообщений из 18, страница 1 из 1
Случайное число
    #32025190
Underking
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Столкнулся с проблемой, как получить случайное число в селекте, желательно чтоб не наворачивать зверскую формулу. Причем чтоб каждый раз был свой набор чисел, чтоб не привязываться к значениям каих-либо полей в таблице.

Можно получить уникальны идентификатор NEWID, но тогда строку с шестнадцатеричным числом надо перевести в число. Как его можно перевести?
...
Рейтинг: 0 / 0
Случайное число
    #32025194
dmitry
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
A rand() - чем не подходит?
...
Рейтинг: 0 / 0
Случайное число
    #32025203
Фотография alexeyvg
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
2Dmitry
А Вы попробуйте:

select rand()
from sysobjects
...
Рейтинг: 0 / 0
Случайное число
    #32025243
dmitry
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ну если Вы хотите использовать именно в запросе и чтоб для каждой его строки было свое значение, то можно так
select convert (bigint,convert(varbinary,'0x'+convert(varchar(36),(newid()))))
from sysobjects

Если лениво каждый раз такую штуку писать, а использовать надо часто, то просто создайте UDF, которое будет преобразовывать newid() в число и вызывать ее соответственно потом как Select dbo.myrnd(newid()) from sysobjects
...
Рейтинг: 0 / 0
Случайное число
    #32025252
Underking
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
>Dmitry
Это то что нужно, спасибо.
...
Рейтинг: 0 / 0
Случайное число
    #32025257
dmitry
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Хм... Щас вот еще посмотрел, и пришел к выводу что это не совсем правильно. Т.е. для Вас может сгодиться и в таком виде, но преобразование некорректно (т.е. если получаемое число не является записью этого newid() в десятиричной системе). Поэтому лучше самому указать на ошибку чем это сделает кто-то другой


Во-первых, собственно, Bigint имеет диапазон -2^63 - 2^63-1
, а у uniqueidentifier (если без палочек) - 0 - 256^16=2^128-1
Поэтому, стремиться перевести его корректно в bigint не стоит
Это также означает, что хоть uniqueidentifier обеспечивает глобальную уникальность - кусок его (например полученный через Right) (который можно перевести в bigint) не будет. (Опять же просто для получения случайного числа этого не требуется, вот если б было надо обязательно уникальное случ. число...)

Во вторых, собственно некорректен перевод строки, представляющей шестнадцатиричную запись в число (в десятичной форме через перевод в varbinary).
Я сделал это потому как напрямую нельзя сделать
select convert(bigint,'0xA'),
хотя можно select convert(bigint,0xA)

Но и перевод через varbinary - неправилен, ибо select convert(varbinary(1),'A') даст 0x61, т.е. ASCII код символа 'А', а не число 10 (шеснадцатирич. -> десят. перевод). И т.д.

Поэтому, для получения случ. числа, наверное можно использовать этот метод, но для преобразвания строки, представляющей шестнадцатиричную запись числа в десятичную - нельзя.
Может я торможу и не соображу как это сделать просто... Может кто-то подскажет.

Но можно решить и в лоб, например так ("корректный" перевод части uniqueidentifier-а в bigint):

CREATE FUNCTION dbo.myrand(@uid uniqueidentifier)
RETURNS bigint AS
BEGIN
declare @a varchar(36),
@b bigint, @i int, @c tinyint
set @a=convert(varchar(36),@uid)
set @a= right(@a,12)
set @b=0
Set @i=11
while @i>=0 begin
if substring(@a,@i,1)>='A'
set @c=ascii(substring(@a,@i,1))
else
set @c=convert(tinyint,substring(@a,@i,1))
set @b=@b+@c*power(convert(bigint,16),@i)
set @i=@i-1
end
return @b
END
...
Рейтинг: 0 / 0
Случайное число
    #32025264
dmitry
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Эээх, не мой сегодня день...


CREATE FUNCTION dbo.myrand(@uid uniqueidentifier)
RETURNS bigint AS
BEGIN
declare @a varchar(36),
@b bigint, @i int, @c tinyint
set @a=convert(varchar(36),@uid)
set @a= right(@a,12)
set @b=0
Set @i=1
while @i<=12 begin
if substring(@a,@i,1)>='A'
set @c=ascii(substring(@a,@i,1))-55
else
set @c=convert(tinyint,substring(@a,@i,1))
set @b=@b+@c*power(convert(bigint,16),12-@i)
set @i=@i+1
end
return @b
END
...
Рейтинг: 0 / 0
Случайное число
    #32025266
Фотография alexeyvg
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Пожалуйста, не извращайтесь с newid().
Смысл случайного числа - не в неповторяемости значений, а в случайном (может, равномерном?) разбросе значений в некотором диапазоне, чего newid() не обеспечит.
...
Рейтинг: 0 / 0
Случайное число
    #32025349
Underking
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Я нашел 2 способа получить случайное число, rand() и newid(). Но т.к. использовать его надо внутри запроса, rand не очень подходит. По умолчанию он считает случайное число относительно даты, поэтому в запросе оно всегда получится одинаковое. Поэтому нужно придумать какой-нибудь аргумент для rand. Можно взять какое-нибудь поле для этого, но тогда каждый раз будет один и тот же набор случайных чисел, а для меня это не годится.

Собственно говоря надо было из большой базы вопросов отобрать несколько по определенному условию и полученные по одному выдавать клиенту в случайной последовательности. Повторение вопросов допускается.
...
Рейтинг: 0 / 0
Случайное число
    #32025373
Glory
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
rand не очень подходит. По умолчанию он считает случайное число относительно даты, поэтому в запросе оно всегда получится одинаковое
Можно и разное получить

SELECT id *rand(), (id)*RAND((DATEPART(mm, GETDATE()) * 100000)+ (DATEPART(ss, GETDATE()) * 1000 )+ DATEPART(ms, GETDATE()))
from sysobjects

По идеи если во 2-3 столбце отбросить целую часть, то можно получить какое-никакое случайное распределение.
...
Рейтинг: 0 / 0
Случайное число
    #32025394
dmitry
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
2 Glory: Из самого первого сообщения, т.е. постановки задачи: "чтоб не привязываться к значениям каих-либо полей в таблице."
А у Вас все завязано на id...
Множитель же на который умножается id - будет одинаковый для всех строк. Т.е. чем больше id, тем больше "случайное число"
Отбросить целую часть - идея ничего, но все равно получается фикция:
Пусть есть 2 строки со значениями id отличающимися в 2 раза (например 1 и 2)
Тогда после перемножения получим, что "случайные" числа отличаются в 2 раза. После отбрасывания целой части будет всего две возможности:
1) в случае, если дробная часть произведения была <0.5 . В этом случае после отбрасывания целой части получим что "случайные" числа для этих строк опять же отличаются в 2 раза.
например, cч1=1.38*1-floor(1.38*1)=0.38
сч2=1.38*2-floor(1.38*2)=0.78
сч2=сч1*2
2) в случае, если дробная часть произведения была >0.5 . В этом случае после отбрасывания целой части получим
что "случайные" числа для этих строк соотносятся по формуле cч1=(сч2+1)/2.
например, cч1=1.56*1-floor(1.56*1)=0.56
сч2=1.56*2-floor(1.56*2)=0.12
сч1=(сч1+1)/2

Для других id отличающихся в 2 раза, но не 1 и 2 - та же ситуация. только немного другое условие - не др. часть произведения> (или &lt0.5, а др. часть от (произведение*id1/2) > (или &lt 0.5, где id1 - меньший из id
Для id, отличающихся не в 2, а n раз - будет просто больше возможностей, но все равно останется кусочно-заданная зависимость.

Т.е. я хочу сказать что все равно получается слишком боьшая корреляция... А для случайных чисел это недопустимо

Проблема то в том, что rand, getdate и т.п. вычисляются в момент начала выполнения запроса и будут применены ко всем кортежам. Согласен, что newid() не предназначен для таких целей, но зато он вычисляется для каждого кортежа отдельно, поэтому может все таки стоит его применить для данной задачи.


2 alexeyvg: ОПять же повторюсь, что согласен, что newid() для этого не предназначен, но предложите чего-нибудь лучше.
Что касается уникальности получаемого "случайного" числа, то оно тут и не обеспечивается, потому что как я писал выше, придется использовать лишь часть от newid(), а уникальность этой части не гарантируется.
...
Рейтинг: 0 / 0
Случайное число
    #32025400
Фотография Александр Степанов
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
А если сделать так:
\nSELECT id *rand(), (id)*RAND((DATEPART(mm, GETDATE()) * 100000)+ (DATEPART(ss, GETDATE()) * 1000 )+ DATEPART(ms, GETDATE())), RAND((DATEPART(mm, GETDATE()) * 100000)+ (DATEPART(ss, GETDATE()) * 1000 )+ DATEPART(ms, GETDATE()))
from sysobjects

то станет ясно, что во всех возвращаемых строках "RAND((DATEPART(mm..." принимает одно и то же значение, домножаемое на id, так что между первым и вторым столбцами различия, в общем-то, нет


Так что курсор, господа, курсор
...
Рейтинг: 0 / 0
Случайное число
    #32025401
Glory
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Полностью согласен
Проблема не в том, чтобы сгенерировать правильно случайное число (способы для это известны), а именно в том как SQL вычисляет значения функций, не привязанных к полю запроса.
...
Рейтинг: 0 / 0
Случайное число
    #32025403
Фотография alexeyvg
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
2Dmitry
Предложение простое - получать список уникальных значений из rand() в цикле.
...
Рейтинг: 0 / 0
Случайное число
    #32025432
Underking
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Мы оставили без внимание простое решение, вернее модификацию уже предложенного.
select rand(convert (int,convert(varbinary,'0x'+convert(varchar(36),(newid())))))
from sysobjects
Или в этом варианте тоже есть недостатки?
...
Рейтинг: 0 / 0
Случайное число
    #32025434
Glory
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Или в этом варианте тоже есть недостатки?

В одной из прошлых дискуссий на тему newid() кажется SergSper говорил, что у него наблюдался эффект последовательной генерации значений через определенный промежуток. Мне лично на 2-х моих серверах такого эффекта добиться не удалось, поэтому как говорится "100% гарантию вам может дать только страховой полис"
...
Рейтинг: 0 / 0
Случайное число
    #32025477
dmitry
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
2 Alexeyvg: Теперь уже и Вы заговариваетесь - случайных, а не уникальных

А вообще - наверное для того, чтобы иметь тот самый страховой полис, о котором говорил Glory, придется таки пойти на такую жертву (курсор), хотя конечно же, хотелось чего-нить покрасивше, одним взмахом волшебной палочки
...
Рейтинг: 0 / 0
Случайное число
    #32025492
Mart
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
1. Довольно часто пользуюсь следующим алгоритмом для заполнения БД большим количеством тестовых значений
rand(id) * rand(datepart(ms,getdate())) * 100000000 - floor(rand(id) * rand(datepart(ms,getdate())) * 100000000)

Для тестирования распределения случайной величины можно воспользоваться скриптиком
create table t_test (id int identity, i int)
go
set nocount on
declare
@i int
set @i = 0
while @i < 100000 begin
set @i = @i + 1
insert into t_test (i)
select @i from sysobjects
end
go
select
ceiling(rand_value * 10),
count(ceiling(rand_value * 10))
from (
select
rand(id) * rand(datepart(ms,getdate())) * 100000000 -
floor(rand(id) * rand(datepart(ms,getdate())) * 100000000) rand_value
from
t_test
) a
group by ceiling(rand_value * 10)
order by ceiling(rand_value * 10)
go
drop table t_test

Полученная выборка при достаточно большом объеме записей говорит о равномерном распределении
----------------------------------------------------- -----------
1.0 410003
2.0 409997
3.0 409999
4.0 410002
5.0 409998
6.0 409999
7.0 410006
8.0 409999
9.0 410004
10.0 409993

Underking говорил, что все это нужно только для того, чтобы выборку "...выдавать клиенту в случайной последовательности"

2. Для случайной выборки в случайном порядке можно воспользоваться следующим
declare @_htmt nvarchar(255), @_max_rows int
set @_max_rows = 10
set @_htmt = N'select top ' + ltrim(str(ceiling(rand() * @_max_rows)))+ ' a.id from sysobjects a order by newid()'
exec (@_htmt)

3. Если необходим просто случайный порядок в выборке, то можно так попробовать
select a.id from sysobjects a order by newid() -- если newid() не последовательно генерирует значения
или
select a.id from sysobjects a order by
rand(id) * rand(datepart(ms,getdate())) * 100000000 -
floor(rand(id) * rand(datepart(ms,getdate())) * 100000000)
...
Рейтинг: 0 / 0
18 сообщений из 18, страница 1 из 1
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / Случайное число
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]