2012-02-28 1 views
5

У меня есть таблица с сделок:SQL: Выберите операции, где строки не являются критериев внутри одной и той же таблицы

Transactions 
------------ 
id | account | type | date_time    | amount 
---------------------------------------------------- 
1 | 001  | 'R' | '2012-01-01 10:01:00' | 1000 
2 | 003  | 'R' | '2012-01-02 12:53:10' | 1500 
3 | 003  | 'A' | '2012-01-03 13:10:01' | -1500 
4 | 002  | 'R' | '2012-01-03 17:56:00' | 2000 
5 | 001  | 'R' | '2012-01-04 12:30:01' | 1000 
6 | 002  | 'A' | '2012-01-04 13:23:01' | -2000 
7 | 003  | 'R' | '2012-01-04 15:13:10' | 3000 
8 | 003  | 'R' | '2012-01-05 12:12:00' | 1250 
9 | 003  | 'A' | '2012-01-06 17:24:01' | -1250 

, и я хочу, чтобы выбрать все определенного типа («R»), а не те, что немедленно (в порядке убывания поля дата_время) имеют другую сделку другого типа («А») на тот же счет, поданном ...

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

id | account |type | date     | amount 
---------------------------------------------------- 
1 | 001  | 'R' | '2012-01-01 10:01:00' | 1000 
5 | 001  | 'R' | '2012-01-04 12:30:01' | 1000 
7 | 003  | 'R' | '2012-01-04 15:13:10' | 3000 

