Eu tenho uma tabela do SQL Server com cerca de 50.000 linhas. Quero selecionar cerca de 5.000 dessas linhas aleatoriamente. Eu pensei em uma maneira complicada, criando uma tabela temporária com uma coluna "número aleatório", copiando minha tabela para isso, percorrendo a tabela temporária e atualizando cada linha com RAND()
e selecionando nessa tabela onde a coluna de número aleatório < 0.1 Estou procurando uma maneira mais simples de fazer isso, em uma única declaração, se possível.
Este artigo sugere o uso da NEWID()
função Parece promissor, mas não vejo como selecionar de maneira confiável uma certa porcentagem de linhas.
Alguém já fez isso antes? Alguma ideia?
sql
sql-server
random
John M Gant
fonte
fonte
Respostas:
Em resposta ao comentário "lixo puro" sobre tabelas grandes: você pode fazer assim para melhorar o desempenho.
O custo disso será a varredura principal dos valores mais o custo da junção, que em uma tabela grande com uma pequena seleção percentual deve ser razoável.
fonte
[yourPk]
refere? EDIT: Nvm, descobri ... Chave Primária. Durrrnewid()
custos de E / S de estimativa de classificação serão muito altos e afetarão o desempenho.Dependendo das suas necessidades,
TABLESAMPLE
você terá desempenho quase tão aleatório e melhor. isso está disponível no MS SQL Server 2005 e posterior.TABLESAMPLE
retornará dados de páginas aleatórias em vez de linhas aleatórias e, portanto, nem recupera os dados que não retornará.Em uma mesa muito grande eu testei
demorou mais de 20 minutos.
demorou 2 minutos.
O desempenho também melhorará em amostras menores,
TABLESAMPLE
enquanto isso não ocorreránewid()
.Lembre-se de que isso não é tão aleatório quanto o
newid()
método, mas fornecerá uma amostra decente.Veja a página do MSDN .
fonte
newid () / order by funcionará, mas será muito caro para grandes conjuntos de resultados porque ele precisa gerar um ID para cada linha e, em seguida, classificá-los.
TABLESAMPLE () é bom do ponto de vista de desempenho, mas você obterá agrupamentos de resultados (todas as linhas em uma página serão retornadas).
Para uma amostra aleatória verdadeira com melhor desempenho, a melhor maneira é filtrar as linhas aleatoriamente. Encontrei o seguinte exemplo de código no artigo Manuais Online do SQL Server Limitando conjuntos de resultados usando TABLESAMPLE :
Quando executado em uma tabela com 1.000.000 de linhas, eis meus resultados:
Se você conseguir usar o TABLESAMPLE, ele fornecerá o melhor desempenho. Caso contrário, use o método newid () / filter. newid () / order by deve ser o último recurso se você tiver um grande conjunto de resultados.
fonte
NewID()
é avaliado apenas uma vez, em vez de por linha, o que eu não gosto ...A seleção aleatória de linhas de uma tabela grande no MSDN possui uma solução simples e bem articulada que trata das preocupações de desempenho em larga escala.
fonte
RAND()
que não retorna o mesmo valor para cada linha (o que anularia aBINARY_CHECKSUM()
lógica). É porque está sendo chamado dentro de outra função em vez de fazer parte da cláusula SELECT?rand()
ou uma combinação dos itens acima - mas eu me afastei dessa solução por esse motivo. Além disso, o número de resultados variou de 1 a 5, portanto, isso também pode não ser aceitável em alguns cenários.RAND()
retorna o mesmo valor para cada linha (é por isso que esta solução é rápida). No entanto, linhas com somas de verificação binárias muito próximas correm alto risco de gerar resultados semelhantes de soma de verificação, causando aglomeração quandoRAND()
pequeno. Por exemplo,(ABS(CAST((BINARY_CHECKSUM(111,null,null) * 0.1) as int))) % 100
==SELECT (ABS(CAST((BINARY_CHECKSUM(113,null,null) * 0.1) as int))) % 100
. Se os seus dados sofre deste problema, multipliqueBINARY_CHECKSUM
por 9923.Este link tem uma comparação interessante entre Orderby (NEWID ()) e outros métodos para tabelas com 1, 7 e 13 milhões de linhas.
Freqüentemente, quando perguntas sobre como selecionar linhas aleatórias são feitas em grupos de discussão, a consulta NEWID é proposta; é simples e funciona muito bem para pequenas mesas.
No entanto, a consulta NEWID tem uma grande desvantagem quando você a usa para tabelas grandes. A cláusula ORDER BY faz com que todas as linhas da tabela sejam copiadas no banco de dados tempdb, onde são classificadas. Isso causa dois problemas:
O que você precisa é uma maneira de selecionar linhas aleatoriamente que não usarão o tempdb e não ficarão muito mais lentas à medida que a tabela aumentar. Aqui está uma nova idéia de como fazer isso:
A idéia básica por trás dessa consulta é que queremos gerar um número aleatório entre 0 e 99 para cada linha da tabela e escolher todas as linhas cujo número aleatório é menor que o valor da porcentagem especificada. Neste exemplo, queremos aproximadamente 10% das linhas selecionadas aleatoriamente; portanto, escolhemos todas as linhas cujo número aleatório é menor que 10.
Leia o artigo completo no MSDN .
fonte
Se você (ao contrário do OP) precisar de um número específico de registros (o que dificulta a abordagem CHECKSUM) e desejar uma amostra mais aleatória do que a TABLESAMPLE fornece por si só, e também desejar uma velocidade melhor que a CHECKSUM, poderá se contentar com uma fusão da Métodos TABLESAMPLE e NEWID (), assim:
No meu caso, esse é o compromisso mais direto entre aleatoriedade (não é realmente, eu sei) e velocidade. Varie a porcentagem (ou linhas) de TABLESAMPLE conforme apropriado - quanto maior a porcentagem, mais aleatória a amostra, mas espere uma queda linear na velocidade. (Observe que TABLESAMPLE não aceitará uma variável)
fonte
Apenas ordene a tabela por um número aleatório e obtenha as primeiras 5.000 linhas usando
TOP
.ATUALIZAR
Apenas tentei e uma
newid()
ligação é suficiente - não há necessidade de todos os elencos e toda a matemática.fonte
Essa é uma combinação da ideia inicial da semente e de uma soma de verificação, que parece fornecer resultados aleatórios corretamente sem o custo de NEWID ():
fonte
No MySQL você pode fazer isso:
fonte
Ainda não vi essa variação nas respostas. Eu tinha uma restrição adicional onde precisava, com base em uma semente inicial, para selecionar o mesmo conjunto de linhas a cada vez.
Para MS SQL:
Exemplo mínimo:
Tempo de execução normalizado: 1,00
Exemplo NewId ():
Tempo de execução normalizado: 1,02
NewId()
é insignificantemente mais lento querand(checksum(*))
, portanto, você pode não querer usá-lo em grandes conjuntos de registros.Seleção com semente inicial:
Se você precisar selecionar o mesmo conjunto dado uma semente, isso parece funcionar.
fonte
Tente o seguinte:
fonte
Parece que newid () não pode ser usado na cláusula where, portanto, esta solução requer uma consulta interna:
fonte
Eu estava usando-o na subconsulta e ele me retornou as mesmas linhas na subconsulta
então eu resolvi com a inclusão de variável de tabela pai em que
Observe a condição where
fonte
A linguagem de processamento do servidor em uso (por exemplo, PHP, .net, etc) não é especificada, mas se for PHP, pegue o número necessário (ou todos os registros) e, em vez de fazer aleatoriamente a consulta, use a função aleatória do PHP. Não sei se o .net tem uma função equivalente, mas se o fizer, use-o se você estiver usando o .net
ORDER BY RAND () pode ter uma penalidade de desempenho, dependendo de quantos registros estão envolvidos.
fonte
Isso funciona para mim:
fonte
select top 10 percent from table_name order by rand()
, mas isso também não funciona porque rand () retorna o mesmo valor em todas as linhas.