Posso usar a instrução CASE em uma condição JOIN?

141

A imagem a seguir faz parte do Microsoft SQL Server 2008 R2 System Views. A partir da imagem, podemos ver que a relação entre sys.partitionse sys.allocation_unitsdepende do valor de sys.allocation_units.type. Então, para juntá-los, eu escreveria algo semelhante a este:

SELECT  *
FROM    sys.indexes i
        JOIN sys.partitions p
            ON i.index_id = p.index_id 
        JOIN sys.allocation_units a
            ON CASE
               WHEN a.type IN (1, 3)
                   THEN a.container_id = p.hobt_id 
               WHEN a.type IN (2)
                   THEN a.container_id = p.partition_id
               END 

Mas o código superior fornece um erro de sintaxe. Eu acho que é por causa da CASEdeclaração. Alguém pode ajudar a explicar um pouco?


Adicionar mensagem de erro:

Msg 102, Nível 15, Estado 1, Linha 6 Sintaxe incorreta próxima a '='.

esta é a imagem

Apenas um aprendiz
fonte
36
Qual software você usou para criar esse belo diagrama de banco de dados?
LearnByReading
2
@LearnByReading Você já descobriu qual software foi usado?
usar o seguinte comando
1
@ User632716 não infelizmente não!
LearnByReading
2
@ User632716 Embora eu realmente ache que foi o MySQL Workbench. Mas eu nunca recebi uma resposta
LearnByReading
@LearnByReading Não faço ideia. É fornecido pela Microsoft.
Apenas um aluno

Respostas:

223

Uma CASEexpressão retorna um valor da THENparte da cláusula. Você poderia usá-lo assim:

SELECT  * 
FROM    sys.indexes i 
    JOIN sys.partitions p 
        ON i.index_id = p.index_id  
    JOIN sys.allocation_units a 
        ON CASE 
           WHEN a.type IN (1, 3) AND a.container_id = p.hobt_id THEN 1
           WHEN a.type IN (2) AND a.container_id = p.partition_id THEN 1
           ELSE 0
           END = 1

Observe que você precisa fazer algo com o valor retornado, por exemplo, compará-lo com 1. Sua instrução tentou retornar o valor de uma atribuição ou teste de igualdade, nenhum dos quais faz sentido no contexto de uma cláusula CASE/ THEN. (Se BOOLEANfosse um tipo de dados, o teste de igualdade faria sentido.)

HABO
fonte
@ HABO obrigado que funcionou para mim ... mas o problema é que, quando faço isso, as condições caem ... por favor, diga-me como faço para quebrar isso?
Sagar Tandel
1
@ SagarTandel - Desculpe, eu não entendo "fazer uma queda" e "como faço para quebrá-lo". Você poderia esclarecer seu comentário? (Recentemente surgiu a partir de um mergulho off Saba Pode ser a Nitrox..)
HABO
Ele verifica todas as condições que eu não quero. Quero que saia do caso, uma vez que corresponda a uma condição.
Sagar Tandel
3
@SagarTandel - Do MSDN : "A instrução CASE avalia suas condições sequencialmente e para com a primeira condição cuja condição é atendida.". Se você quiser todas as linhas unidas que não correspondam a nenhuma das condições explicitamente declaradas, basta alterar o final da cauda de = 1para = 0, mas acho que você não gostará do resultado.
HABO 18/10/2013
JOIN sys.allocation_units a ON CASE WHEN a.type IN (1, 3) AND a.container_id = p.hobt_id THEN 1 WHEN a.type IN (2) AND a.container_id = p.partition_id THEN 1 ELSE 0 END = 1 você poderia escrevê-lo no quadro Entidade acima de sua solução
r.hamd
36

Em vez disso, você simplesmente JOIN para ambas as tabelas e, na sua cláusula SELECT, retorna dados daquela que corresponde:

Sugiro que você acesse este link Associações condicionais no SQL Server e declaração de caso T-SQL em uma cláusula JOIN ON

