如何在没有 loops/cursor 的情况下合并两个分配销售量与需求的表

How to combine two tables allocating Sold amounts vs Demand without loops/cursor

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

任务是获得一个 table,它将显示卸载如何满足需求。我有一个解决方案 (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    |
-------------------------------------------------

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

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


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

解决此任务

我不知道您的要求是什么、业务规则是什么或目标是什么,但我可以这样说——您做错了。

这是SQL。在 SQL 你不做循环。在 SQL 中,您使用集合。集合由 select 语句定义。

如果使用 select 语句(可能使用 sub-selects)不能解决此问题,那么您可能希望以其他方式实现它。 (C# 程序?其他一些 ETL 系统?)。

不过,我也可以说可能有一种方法可以用一条 select 语句来做到这一点。但是,您没有提供足够的信息让我知道该声明是什么。说你有一个工作示例并且在这个站点上应该足够失败,因为这个站点是关于回答问题的问题而你没有问题你有一些代码。

用输入重新表述问题,期望输出,你尝试了什么以及你的问题是什么。这在常见问题解答中有很好的介绍。

或者,如果您有要审查的工作代码,它可能适用于代码审查网站。

我看到另外 2 种可能的方式:

1. 'advanced' 数据处理和计算可以使用游标。
2. 你可以使用 SELECT 和 CASE 构造

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

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

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

这是一个使用 table 个数字的查询。了解它是如何工作的 运行 分别在 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 中的所有行传输到客户端。因此,通过在服务器上加入 tables,您不会减少必须通过网络传输的字节数。事实上,你增加它,因为原始行可能被拆分成几行。

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

中很棘手