Как найти N последовательных записей в таблице, используя SQL - PullRequest
8 голосов
/ 28 апреля 2010

У меня есть следующее определение таблицы с образцами данных. В следующей таблице Customer Product & Date являются ключевыми полями

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

В приведенной выше таблице мне нужно найти N или> N последовательных записей, где не было продажи, продажная стоимость была «НЕТ» Например, если N равно 2, результирующий набор вернет следующее

     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

Может ли кто-нибудь помочь мне с запросом SQL, чтобы получить желаемые результаты. Я использую SQL Server 2005. Я начал играть, используя предложения ROW_NUMBER () AND PARTITION, но не повезло. Спасибо за любую помощь

Ответы [ 4 ]

4 голосов
/ 28 апреля 2010

Вам нужно сопоставить вашу таблицу с самим собой, как если бы там было 2 таблицы. Таким образом, вы используете два псевдонима, o1 и o2, чтобы обратиться к вашей таблице:

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

Обратите внимание, что я выполнил запрос к базе данных postgresql - возможно, синтаксис отличается на ms-sql-сервере, возможно, на псевдониме 'ОТ одного AS o1', возможно, и, возможно, вы не можете добавить / вычесть таким образом.

1 голос
/ 28 апреля 2010

Другой подход, вдохновленный последней строкой хруста.

Получить - для заданной даты первая дата с YES позже этого и последняя дата с YES ранее этого. Они образуют границу, где наши даты должны соответствовать.

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;

Может быть, нужна какая-то оптимизация, потому что первая таблица участвует в запросе 5 раз. :)

0 голосов
/ 28 апреля 2010

Спасибо всем за размещение вашего решения. Думаю, я бы тоже поделился своим решением со всеми. Так же, как к вашему сведению, я получил это решение от другого участника форума по SQL Server Central. Я определенно не собираюсь брать кредит на это решение.

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 голосов
/ 28 апреля 2010

Хорошо, нам нужен переменный ответ. Мы ищем дату, где у нас есть N следующих дат, все с полем продажи НЕТ.

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; 

Это даст нам дату, которую мы используем для подзапроса.

Примечания:

  • Я использовал 'datum' вместо date, потому что date является зарезервированным ключевым словом в postgresql.

  • В Oracle вы можете использовать пустышку виртуальной таблицы, которая содержит все, что вы просите, например «SELCT foo FROM dual WHERE foo in (1, 2, 3);» который даст вам 1, 2, 3, если я правильно помню. В зависимости от поставщика, могут быть другие приемы, чтобы получить последовательность от 1 до N. Я создал таблицу i со столбцом i и заполнил ее значениями от 1 до 100, и я ожидаю, что N не будет превышать 100; Начиная с нескольких версий, postgresql содержит функцию 'generate_series (from, to), которая также решит проблему и может иметь сходство с решениями для вашей конкретной базы данных. Но я должен работать независимо от поставщика.

  • если N == 17, вам нужно изменить 3 места с 3 на 17.

Окончательный запрос будет:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...