por exemplo

    SELECT  *
FROM    sys.indexes i
        JOIN sys.partitions p
            ON i.index_id = p.index_id 
        JOIN sys.allocation_units a
            ON a.container_id =
            CASE
               WHEN a.type IN (1, 3)
                   THEN  p.hobt_id 
               WHEN a.type IN (2)
                   THEN p.partition_id
               END 

Editar: conforme comentários.

Você não pode especificar a condição de junção como está fazendo. Verifique a consulta acima que não apresenta erro. Retirei a coluna comum e o valor da coluna direita será avaliado sob condição.

Niranjan Singh
fonte
1
o que conditional joinsignifica Cada junção (excluindo cruz) é uma condicional. Como esse caso é diferente de qualquer outro? Sua amostra tem associação interna com a condição, assim como a consulta OPs tem associação com a condição.
Zerkms
@zerkms: Concordo, parece confuso. Acredito que uma junção condicional nesse contexto significa uma junção cuja condição depende de outra condição.
precisa
@ Andy M: alguma razão para pensar em outro termo para uma condição trivial OR?
Zerkms
@zerkms: Er, sim, é porque parece confuso. :) Mas acredito que você quis perguntar se havia alguma razão para não pensar em outro termo; nesse caso, não tenho certeza. Se você está perguntando por meus motivos, bem, acho que não me incomodei o suficiente. :) Que tal uma junção com condições alternativas ? Receio não ser muito bom em cunhar termos. Observe, porém, que conceitualmente se trata de "condição condicional", não de "condição com OR". Usar ORé apenas uma maneira de implementá-lo.
precisa
@Andriy M: ok. Mas, pessoalmente, ainda não vejo o motivo para dar um nome à condição trivial com 1 ORe 2 ANDsou com 1 CASE. É uma consulta de rotina que não tem nada que a diferencie de qualquer outra.
Zerkms
17

Tente o seguinte:

...JOIN sys.allocation_units a ON 
  (a.type=2 AND a.container_id = p.partition_id)
  OR (a.type IN (1, 3) AND a.container_id = p.hobt_id)
richardtallent
fonte
Mesmo que funcionasse - a consulta na pergunta parece perfeitamente válida. Portanto, ainda não explica o que há de errado com o código do OP
zerkms
10

Eu acho que você precisa de duas declarações de caso:

SELECT  *
FROM    sys.indexes i
    JOIN sys.partitions p
        ON i.index_id = p.index_id 
    JOIN sys.allocation_units a
        ON 
        -- left side of join on statement
            CASE
               WHEN a.type IN (1, 3)
                   THEN a.container_id
               WHEN a.type IN (2)
                   THEN a.container_id
            END 
        = 
        -- right side of join on statement
            CASE
               WHEN a.type IN (1, 3)
                   THEN p.hobt_id
               WHEN a.type IN (2)
                   THEN p.partition_id
            END             

Isto é porque:

  • a instrução CASE retorna um único valor no END
  • a instrução ON compara dois valores
  • sua instrução CASE estava fazendo a comparação dentro da instrução CASE. Suponho que, se você colocar sua instrução CASE no seu SELECT, obteria um booleano '1' ou '0' indicando se a instrução CASE foi avaliada como Verdadeira ou Falsa
DonkeyKong
fonte
5

Peguei seu exemplo e editei:

SELECT  *
FROM    sys.indexes i
    JOIN sys.partitions p
        ON i.index_id = p.index_id 
    JOIN sys.allocation_units a
        ON a.container_id = (CASE
           WHEN a.type IN (1, 3)
               THEN p.hobt_id 
           WHEN a.type IN (2)
               THEN p.partition_id
           ELSE NULL
           END)
Gont
fonte
1

Isso parece legal

https://bytes.com/topic/sql-server/answers/881862-joining-different-tables-based-condition

