Как выбрать последовательные строки до выполнения условия в другом столбце? - PullRequest
0 голосов
/ 08 июня 2019

У меня есть таблица (в Redshift), которая имеет следующие четыре столбца:

cust_id | timestamp | color | visted_pages_sequence 

Я хочу выбрать для каждого cust_id строки между visted_pages_sequence LIKE '%first-page% and visted_pages_sequence LIKE '%end-page%. Зная, что могут быть последовательности, имеющие только visited_pages_sequence, у которого есть только строка LIKE %first-page%, и после этого ничего не происходит. И другие, имеющие последовательность строк, которые имеют в столбце visit_pages_sequence строку, которая удовлетворяет условию LIKE %first-page%, последовательную строку, удовлетворяющую %mid-page-1% другой последовательной строке, удовлетворяющей условию LIKE %mid-page-2% НО: ни одна строка, удовлетворяющая условию LIKE %end-page% .

Как выбрать данные, отсортированные по customer_ids?

Вот пример моей таблицы:

| cust_id | timestamp           | color   |   visited_page_sequence |
|---------|---------------------|---------|-------------------------|
| 54628   | 11/11/2015 11:46:00 |  black  |    this-first-page      |
|54628    | 11/11/2015 11:47:00 |  white  |    this-middle-page1    |
|94254    | 11/11/2015 11:48:00 |         |                         |
|45456    | 11/11/2015 11:49:00 |  braun  |    this-first-page      |
|45456    | 11/11/2015 11:50:00 |  beige  |    this-middle-page1    |
|45456    | 11/11/2015 11:52:00 |         |   this-end-page         |
|55411    | 11/11/2015 11:53:00 |  red    |                         |
|42462    | 11/11/2015 11:54:00 |  cyan   |     this-another-page   |
|24177    | 11/11/2015 11:55:00 |  orange |   this-first-page       |
|24177    | 11/11/2015 11:56:00 |  gray   |     this-next-page      |
|88888    | 11/11/2015 11:57:00 |  pink   |                         |
|94476    | 11/11/2015 11:58:00 |  black  |    this-first-page      |
|94476    | 11/11/2015 11:59:00 |  braun  |    this-middle-page1    |
|94476    | 11/11/2015 12:00:00 |         |    this-middle-page2    |
|94476    | 11/11/2015 12:01:00 |  white  |    this-end-page        |
|64579    | 11/11/2015 12:02:00 |  green  |    this-another-page    |

Я бы хотел что-то вроде этого:

| cust_id | timestamp            | color     | visited_page_sequence |   
|---------|----------------------|-----------|-----------------------|
| 45456   | 11/11/2015 11:49:00  | braun     |this-first-page        |
| 45456   | 11/11/2015 11:50:00  | beige     |this-middle-page1      |
| 45456   | 11/11/2015 11:52:00  |           |this-end-page          |
| 94476   | 11/11/2015 11:58:00  | black     |this-first-page        |
| 94476   | 11/11/2015 11:59:00  | braun     |this-middle-page1      |
| 94476   | 11/11/2015 12:00:00  |           |this-middle-page2      |
| 94476   | 11/11/2015 12:01:00  | white     |this-end-page          |

PS: 1) МОЖЕТ быть более одной строки для каждого cust_id, с посещенной_последовательностью, например «% first-page%» 2) МОЖЕТ быть более одной строки для каждого cust_id с значением visit_page_sequence, например «% middle-page-1%» или middle-page-2, или любых других средних страниц, не перечисленных здесь. 3) существует не более одной строки на cust_id с значением visit_page_sequence, например «% end-page%» 4) комбинация (cust_id, timestamp) не имеет дубликатов

РЕДАКТИРОВАТЬ после комментариев: 5) ЕСЛИ значение в visit_page_sequence появляется два раза подряд, должен быть возвращен только последний случай!

Ответы [ 2 ]

0 голосов
/ 08 июня 2019

Один из способов сделать это - сначала узнать минимальные / максимальные временные метки для каждого клиента, отфильтровывая строки за пределами этого диапазона.
Что-то вроде:

/*
  for each customer, find out the min/max timestamp we are interested in,
  ie when they first visited a 'first-page' and last visited a 'end-page'
*/
WITH
min_max_by_customer AS (
  SELECT
    cust_id,
    MIN(
      CASE
        WHEN visited_page_sequence LIKE '%first-page%' THEN timestamp
        ELSE null
      END
    ) AS min_first_page_timestamp,
    MAX(
      CASE
        WHEN visited_page_sequence LIKE '%end-page%' THEN timestamp
        ELSE null
      END
    ) AS max_end_page_timestamp
  FROM i
  GROUP BY cust_id
),
/*
  fetch the actual data we're interested in (ie timestamp between first-page/end-page).
  also flag a row to be removed if the next row contains the same 'visited_page_sequence'
*/
rows_per_customer AS(
  SELECT
    i.*,
    visited_page_sequence = LEAD(visited_page_sequence) OVER (PARTITION BY cust_id ORDER BY timestamp ASC) AS same_page_as_next_row
  FROM i
  JOIN min_max_by_customer
  USING (cust_id)
  WHERE i.timestamp BETWEEN min_first_page_timestamp AND max_end_page_timestamp
)
SELECT *
FROM rows_per_customer
WHERE same_page_as_next_row IS NOT TRUE /* XXX not the same as 'IS FALSE' due to SQL's three-value logic */
;

возвращает

┌─────────┬─────────────────────┬───────────────────────┐
│ cust_id │      timestamp      │ visited_page_sequence │
├─────────┼─────────────────────┼───────────────────────┤
│   45456 │ 2015-11-11 11:49:00 │  this-first-page      │
│   45456 │ 2015-11-11 11:50:00 │  this-middle-page1    │
│   45456 │ 2015-11-11 11:52:00 │ this-end-page         │
│   94476 │ 2015-11-11 11:58:00 │  this-first-page      │
│   94476 │ 2015-11-11 11:59:00 │  this-middle-page1    │
│   94476 │ 2015-11-11 12:00:00 │  this-middle-page2    │
│   94476 │ 2015-11-11 12:01:00 │  this-end-page        │
└─────────┴─────────────────────┴───────────────────────┘
(7 rows)
0 голосов
/ 08 июня 2019

Предполагая, что

  • не более одной строки на cust_id с visited_page_sequence like '%first-page%'
  • не более одной строки на cust_id с visited_page_sequence like '%end-page%'
  • комбинация (cust_id, timestamp) не имеет дубликатов

вы можете использовать:

select t.*
from myTable f
join myTable l on l.cust_id = f.cust_id
join myTable t
  on  t.cust_id = f.cust_id
  and t.timestamp between f.timestamp and l.timestamp
where f.visited_page_sequence like '%first-page%'
  and l.visited_page_sequence like '%end-page%'
order by t.cust_id, t.timestamp

db-fiddle

...