2011-01-11 11 views
6

tengo una opinión de que es algo así comoSQL Server - USE existe cláusula en Dónde y Seleccionar

CREATE VIEW OrdersView WITH SCHEMABINDING AS 
SELECT o.Id, o.OrderDate, o.LastName, o.TotalPrice, s.Status 
FROM dbo.Orders o INNER JOIN dbo.OrderStatus s on o.Id = s.OrderId 
WHERE NOT EXISTS (SELECT NULL from dbo.OrderStatus where OrderId = s.OrderId and StatusDate > s.StatusDate 
AND EXISTS (SELECT NULL FROM dbo.OrderLineItemType1 WHERE OrderId = o.Id) 

La intención es traer todas las órdenes que tienen al menos una línea de tipo 1, junto con su estado actual.

Estamos en el proceso de añadir un segundo tipo de elemento de línea, y he modificado el punto de vista que va a incluir órdenes que tienen al menos una línea de tipo 1 o tipo 2:

CREATE VIEW OrdersView WITH SCHEMABINDING AS 
SELECT o.Id, o.OrderDate, o.LastName, o.TotalPrice, s.Status 
FROM dbo.Orders o INNER JOIN dbo.OrderStatus s on o.Id = s.OrderId 
WHERE NOT EXISTS (SELECT NULL from dbo.OrderStatus where OrderId = s.OrderId and StatusDate > s.StatusDate 
AND (EXISTS (SELECT NULL FROM dbo.OrderLineItemType1 WHERE OrderId = o.Id) 
    OR EXISTS (SELECT NULL FROM dbo.OrderLineItemType2 WHERE OrderId = o.Id)) 

bastante fácil, pero acabo de tener un requisito añadido para mostrar si un pedido contiene elementos de línea de tipo 1 o tipo 2 (o ambos) en la red donde se muestran estos resultados:

 
Order ID | T1 | T2 | Last name | Price | Status 
============================================================ 
12345 | x | | Smith  | $100.00 | In Production 
12346 | x | x | Jones  | $147.23 | Part Dispatched 
12347 | | x | Atwood | $12.50 | Dispatched 

el único La forma en que puedo pensar es hacer:

CREATE VIEW OrdersView WITH SCHEMABINDING AS 
SELECT o.Id, 
     CASE WHEN EXISTS (SELECT NULL FROM dbo.OrderLineItemType1 WHERE OrderID = o.Id) THEN 1 ELSE 0 END AS HasType1, 
     CASE WHEN EXISTS (SELECT NULL FROM dbo.OrderLineItemType2 WHERE OrderId = o.ID) THEN 1 ELSE 0 END AS HasType2, 
     o.OrderDate, o.LastName, o.TotalPrice, s.Status 
FROM dbo.Orders o INNER JOIN dbo.OrderStatus s on o.Id = s.OrderId 
WHERE NOT EXISTS (SELECT NULL from dbo.OrderStatus where OrderId = s.OrderId and StatusDate > s.StatusDate 
AND (EXISTS (SELECT NULL FROM dbo.OrderLineItemType1 WHERE OrderId = o.Id) 
    OR EXISTS (SELECT NULL FROM dbo.OrderLineItemType2 WHERE OrderId = o.Id)) 

Pero esto huele mal con la duplicación de las cláusulas EXISTS. ¿Hay una mejor manera de escribirlo? ¿Puedo hacerlo funcionar mejor?

+1

SELECT null'? No he visto código usando un constructo así durante bastante tiempo: arreglaron el optimizador de modo que 'SELECT *' en las cláusulas 'EXISTS' sea tan eficiente (si no más) que las construcciones alternativas de nuevo, creo, SQL Servidor 2000 (si no es anterior) –

+0

@Damien No escribí la vista original: P – Jon

+0

Si examina el plan de ejecución, ¿cuántos escaneos/búsquedas contra las tablas OrderLineItemTypeX hay? Si solo hay uno, entonces el optimizador ha sido lo suficientemente inteligente para detectar que solo lo necesita una vez, y es poco probable que mejore el rendimiento (pero puede haber mejores formas de escribirlo, aún pensando en eso) –

Respuesta

3

puede DEJAR UNIRSE en OrderLineItemType1 y OrderLineItemType2 y luego filtrar las filas donde ambas columnas son NULL en la cláusula WHERE.

+0

También tendría que introducir un 'GROUP BY ', y algunos agregados en su cláusula' SELECT', ya que no puede (según su descripción) ser más de uno de estos elementos de línea para un orden en particular. –

+0

buena llamada, pasó por alto esa parte – leeny

+0

De acuerdo en LEFT JOINs, solo que no las tablas en sí mismas sino las listas derivadas como 'SELECT DISTINCT OrderId FROM ...'. –

0

Un cambio que puede valer la pena realizar un perfil (pero no directamente relacionado con su pregunta específica).

Las siguientes dos líneas:

FROM dbo.Orders o INNER JOIN dbo.OrderStatus s on o.Id = s.OrderId 
WHERE NOT EXISTS (SELECT NULL from dbo.OrderStatus where OrderId = s.OrderId and StatusDate > s.StatusDate 

Puede ser mejor escribir esto como:

FROM dbo.Orders o INNER JOIN dbo.OrderStatus s on o.Id = s.OrderId 
     LEFT JOIN dbo.OrderStatus s_later on o.Id = s_later.OrderId and s_later.StatusDate > s.StatusDate 
WHERE s_later.OrderId is null 

Por lo general encontramos que este se comporta mejor (pero es una de las que vale la pena perfiles tanto formas).

LEFT JOIN intenta buscar filas posteriores que se apliquen al mismo pedido, luego la cláusula WHERE rechaza cualquier fila de resultados potenciales donde se produjo dicha coincidencia, por lo que la única fila coincidente de s debe ser la última para este pedido.

+0

El constructo básicamente selecciona solo los registros que un referido por OrderStatus con los primeros valores de StatusDate, o estoy equivocado? Personalmente, preferiría INNER JUNIN con 'SELECT OrderId, MIN (StatusDate) FROM OrderStatus GROUP BY OrderId' en ambas columnas en lugar de la opción LEFT JOIN + WHERE propuesta. Parece más propenso a la optimización. –

+0

@Andriy M: en realidad está buscando la última fila en OrderStatus para este pedido en particular, y está utilizando algo más que la información de fecha de esta fila ('s.Status' es la última columna de la selección) - para que pueda Simplemente haga 'MAX (StatusDate)' para cada OrderID. –

+0

Tiene razón, y no quise eliminar la parte 'INNER JOIN OrderStatus'. El 'INNER JOIN (tabla derivada)' solo reemplaza la condición 'NOT EXISTS()' en el script original (y la condición LEFT JOIN + WHERE en el suyo). En realidad, se une a los valores OrderId y MIN (StatusDate), el último de los cuales solo se puede comparar con s.StatusDate. –

0

No es necesario EXISTS aquí en absoluto:

SELECT o.Id, HasType1, HasType2, o.OrderDate, o.LastName, o.TotalPrice, s.Status 
FROM dbo.Orders o 
CROSS APPLY 
     (
     SELECT TOP 1 s.* 
     FROM dbo.OrderStatus 
     WHERE OrderId = o.Id 
     ORDER BY 
       StatusDate DESC 
     ) s 
OUTER APPLY 
     (
     SELECT TOP 1 1 AS HasType1 
     FROM dbo.OrderLineItemType1 
     WHERE OrderID = o.Id 
     ) olt1 
OUTER APPLY 
     (
     SELECT TOP 1 1 AS HasType2 
     FROM dbo.OrderLineItemType2 
     WHERE OrderID = o.Id 
     ) olt2 
Cuestiones relacionadas