Kann ich die CASE-Anweisung in einer JOIN-Bedingung verwenden?

140

Das folgende Bild ist Teil der Microsoft SQL Server 2008 R2-Systemansichten. Aus dem Bild können wir erkennen, dass die Beziehung zwischen sys.partitionsund sys.allocation_unitsvom Wert von abhängt sys.allocation_units.type. Um sie zusammenzufügen, würde ich etwas Ähnliches schreiben:

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 

Der obere Code gibt jedoch einen Syntaxfehler aus. Ich denke, das liegt an der CASEAussage. Kann jemand helfen, ein wenig zu erklären?


Fehlermeldung hinzufügen:

Meldung 102, Ebene 15, Status 1, Zeile 6 Falsche Syntax in der Nähe von '='.

Das ist das Bild

Nur ein Lernender
quelle
36
Mit welcher Software haben Sie dieses schöne DB-Diagramm erstellt?
LearnByReading
2
@LearnByReading Haben Sie jemals herausgefunden, welche Software verwendet wurde?
User632716
1
@ User632716 nein leider nicht!
LearnByReading
2
@ User632716 Obwohl ich wirklich denke, dass es MySQL Workbench war. Aber ich habe nie eine Antwort erhalten
LearnByReading
@LearnByReading Ich habe keine Ahnung. Es wird von Microsoft bereitgestellt.
Nur ein Lernender

Antworten:

222

Ein CASEAusdruck gibt einen Wert aus dem THENTeil der Klausel zurück. Sie könnten es so verwenden:

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

Beachten Sie, dass Sie mit dem zurückgegebenen Wert etwas tun müssen, z. B. mit 1 vergleichen. Ihre Anweisung hat versucht, den Wert einer Zuweisung oder eines Gleichheitstests zurückzugeben, was im Kontext einer CASE/ THEN-Klausel keinen Sinn ergibt . (Wenn BOOLEANes sich um einen Datentyp handelt, ist der Test auf Gleichheit sinnvoll.)

HABO
quelle
@ HABO danke, das hat bei mir funktioniert ... aber das Problem ist, wenn ich das mache, fallen die Bedingungen durch ... bitte sag mir, wie ich es kaputt mache?
Sagar Tandel
1
@SagarTandel - Entschuldigung, ich verstehe nicht "Durchfallen" und "Wie kann ich es brechen". Könnten Sie Ihren Kommentar klarstellen? (Kürzlich von einem Tauchgang vor Saba aufgetaucht.
Könnte
Es prüft auf alle Bedingungen, die ich nicht will. Ich möchte, dass der Fall beendet wird, sobald er einer Bedingung entspricht.
Sagar Tandel
3
@SagarTandel - Von MSDN : "Die CASE-Anweisung wertet ihre Bedingungen nacheinander aus und stoppt mit der ersten Bedingung, deren Bedingung erfüllt ist." Wenn Sie alle verknüpften Zeilen möchten, die keiner der explizit angegebenen Bedingungen entsprechen, ändern Sie einfach das hintere Ende von = 1auf = 0, aber ich denke, das Ergebnis wird Ihnen nicht gefallen.
HABO
JOIN sys.allocation_units a ON CASE WENN a.type IN (1, 3) UND a.container_id = p.hobt_id DANN 1 WENN a.type IN (2) UND a.container_id = p.partition_id DANN 1 ELSE 0 END = 1
Könnten
36

Stattdessen verbinden Sie sich einfach mit beiden Tabellen und geben in Ihrer SELECT-Klausel Daten von derjenigen zurück, die übereinstimmt:

Ich empfehle Ihnen, diesen Link zu verwenden. Bedingte Verknüpfungen in SQL Server und T-SQL-Fallanweisung in einer JOIN ON-Klausel

z.B

    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 

Bearbeiten: Gemäß Kommentaren.

Sie können die Verknüpfungsbedingung nicht angeben, während Sie dies tun. Überprüfen Sie die obige Abfrage, die keinen Fehler aufweist. Ich habe die gemeinsame Spalte herausgenommen und der richtige Spaltenwert wird unter der Bedingung ausgewertet.

Niranjan Singh
quelle
1
was conditional joinheißt Jeder Join (ohne Kreuz) ist eine Bedingung. Wie unterscheidet sich dieser Fall von jedem anderen? Ihr Beispiel hat eine innere Verknüpfung mit Bedingung, und die OP-Abfrage hat eine Verknüpfung mit Bedingung.
Zerkms
@zerkms: Ich stimme zu, es klingt verwirrend. Ich glaube, ein bedingter Join bedeutet in diesem Zusammenhang einen Join, dessen Bedingung von einer anderen Bedingung abhängt.
Andriy M
@Andriy M: Gibt es einen Grund, an einen anderen Begriff für triviale Zustände mit zu denken OR?
Zerkms
@ zerkms: Äh, ja, weil es verwirrend klingt. :) Aber ich glaube, du wolltest fragen, ob es einen Grund gibt, nicht an einen anderen Begriff zu denken. In diesem Fall kann ich nicht sicher sein. Wenn Sie nach meinen Gründen fragen , denke ich, dass ich einfach nicht genug gestört werden konnte. :) Wie wäre es mit einem Join mit alternativen Bedingungen ? Ich fürchte, ich bin nicht sehr gut darin, Begriffe zu prägen. Beachten Sie jedoch, dass es sich konzeptionell um "bedingte Bedingung" handelt, nicht um "Bedingung mit OR". Die Verwendung ORist nur eine Möglichkeit, sie zu implementieren.
Andriy M
@Andriy M: ok. Aber ich persönlich sehe immer noch keinen Grund, der trivialen Bedingung mit 1 ORund 2 ANDsoder mit 1 einen Namen zu geben CASE. Es ist eine Routineabfrage, die nichts hat, was sie von anderen unterscheidet.
Zerkms
17

