2010-04-27 24 views
7

Tengo la siguiente definición de tabla con datos de muestra. En la siguiente tabla, el Cliente Producto & fecha son campos claveCómo encontrar N registros consecutivos en una tabla usando SQL

Table One 
Customer Product Date   SALE 
    X   A  01/01/2010 YES 
    X   A  02/01/2010 YES 
    X   A  03/01/2010 NO 
    X   A  04/01/2010 NO 
    X   A  05/01/2010 YES 
    X   A  06/01/2010 NO 
    X   A  07/01/2010 NO 
    X   A  08/01/2010 NO 
    X   A  09/01/2010 YES 
    X   A  10/01/2010 YES 
    X   A  11/01/2010 NO 
    X   A  12/01/2010 YES 

En la tabla anterior, necesito encontrar el N o> N registros consecutivos en que no había venta, Valor de venta fue 'no' Por ejemplo , si n es 2, el conjunto, el resultado sería devolver el siguiente

 Customer Product Date   SALE 
     X   A  03/01/2010 NO 
     X   A  04/01/2010 NO 
     X   A  06/01/2010 NO 
     X   A  07/01/2010 NO 
     X   A  08/01/2010 NO 

¿Puede alguien ayudarme con una consulta SQL para obtener los resultados deseados. Estoy usando SQL Server 2005. Empecé a jugar usando las cláusulas ROW_NUMBER() Y PARTITION, pero no tuve suerte. Gracias por cualquier ayuda

+0

¿Tiene acceso para cambiar el esquema de la tabla? – munch

+0

Hola, Tengo acceso al esquema de la tabla. – user320587

+0

Las fechas en el ejemplo parecen ser el 1 ° del mes. ¿Es así como los datos están realmente organizados o solo son datos de muestra? De lo contrario, ¿qué significa "consecutivo" en este contexto? – MJB

Respuesta

3

Necesita hacer coincidir su tabla consigo mismo, como si hubiera 2 tablas. Por lo que utilizar dos alias, O1 y O2 para referirse a su mesa:

SELECT DISTINCT o1.customer, o1.product, o1.datum, o1.sale 
    FROM one o1, one o2 
    WHERE (o1.datum = o2.datum-1 OR o1.datum = o2.datum +1) 
    AND o1.sale = 'NO' 
    AND o2.sale = 'NO'; 
customer | product | datum | sale 
----------+---------+------------+------ 
X  | A  | 2010-01-03 | NO 
X  | A  | 2010-01-04 | NO 
X  | A  | 2010-01-06 | NO 
X  | A  | 2010-01-07 | NO 
X  | A  | 2010-01-08 | NO 

Tenga en cuenta que he realizado la consulta en una base de datos PostgreSQL - tal vez la sintaxis difiere en ms-sql-servidor, tal vez en el alias 'FROM uno AS o1 'quizás, y tal vez no pueda agregar/sustraer de esa manera.

+0

Hola Stefan, Su sugerencia funciona perfecto. ¿Cómo manejo el N caso consecutivo? – user320587

+0

Me temo que recibo esa parte N de su pregunta en este momento. Hm. Difícil. ¿Quieres especificar N = 16 o N = 375 y obtener un resultado analógico para al menos 16 o 375 fechas? ¿De qué son las N de las que estamos hablando? Necesito más espacio y agrego una respuesta adicional. –

+0

Hola Stefan, la N es variable. Este N sería especificado por el usuario en el tiempo de ejecución de la consulta. Pude encontrar una solución para esto y pronto lo publicaré. Gracias por toda su ayuda. Lo aprecio. Javid – user320587

0

Ok, necesitamos una respuesta variable. Buscamos una fecha, donde tenemos N siguientes fechas, todas con el campo de venta NO.

SELECT d1.datum 
FROM one d1, one d2, i 
WHERE d1.sale = 'NO' AND d2.sale = 'NO' 
    AND d1.datum = (d2.datum - i) 
    AND i > 0 AND i < 4 
GROUP BY d1.datum 
HAVING COUNT (*) = 3; 

Esto nos dará la fecha, que utilizamos para subquerying.

