O SQL Server oferece suporte a MAIOR e MENOS, se não, qual é a solução comum?

15

Revendo esta pergunta , parece que é preciso muito trabalho. Eles estão tentando estender um intervalo com uma data. Em outros bancos de dados, você apenas usaria greateste least..

least(extendDate,min), greatest(extendDate,max)

Quando tento usá-las, recebo

'least' is not a recognized built-in function name.
'greatest' is not a recognized built-in function name.

Isso cobriria a extensão em qualquer direção.

Para os fins da pergunta, você ainda teria que fazer a substituição exclusiva da faixa.

Só estou me perguntando como os usuários do SQL Server implementam padrões de consulta para imitar leaste greatestfuncionalidade.

Você desenrola as condições em CASEinstruções ou há uma extensão, complemento de terceiros ou licença da Microsoft que habilita essa funcionalidade?

Evan Carroll
fonte
É incrível que o MSSQL não tenha uma implementação para LEAST/ GREATESTfunções - quase todos os concorrentes do RDBMS têm pelo menos equivalentes. A única exceção que pude encontrar é o Sybase, mas que também foi descontinuado por muitos anos.
Bsplosion # 15/19
1
feedback.azure.com/forums/908035-sql-server/suggestions/... se você gostaria de votar para este
J Brune

Respostas:

32

Um método comum é a utilização da VALUEScláusula, e CROSS APPLYas duas colunas com alias como uma única coluna, em seguida obter a MINea MAXde cada um.

SELECT MIN(x.CombinedDate) AS least, MAX(x.CombinedDate) AS greatest
FROM   dbo.Users AS u
CROSS APPLY ( VALUES ( u.CreationDate ), ( u.LastAccessDate )) AS x ( CombinedDate );

Existem outras maneiras de escrevê-lo, por exemplo, usando UNION ALL

SELECT MIN(x.CombinedDate) AS least, MAX(x.CombinedDate) AS greatest
FROM   dbo.Users AS u
CROSS APPLY ( SELECT u.CreationDate UNION ALL SELECT u.LastAccessDate ) AS x(CombinedDate);

No entanto, os planos de consulta resultantes parecem ser os mesmos.

Erik Darling
fonte
12

Você também pode colocar os valores embutidos em uma subconsulta. Como isso:

select (select max(i) from (values (1), (2), (5), (1), (6)) AS T(i)) greatest,
       (select min(i) from (values (1), (2), (5), (1), (6)) AS T(i)) least
David Browne - Microsoft
fonte
3

Este seria um bom começo -

CASE WHEN A > B THEN A ELSE B END
Jim Gettma
fonte
É uma boa sugestão, mas foi mencionada na pergunta com "desenrolando a condição nas instruções CASE"
Evan Carroll
3

MENOS equivalente:

IIF(@a < @b, @a, @b)

MAIOR equivalente:

IIF(@a > @b, @a, @b)
Elnur
fonte
3
Como você faz isso por três ou mais valores, por exemplo least(5,6,7,8,9)?
A_horse_with_no_name 20/09/18
@a_horse_with_no_name Use IIF's aninhadas
Elnur
Essa abordagem rapidamente se tornaria um desafio para ler e verificar ... Como se sai em termos de desempenho?
Dodecaphone
0

Crio funções definidas pelo usuário, por exemplo

create function dbo.udf_LeastInt(@a int, @b int)
returns int
with schemabinding
as
begin
  return case when @a <= @b then @a 
              when @b < @a  then @b
              else null
         end
end

