Posso usare l'istruzione CASE in una condizione JOIN?


141

L'immagine seguente fa parte delle visualizzazioni di sistema di Microsoft SQL Server 2008 R2. Dall'immagine possiamo vedere che la relazione tra sys.partitionse sys.allocation_unitsdipende dal valore di sys.allocation_units.type. Quindi per unirli insieme scriverei qualcosa di simile a questo:

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 

Ma il codice superiore fornisce un errore di sintassi. Immagino che sia a causa della CASEdichiarazione. Qualcuno può aiutare a spiegare un po '?


Aggiungi messaggio di errore:

Messaggio 102, livello 15, stato 1, riga 6 Sintassi errata vicino a '='.

questa è l'immagine


36
Quale software hai usato per realizzare questo bellissimo diagramma DB?
LearnByLeggi il

2
@LearnByReading hai mai scoperto quale software è stato utilizzato?
Utente 632716,

1
@ User632716 no, sfortunatamente no!
LearnByReading

2
@ User632716 Anche se penso davvero che sia stato MySQL Workbench. Ma non ho mai ricevuto risposta
LearnByReading

@LearnByReading Non ne ho idea. È fornito da Microsoft.
Solo uno studente

Risposte:


223

CASEUn'espressione restituisce un valore dalla THENporzione della clausola. Potresti usarlo così:

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

Nota che devi fare qualcosa con il valore restituito, ad es. Confrontalo con 1. La tua dichiarazione ha tentato di restituire il valore di un compito o un test per l'uguaglianza, nessuno dei quali ha senso nel contesto di una clausola CASE/ THEN. (Se BOOLEANfosse un tipo di dati, allora il test per l'uguaglianza avrebbe senso.)


@HABO grazie che ha funzionato per me ... ma il problema è che quando lo faccio le condizioni fanno crollare ... per favore dimmi come posso romperlo?
Sagar Tandel,

1
@SagarTandel - Mi dispiace, non capisco "fare una caduta" e "come posso romperlo". Potresti chiarire il tuo commento? (Di recente è emerso da un'immersione al largo di Saba. Potrebbe essere il Nitrox.)
HABO

Verifica tutte le condizioni che non desidero. Voglio che esca dal caso una volta che soddisfa una condizione.
Sagar Tandel

3
@SagarTandel - Da MSDN : "L'istruzione CASE valuta le sue condizioni in sequenza e si interrompe con la prima condizione la cui condizione è soddisfatta.". Se vuoi tutte le righe unite che non corrispondono a nessuna delle condizioni esplicitamente dichiarate, cambia semplicemente la coda da = 1a = 0, ma non credo che ti piacerà il risultato.
HABO,

UNISCITI a sys.allocation_units a ON CASE QUANDO a.type IN (1, 3) AND a.container_id = p.hobt_id THEN 1 QUANDO a.type IN (2) AND a.container_id = p.partition_id THEN 1 ELSE 0 END = 1 potresti scriverlo nel framework Entity sopra la tua soluzione
r.hamd

36

Al contrario, è sufficiente ISCRIVERSI ad entrambe le tabelle e, nella clausola SELECT, restituire i dati da quello corrispondente:

Ti suggerisco di passare attraverso questo collegamento Join condizionali in SQL Server e T-SQL Case Statement in una clausola JOIN ON

per esempio

    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 

Modifica: come da commenti.

Non è possibile specificare la condizione di join mentre si sta eseguendo. Controllare la query sopra che non presenta errori. Ho eliminato la colonna comune e il valore della colonna destra verrà valutato a condizione.


1
cosa conditional joinsignifica? Ogni join (escluso il cross) è un condizionale. In che modo questo caso è diverso dagli altri? L'esempio ha un join interno con condizione, così come la query OP ha un join con condizione.
zerkms,

@zerkms: sono d'accordo, sembra confuso. Credo che un join condizionale in questo contesto significhi un join la cui condizione dipende da un'altra condizione.
Andriy M

@Andriy M: qualche motivo per pensare a un altro termine per condizione banale con OR?
zerkms,

@zerkms: Ehm, sì, è perché sembra confuso. :) Ma credo che intendevi chiederti se c'era qualche motivo per non pensare a un altro termine, nel qual caso non posso esserne sicuro. Se mi stai chiedendo le mie ragioni, beh, penso che non potrei essere disturbato abbastanza. :) Che ne dici di un join con condizioni alternative ? Temo di non essere molto bravo a coniare i termini. Si noti, tuttavia, che concettualmente si tratta di "condizione condizionale", non di "condizione con OR". L'uso ORè semplicemente un modo per implementarlo.
Andriy M,

@Andriy M: ok. Ma personalmente non vedo ancora il motivo per dare un nome alla condizione banale con 1 ORe 2 ANDso con 1 CASE. È una query di routine che non ha nulla che la differenzi da qualsiasi altra.
zerkms,

17

Prova questo:

...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)

Anche se funzionerebbe: la query nella domanda sembra perfettamente valida. Quindi ancora non spiega cosa c'è che non va nel codice di OP
zerkms,

10

Penso che tu abbia bisogno di due dichiarazioni di casi:

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             

Questo è perché:

  • l'istruzione CASE restituisce un singolo valore in END
  • l'istruzione ON confronta due valori
  • la tua dichiarazione CASE stava facendo il confronto all'interno della dichiarazione CASE. Immagino che se metti la tua istruzione CASE nel tuo SELECT otterrai un '1' o '0' booleano che indica se l'istruzione CASE viene valutata su True o False

5

Ho preso il tuo esempio e l'ho modificato:

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)


1

Si, puoi. Ecco un esempio

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 

1
Sebbene questo codice possa risolvere la domanda, inclusa una spiegazione di come e perché questo risolva il problema, contribuirebbe davvero a migliorare la qualità del tuo post e probabilmente darebbe più voti positivi. Ricorda che stai rispondendo alla domanda per i lettori in futuro, non solo per la persona che chiede ora. Si prega di modificare la risposta per aggiungere spiegazioni e dare un'indicazione di ciò si applicano le limitazioni e le assunzioni.
doppio segnale acustico,

0

Ha preso l'esempio di DonkeyKong.

Il problema è che ho bisogno di usare una variabile dichiarata. Ciò consente di indicare il lato sinistro e destro di ciò che è necessario confrontare. Questo è per supportare un report SSRS in cui diversi campi devono essere collegati in base alla selezione da parte dell'utente.

Il caso iniziale imposta la scelta del campo in base alla selezione e quindi posso impostare il campo che devo abbinare per il join.

Una seconda istruzione case potrebbe essere aggiunta per il lato destro se la variabile è necessaria per scegliere tra diversi campi

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

0

Qui ho confrontato la differenza in due diversi set di risultati:

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 
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.