¿Puedo usar la declaración CASE en una condición JOIN?

141

La siguiente imagen es parte de las Vistas del sistema de Microsoft SQL Server 2008 R2. De la imagen podemos ver que la relación entre sys.partitionsy sys.allocation_unitsdepende del valor de sys.allocation_units.type. Entonces, para unirlos, escribiría algo similar a esto:

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 

Pero el código superior da un error de sintaxis. Supongo que es por la CASEdeclaración. ¿Alguien puede ayudar a explicar un poco?


Añadir mensaje de error:

Mensaje 102, Nivel 15, Estado 1, Línea 6 Sintaxis incorrecta cerca de '='.

esta es la imagen

Solo un alumno
fuente
36
¿Qué software usaste para hacer este hermoso diagrama de DB?
LearnByReading
2
@LearnByReading ¿alguna vez descubrió qué software se utilizó?
Usuario632716
1
@ User632716 no, lamentablemente no!
LearnByReading
2
@ User632716 Aunque realmente creo que fue MySQL Workbench. Pero nunca recibí una respuesta
LearnByReading
@LearnByReading No tengo idea. Es proporcionado por Microsoft.
Solo aprendiz el

Respuestas:

223

Una CASEexpresión devuelve un valor de la THENparte de la cláusula. Podrías usarlo así:

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

Tenga en cuenta que debe hacer algo con el valor devuelto, por ejemplo, compárelo con 1. Su declaración intentó devolver el valor de una asignación o prueba de igualdad, ninguno de los cuales tiene sentido en el contexto de una cláusula CASE/ THEN. (Si BOOLEANse tratara de un tipo de datos, la prueba de igualdad tendría sentido).

HABO
fuente
@HABO gracias, eso funcionó para mí ... pero el problema es que cuando hago esto las condiciones no se cumplen ... por favor, dime cómo lo rompo.
Sagar Tandel
1
@SagarTandel - Lo siento, no entiendo "hacer un fracaso" y "cómo lo rompo". ¿Podrías aclarar tu comentario? (Recientemente surgió de una inmersión en Saba. Podría ser el Nitrox.)
HABO
Comprueba todas las condiciones que no quiero. Quiero que salga del caso una vez que coincida con una condición.
Sagar Tandel
3
@SagarTandel - Desde MSDN : "La instrucción CASE evalúa sus condiciones secuencialmente y se detiene con la primera condición cuya condición se cumple". Si desea todas las filas unidas que no coinciden con ninguna de las condiciones establecidas explícitamente, simplemente cambie el final de la cola = 1a = 0, pero no creo que le guste el resultado.
HABO
ÚNETE a sys.allocation_units a EN CASO CUANDO a.type IN (1, 3) Y a.container_id = p.hobt_id ENTONCES 1 CUANDO a.type IN (2) Y a.container_id = p.partition_id ENTONCES 1 ELSE 0 END = 1 ¿podría escribirlo en Entity Framework lo anterior de su solución
r.hamd
36

En cambio, simplemente SE UNE a ambas tablas y, en su cláusula SELECT, devuelva datos de la que coincida:

Le sugiero que vaya a través de este enlace Uniones condicionales en SQL Server y T-SQL Case Statement en una cláusula JOIN ON

p.ej

    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: según los comentarios.

No puede especificar la condición de unión como lo está haciendo. Compruebe la consulta anterior que no tiene ningún error. He sacado la columna común hacia arriba y el valor de la columna derecha se evaluará a condición.

Niranjan Singh
fuente
1
que conditional joinsignifica Cada unión (excluyendo cruz) es condicional. ¿Cómo es este caso diferente de cualquier otro? Su muestra tiene unión interna con condición, así como la consulta de OP tiene unión con condición.
zerkms
@zerkms: Estoy de acuerdo, suena confuso. Creo que una unión condicional en este contexto significa una unión cuya condición depende de otra condición.
Andriy M
@Andriy M: ¿alguna razón para pensar en otro término para condición trivial OR?
zerkms
@zerkms: Er, sí, es porque suena confuso. :) Pero creo que querías preguntar si había alguna razón para no pensar en otro término, en cuyo caso no estoy seguro. Si pregunta por mis razones, bueno, creo que no podría molestarme lo suficiente. :) ¿Qué tal una unión con condiciones alternativas ? Me temo que no soy muy bueno para acuñar términos. Tenga en cuenta, sin embargo, que conceptualmente se trata de "condición condicional", no de "condición con OR". Usar ORes simplemente una forma de implementarlo.
Andriy M
@Andriy M: ok. Pero personalmente todavía no veo la razón para dar un nombre a la condición trivial con 1 ORy 2 ANDso con 1 CASE. Es una consulta de rutina que no tiene nada que la diferencie de ninguna otra.
zerkms
17

Prueba esto:

...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
fuente
Aunque funcionaría, la consulta en la pregunta parece perfectamente válida. Así que todavía no explica qué hay de malo con el código de OP
zerkms
10

Creo que necesita dos declaraciones 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             

Esto es porque:

  • la declaración CASE devuelve un valor único al FIN
  • la declaración ON compara dos valores
  • su declaración CASE estaba haciendo la comparación dentro de la declaración CASE. Supongo que si pones tu declaración CASE en tu SELECT obtendrás un '1' o '0' booleano que indica si la declaración CASE se evaluó como Verdadero o Falso
Donkey Kong
fuente
5

Tomé tu ejemplo y lo edité:

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

Sí tu puedes. Aquí hay un ejemplo.

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
fuente
1
Si bien este código puede resolver la pregunta, incluir una explicación de cómo y por qué esto resuelve el problema realmente ayudaría a mejorar la calidad de su publicación, y probablemente resultaría en más votos positivos. Recuerde que está respondiendo la pregunta para los lectores en el futuro, no solo la persona que pregunta ahora. Por favor, editar su respuesta para agregar explicaciones y dar una indicación de lo que se aplican limitaciones y supuestos.
doble pitido
0

Tomó el ejemplo de DonkeyKong.

El problema es que necesitaba usar una variable declarada. Esto permite indicar su lado izquierdo y derecho de lo que necesita comparar. Esto es para respaldar un informe SSRS donde se deben vincular diferentes campos según la selección realizada por el usuario.

El caso inicial establece la elección del campo en función de la selección y luego puedo configurar el campo con el que necesito hacer coincidir la unión.

Se podría agregar una segunda declaración de caso para el lado derecho si se necesita la variable para elegir entre diferentes campos

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

Aquí he comparado la diferencia en dos 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
fuente