Могу ли я использовать оператор CASE в состоянии JOIN?


141

Следующее изображение является частью системных представлений Microsoft SQL Server 2008 R2. Из изображения видно, что отношения между sys.partitionsи sys.allocation_unitsзависят от стоимости sys.allocation_units.type. Чтобы объединить их, я бы написал что-то похожее на это:

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 

Но верхний код дает синтаксическую ошибку. Я думаю, это из-за CASEзаявления. Может кто-нибудь помочь объяснить немного?


Добавить сообщение об ошибке:

Сообщение 102, уровень 15, состояние 1, строка 6 Неверный синтаксис рядом с '='.

это изображение


36
Какое программное обеспечение вы использовали для создания этой красивой диаграммы БД?
LearnByReading

2
@LearnByReading ты когда-нибудь узнавал, какое программное обеспечение использовалось?
User632716

1
@ User632716 нет, к сожалению, нет!
LearnByReading

2
@ User632716 Хотя я действительно думаю, что это был MySQL Workbench. Но я так и не получил ответ
LearnByReading

@LearnByReading Я понятия не имею. Это предоставлено Microsoft.
Просто ученик

Ответы:


223

CASEВыражение возвращает значение из THENчасти пункта. Вы можете использовать это так:

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

Обратите внимание, что вам нужно что-то сделать с возвращаемым значением, например сравнить его с 1. Ваше утверждение попыталось вернуть значение присваивания или проверки на равенство, что не имеет смысла в контексте предложения CASE/ THEN. (Если бы BOOLEANбыл тип данных, тогда проверка на равенство имела бы смысл.)


@ HABO спасибо, это сработало для меня ... но проблема в том, что когда я делаю это, условия проваливаются ... скажите, пожалуйста, как мне это сломать?
Сагар Тандель

1
@SagarTandel - Извините, я не понимаю "провалиться" и "как мне его сломать". Не могли бы вы уточнить свой комментарий? (Недавно всплыло после погружения с Сабы. Может быть, Нитрокс.)
HABO

Он проверяет все условия, которые я не хочу. Я хочу, чтобы он вышел из дела, как только оно соответствует условию.
Сагар Тандель

3
@SagarTandel - из MSDN : «Оператор CASE последовательно оценивает свои условия и останавливается на первом условии, условие которого выполнено». Если вы хотите, чтобы все соединенные строки не соответствовали ни одному из явно указанных условий, просто измените хвостовую часть с = 1на = 0, но я не думаю, что вам понравится результат.
HABO

Присоединиться не могли бы вы написать это в рамках Entity вышеупомянутого вашего решения
r.hamd

36

Вместо этого вы просто ПРИСОЕДИНЯЕТЕСЬ к обеим таблицам, и в своем предложении SELECT возвращаете данные из той, которая соответствует:

Я предлагаю вам пройти по этой ссылке Условные объединения в SQL Server и Оператор T-SQL в предложении JOIN ON

например

    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 

Изменить: в соответствии с комментариями.

Вы не можете указать условие соединения, как вы делаете .. Проверьте запрос выше, без ошибок. Я вынул общий столбец вверх, и значение правильного столбца будет оценено при условии.


1
что conditional joinзначит? Каждое соединение (исключая крест) является условным. Чем этот случай отличается от любого другого? Ваш образец имеет внутреннее соединение с условием, а также запрос OPs имеет соединение с условием.
zerkms

@zerkms: согласен, звучит странно. Я полагаю, что условное соединение в этом контексте означает соединение, состояние которого зависит от другого условия.
Андрей М

@ Андрей М: есть ли основания думать о другом термине для тривиального состояния с OR?
zerkms

@zerkms: Да, это потому, что это звучит странно. :) Но я полагаю, что вы хотели спросить, была ли какая-либо причина не думать о другом термине, и в этом случае я не могу быть уверен. Если вы спрашиваете о моих причинах, ну, я думаю, я просто не мог быть достаточно обеспокоен. :) Как насчет объединения с альтернативными условиями ? Боюсь, я не очень хорош в терминах выдумки. Заметим, однако, что концептуально это об «условном состоянии», а не о «состоянии с OR». Использование OR- это всего лишь один из способов его реализации.
Андрей М

@ Андрей М: окей. Но лично я до сих пор не вижу причины давать имя для тривиального условия с 1 ORи 2 ANDsили с 1 CASE. Это обычный запрос, который ничем не отличается от других.
zerkms

17

Попробуй это:

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

Даже при том, что это работало бы - запрос в вопросе выглядит совершенно правильным. Так что до сих пор не объясняется, что не так с кодом OP
zerkms

10

Я думаю, что вам нужно два случая:

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             

Это потому что:

  • оператор CASE возвращает одно значение в конце
  • оператор ON сравнивает два значения
  • ваш оператор CASE делал сравнение внутри оператора CASE. Я полагаю, что если вы поместите свой оператор CASE в свой SELECT, вы получите логическое «1» или «0», указывающее, был ли оператор CASE оценен как True или False

5

Я взял твой пример и отредактировал его:

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

Это кажется хорошим

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

Пытаясь это, я получаю ошибку: имя корреляции 'xx' указано несколько раз в предложении FROM.
Этьен

1

Да, ты можешь. Вот пример.

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
Хотя этот код может решить вопрос, в том числе объяснение того, как и почему это решает проблему, действительно поможет улучшить качество вашего сообщения и, вероятно, получит больше голосов "за". Помните, что вы отвечаете на вопрос для читателей в будущем, а не только для того, кто спрашивает сейчас. Пожалуйста, отредактируйте свой ответ, чтобы добавить объяснения и указать, какие ограничения и предположения применяются.
двойной сигнал

0

Взял пример ДонкиКонга.

Проблема в том, что мне нужно было использовать объявленную переменную. Это позволяет указать вашу левую и правую часть того, что вам нужно сравнить. Это для поддержки отчета SSRS, где различные поля должны быть связаны на основе выбора пользователем.

Первоначальный случай устанавливает выбор поля на основе выбора, а затем я могу установить поле, которое мне нужно сопоставить для объединения.

Второй оператор case может быть добавлен для правой части, если переменная нужна для выбора из разных полей

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

Здесь я сравнил разницу в двух разных наборах результатов:

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 
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.