Notas:

  • he usado 'dato' en lugar de la fecha, porque la fecha es una palabra clave reservada en PostgreSQL.

  • En Oracle puede utilizar un maniquí tabla virtual, que contiene cualquier cosa que pidan, como 'foo SELCT de la doble donde foo en (1, 2, 3);' que te dará 1, 2, 3, si mal no recuerdo. Dependiendo del proveedor, puede haber otros trucos para obtener una secuencia de 1 a N. Creé una tabla i con la columna i, y la llené con los valores de 1 a 100, y espero que N no exceda 100; Desde algunas versiones, postgresql contiene una función 'generate_series (from, to) que también resolvería el problema y podría tener similitudes con soluciones para su base de datos específica. Pero la tabla debería funcionar independiente del vendedor.

  • si N == 17, tiene que modificar 3 lugares de 3 a 17.

La consulta final será:

SELECT o4.* 
FROM one o3, one o4 
WHERE o3.datum = (
    SELECT d1.datum 
    FROM one d1, one d2, i 
    WHERE d1.sale = 'NO' AND d2.sale = 'NO' 
     AND d1.datum = (d2.datum - i) 
     AND i > 0 AND i <= 3 
    GROUP BY d1.datum 
    HAVING COUNT (*) = 3) 
AND o4.datum <= o3.datum + 3 
AND o4.datum >= o3.datum; 
customer | product | datum | sale 
----------+---------+------------+------ 
X  | A  | 2010-02-06 | NO 
X  | A  | 2010-02-07 | NO 
X  | A  | 2010-02-08 | NO 
X  | A  | 2010-02-09 | NO 
+0

Ah, mencioné que esto solo devolverá coincidencias exactas donde N = 3, no N> = 3. –

1

Un enfoque diferente, inspirado en Munchs últimos línea.

Obtenga, para una fecha determinada, la primera fecha con SÍ posterior a esa fecha y la última con SÍ anterior a esa fecha. Estos forman la frontera, donde nuestras fechas deberán encajar.

SELECT (o1.datum), 
    MAX (o3.datum) - MIN (o2.datum) AS diff 
FROM one o1, one o2, one o3 
WHERE o1.sale = 'NO' 
AND o3.datum < 
    (SELECT MIN (datum) 
    FROM one 
    WHERE datum >= o1.datum 
    AND SALE = 'YES') 
AND o2.datum > 
    (SELECT MAX (datum) 
    FROM one 
    WHERE datum <= o1.datum 
    AND SALE = 'YES') 
GROUP BY o1.datum 
HAVING MAX (o3.datum) - MIN (o2.datum) >= 2 
ORDER BY o1.datum; 

Tal vez sea necesario algún tipo de optimización, porque la tabla es 5 veces implicadas en la consulta. :)

+0

-1 Para usar la sintaxis 'where' implícita – Johan

+0

¿Simplemente no te gusta, o tienes argumentos justificados en contra? –

+0

A. es confuso, B. no puede decir cuáles son los criterios de unión y cuáles los criterios de filtro, C. es un error propenso a un error y tiene una combinación cruzada, D. No permite las uniones a la izquierda. E. Es inconsistente, realiza uniones internas con sintaxis implícita pero tiene que hacer uniones externas con sintaxis F. explícita. No sigue las mejores prácticas G. Hace más difícil para el optimizador de consultas producir código eficiente (esp en el servidor SQL). – Johan

0

Gracias a todos por publicar su solución.Pensamiento, también compartiría mi solución con todos. Solo como un FYI, recibí esta solución de otro miembro del foro de SQL Server Central. Definitivamente no me voy a atribuir el mérito de esta solución.

DECLARE @CNT INT 
SELECT @CNT = 3 

SELECT * FROM 
(
    SELECT 
    [Customer], [Product], [Date], [Sale], groupID, 
    COUNT(*) OVER (PARTITION BY [Customer], [Product], [Sale], groupID) AS groupCnt 
    FROM 
    (
    SELECT 
     [Customer], [Product], [Date], [Sale], 
     ROW_NUMBER() OVER (PARTITION BY [Customer], [Product] ORDER BY [Date]) 
     - ROW_NUMBER() OVER (PARTITION BY [Customer], [Product], [Sale] ORDER BY [Date]) AS groupID 
    FROM 
     [TableSales] 
) T1 
) T2 
WHERE 
    T2.[Sale] = 'NO' AND T2.[groupCnt] >= @CNT 
+0

Esta consulta tiene el potencial de ser muy, muy lenta (consultas N^3 si la base de datos no puede optimizarla de ninguna manera) – Nicole

Cuestiones relacionadas