sql - 如何在没有循环/游标的情况下组合两个表来分配销售量与需求

标签 sql sql-server tsql sql-server-2014

我的任务是以特定方式组合两个表。我有一个包含某些商品 (tovar) 需求的表 Demands。每条记录都有自己的 ID、Tovar、需求日期和金额。我还有另一个表 Unloads,其中包含 tovar 的卸载。每条记录都有自己的 ID、Tovar、卸载顺序和金额。 DemandsUnloads 彼此不对应,需求量和卸载量不完全相等。一个需求可能有 10 个单位,并且可以有两个卸载 4 和 6 个单位。并且可以有3个和5个单位的两个需求,11个单位可以有一个卸载。

任务是获取一个表格,该表格将显示卸载量如何覆盖需求。我有一个解决方案 ( SQL Fiddle ),但我认为还有更好的解决方案。谁能告诉我这些任务是如何解决的?

我有什么:

------------------------------------------
| DemandNumber | Tovar  | Amount | Order |
|--------------------------------|--------
|  Demand#1    |  Meat  |   2    |   1   |
|  Demand#2    |  Meat  |   3    |   2   |
|  Demand#3    |  Milk  |   6    |   1   |
|  Demand#4    |  Eggs  |   1    |   1   |
|  Demand#5    |  Eggs  |   5    |   2   |
|  Demand#6    |  Eggs  |   3    |   3   |
------------------------------------------

------------------------------------------
|  SaleNumber  | Tovar  | Amount | Order |
|--------------------------------|--------
|  Sale#1      |  Meat  |   6    |   1   |
|  Sale#2      |  Milk  |   2    |   1   |
|  Sale#3      |  Milk  |   1    |   2   |
|  Sale#4      |  Eggs  |   2    |   1   |
|  Sale#5      |  Eggs  |   1    |   2   |
|  Sale#6      |  Eggs  |   4    |   3   |
------------------------------------------

我想收到什么

-------------------------------------------------
| DemandNumber |  SaleNumber  | Tovar  | Amount |
-------------------------------------------------
|  Demand#1    |  Sale#1      |  Meat  |   2    |
|  Demand#2    |  Sale#1      |  Meat  |   3    |
|  Demand#3    |  Sale#2      |  Milk  |   2    |
|  Demand#3    |  Sale#3      |  Milk  |   1    |
|  Demand#4    |  Sale#4      |  Eggs  |   1    |
|  Demand#5    |  Sale#4      |  Eggs  |   1    |
|  Demand#5    |  Sale#5      |  Eggs  |   1    |
|  Demand#5    |  Sale#6      |  Eggs  |   3    |
|  Demand#6    |  Sale#6      |  Eggs  |   1    |
-------------------------------------------------

这里是作者评论的补充说明:

  • 需求#1 需要 2 block 肉,它可以从销售#1 中拿走它们。
  • 需求#2 需要 3 block 肉,可以从销售#1 中拿走。
  • 需求#3 需要 6 牛奶,但销售#3 中只有 2 牛奶,销售#4 中只有 1 牛奶,因此我们仅显示可用数量。
  • 等等。

示例中的字段Order 决定了计算的顺序。我们必须根据他们的订单处理需求。需求#1 必须在需求#2 之前处理。并且销售也必须根据他们的订单号进行分配。如果销售订单较低的鸡蛋和未分配的鸡蛋,我们不能分配销售的鸡蛋。


我能得到这个的唯一方法是使用循环。是否可以避免循环并仅使用 t-sql 解决此任务?

最佳答案

如果 Amount 值是 int 并且不是太大(不是数百万),那么我会使用 table of numbers生成与每个 Amount 的值一样多的行。 这里有个好article描述如何生成它。

然后很容易加入 DemandSale 并根据需要分组和求和。

否则,一个简单明了的游标(实际上是两个游标)将易于实现、易于理解并且具有 O(n) 的复杂性。如果 Amounts 很小,基于集合的变体可能比游标更快。如果 Amounts 很大,光标可能会更快。您需要使用实际数据来衡量性能。

这是一个使用数字表的查询。要了解其工作原理,请分别运行 CTE 中的每个查询并检查其输出。

SQLFiddle

WITH
CTE_Demands
AS
(
    SELECT
        D.DemandNumber
        ,D.Tovar
        ,ROW_NUMBER() OVER (PARTITION BY D.Tovar ORDER BY D.SortOrder, CA_D.Number) AS rn
    FROM
        Demands AS D
        CROSS APPLY
        (
            SELECT TOP(D.Amount) Numbers.Number
            FROM Numbers
            ORDER BY Numbers.Number
        ) AS CA_D
)
,CTE_Sales
AS
(
    SELECT
        S.SaleNumber
        ,S.Tovar
        ,ROW_NUMBER() OVER (PARTITION BY S.Tovar ORDER BY S.SortOrder, CA_S.Number) AS rn
    FROM
        Sales AS S
        CROSS APPLY
        (
            SELECT TOP(S.Amount) Numbers.Number
            FROM Numbers
            ORDER BY Numbers.Number
        ) AS CA_S
)
SELECT
    CTE_Demands.DemandNumber
    ,CTE_Sales.SaleNumber
    ,CTE_Demands.Tovar
    ,COUNT(*) AS Amount
FROM
    CTE_Demands
    INNER JOIN CTE_Sales ON
        CTE_Sales.Tovar = CTE_Demands.Tovar
        AND CTE_Sales.rn = CTE_Demands.rn
GROUP BY
    CTE_Demands.Tovar
    ,CTE_Demands.DemandNumber
    ,CTE_Sales.SaleNumber
ORDER BY
    CTE_Demands.DemandNumber
    ,CTE_Sales.SaleNumber
;

综上所述,通常最好使用过程编程语言在客户端执行此类处理。您仍然需要将 DemandsSales 中的所有行传输到客户端。因此,通过连接服务器上的表,您不会减少必须通过网络传输的字节数。事实上,你增加了它,因为原始行可能被分成几行。

这种处理本质上是顺序的,而不是基于集合的,所以用数组很容易做,但在 SQL 中很棘手。

关于sql - 如何在没有循环/游标的情况下组合两个表来分配销售量与需求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33377023/

相关文章:

mysql - 如何比较不同字段的日期字段?

sql - 仅 MS SQL 更新日期

sql - 如何跟踪列更改其值的次数?

重负载下SQL Server检索错误(200并发用户)

sql - 奇怪的 SQL 结果

mysql - 错误 #1248 - 每个派生表必须有自己的别名

sql-server - 匿名用户执行的远程Powershell脚本

sql - ',' 附近的语法不正确

SQL 全外连接重复问题

c# - 如何在数据库中存储十进制值?