Czy mogę używać instrukcji CASE w warunku JOIN?


141

Poniższy obraz jest częścią widoków systemu Microsoft SQL Server 2008 R2. Z obrazu widać, że zależność między sys.partitionsi sys.allocation_unitszależy od wartości sys.allocation_units.type. Aby połączyć je razem, napisałbym coś podobnego do tego:

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 

Ale górny kod daje błąd składni. Myślę, że to z powodu CASEoświadczenia. Czy ktoś może pomóc trochę wyjaśnić?


Dodaj komunikat o błędzie:

Msg 102, poziom 15, stan 1, wiersz 6 Niepoprawna składnia w pobliżu znaku „=”.

to jest obraz


36
Jakiego oprogramowania użyłeś do stworzenia tego pięknego diagramu DB?
LearnByReading

2
@LearnByReading Czy kiedykolwiek dowiedziałeś się, które oprogramowanie było używane?
User632716

1
@ User632716 nie, niestety nie!
LearnByReading

2
@ User632716 Chociaż naprawdę myślę, że to MySQL Workbench. Ale nigdy nie otrzymałem odpowiedzi
LearnByReading

@LearnByReading Nie mam pojęcia. Jest dostarczany przez firmę Microsoft.
Wystarczy uczący się

Odpowiedzi:


223

CASEWyrażenie zwraca wartość z THENczęści punktu. Możesz go użyć w ten sposób:

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

Zauważ, że musisz coś zrobić ze zwróconą wartością, np. Porównać ją z 1. Twoja instrukcja próbowała zwrócić wartość przypisania lub testu na równość, z których żadne nie ma sensu w kontekście klauzuli CASE/ THEN. (Gdyby BOOLEANbył to typ danych, test równości miałby sens).


@HABO dzięki, że dla mnie zadziałało ... ale problem polega na tym, że kiedy to robię, warunki się psują ... proszę, powiedz mi, jak to zepsuć?
Sagar Tandel

1
@SagarTandel - Przepraszam, nie rozumiem „upaść” i „jak mam to złamać”. Czy mógłbyś wyjaśnić swój komentarz? (Niedawno wynurzył się po nurkowaniu z Saba. Może to być Nitrox.)
HABO,

Sprawdza wszystkie warunki, których nie chcę. Chcę, aby zakończyło sprawę, gdy spełni warunek.
Sagar Tandel

3
@SagarTandel - From MSDN : "Instrukcja CASE ocenia swoje warunki sekwencyjnie i zatrzymuje się na pierwszym warunku, którego warunek jest spełniony.". Jeśli chcesz, aby wszystkie połączone wiersze nie pasowały do ​​żadnego z jawnie określonych warunków, po prostu zmień koniec z = 1na = 0, ale nie sądzę, że spodoba ci się wynik.
HABO

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 czy mógłbyś napisać to w ramach Entity powyższe rozwiązanie
hamd

36

Zamiast tego po prostu JOIN do obu tabel i w klauzuli SELECT zwracasz dane z tej, która pasuje:

Proponuję przejść przez ten link Łączenia warunkowe w SQL Server i instrukcja T-SQL Case w klauzuli JOIN ON

na przykład

    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 

Edycja: zgodnie z komentarzami.

Nie możesz tak określić warunku łączenia. Sprawdź powyższe zapytanie, które nie zawiera błędów. Wyciągnąłem wspólną kolumnę w górę i wartość prawej kolumny zostanie oceniona pod warunkiem.


1
co to conditional joinznaczy Każde sprzężenie (z wyłączeniem krzyża) jest warunkowe. Czym ten przypadek różni się od innych? Twoja próbka ma sprzężenie wewnętrzne z warunkiem, a zapytanie OPs ma sprzężenie z warunkiem.
zerkms

@zerkms: Zgadzam się, brzmi to zagmatwane. Uważam, że złączenie warunkowe w tym kontekście oznacza złączenie, którego stan zależy od innego warunku.
Andriy M

@Andriy M: czy jest jakiś powód, by pomyśleć o innym określeniu trywialnego stanu z OR?
zerkms

@zerkms: Eee, tak, to dlatego, że brzmi to zagmatwane. :) Ale myślę, że chciałeś zapytać, czy był jakiś powód, by nie wymyślić innego terminu, w takim przypadku nie jestem pewien. Jeśli pytasz o moje powody, cóż, myślę, że nie mogłem się wystarczająco przejmować. :) Co powiesz na złączenie z alternatywnymi warunkami ? Obawiam się, że nie jestem dobry w tworzeniu terminów. Należy jednak pamiętać, że koncepcyjnie chodzi o „warunek warunkowy”, a nie o „warunek z OR”. Używanie ORjest tylko jednym ze sposobów jego implementacji.
Andriy M

@Andriy M: ok. Ale osobiście nadal nie widzę powodu, aby nadawać nazwę trywialnemu warunkowi z 1 ORi 2 ANDslub z 1 CASE. Jest to rutynowe zapytanie, które nie różni się niczym od innych.
zerkms

17

Spróbuj tego:

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

Nawet gdyby to zadziałało - zapytanie w pytaniu wygląda na całkowicie poprawne. Więc nadal nie wyjaśnia, co jest nie tak z kodem OP
zerkms

10

Myślę, że potrzebujesz dwóch przypadków:

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             

To dlatego, że:

  • instrukcja CASE zwraca pojedynczą wartość na końcu
  • instrukcja ON porównuje dwie wartości
  • Twoja instrukcja CASE dokonywała porównania wewnątrz instrukcji CASE. Wydaje mi się, że jeśli umieścisz swoją instrukcję CASE w swoim SELECT, otrzymasz wartość logiczną „1” lub „0” wskazującą, czy instrukcja CASE została oceniona jako True czy False

5

Wziąłem twój przykład i zredagowałem go:

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

Tak, możesz. Oto przykład.

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
Chociaż ten kod może rozwiązać problem, w tym wyjaśnienie, jak i dlaczego to rozwiązuje problem, naprawdę pomogłoby poprawić jakość twojego posta i prawdopodobnie zaowocowałoby większą liczbą pozytywnych głosów. Pamiętaj, że odpowiadasz na pytanie do czytelników w przyszłości, a nie tylko osoba, która zapyta teraz. Proszę edytować swoje odpowiedzi, aby dodać wyjaśnień i dać wskazówkę co zastosować ograniczenia i założenia.
podwójny sygnał

0

Wziął przykład DonkeyKonga.

Problem polega na tym, że muszę użyć zadeklarowanej zmiennej. Pozwala to na określenie lewej i prawej strony tego, co chcesz porównać. Służy to do obsługi raportu SSRS, w którym różne pola muszą być połączone na podstawie wyboru dokonanego przez użytkownika.

Początkowy przypadek ustawia wybór pola na podstawie wyboru, a następnie mogę ustawić pole, które muszę dopasować do sprzężenia.

Drugie stwierdzenie przypadku można dodać po prawej stronie, jeśli zmienna ma wybierać spośród różnych pól

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

Tutaj porównałem różnicę w dwóch różnych zestawach wyników:

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 
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.