Predeterminado de cambio de caso de SQL para mostrar imagen de marcador de posición en nulos

Estoy tratando de obtener una lista de productos e imágenes de dos tablas. Cuando me uno a ellos y uso un cambio de caso en la columna HasImage de la tabla de productos, aparece este error:

Error:

Subquery returned more than 1 value. 
This is not permitted when the subquery follows =, !=, <, <= , >, >= 
or when the subquery is used as an expression.

Cuando un producto no tiene una imagen, quiero reemplazarlo con una imagen predeterminada.

Aquí está la declaración de selección:

SELECT 

P.[ProductId]
,P.[ProductName]

--If HasImage is false show the default.jpg
,Case P.[HasImage]
WHEN 'True' THEN (Select I.[FileName] as ProductImage FROM [ProductImages] I
INNER JOIN [Product] P on P.ProductId = I.ProductId
  WHERE I.Sequence=0)
WHEN 'False' THEN 'default.jpg'
END

FROM [Product] P
LEFT JOIN [ProductImages] I
on P.ProductId = I.ProductId

El problema está en el Caso Cuando 'Verdadero'. Eso es lo que arroja el error.

Tabla de productos

ProductId         ProductName       HasImage
1                 Coffee Mug        True
2                 Pen               False
3                 Pencil            False

Tabla de imágenes del producto

ProductId         Sequence          FileName
1                 0                 Mug_Image1.jpg
1                 1                 Mug_2.jpg
1                 2                 Mug_Img3.jpg

Hay varias imágenes para ProductId=1, pero uso Sequence = 0 para devolver solo una.

Los datos devueltos que quiero deberían verse así:

ProductId         ProductName       ProductImage
1                 Coffee Mug        Mug_Image1.jpg
2                 Pen               default.jpg
3                 Pencil            default.jpg

Probé varias combinaciones de coalesce (NULLIF, Left Joins y diferentes declaraciones, pero no logré que los tres productos se muestren como deseaba.

preguntado el 22 de mayo de 12 a las 19:05

¿Podría probar una cláusula LIMIT 1 (o TOP 1, según el dbms) en su consulta interna? -

Sí, eso funcionó. y tuve que agregar un DISTINCT en la parte superior. ¡Eso funcionó! SELECCIONE DISTINCT P.[ProductId] ,P.[ProductName] ,Case P.[HasImage] WHEN 'True' THEN (Seleccione TOP 1 I.[FileName] como ProductImage FROM [ProductImages] I INNER JOIN [Product] P on P. ProductId = I.ProductId WHERE I.Sequence=0 ) CUANDO 'False' THEN 'default.jpg' END FROM [Product] P LEFT JOIN [ProductImages] I on P.ProductId = I.ProductId -

Solo parece estar funcionando. @Juniad lo entendió bien, pero agregó INNER JOIN [Product] P de nuevo y convertida WHERE P.ProductId = I.ProductId a la cláusula On. Su subconsulta devuelve una imagen incorrecta porque no está conectada a una consulta externa, específicamente FROM [Product] P. También debe eliminar las imágenes de producto de LEFT JOIN de la consulta externa porque no obtiene nada de ella y porque duplica los registros del producto. Entonces también puedes eliminar distintos. -

Gracias Nikola. No noté la UNIÓN IZQUIERDA adicional. -

2 Respuestas

Además de mi comentario debajo de OP, esta es una consulta que creo que debería haberse escrito en primer lugar.

SELECT 
    P.[ProductId]
   ,P.[ProductName]
   --If HasImage is false show the default.jpg
   ,Case P.[HasImage]
         WHEN 'True' 
         THEN I.[FileName]
         WHEN 'False' 
         THEN 'default.jpg'
   END
  FROM [Product] P
  LEFT JOIN [ProductImages] I
    ON P.ProductId = I.ProductId
   -- Filter Sequence 0 only
   -- All products will be retrieved
   -- whether they have associated Image with Sequence = 0
   AND I.Sequence = 0

Filtrar el lado derecho de la unión izquierda le permite conservar las propiedades de la unión izquierda Y unir solo las filas de interés. Si HasImage sirve solo para marcar la existencia de imágenes y no como una regla comercial (mostrar/no mostrar la imagen de este producto en particular), puede eliminar el caso a favor de simple isnull(I.FileName, 'default.jpg').

Alternativamente (Sql Server 2005 y más reciente) puede usar APLICAR CRUZ para recuperar imágenes:

SELECT 
    P.[ProductId]
   ,P.[ProductName]
   ,I.[FileName]
  FROM [Product] P
 OUTER APPLY
 (
    SELECT CASE P.[HasImage]
           WHEN 'True' 
           THEN ProductImages.[FileName]
           WHEN 'False' 
           THEN 'default.jpg'
       END FileName
      FROM [ProductImages]
     WHERE P.ProductId = ProductImages.ProductId
       AND I.Sequence = 0
 ) I

contestado el 23 de mayo de 12 a las 15:05

¿Por qué quieres ir a por un INNER JOIN?

Si estás seguro de que cuando HasImage es VERDADERO, su tabla tendrá una imagen, entonces mejor elija una unión directa.

Prueba esta consulta:

SELECT P.[ProductId], 
       P.[ProductName],
       Case P.[HasImage] 
            WHEN 'True' THEN 
            (Select 
                   TOP 1 -- For Oracle 
                   I.[FileName] as ProductImage FROM [ProductImages] I
             WHERE P.ProductId = I.ProductId
             ORDER BY I.Sequence 
             LIMIT 1 -- For MySQL
             )
            WHEN 'False' THEN 'default.jpg'
         END
FROM [Product] P

contestado el 22 de mayo de 12 a las 22:05

No, dice Sintaxis incorrecta cerca de 'LIMIT'. También arreglé el error tipográfico Order By I.Sequence. - User970008

¿Que estas usando? El límite 1 es para MySQL. Prueba TOP 1. - JHS

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.