2010-07-29 16 views
17

Estoy escribiendo procs almacenados que están siendo llamados por un sistema heredado. Una de las limitaciones del sistema heredado es que debe haber al menos una fila en el único conjunto de resultados devuelto por el proceso almacenado. El estándar es devolver un cero en la primera columna (sí, lo sé).Agregar fila vacía a los resultados de la consulta si no se encontraron resultados

La forma obvia de lograr esto es crear una tabla temporal, poner los resultados en ella, probar las filas en la tabla temporal y devolver los resultados de la tabla temporal o el resultado vacío simple.

Otra forma podría ser hacer un EXISTS contra la misma cláusula where en la consulta principal antes de que se ejecute la consulta principal.

Ninguno de estos es muy satisfactorio. ¿Alguien puede pensar en una mejor manera? Estaba pensando por las líneas de una unión algo así como esto (soy consciente de que esto no funciona):

--create table #test 
--(
-- id int identity, 
-- category varchar(10) 
--) 
--go 
--insert #test values ('A') 
--insert #test values ('B') 
--insert #test values ('C') 

declare @category varchar(10) 

set @category = 'D' 

select 
    id, category 
from #test 
where category = @category 
union 
select 
    0, '' 
from #test 
where @@rowcount = 0 
+1

Considera la posibilidad de revisar la respuesta aceptada después de leer la solución de @ swe! Es tan bueno que admito que sentí la necesidad de empujarte aquí;) http: // stackoverflow.com/a/32586119/2979473 – ensisNoctis

+0

@ensisNoctis a menos que esté muy equivocado, esa solución solo funcionará en situaciones en las que espere cero o un resultado –

+1

Afortunadamente, usted es, por favor permítame compartir con usted este código que es actualmente en producción en mi compañía, todo gracias a StackOverflow;) No cabía todo en este comentario, así que publiqué otra respuesta, pero sigue siendo de @ swe, solo mejor formateada. http://stackoverflow.com/a/37046650/2979473 Devuelve filas de la tabla deseada si existen (cualquier número> = 1), de lo contrario filas de otra tabla (o literal, como en mi ejemplo). – ensisNoctis

Respuesta

10

Es una vieja pregunta, pero tuve el mismo problema. solución es muy simple sin dobles seleccionar:

select top(1) WITH TIES * FROM (
select 
id, category, 1 as orderdummy 
from #test 
where category = @category 
union select 0, '', 2) ORDER BY orderdummy 

por la "CON LAZOS" usted obtiene todas las filas (todos tienen un 1 como "orderdummy", por lo que todos son lazos), o si no hay resultado, obtener su defecto.

+0

¡Esto es asombroso! ¡Simple, rápido y mucho mejor que la respuesta aceptada, ya que no utiliza tablas temporales, no duplica las consultas ni declara variables (por lo que se puede usar fácilmente en TVF en línea)! – ensisNoctis

0

Creo que se puede intentar:

Declare @count int 
set @count = 0 

Begin 
Select @count = Count([Column]) 
From //Your query 

if(@Count = 0) 
    select 0 
else //run your query 

La desventaja es que se Está ejecutando efectivamente su consulta dos veces, la parte positiva es que está descartando la tabla temporal.

+0

Este es un método similar a la declaración EXISTS que sugerí en la pregunta. La diferencia es que el recuento es un poco menos eficiente. –

27

Muy pocas opciones, me temo.

Siempre hay que tocar la mesa dos veces, si COUNT, existe antes, existe en UNION, cláusula TOP etc

select 
    id, category 
from mytable 
where category = @category 
union all --edit, of course it's quicker 
select 
    0, '' 
where NOT EXISTS (SELECT * FROM mytable where category = @category) 

Un EXISTE solución es mejor que cuentan porque se detendrá cuando encuentre una fila. COUNT recorrerá todas las filas para contarlas realmente

+0

Hay una nueva información para mí re: EXISTS se detiene cuando encuentra una fila. Gracias @gbn! –

+0

+1: My WITH/TOP no devolvió la fila ficticia cuando el WITH no tiene ningún dato. –

+0

Sí, definitivamente prefiero usar EXISTS ya que es más rápido. Es una lástima que tenga que escribir la misma cláusula where en dos ocasiones. Creo que probablemente voy a ir por esto, gracias. –

2

Puede usar una unión externa completa. Algo en el sentido de ...

declare @category varchar(10) 

set @category = 'D' 

select #test.id, ISNULL(#test.category, @category) as category from (
    select 
     id, category 
    from #test 
    where category = @category 
) 
FULL OUTER JOIN (Select @category as CategoryHelper) as EmptyHelper on 1=1 

Actualmente pruebas de rendimiento de este escenario a mí mismo que no estoy seguro de qué tipo de impacto que esto tendría, pero le dará una fila en blanco con la categoría de población.

+0

Esa es una solución realmente interesante que evita escribir la cláusula where dos veces. Me interesaría saber si esto funciona y cuáles son los costos de rendimiento. No puedo entender por qué envuelve la consulta principal en una tabla derivada. ¿Es esto necesario? –

+0

El ajuste principal de la consulta se debe a la cláusula where, pero puede reescribirse según la estructura de la consulta subyacente. Si no se une en el área correcta (después del filtrado), entonces el criterio se aplica a ambas tablas. Los costos parecen ser mínimos (0% en el plan de ejecución real), sin embargo, la consulta que tengo es bastante compleja en su funcionamiento. –

+0

Sí, eso tiene más sentido ahora que ha incluido el isnull. Gracias –

0

Para evitar la duplicación de la consulta de selección, ¿qué tal una tabla temporal para almacenar primero el resultado de la consulta? ¿Y basado en la tabla temporal, devuelva la fila predeterminada si la tabla temporal está vacía o devuelve la temperatura cuando tiene resultado?

1

Esta es la respuesta de @ swe, solo mejor formateada.

CREATE FUNCTION [mail].[f_GetRecipients] 
(
    @MailContentCode VARCHAR(50) 
) 
RETURNS TABLE 
AS 
RETURN 
(
    SELECT TOP 1 WITH TIES -- returns all rows having highest priority found 
     [To], 
     CC, 
     BCC 
    FROM (
     SELECT 
      [To], 
      CC, 
      BCC, 
      1 AS Priority -- if no rows, priority 2 under UNION will get returned 
     FROM mail.Recipients 
     WHERE 1 = 1 
      AND IsActive = 1 
      AND MailContentCode = @MailContentCode 

     UNION ALL 

     SELECT 
      * 
     FROM (VALUES 
      (N'[email protected]', NULL, NULL, 2), 
      (N'[email protected]', NULL, NULL, 2) 
     ) defaults([To], CC, BCC, Priority) 
    ) emails 
    ORDER BY Priority 
) 
Cuestiones relacionadas