Получить все записи перед определенным событием в SQL - PullRequest
0 голосов
/ 05 октября 2018

У меня есть такая таблица DATA

+--------+--------+----------+------+
| TranID | CustID | TransSeq | Type |
+--------+--------+----------+------+
|    1   |  100   |      1   | A    |
|    2   |  100   |      2   | A    |
|    3   |  100   |      3   | B    |
|    4   |  200   |      1   | A    |
|    5   |  200   |      2   | B    |
|    6   |  200   |      3   | A    |
|    7   |  200   |      4   | A    |
|    8   |  200   |      5   | A    |
+--------+--------+----------+------+

Я хочу получить все записи до Type B.Следовательно, мой вывод будет выглядеть так:

+--------+--------+----------+------+
| TranID | CustID | TransSeq | Type |
+--------+--------+----------+------+
|    1   |  100   |      1   | A    |
|    2   |  100   |      2   | A    |
|    4   |  200   |      1   | A    |
+--------+--------+----------+------+

Один из способов, который я могу придумать, следующий:

Шаг 1 - Создать временную таблицу, хранящую CustID и TransSeq, где type == B

CREATE TABLE TEMP AS
select CustID, TransSeq as TransSeq_B from DATA 
where Type = "B"

Вывод шага 1 выглядит следующим образом

+--------+------------+------+
| CustID | TransSeq_B | Type |
+--------+------------+------+
|  100   |      3     | B    |
|  200   |      2     | B    |
+--------+------------+------+

Шаг 2 - объединить TEMP с данными, используя CustID

CREATE TABLE DATA_NEW AS
select D.TranID, D.CustID, D.TransSeq, D.Type, T.TransSeq_B
from DATA inner join TEMP on D.CustID = T.CustID

Вывод шага 2 выглядит следующим образом

+--------+--------+----------+------+------------+
| TranID | CustID | TransSeq | Type | TransSeq_B |
+--------+--------+----------+------+------------+
|    1   |  100   |      1   | A    |       3    |
|    2   |  100   |      2   | A    |       3    |
|    3   |  100   |      3   | B    |       3    |
|    4   |  200   |      1   | A    |       2    |
|    5   |  200   |      2   | B    |       2    |
|    6   |  200   |      3   | A    |       2    |
|    7   |  200   |      4   | A    |       2    |
|    8   |  200   |      5   | A    |       2    |
+--------+--------+----------+------+------------+

Шаг 3 - Запросите эту новую таблицу из шага 2 и сохраняйте записи, где все TransSeq меньше TransSeq_B

select * from DATA_NEW
where TransSeq < TransSeq_B

Есть ли эффективный способ сделать это, так как у меня много записей (> 20M)

Ответы [ 3 ]

0 голосов
/ 05 октября 2018

Вы в принципе поняли идею совершенно правильно - единственное, что вам нужно сделать, это вместо создания новых таблиц использовать соответствующие запросы в качестве представлений в JOIN:

SELECT
  beforeB.*
FROM
  Table1 AS beforeB
  INNER JOIN (
    SELECT 
      CustID,
      MIN(TransSeq) AS TransSeq
    FROM Table1
    WHERE Type='B'
    GROUP BY CustID
  ) AS theB
  ON beforeB.CustID=theB.CustID
WHERE
  beforeB.TransSeq<theB.TransSeq

Обязательный SQLfiddle here .

Объяснение: Представление theB фильтрует события типа B из таблицы для каждого клиента.Он присоединяется к таблице транзакций по идентификатору клиента, действуя как селектор, сохраняя только строки с меньшим значением TransSqq.

Это может быть более эффективным, чем EXISTS, так как в зависимости от размеров и индексов результирующего набора нет необходимости запускать подзапрос для каждой строки, если представление JOIN ed можно сохранить в ОЗУ поверхдлительность запроса.

0 голосов
/ 05 октября 2018

Возможна возможность самостоятельного присоединения к столу.Приведенный ниже запрос имеет псевдоним A для строк типа A и B для строк типа B (при этом все еще используется та же фактическая таблица, DATA).Группируя по A, вы можете использовать агрегатную функцию min(B.TransSeq), чтобы получить наименьшую последовательность B для этого клиента.Это позволяет вам получить все A, которые находятся «до» мин. (B).

Я собирался предложить второе решение с объединением на подвыборке, но это в основном то, что предложил Eugen Rieck, поэтому я будупридерживайтесь этого, и вы сможете проверить, что лучше всего работает в вашем сценарии.Общая идея та же.

Я не знаю, быстрее или медленнее, чем другие решения.Я думаю, что этот запрос определенно выиграл бы от наличия объединенного индекса (CustId, Type) и / или (CustId, Type, TransId).И если это так, он может быть более эффективным, потому что он сначала объединяет, а затем группы (что позволяет лучше использовать индексы), или он может быть менее эффективным, потому что он должен работать с большим промежуточным набором данных.Поэтому это зависит от различных факторов, в том числе от имеющихся у вас индексов, конфигурации оборудования и от того, будете ли вы запрашивать это для небольшого набора клиентов или для всей таблицы.

select
  A.*
from 
  DATA A
  inner join DATA B on B.custid =  A.custID and B.Type = 'B'
where 
  A.Type = 'A'
  -- and A.CustId = 100 -- if you like to filter by customer
group by
  A.TranId
having
  A.TransSeq < min(B.TransSeq);
0 голосов
/ 05 октября 2018

Один подход использует запрос EXISTS.В приведенном ниже предложении EXISTS для каждой записи в вашей таблице проверяются все остальные записи, имеющие такое же значение CustID, чтобы проверить, существуют ли какие-либо более ранние записи с типом B.Если нет, то эта запись будет добавлена ​​в набор результатов.

SELECT *
FROM DATA d1
WHERE
    d1.Type = 'A' AND
    NOT EXISTS (SELECT 1 FROM DATA d2
                WHERE d1.CustID = d2.CustID AND d2.TranID < d1.TranID AND
                      d2.Type = 'B');

enter image description here

Демо

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...