(Как вы можете видеть, строка 2 не отображается, потому что строка 3 «отменяет» ее ... также строка 4 отменяется по строке 6 '; Появится строка 7 (хотя учетная запись 003 принадлежит к отмененной строке №2, на этот раз в строке 7 она не отменяется ни одной строкой «А»); И строка 8 не появится (это тоже для учетной записи 003, так как теперь она отменяется на 9, что также не отменяет 7: только 8: 0 ...

Я пробовал Соединения, подзапросы в Где пункты, но я действительно не знаю, как я должен сделать мой запрос ...

То, что я пробовал:

Попытка присоединяется:

SELECT trans.type as type, 
      trans.amount as amount, 
      trans.date_time as dt, 
      trans.account as acct, 
    FROM Transactions trans 
INNER JOIN (SELECT t.type AS type, t.acct AS acct, t.date_time AS date_time 
       FROM Transactions t 
       WHERE t.date_time > trans.date_time 
      ORDER BY t.date_time DESC 
     ) AS nextTrans 
     ON nextTrans.acct = trans.acct 
    WHERE trans.type IN ('R') 
     AND nextTrans.type NOT IN ('A') 
ORDER BY DATE(trans.date_time) ASC 

Это выдает ошибку, так как я могу Внедрить внешние значения в JOIN в MySQL.

Попытка подзапрос в том, где:

SELECT trans.type as type, 
      trans.amount as amount, 
      trans.date_time as dt, 
      trans.account as acct, 
    FROM Transactions trans 
    WHERE trans.type IN ('R') 
     AND trans.datetime < 
      (SELECT t.date_time AS date_time 
       FROM Transactions t 
       WHERE t.account = trans.account 
      ORDER BY t.date_time DESC 
     ) AS nextTrans 
     ON nextTrans.acct = trans.acct 

ORDER BY DATE(trans.date_time) ASC 

Это неправильно, я могу ввести внешние значения в WHERE в MySQL, но я не могу управлять, чтобы найти способ, чтобы правильно фильтровать для того, что мне нужно ...

ВАЖНЫЙ РЕДАКТИРОВАНИЕ:

Мне удалось найти решение, но теперь оно нуждается в серьезной оптимизации. Вот он:

SELECT * 
    FROM (SELECT t1.*, tFlagged.id AS cancId, tFlagged.type AS cancFlag 
      FROM transactions t1 
    LEFT JOIN (SELECT t2.* 
        FROM transactions t2 
       ORDER BY t2.date_time ASC) tFlagged 
      ON (t1.account=tFlagged.account 
        AND 
       t1.date_time < tFlagged.date_time) 
     WHERE t1.type = 'R' 
     GROUP BY t1.id) tCanc 
WHERE tCanc.cancFlag IS NULL 
    OR tCanc.cancFlag <> 'A' 

Я присоединился к таблице с собой, просто учитывая тот же аккаунт и отличную дату. Присоединение отправляется по дате_time. Группировка по id Мне удалось получить только первый результат соединения, который является следующей транзакцией для той же учетной записи.

Затем на внешнем элементе я отфильтровываю те, у которых есть «А», так как это означает, что следующая транзакция была фактически отменена для него. Другими словами, если для одной и той же учетной записи нет следующей транзакции или если следующая транзакция является «R», то она не отменяется, и она должна быть указана в результате ...

Я получил это:

+----+---------+------+---------------------+--------+--------+----------+ 
| id | account | type | date_time   | amount | cancId | cancFlag | 
+----+---------+------+---------------------+--------+--------+----------+ 
| 1 | 001  | R | 2012-01-01 10:01:00 | 1000 |  5 | R  | 
| 5 | 001  | R | 2012-01-04 12:30:01 | 1000 | NULL | NULL  | 
| 7 | 003  | R | 2012-01-04 15:13:10 | 3000 |  8 | R  | 
+----+---------+------+---------------------+--------+--------+----------+ 

Он связывает каждую транзакцию со следующей за той же учетной записью, а затем отфильтровывает те, которые были отменены ... Успех!

Как я уже сказал, проблема теперь в оптимизации. У моих реальных данных много строк (как ожидается, будет отображаться таблица с транзакциями во времени), а для таблицы размером ~ 10 000 строк я получил положительный результат с этим запросом в 1мин.44 сек. Я полагаю, что это связано с присоединением ... (Для тех, кто знает протокол здесь, что мне делать? Запустите здесь новый вопрос и опубликуйте это как решение для этого?? Или просто ждать больше ответов здесь)

+0

Опубликовать то, что вы пробовали. –

+0

там это ... –

+0

Смотрите: http://meta.stackexchange.com/questions/2950/should-hi-thanks-taglines-and-salutations-be-removed-from-posts –

ответ

1

Вот решение, основанное на вложенных подзапросов. Во-первых, я добавил несколько строк, чтобы поймать еще несколько случаев. Например, транзакция 10 не должна отменяться транзакцией 12, поскольку транзакция 11 входит между ними.

> select * from transactions order by date_time; 
+----+---------+------+---------------------+--------+ 
| id | account | type | date_time   | amount | 
+----+---------+------+---------------------+--------+ 
| 1 |  1 | R | 2012-01-01 10:01:00 | 1000 | 
| 2 |  3 | R | 2012-01-02 12:53:10 | 1500 | 
| 3 |  3 | A | 2012-01-03 13:10:01 | -1500 | 
| 4 |  2 | R | 2012-01-03 17:56:00 | 2000 | 
| 5 |  1 | R | 2012-01-04 12:30:01 | 1000 | 
| 6 |  2 | A | 2012-01-04 13:23:01 | -2000 | 
| 7 |  3 | R | 2012-01-04 15:13:10 | 3000 | 
| 8 |  3 | R | 2012-01-05 12:12:00 | 1250 | 
| 9 |  3 | A | 2012-01-06 17:24:01 | -1250 | 
| 10 |  3 | R | 2012-01-07 00:00:00 | 1250 | 
| 11 |  3 | R | 2012-01-07 05:00:00 | 4000 | 
| 12 |  3 | A | 2012-01-08 00:00:00 | -1250 | 
| 14 |  2 | R | 2012-01-09 00:00:00 | 2000 | 
| 13 |  3 | A | 2012-01-10 00:00:00 | -1500 | 
| 15 |  2 | A | 2012-01-11 04:00:00 | -2000 | 
| 16 |  2 | R | 2012-01-12 00:00:00 | 5000 | 
+----+---------+------+---------------------+--------+ 
16 rows in set (0.00 sec) 

Сначала создайте запрос для захвата, для каждой транзакции, «дата самой последней сделки до того, что один в той же учетной записи»:

SELECT t2.*, 
     MAX(t1.date_time) AS prev_date 
FROM transactions t1 
JOIN transactions t2 
ON (t1.account = t2.account 
    AND t2.date_time > t1.date_time) 
GROUP BY t2.account,t2.date_time 
ORDER BY t2.date_time; 

+----+---------+------+---------------------+--------+---------------------+ 
| id | account | type | date_time   | amount | prev_date   | 
+----+---------+------+---------------------+--------+---------------------+ 
| 3 |  3 | A | 2012-01-03 13:10:01 | -1500 | 2012-01-02 12:53:10 | 
| 5 |  1 | R | 2012-01-04 12:30:01 | 1000 | 2012-01-01 10:01:00 | 
| 6 |  2 | A | 2012-01-04 13:23:01 | -2000 | 2012-01-03 17:56:00 | 
| 7 |  3 | R | 2012-01-04 15:13:10 | 3000 | 2012-01-03 13:10:01 | 
| 8 |  3 | R | 2012-01-05 12:12:00 | 1250 | 2012-01-04 15:13:10 | 
| 9 |  3 | A | 2012-01-06 17:24:01 | -1250 | 2012-01-05 12:12:00 | 
| 10 |  3 | R | 2012-01-07 00:00:00 | 1250 | 2012-01-06 17:24:01 | 
| 11 |  3 | R | 2012-01-07 05:00:00 | 4000 | 2012-01-07 00:00:00 | 
| 12 |  3 | A | 2012-01-08 00:00:00 | -1250 | 2012-01-07 05:00:00 | 
| 14 |  2 | R | 2012-01-09 00:00:00 | 2000 | 2012-01-04 13:23:01 | 
| 13 |  3 | A | 2012-01-10 00:00:00 | -1500 | 2012-01-08 00:00:00 | 
| 15 |  2 | A | 2012-01-11 04:00:00 | -2000 | 2012-01-09 00:00:00 | 
| 16 |  2 | R | 2012-01-12 00:00:00 | 5000 | 2012-01-11 04:00:00 | 
+----+---------+------+---------------------+--------+---------------------+ 
13 rows in set (0.00 sec) 

использовать это в качестве подзапроса, чтобы получить каждая транзакция и ее предшественник в той же строке. Используйте некоторую фильтрацию, чтобы вытащить сделки мы заинтересованы в том, а именно - «А» операции, чьи предшественники представляют собой операции «R», что именно они компенсируют -

SELECT 
    t3.*,transactions.* 
FROM 
    transactions 
    JOIN 
    (SELECT t2.*, 
      MAX(t1.date_time) AS prev_date 
    FROM transactions t1 
    JOIN transactions t2 
    ON (t1.account = t2.account 
     AND t2.date_time > t1.date_time) 
    GROUP BY t2.account,t2.date_time) t3 
    ON t3.account = transactions.account 
    AND t3.prev_date = transactions.date_time 
    AND t3.type='A' 
    AND transactions.type='R' 
    AND t3.amount + transactions.amount = 0 
    ORDER BY t3.date_time; 


+----+---------+------+---------------------+--------+---------------------+----+---------+------+---------------------+--------+ 
| id | account | type | date_time   | amount | prev_date   | id | account | type | date_time   | amount | 
+----+---------+------+---------------------+--------+---------------------+----+---------+------+---------------------+--------+ 
| 3 |  3 | A | 2012-01-03 13:10:01 | -1500 | 2012-01-02 12:53:10 | 2 |  3 | R | 2012-01-02 12:53:10 | 1500 | 
| 6 |  2 | A | 2012-01-04 13:23:01 | -2000 | 2012-01-03 17:56:00 | 4 |  2 | R | 2012-01-03 17:56:00 | 2000 | 
| 9 |  3 | A | 2012-01-06 17:24:01 | -1250 | 2012-01-05 12:12:00 | 8 |  3 | R | 2012-01-05 12:12:00 | 1250 | 
| 15 |  2 | A | 2012-01-11 04:00:00 | -2000 | 2012-01-09 00:00:00 | 14 |  2 | R | 2012-01-09 00:00:00 | 2000 | 
+----+---------+------+---------------------+--------+---------------------+----+---------+------+---------------------+--------+ 
4 rows in set (0.00 sec) 

Из результата выше, это очевидно, мы почти там - мы выявили нежелательные транзакции. Используя LEFT JOIN, мы можем отфильтровать их из целого ряда транзакций:

SELECT 
    transactions.* 
FROM 
    transactions 
LEFT JOIN 
    (SELECT 
    transactions.id 
    FROM 
    transactions 
    JOIN 
    (SELECT t2.*, 
      MAX(t1.date_time) AS prev_date 
     FROM transactions t1 
     JOIN transactions t2 
     ON (t1.account = t2.account 
     AND t2.date_time > t1.date_time) 
     GROUP BY t2.account,t2.date_time) t3 
    ON t3.account = transactions.account 
     AND t3.prev_date = transactions.date_time 
     AND t3.type='A' 
     AND transactions.type='R' 
     AND t3.amount + transactions.amount = 0) t4 
    USING(id) 
    WHERE t4.id IS NULL 
    AND transactions.type = 'R' 
    ORDER BY transactions.date_time; 

+----+---------+------+---------------------+--------+ 
| id | account | type | date_time   | amount | 
+----+---------+------+---------------------+--------+ 
| 1 |  1 | R | 2012-01-01 10:01:00 | 1000 | 
| 5 |  1 | R | 2012-01-04 12:30:01 | 1000 | 
| 7 |  3 | R | 2012-01-04 15:13:10 | 3000 | 
| 10 |  3 | R | 2012-01-07 00:00:00 | 1250 | 
| 11 |  3 | R | 2012-01-07 05:00:00 | 4000 | 
| 16 |  2 | R | 2012-01-12 00:00:00 | 5000 | 
+----+---------+------+---------------------+--------+ 
+0

да почти там! но почему 11, если он отменен на 12, появляется в конечном результате? –

+0

OK Я неправильно понял вопрос; Я думал, что для отмены A это поле 'amount' должно было быть отрицанием суммы в строке R. Избавление от предложения 'AND t3.amount + transaction.amount = 0' должно отнять это ограничение (и дать то, что вы ищете) – gcbenison

+0

wait! ты прав, чем я! Я не заметил эту деталь, это, конечно, то, что я ищу :) Позвольте мне провести дополнительные тесты! Я думаю, что это, наконец, правильный :) –

