Eu tenho uma consulta em que desejo que os registros resultantes sejam ordenados aleatoriamente. Ele usa um índice em cluster, portanto, se eu não incluir um order by
, provavelmente retornará registros na ordem desse índice. Como posso garantir um pedido aleatório de linhas?
Entendo que provavelmente não será "verdadeiramente" aleatório, o pseudo-aleatório é bom o suficiente para minhas necessidades.
sql-server
goric
fonte
fonte
CryptGenRandom
no final. dba.stackexchange.com/a/208069/3690A primeira sugestão do Pradeep Adiga,,
ORDER BY NEWID()
é boa e algo que usei no passado por esse motivo.Cuidado ao usar
RAND()
- em muitos contextos, ele é executado apenas uma vez por instrução, portantoORDER BY RAND()
não terá efeito (pois você está obtendo o mesmo resultado de RAND () para cada linha).Por exemplo:
retorna cada nome da nossa tabela pessoal e um número "aleatório", que é o mesmo para cada linha. O número varia cada vez que você executa a consulta, mas é o mesmo para cada linha de cada vez.
Para mostrar que o mesmo é o caso de
RAND()
usado em umaORDER BY
cláusula, tento:Os resultados ainda são ordenados pelo nome, indicando que o campo de classificação anterior (aquele que se espera que seja aleatório) não tem efeito, portanto, presumivelmente, sempre tem o mesmo valor.
A ordenação por
NEWID()
funciona, no entanto, porque se NEWID () nem sempre fosse reavaliado, a finalidade dos UUIDs seria quebrada ao inserir muitas novas linhas em uma statemnt com identificadores exclusivos, conforme a chave:não pedir os nomes "aleatoriamente".
Outros DBMS
O acima exposto é verdadeiro para o MSSQL (pelo menos em 2005 e 2008, e se bem me lembro de 2000). Uma função que retorne um novo UUID deve ser avaliada sempre que todos os DBMSs NEWID () estiverem no MSSQL, mas vale a pena verificar isso na documentação e / ou nos seus próprios testes. É mais provável que o comportamento de outras funções de resultado arbitrário, como RAND (), varie entre DBMSs; verifique novamente a documentação.
Também vi a ordenação por valores UUID sendo ignorada em alguns contextos, pois o banco de dados assume que o tipo não possui ordenação significativa. Se você achar que esse é o caso, converta explicitamente o UUID para um tipo de seqüência de caracteres na cláusula de ordenação ou agrupe alguma outra função ao redor, como
CHECKSUM()
no SQL Server (pode haver uma pequena diferença de desempenho disso também, pois a ordenação será feita em valores de 32 bits e não de 128 bits, embora o benefício disso supere o custo de execuçãoCHECKSUM()
por valor primeiro, deixarei você testar).Nota
Se você deseja uma ordem arbitrária, mas um tanto repetível, solicite por algum subconjunto relativamente descontrolado dos dados nas próprias linhas. Por exemplo, um ou estes retornarão os nomes em uma ordem arbitrária, mas repetível:
Ordens arbitrárias, mas repetíveis, geralmente não são úteis em aplicativos, mas podem ser úteis em testes, se você quiser testar algum código nos resultados em uma variedade de ordens, mas desejar repetir cada execução da mesma maneira várias vezes (para obter um tempo médio resultados em várias execuções ou testar se uma correção feita no código remove um problema ou ineficiência destacado anteriormente por um determinado conjunto de resultados de entrada ou apenas para testar se o seu código é "estável" e retorna o mesmo resultado sempre se enviou os mesmos dados em uma determinada ordem).
Esse truque também pode ser usado para obter resultados mais arbitrários de funções, que não permitem chamadas não determinísticas como NEWID () dentro de seus corpos. Novamente, isso não é algo que provavelmente seja útil no mundo real, mas pode ser útil se você quiser que uma função retorne algo aleatório e "random-ish" seja bom o suficiente (mas tenha cuidado para lembrar as regras que determinam quando as funções definidas pelo usuário são avaliadas, ou seja, geralmente apenas uma vez por linha, ou seus resultados podem não ser o que você espera / exige).
atuação
Como aponta EBarr, pode haver problemas de desempenho com qualquer uma das opções acima. Por mais de algumas linhas, você tem quase a garantia de ver a saída em spool para tempdb antes que o número solicitado de linhas seja lido na ordem correta, o que significa que, mesmo se você estiver procurando pelas 10 principais, poderá encontrar um índice completo A verificação (ou pior, a verificação da tabela) acontece junto com um enorme bloco de gravação no tempdb. Portanto, pode ser de vital importância, como na maioria das coisas, fazer benchmarks com dados realistas antes de usá-los na produção.
fonte
Esta é uma pergunta antiga, mas um aspecto da discussão está faltando, na minha opinião - DESEMPENHO.
ORDER BY NewId()
é a resposta geral. Quando fantasia alguém get acrescentam que você realmente deve envolverNewID()
emCheckSum()
, você sabe, para o desempenho!O problema com esse método é que você ainda garante uma verificação completa do índice e, em seguida, uma espécie completa dos dados. Se você trabalhou com qualquer volume de dados sério, isso pode rapidamente se tornar caro. Veja este plano de execução típico e observe como a classificação leva 96% do seu tempo ...
Para dar uma idéia de como isso é dimensionado, darei dois exemplos de um banco de dados com o qual trabalho.
Order By newid()
nesta tabela gera 53.700 leituras e leva 16 segundos.A moral da história é que, se você tiver tabelas grandes (pense em bilhões de linhas) ou precisar executar essa consulta com frequência, o
newid()
método será quebrado. Então, o que um garoto deve fazer?Conheça TABLESAMPLE ()
No SQL 2005, um novo recurso chamado
TABLESAMPLE
foi criado. Eu só vi um artigo discutindo seu uso ... deveria haver mais. Documentos do MSDN aqui . Primeiro um exemplo:A idéia por trás da amostra da tabela é fornecer aproximadamente o tamanho do subconjunto solicitado. O SQL numera cada página de dados e seleciona X por cento dessas páginas. O número real de linhas que você recebe pode variar com base no que existe nas páginas selecionadas.
Então, como eu o uso? Selecione um tamanho de subconjunto que cubra mais do que o número de linhas necessárias e adicione a
Top()
. A idéia é que você possa fazer com que sua mesa ginormous pareça menor antes do tipo caro.Pessoalmente, tenho usado para limitar o tamanho da minha tabela. Portanto, nessa tabela de milhões de linhas,
top(20)...TABLESAMPLE(20 PERCENT)
a consulta cai para 5600 leituras em 1600ms. Há também umaREPEATABLE()
opção em que você pode passar um "Seed" para a seleção da página. Isso deve resultar em uma seleção de amostra estável.Enfim, apenas pensei que isso deveria ser adicionado à discussão. Espero que ajude alguém.
fonte
TABLESAMPLE()
base na quantidade de dados que possui. Eu acho queTABLESAMPLE(x ROWS)
isso nem garantiria que pelo menos asx
linhas fossem retornadas porque a documentação diz “O número real de linhas retornadas pode variar significativamente. Se você especificar um número pequeno, como 5, poderá não receber resultados na amostra. ”- então aROWS
sintaxe realmente ainda é apenas um disfarcePERCENT
?Muitas tabelas possuem uma coluna de ID numérica indexada relativamente densa (poucos valores ausentes).
Isso nos permite determinar o intervalo de valores existentes e escolher linhas usando valores de ID gerados aleatoriamente nesse intervalo. Isso funciona melhor quando o número de linhas a serem retornadas é relativamente pequeno e o intervalo de valores de ID é densamente preenchido (portanto, a chance de gerar um valor ausente é pequena o suficiente).
Para ilustrar, o código a seguir escolhe 100 usuários aleatórios distintos da tabela de usuários Estouro de pilha, que possui 8.123.937 linhas.
A primeira etapa é determinar o intervalo de valores de ID, uma operação eficiente devido ao índice:
O plano lê uma linha de cada extremidade do índice.
Agora, geramos 100 IDs aleatórios distintos no intervalo (com linhas correspondentes na tabela de usuários) e retornamos essas linhas:
O plano mostra que, nesse caso, eram necessários 601 números aleatórios para encontrar 100 linhas correspondentes. É bem rápido:
Experimente no Stack Exchange Data Explorer.
fonte
Como expliquei neste artigo , para embaralhar o conjunto de resultados SQL, você precisa usar uma chamada de função específica do banco de dados.
Portanto, supondo que tenhamos a seguinte tabela de banco de dados:
E as seguintes linhas na
song
tabela:No SQL Server, você precisa usar a
NEWID
função, conforme ilustrado no seguinte exemplo:Ao executar a consulta SQL acima mencionada no SQL Server, obteremos o seguinte conjunto de resultados:
fonte