Embora possa funcionar em casos simples, existem vários problemas com essa abordagem:

  • Irritantemente, você precisa criar funções separadas para cada tipo de dados.
  • Ele lida com apenas 2 parâmetros, portanto, pode ser necessário mais funções para lidar com muitos parâmetros ou usar chamadas aninhadas das mesmas funções.
  • Seria melhor (mais eficiente) como um TVF embutido do que como uma função escalar. Isso tem a ver com a implementação de funções escalares no coração. Existem muitos blogs sobre isso, veja, por exemplo, SQL 101: Inibidores de Paralelismo - Funções Definidas pelo Usuário Escalares (por John Kehayias .
  • Se um dos argumentos for nulo, ele retornará nulo. Isso corresponde ao que o leastoperador faz no Oracle e MySQL, mas difere do Postgres. Mas essa proteção contra o nulo o torna mais detalhado (se você souber que eles não serão nulos, uma planície case when @a <= @b then @a else @b endfuncionaria).

Em suma, pode ser melhor escrever a casedeclaração à mão, se o desempenho for importante. Eu até recorri à geração de caseinstruções aninhadas no lado do cliente quando há vários valores para comparar.

Ed Avis
fonte
0

Pretendia adicionar um comentário à resposta @ ed-avis, mas não consegui fazê-lo, devido à falta de reputação, postando isso como extensão à sua resposta.

Eu eliminei a desvantagem de "Irritantemente você precisa criar funções separadas para cada tipo de dados". Usando SQL_VARIANT .

Aqui está a minha implementação:

CREATE OR ALTER FUNCTION my_least(@a SQL_VARIANT, @b SQL_VARIANT)
returns SQL_VARIANT
with schemabinding
as
begin
  return case when @a <= @b then @a 
              when @b < @a  then @b
              WHEN @a IS NULL THEN @b
              WHEN @b IS NULL THEN @a
              else null
         end
END;

Além disso, esta função lida com NULL s como a versão do postgresql.

Essa função pode ser adicionada ao DB por conveniência, mas é 10 vezes mais lenta que o uso interno IIF. Meus testes mostram que essa função com o tipo exato ( datetime ) executa o mesmo que a versão sql_variant .

PS: Eu executo alguns testes no conjunto de dados de valores de 350k e parece que o desempenho é o mesmo, sql_variant é um pouco mais rápido, mas acredito que seja apenas nervosismo.

Mas de qualquer maneira a versão IIF é 10x vezes mais rápida !!!

Eu não testei em linha, CASE WHENmas basicamente o t-sql IIF é o mesmo que o caso , e o iif get é convertido pelo otimizador em expressão de caso.

O fato de o IIF ser traduzido para o CASE também afeta outros aspectos do comportamento dessa função.

CONCLUSÃO: É mais rápido usar o IIF se o desempenho importa, mas para a criação de protótipos ou se a clareza do código é mais necessária e não há grandes cálculos envolvidos, desde que a função possa ser usada.

Bogdan Mart
fonte
1
Você diz que "sqlvariant é um pouco mais rápido" e que "a versão IIF é 10x vezes mais rápida". mais rápido que o que?
precisa saber é o seguinte
A versão variante sql tem a mesma velocidade da versão concreete, como fornecida por outra resposta. No meu teste, foram 80ms depois (de 15seg), presumo que apenas erro de estatística. E o uso iif(a<b, a, b) é 10 vezes mais rápido que qualquer função definida pelo usuário.
Bogdan Mart
Para esclarecer, usei meu código com sql_variant substituído por datetime, como segunda função. Após testes, parece que sql_variant não adicionar qualquer sobrecarga, mas funções definidas pelo utilizador são a forma mais lenta do que built-in
Bogdan Mart
Mas alguma dessas funções - inclusive IIF()- é mais rápida do que usar uma CASEexpressão? O que quero dizer é que, desde que você enfrentou problemas no teste de desempenho, deve testar todos os métodos / respostas sugeridos.
precisa saber é o seguinte
1
@ yper-crazyhat-cubeᵀᴹ resposta atualizada. Será que não editá-lo mais, só queria adicionar comentário sobre sql_variant a resposta de Ed-avis, mas devido à falta de pintas tinha que escrever resposta expandida :-)
Bogdan Mart