1

(отредактирован 2) ПОПРОБУЙТЕ ЭТО:

SELECT trans.tp as type, 
trans.id as id, 
trans.amount as amount, 
trans.date_time as dt, 
trans.account as acct 
FROM Transactions trans 
WHERE trans.tp = 'R' 
AND trans.account NOT IN (SELECT t.account AS acct 
    FROM Transactions t 
WHERE t.date_time > trans.date_time 
AND t.tp = 'A' 
AND t.amount = (trans.amount)-((trans.amount)*2) 
    ORDER BY t.date_time DESC 
) ; 
+0

mmmm возможно ... просто сомнение ... почему <= в предложении date_time where? строки «А» должны существовать ПОСЛЕ некоторой строки «R», поэтому для любого «R», который должен быть отменен, тогда может быть строка «A», но ее дата будет на будущее «R» 'row, а не в прошлом ... –

+0

Если вы удалите его из предложения where, он даст вам правильный результат? – prukuhkoo

+0

Мне просто нужно его протестировать, у меня много данных, поэтому тесты непростые. Я попросил просто увидеть предположения в нижней части запроса (которые я просто не вижу прямо сейчас, возможно, мне нужен отдых;), а затем, чтобы проверить, могу ли я проверить его дальше или просто отбросить, если они неправильные .. –

