【问题标题】:Query to filter out transactions with exact offset amount查询以过滤掉具有确切偏移量的交易
【发布时间】:2018-04-08 05:23:28
【问题描述】:

下面的查询是一个粗略的草稿,其中包含我要编写的查询中的相关列,因此不要将其视为解决方案。将其用作表名和列名的指南。我正在尝试删除相同 ORDER_ID 和 ACCOUNT_ID 的任何相互抵消的交易。我认为我不能使用 SUM 进行聚合,因为这会将分组的所有 TX_AMOUNT 值加在一起。请参阅 TX_ID 6 和 7。它们都需要显示在结果集中。如何从下表中输出 TX_ID,并过滤掉任何不显示“SHOW THIS”的内容?

SELECT 
T1.ACCOUNT_ID
T1.ORDER_ID,
T1.TX_ID
FROM TRANSACTION AS T1
WHERE
T1.ACCOUNT_ID IN (
SELECT T2.ACCOUNT_ID
FROM TRANSACTION AS T2
GROUP BY T2.ACCOUNT_ID, T2.ORDER_ID
HAVING SUM(T2.TX_AMOUNT) != 0 AND T2.ORDER_ID IS NOT NULL 
)
AND T1.ORDER_ID IN (
SELECT T3.ORDER_ID
FROM TRANSACTION AS T3
GROUP BY T3.ACCOUNT_ID, T3.ORDER_ID
HAVING SUM(T3.TX_AMOUNT) != 0 AND T3.ORDER_ID IS NOT NULL 
)

TX_ID   ORDER_ID ACCOUNT_ID  TX_AMOUNT
------------------------------------
1       A1       200         -3.00  <--------- DON'T SHOW THIS; OFFSET BY #2
2       A1       200         3.00   <--------- DON'T SHOW THIS; OFFSET BY #1
3       A1       200         3.00   <--------- SHOW THIS
4       A2       999         -10.01 <--------- DON'T SHOW THIS; OFFSET BY #5
5       A2       999         10.01  <--------- DON'T SHOW THIS; OFFSET BY #4
6       A2       999         10.01  <--------- SHOW THIS
7       A2       999         5.02   <--------- SHOW THIS

【问题讨论】:

  • 我认为显示 TX_ID 2 或 3 无关紧要,只要为 tx_ID 1 删除 1 还是应该始终是更高的 tax_Id? (通过基于 FIFO 方法的网络结算,先入先出,不使用 TX_ID)我还以为你离开了凤凰基金会,和你的好朋友 Teal'c(他在 MacGyver episode 和你)
  • 正确,显示#2 或#3 很好。并且显示#5 或#6 也很好。我留了太多的水,所以我不得不永远退休。
  • 真;但不时调用它仍然很有趣。
  • 感谢好思想实验(偏离我目前的兔子洞);我需要一个!

标签: sql sql-server filter sum offset


【解决方案1】:

版本 2:MUCH 更清洁...Working DEMO with comments(您可能需要单击运行它!)以查看所需的结果(或者我可能有缓存问题)

  • CTE(公用表表达式)只是您展示的数据设置
  • CTE2 只需添加由 tx_amount、order_Id、account_Id 分区的行号。这里的关键是我们为每个 order_ID、Account_ID 和 tax_Amount 获得一个 row_number,当这 3 个值发生变化时重新开始,但当它们保持不变时增加。这稍后允许我们排除相反 tx_amounts 上的类似匹配项,而不会在一方拥有多于另一方时消除那些匹配项(您的 3.00 美元示例)
  • Select 只是从基本集中提取记录,其中存在具有相同行号 order_id 和 account 的相反值。如果没有,那么我们知道它是一个没有匹配相反 tx_Amount 的值,因此我们想要保留它。
  • 如有问题请咨询!如果有不清楚的地方很乐意提供帮助
  • 最后,如果我们更改 CTE2,因此 rowNumber() 由 tx_ID asc 而不是 tx_Amount desc 排序(除了我需要在 row_nubmer 上排序之外,这实际上没有任何作用),那么我们将摆脱遵循 FIFO 方法,最低数量首先匹配)

.

With CTE (TX_ID,   ORDER_ID, ACCOUNT_ID,  TX_AMOUNT) as (

SELECT 1,       'A1',       200,         -3.00  UNION ALL
SELECT 2,       'A1',       200,         3.00   UNION ALL
SELECT 3,       'A1',       200,         3.00   UNION ALL
SELECT 4,       'A2',       999,         -10.01 UNION ALL
SELECT 5,       'A2',       999,         10.01  UNION ALL
SELECT 6,       'A2',       999,         10.01  UNION ALL
SELECT 7,       'A2',       999,         5.02 ),

