首页 > 解决方案 > 在 SQL 中编写或替换双 for 循环(表遍历另一个表遍历)的有效方法

问题描述

所以现在我有两个表,即dbo.DEMANDand dbo.SUPPLY,其中包含包括bidderID/ vendorIDitemIDquantitybidPrice/sellPrice等列。现在我想定期(通过使用 SQL Server 的代理完成)并自动匹配符合条件的行(这是我的问题)指定下面,按 排序publishTime,并将一些信息转移到另一个表中dbo.DEAL

我目前正在做的方式是首先declare @temp table将所有内容dbo.DEMAND插入@temp. 然后我使用WHILE遍历dbo.DEMAND表,对于每一行在SELECT TOP 1 XXX FROM满足特定条件dbo.SUPPLY时使用表。最后,当遍历完成时(变为 0,或者在表中找不到匹配项时),我在表中的行中重新开始遍历下一个行。更具体地说,我的目标是找到所有满足的匹配DELETE@tempdbo.SUPPLY@temp.Quantitydbo.SUPPLY

{1. @temp.ItemID = SUPPLY.ItemID;
 2. @temp.Bid > SUPPLY.Price;
 3. @temp.LowestDegre > SUPPLY.DegreeN.}

一个接一个,首先遍历@temp由 排序的DEMAND.PublishTime,当找到一个匹配时做:

{1. For the 2 rows (in the two tables), DELETE both rows and the 1 corresponding row in dbo.DEMAND if Quantities are equal, or, 
DELETE the row with smaller Quantity, and SUBTRACT the Quantity in the row with larger Quantity with the smaller one;
2. Add a row in the DEAL table, containing BuyerID, SellerID, ItemID, BidPrice, etc;
3. Redo the MATCH procedure.}

基本上在我看来,这种方式与 C++ 中的双循环相同。但是,我听说 SQL 擅长处理集合(大概就像我们总是希望在 MATLAB 中进行矩阵运算),并且逐行遍历确实需要很长时间。如何优化我当前使用的算法?

样本数据: 来自三个表的样本数据

示例代码:

DECLARE @temp TABLE
(
buyer char(20),
bid float,
quantity smallint,
item char(20),
lowestnew decimal(18,2),
lowestrep decimal(18,2),
pubtime date
);

INSERT INTO @temp(buyer,bid,quantity,item,lowestnew,lowestrep,pubtime)
SELECT BuyerID,Bid,Quantity,ItemID,LowestDegreeNew,LowestReputation,PublishTime FROM DEMAND
ORDER BY PublishTime;
DECLARE @flag int
SET @flag = 0

DECLARE @seller char(20), @buyer CHAR(20), @item char(20), @lowestdegree decimal(18,2), @lowestrep decimal(18,2)

WHILE EXISTS (SELECT buyer, item, lowestnew, lowestrep FROM @temp)

BEGIN

SET @flag = 1

SELECT @buyer = buyer, @item = item, @lowestdegree = lowestnew, @lowestrep = lowestrep FROM @temp

DECLARE @price FLOAT, @quantity SMALLINT

IF EXISTS (select Price, SellerID, Quantity from SUPPLY, SELLER where SellerID = ID and @lowestdegree >= DegreeNew and @lowestrep>=Reputation order by Price)

BEGIN

SELECT TOP 1 @price=Price, @seller = SellerID, @quantity = Quantity FROM SUPPLY, SELLER WHERE SellerID = ID AND @lowestdegree >= DegreeNew AND @lowestrep>=Reputation
IF ((SELECT Quantity FROM DEMAND WHERE SellerID = @seller AND ItemID = @item AND (DegreeNew >= @LowestDegreeNew) AND (Price <= @bid)) IS NOT NULL)
        BEGIN /* TRANSFER INTO NEW TABLE */

脚注:我知道这可以很容易地通过在前端编写简单的 C# 或 Python 代码来实现,但我想看看是否可以单独在数据库系统中执行此操作。

标签: sqlsql-serverfor-loop

解决方案


TLDR:不,您必须继续使用循环结构。

我去过那儿。“最近一天”的加入使得很难使用一个好的集合操作。这就是为什么。考虑那些具有相同 equijoin 属性(vendor=item 等)的记录:

Supply: Day 1
Supply: Day 2
Demand: Day 2

在这种情况下,由于时差,您可能希望将 Supply Day 2 与 Demand Day 2 结合起来。但如果是这样:

Supply: Day 1 
Supply: Day 2 
Demand: Day 2 
Demand: Day 3

您可能需要 Supply Day 1 - Demand Day 2 和 Supply Day 2 - Demand Day 3。

这意味着每一行都以一种不容易的方式依赖于其他行(即:滞后/领先窗口函数不会拯救你)。

这是您需要游标/while 的罕见情况之一。


推荐阅读