1

здесь я попытался в MSSQL. Проверьте логику и попробуйте в mysql. Я предполагаю, что логика заключается в том, что новая транзакция после первой транзакции отменяется. На вашей иллюстрации id = 7 выполняется после отмены id = 3.

Я проверил в MSSQL

create table Transactions(id int,account varchar(5), tp char(1),date_time datetime,amount int) 

insert into Transactions values (1,'001','R','2012-01-01 10:01:00',1000) 
insert into Transactions values (2,'003','R','2012-01-02 12:53:10',1500) 
insert into Transactions values (3,'003','A','2012-01-03 13:10:01',-1500) 
insert into Transactions values (4,'002','R','2012-01-03 17:56:00',2000) 
insert into Transactions values (5,'001','R','2012-01-04 12:30:01',1000) 
insert into Transactions values (6,'002','A','2012-01-04 13:23:01',-2000) 
insert into Transactions values (7,'003','R','2012-01-04 15:13:10',3000) 


select t.id, t.account, t.date_time, t.amount 
from Transactions t 
where t.tp = 'R' 
and not exists 
(
    select account, date_time 
    from Transactions 
    where tp = 'A' 
    and account = t.account 
    and t.date_time < date_time 
) 
+0

логика, которую вы предполагали, права, спасибо за ваше время :) На самом деле, если где-то после строка 7, для учетной записи 003 была другая строка «R», то она также должна быть показана. Но если после этого будет создана одна строка «А» для учетной записи 003, новая строка не появится, но 7 должно появиться. Позвольте мне изменить мой пример, чтобы проиллюстрировать это. Что вы знаете о логике, которую вы использовали в этом скрипте, думаете ли вы, что это должно вести себя так? –

