SQL Server:使用循环连接两个表

时间:2017-11-04 20:14:29

标签: sql-server join

我有两张桌子。我是T-SQL的新手。

首先选择返回此结果:

ID  Quantity
-------------
1      30
2      25

第二个选择返回此结果:

ID    Document   QuantityS    Date
----------------------------------------
 1    DocA          12        22-03-2017
 1    DocB          10        18-03-2017
 1    DocC          10        15-03-2017
 1    DocD           8         6-03-2017
 2    DocA          20        21-04-2017
 2    DocB          12        18-04-2017
 2    DocC          10        13-04-2017

我需要在ID上加入这两个表并从第二个表中获取行,而quantityS的总和大于或等于第一个表中的Quantity,按Date desc排序。

我需要得到这样的表:

ID    Document    QuantityS     Date
------------------------------------------
 1    DocA           12         22-03-2017
 1    DocB           10         18-03-2017
 1    DocC           10         15-03-2017
 2    DocA           20         21-04-2017
 2    DocB           12         18-04-2017

我不需要第二张表的最后一行,因为三行的数量总和是32,大于30。

有什么想法吗?

1 个答案:

答案 0 :(得分:0)

您需要windowed aggregate function

SELECT ItemRecord.id, ItemRecord.document, ItemRecord.quantity, ItemRecord.uploadedOn
FROM ItemLimit
JOIN (SELECT ItemRecord.id, ItemRecord.document, ItemRecord.quantity, ItemRecord.uploadedOn, 
             COALESCE(SUM(quantity) OVER(PARTITION BY id 
                                         ORDER BY uploadedOn DESC 
                                         ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING), 0) AS runningTotal
      FROM ItemRecord) ItemRecord
   ON ItemRecord.id = ItemLimit.id
      AND ItemRecord.runningTotal <= ItemLimit.quantity
ORDER BY ItemRecord.id, ItemRecord.uploadedOn DESC

Fiddle Example

那么这是如何工作的?最重要的部分是聚合函数SUM。您不能只使用常规的,但由于您没有(甚至不想要)GROUP BY,这意味着您需要使用OVER子句。第一步:

SELECT id, document, quantity, uploadedOn, 
       SUM(quantity) OVER() AS runningTotal
  FROM ItemRecord

结果:

| id | document | quantity | uploadedOn | runningTotal |
|----|----------|----------|------------|--------------|
| 1  | DocA     | 12       | 2017-03-22 | 82           |
| 1  | DocB     | 10       | 2017-03-18 | 82           |
| 1  | DocC     | 10       | 2017-03-15 | 82           |
| 1  | DocD     | 8        | 2017-03-06 | 82           |
| 2  | DocA     | 20       | 2017-04-21 | 82           |
| 2  | DocB     | 12       | 2017-04-18 | 82           |
| 2  | DocC     | 10       | 2017-04-13 | 82           |

......很明显,这笔款项的金额是多少!这个原因很明显:我们还没有说过&#34;命令&#34;的行是!所以让我们在下一步中包含窗口:

SELECT id, document, quantity, uploadedOn, 
       SUM(quantity) OVER(ORDER BY uploadedOn DESC) AS runningTotal
FROM ItemRecord

结果:

| id | document | quantity | uploadedOn | runningTotal |
|----|----------|----------|------------|--------------|
| 1  | DocA     | 12       | 2017-03-22 | 54           |
| 1  | DocB     | 10       | 2017-03-18 | 64           |
| 1  | DocC     | 10       | 2017-03-15 | 74           |
| 1  | DocD     | 8        | 2017-03-06 | 82           |
| 2  | DocA     | 20       | 2017-04-21 | 20           |
| 2  | DocB     | 12       | 2017-04-18 | 32           |
| 2  | DocC     | 10       | 2017-04-13 | 42           |

...除了因为某些原因,它没有为id = 1重启!我们必须将它告诉 partition ,或将结果分开:

SELECT id, document, quantity, uploadedOn, 
       SUM(quantity) OVER(PARTITION BY id 
                          ORDER BY uploadedOn DESC) AS runningTotal
FROM ItemRecord

结果:

| id | document | quantity | uploadedOn | runningTotal |
|----|----------|----------|------------|--------------|
| 1  | DocA     | 12       | 2017-03-22 | 12           |
| 1  | DocB     | 10       | 2017-03-18 | 22           |
| 1  | DocC     | 10       | 2017-03-15 | 32           |
| 1  | DocD     | 8        | 2017-03-06 | 40           |
| 2  | DocA     | 20       | 2017-04-21 | 20           |
| 2  | DocB     | 12       | 2017-04-18 | 32           |
| 2  | DocC     | 10       | 2017-04-13 | 42           |

......那更好,但我们遇到了问题 - 我们想要第3行,但它太高了!问题在于我们不希望总和包含此行,而只包含前面的行。幸运的是,我们可以指定窗口的边界:

SELECT id, document, quantity, uploadedOn, 
       SUM(quantity) OVER(PARTITION BY id
                          ORDER BY uploadedOn DESC 
                          ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) AS runningTotal
FROM ItemRecord
| id | document | quantity | uploadedOn | runningTotal |
|----|----------|----------|------------|--------------|
| 1  | DocA     | 12       | 2017-03-22 | (null)       |
| 1  | DocB     | 10       | 2017-03-18 | 12           |
| 1  | DocC     | 10       | 2017-03-15 | 22           |
| 1  | DocD     | 8        | 2017-03-06 | 32           |
| 2  | DocA     | 20       | 2017-04-21 | (null)       |
| 2  | DocB     | 12       | 2017-04-18 | 20           |
| 2  | DocC     | 10       | 2017-04-13 | 32           |

...好吧,现在第三行是在界限内,第四行是(仍然)超出范围,但是我们在第一行行遇到了问题:&& #39; s null!这并不令人惊讶:对于第一行,没有前一行,因此没有数量。但它很烦人:null不能与任何东西(甚至是它自己)进行比较,所以我们不能在简单的比较中使用它。幸运的是,有一种方法可以将空值更改为另一个值:

SELECT ItemRecord.id, ItemRecord.document, ItemRecord.quantity, ItemRecord.uploadedOn, 
       COALESCE(SUM(quantity) OVER(PARTITION BY id
                                   ORDER BY uploadedOn DESC 
                                   ROWS BETWEEN UNBOUNDED PRECEDING 
                                                AND 1 PRECEDING), 0) AS runningTotal
FROM ItemRecord

哪个收益率:

| id | document | quantity | uploadedOn | runningTotal |
|----|----------|----------|------------|--------------|
| 1  | DocA     | 12       | 2017-03-22 | 0            |
| 1  | DocB     | 10       | 2017-03-18 | 12           |
| 1  | DocC     | 10       | 2017-03-15 | 22           |
| 1  | DocD     | 8        | 2017-03-06 | 32           |
| 2  | DocA     | 20       | 2017-04-21 | 0            |
| 2  | DocB     | 12       | 2017-04-18 | 20           |
| 2  | DocC     | 10       | 2017-04-13 | 32           |

...此时,我们需要的形式,准备加入另一个限制的表。

<小时/> 另外,还有另一种从正在运行的总计中删除当前行的方法,也不需要COALESCE。选择当前形式的查询是因为它意味着窗口完全指定了所需的行。这个版本将留给读者练习,或者性能好奇。