cte2 as (
SELECT A.*, row_number() over (partition by order_ID, Account_ID, Tx_Amount order by tx_Amount desc) RN
FROM cte A)

SELECT * 
FROM cte2 A
WHERE NOT exists (SELECT * 
                  FROM cte2 B
                  WHERE A.Order_ID = B.Order_ID
                    and A.Account_ID = B.Account_Id
                    and A.tx_Amount*-1 = B.tx_Amount
                    and A.RN = B.RN)

给我们:(注意我们应该通过将 * 更改为所需字段来消除 RN,但我现在太懒了)

+----+-------+----------+------------+-----------+----+
|    | TX_ID | ORDER_ID | ACCOUNT_ID | TX_AMOUNT | RN |
+----+-------+----------+------------+-----------+----+
|  1 |     2 | A1       |        200 |      3,00 |  2 |
|  2 |     7 | A2       |        999 |      5,02 |  1 |
|  3 |     5 | A2       |        999 |     10,01 |  2 |
+----+-------+----------+------------+-----------+----+

第 1 版:(刮掉这个丑陋的东西;我的意思是认真的;谁会这样想?)我确实...

  1. 做点什么。 (第 1 版)
  2. 笑(重要但被忽视的一步)
  3. 那就做对了(参见上面的第 2 版)
  4. 现在让它变得更好。 (索引、调整连接拼写、布局 cmets、在 CTE2 中的 row_number 逻辑上使用正确的 order by)

DEMO

With CTE (TX_ID,   ORDER_ID, ACCOUNT_ID,  TX_AMOUNT) as (

SELECT 1,       'A1',       200,         -3.00  UNION ALL
SELECT 2,       'A1',       200,         3.00   UNION ALL
SELECT 3,       'A1',       200,         3.00   UNION ALL
SELECT 4,       'A2',       999,         -10.01 UNION ALL
SELECT 5,       'A2',       999,         10.01  UNION ALL
SELECT 6,       'A2',       999,         10.01  UNION ALL
SELECT 7,       'A2',       999,         5.02 ),
cte2 as (
SELECT * 
FROM (Select A.Tx_Id aTx_ID
           , A.order_ID as AOrderID
           , A.Account_ID as AAccount_ID
           , A.tx_Amount as ATx_Amount
           , Row_number() over (partition by Order_ID, Account_ID, tx_Amount order by tx_Amount asc) ARN

      from cte a 
      WHERE tx_Amount <=0) A
FULL OUTER JOIN (SELECT b.tx_Id
                      , b.order_Id
                      , b.Account_Id
                      , b.tx_Amount
                      ,  Row_number() over (partition by Order_ID, Account_ID, tx_Amount order by tx_Amount desc) BRN 
                 FROM  CTE B 
                 WHERE  tx_Amount>0) B
  on A.AOrderID = B.Order_ID
 and A.AAccount_ID = B.Account_ID
 and A.ATx_Amount*-1 = B.tx_Amount
 and A.ARN=B.BRN
Where a.Atx_ID is null
  or B.tx_ID is null)

  Select ATX_ID, AORDERID, AAccount_ID, ATX_AMOUNT from cte2 where ATX_ID is not null
  UNION ALL
  Select TX_ID, ORDER_ID, Account_ID, TX_AMOUNT from cte2 where TX_ID is not null

【讨论】:

  • 哇,这些都是巨大的查询!可以将您拥有 UNION ALL 的顶部表格替换为表格吗?
  • 你现在要生我的气了……我们的数据仓库中有一个冲销表,有 11,200 多个表。哈哈。虽然表名有“CORRECTED_TX”。你不知道吗!我仍然会为你的努力 +1。
  • 哈哈,这就是生活。我会避免第二个(版本1)第一个查询(版本2)如果需要的话会干净得多。我的第一个目标是做一些能让我得到答案的事情,我的第二个方法是重构它并用更清晰的逻辑获得更好的答案。我想跳过第 1 步,但我很少这样做。
  • 我的版本 2 的结果应该与您的 Corrected_tx :P 匹配,我们可以在查询中执行此操作的事实让我想知道为什么要存储数据;但如果它有很大的容量,我可以从性能/索引的角度看到原因。老实说,我看不出第 2 版如何更简单。如果执行循环游标等,它将比任何必须执行上下文切换的 T-SQL 快得多。但很高兴你 MacGyvered 一个解决方案:P
  • 然后“洗掉”并咬一口? (我不得不租它,对不起我租了)
猜你喜欢
  • 2021-12-17
  • 2014-03-31
  • 2020-10-21
  • 2019-02-18
  • 1970-01-01
  • 2016-10-02
  • 2012-05-30
  • 2017-11-17
  • 2022-01-02
相关资源
最近更新 更多