如何在没有 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、卸载顺序和金额。 Demands
和Unloads
不对应,需求量和卸载量不完全相等。一个需求可能有 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 |
-------------------------------------------------
这里是作者评论的补充说明:
- 需求#1 需要 2 块肉,它可以从销售#1 中拿走它们。
- 需求#2 需要 3 块肉,可以从销售#1 中拿走。
- 需求#3 需要 6 牛奶,但销售#3 中只有 2 牛奶,销售#4 中只有 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 描述如何生成它。
然后很容易加入 Demand
和 Sale
并根据需要分组和求和。
否则,一个简单明了的游标(实际上是两个游标)将易于实现、易于理解并且具有 O(n)
的复杂性。如果 Amounts
很小,基于集合的变体可能比游标更快。如果 Amounts
很大,光标可能会更快。您需要用实际数据来衡量性能。
这是一个使用 table 个数字的查询。了解它是如何工作的 运行 分别在 CTE 中进行每个查询并检查其输出。
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
;
综上所述,通常最好使用过程编程语言在客户端执行这种处理。您仍然必须将 Demands
和 Sales
中的所有行传输到客户端。因此,通过在服务器上加入 tables,您不会减少必须通过网络传输的字节数。事实上,你增加它,因为原始行可能被拆分成几行。
这种处理本质上是顺序的,不是基于集合的,所以用数组很容易做,但在SQL.
中很棘手
我的任务是以特定方式组合两个 table。我有一个 table Demands
,其中包含一些商品 (tovar) 的需求。每条记录都有自己的 ID、Tovar、需求日期和金额。我还有另一个 table Unloads
,其中包含卸载的 tovar。每条记录都有自己的 ID、Tovar、卸载顺序和金额。 Demands
和Unloads
不对应,需求量和卸载量不完全相等。一个需求可能有 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 |
-------------------------------------------------
这里是作者评论的补充说明:
- 需求#1 需要 2 块肉,它可以从销售#1 中拿走它们。
- 需求#2 需要 3 块肉,可以从销售#1 中拿走。
- 需求#3 需要 6 牛奶,但销售#3 中只有 2 牛奶,销售#4 中只有 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 描述如何生成它。
然后很容易加入 Demand
和 Sale
并根据需要分组和求和。
否则,一个简单明了的游标(实际上是两个游标)将易于实现、易于理解并且具有 O(n)
的复杂性。如果 Amounts
很小,基于集合的变体可能比游标更快。如果 Amounts
很大,光标可能会更快。您需要用实际数据来衡量性能。
这是一个使用 table 个数字的查询。了解它是如何工作的 运行 分别在 CTE 中进行每个查询并检查其输出。
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
;
综上所述,通常最好使用过程编程语言在客户端执行这种处理。您仍然必须将 Demands
和 Sales
中的所有行传输到客户端。因此,通过在服务器上加入 tables,您不会减少必须通过网络传输的字节数。事实上,你增加它,因为原始行可能被拆分成几行。
这种处理本质上是顺序的,不是基于集合的,所以用数组很容易做,但在SQL.
中很棘手