Versuche dies:

...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
quelle
Auch wenn es funktionieren würde - die Abfrage in der Frage sieht vollkommen gültig aus. Also immer noch nicht erklären, was mit OPs Code falsch ist
zerkms
10

Ich denke, Sie brauchen zwei Fallaussagen:

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             

Das ist weil:

  • Die CASE-Anweisung gibt am ENDE einen einzelnen Wert zurück
  • Die ON-Anweisung vergleicht zwei Werte
  • Ihre CASE-Anweisung hat den Vergleich innerhalb der CASE-Anweisung durchgeführt. Ich würde vermuten, dass Sie, wenn Sie Ihre CASE-Anweisung in SELECT einfügen, eine boolesche '1' oder '0' erhalten, die angibt, ob die CASE-Anweisung mit True oder False bewertet wurde
DonkeyKong
quelle
5

Ich habe Ihr Beispiel genommen und es bearbeitet:

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
quelle
1

Das scheint schön

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

quelle
Beim Versuch wird eine Fehlermeldung angezeigt: Der Korrelationsname 'xx' wird in einer FROM-Klausel mehrfach angegeben.
Etienne
1

Ja, du kannst. Hier ist ein Beispiel.

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
quelle
1
Während dieser Code die Frage lösen kann, einschließlich einer Erklärung, wie und warum dies das Problem löst, würde dies wirklich dazu beitragen, die Qualität Ihres Beitrags zu verbessern, und wahrscheinlich zu mehr Up-Votes führen. Denken Sie daran, dass Sie in Zukunft die Frage für die Leser beantworten, nicht nur für die Person, die jetzt fragt. Bitte bearbeiten Sie Ihre Antwort, um Erklärungen hinzuzufügen und anzugeben, welche Einschränkungen und Annahmen gelten.
Doppelton
0

Hat DonkeyKongs Beispiel genommen.

Das Problem ist, dass ich eine deklarierte Variable verwenden muss. Auf diese Weise können Sie auf der linken und rechten Seite angeben, was Sie vergleichen möchten. Dies dient zur Unterstützung eines SSRS-Berichts, bei dem je nach Auswahl durch den Benutzer verschiedene Felder verknüpft werden müssen.

Im ersten Fall wird die Feldauswahl basierend auf der Auswahl festgelegt, und dann kann ich das Feld festlegen, auf das ich für den Join passen möchte.

Eine zweite case-Anweisung könnte für die rechte Seite hinzugefügt werden, wenn die Variable zur Auswahl aus verschiedenen Feldern benötigt wird

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
quelle
0

Hier habe ich den Unterschied in zwei verschiedenen Ergebnismengen verglichen:

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 
Scheich Kawser
quelle