FROM YourMainTable
LEFT JOIN AirportCity DepCity ON @TravelType = 'A' and DepFrom =  DepCity.Code
LEFT JOIN AirportCity DepCity ON @TravelType = 'B' and SomeOtherColumn = SomeOtherColumnFromSomeOtherTable

fonte
Ao tentar isso, estou recebendo um erro: O nome da correlação 'xx' é especificado várias vezes em uma cláusula FROM.
Etienne
1

Sim você pode. Aqui está um exemplo.

SELECT a.*
FROM TableA a
LEFT OUTER JOIN TableB j1 ON  (CASE WHEN LEN(COALESCE(a.NoBatiment, '')) = 3 
                                THEN RTRIM(a.NoBatiment) + '0' 
                                ELSE a.NoBatiment END ) = j1.ColumnName 
Stefan Gabor
fonte
1
Embora esse código possa resolver a questão, incluir uma explicação de como e por que isso resolve o problema realmente ajudaria a melhorar a qualidade da sua postagem e provavelmente resultaria em mais votos positivos. Lembre-se de que você está respondendo à pergunta dos leitores no futuro, não apenas à pessoa que está perguntando agora. Por favor edite sua resposta para adicionar explicações e dar uma indicação do que limitações e premissas se aplicam.
bip duplo
0

Tomou o exemplo de DonkeyKong.

O problema é que eu precisava usar uma variável declarada. Isso permite indicar o lado esquerdo e direito do que você precisa comparar. Isso serve para dar suporte a um relatório do SSRS em que campos diferentes devem ser vinculados com base na seleção do usuário.

O caso inicial define a escolha do campo com base na seleção e, em seguida, posso definir o campo que eu preciso corresponder para a associação.

Uma segunda declaração de caso pode ser adicionada para o lado direito se a variável for necessária para escolher entre campos diferentes

LEFT OUTER JOIN Dashboard_Group_Level_Matching ON
       case
         when @Level  = 'lvl1' then  cw.Lvl1
         when @Level  = 'lvl2' then  cw.Lvl2
         when @Level  = 'lvl3' then  cw.Lvl3
       end
    = Dashboard_Group_Level_Matching.Dashboard_Level_Name
Kenneth Wilson
fonte
0

Aqui, comparei a diferença em dois conjuntos de resultados diferentes:

SELECT main.ColumnName, compare.Value PreviousValue,  main.Value CurrentValue
FROM 
(
    SELECT 'Name' AS ColumnName, 'John' as Value UNION ALL
    SELECT 'UserName' AS ColumnName, 'jh001' as Value UNION ALL
    SELECT 'Department' AS ColumnName, 'HR' as Value UNION ALL
    SELECT 'Phone' AS ColumnName, NULL as Value UNION ALL
    SELECT 'DOB' AS ColumnName, '1993-01-01' as Value UNION ALL
    SELECT 'CreateDate' AS ColumnName, '2017-01-01' as Value UNION ALL
    SELECT 'IsActive' AS ColumnName, '1' as Value
) main
INNER JOIN
(
    SELECT 'Name' AS ColumnName, 'Rahul' as Value UNION ALL
    SELECT 'UserName' AS ColumnName, 'rh001' as Value UNION ALL
    SELECT 'Department' AS ColumnName, 'HR' as Value UNION ALL
    SELECT 'Phone' AS ColumnName, '01722112233' as Value UNION ALL
    SELECT 'DOB' AS ColumnName, '1993-01-01' as Value UNION ALL
    SELECT 'CreateDate' AS ColumnName, '2017-01-01' as Value UNION ALL
    SELECT 'IsActive' AS ColumnName, '1' as Value
) compare
ON main.ColumnName = compare.ColumnName AND
CASE 
    WHEN main.Value IS NULL AND compare.Value IS NULL THEN 0
    WHEN main.Value IS NULL AND compare.Value IS NOT NULL THEN 1
    WHEN main.Value IS NOT NULL AND compare.Value IS NULL THEN 1
    WHEN main.Value <> compare.Value THEN 1
END = 1 
Sheikh Kawser
fonte