+1

Хорошо, вам лучше предоставить достаточное количество иллюстраций для удовлетворения всех желаний. Попробуй еще раз. –

+0

Вы правы ... Думая о проблеме, и насколько я могу судить, это, наконец, последние случаи использования, которые мне нужно включить ... Тестирование с вашим скриптом как есть, строка 7 исключена, t, поэтому необходимы дальнейшие изменения :) Давайте подумаем об этом ... –

0

Если ID действительно соответствует индексу строк при сортировке по date_time - как это делается в примере (и если это не так, вы можете создать такие поле ID) - вы можете сделать это:

SELECT t1.* 
FROM transactions t1 JOIN transactions t2 ON(t2.id = t1.id + 1) 
WHERE t1.type = 'R' 
    AND NOT((t2.type = 'A') AND ((t1.amount + t2.amount) = 0)) 

т.е. использовать поле ID, чтобы получить каждую строку и его преемника в том же результате ряда; затем отфильтруйте нужные свойства.

+0

I ' вы пробовали это, но это не работает. Даже с данными, которые я использую в качестве примера (и это именно так, я попытался упростить мою модель, чтобы показать здесь только суть проблемы), этот скрипт показывает мне строки с идентификаторами 1,4,5,7 но с самого начала строка 4 не должна отображаться. Как вы можете видеть, строка 4 «отменена» в строке 6, а не сразу в строке 5, поэтому приращение id на 1 не работает для этого конкретного случая ... В любом случае факт, что идентификатор показывает, как будто индексирование по дате просто артефакт моего примера ... –

+1

@javier OK Я вижу, что теперь - почистите этот подход и посмотрим на мой новый ответ – gcbenison

0

Чтобы улучшить ваш запрос, попробуйте следующее:

SELECT t1.*, tFlagged.id AS cancId, tFlagged.tp AS cancFlag FROM t t1 
LEFT JOIN t tFlagged 
ON t1.account = tFlagged.account AND t1.date_time < tFlagged.date_time 
WHERE t1.tp = 'R' 
GROUP BY t1.id 
HAVING tFlagged.tp is null or tFlagged.tp <> 'A' 

Он будет работать намного быстрее ... надеюсь, обеспечивая те же результаты: P

+0

. В запросе для решения нет даже такого условия ... и правильного с аналогичными условиями нужно, чтобы правильно фильтровать данные в соединении ... –

+1

хорошая точка :) Я скопировал из любого места –

+1

ok ... позвольте мне проверить его с помощью моих реальных данных. Запрос действительно быстрый по сравнению с тем, что у меня есть, но дает мне различное количество строк результатов, поэтому я должен сначала проанализировать его ... Спасибо! –

Смежные вопросы