我正在搜索一个SQL-Query,它可以将一个单独大小的项目映射到一个单独大小的存储桶。
我想满足以下条件:
举个例子,让我们说我的桶和物品表看起来像这样:
Bucket: Item:
+---------------------+ +---------------------+
| BucketID | Size | | ItemID | Size |
+---------------------+ +---------------------+
| 1 | 2 | | 1 | 2 |
| 2 | 2 | | 2 | 2 |
| 3 | 2 | | 3 | 5 |
| 4 | 4 | | 4 | 11 |
| 5 | 4 | | 5 | 12 |
| 6 | 7 | +---------------------+
| 7 | 9 |
| 8 | 11 |
| 9 | 11 |
| 10 | 12 |
+---------------------+
然后,我想要一个返回以下结果表的映射:
Result:
+---------------------+
| BucketID | ItemID |
+---------------------+
| 1 | 1 |
| 2 | 2 |
| 3 | NULL |
| 4 | NULL |
| 5 | NULL |
| 6 | 3 |
| 7 | NULL |
| 8 | 4 |
| 9 | NULL |
| 10 | 5 |
+---------------------+
由于没有外键关系或者我可以将列固定到相应的存储桶(但只有关系Bucket.Size> = Item.Size),我在描述结果时遇到了很多麻烦一个有效的SQL查询。每当我使用连接或子选择时,我都会获得桶中的物品,这些物品要大(比如大小为12的桶中的物品大小为2,而大小为2的桶仍然可用)或者我得到相同的物品多个桶。
我现在花了一些时间自己找到解决方案,我接近说,最好不要在SQL中声明问题,而是在应用程序中,这只是获取表。
你认为这个任务在SQL中是可行的吗?如果是这样的话,如果你能帮我解决一个有效的问题,我将非常感激。
编辑:查询应至少与Oracle,Postgres和SQLite数据库兼容
编辑II:一个SQL小提琴,上面是一个示例查询的给定测试集,它返回一个错误的结果,但是接近结果看起来像http://sqlfiddle.com/#!15/a6c30/1
答案 0 :(得分:4)
试试这个......
我能够使用recursive CTE
实现这一点,所有这些都在一个单一的 SQL 语句中
我唯一的假设是 Bucket and Item 数据集已经过排序。
DECLARE @BUCKET TABLE
(
BUCKETID INT
, SIZE INT
)
DECLARE @ITEM TABLE
(
ITEMID INT
, SIZE INT
)
;
INSERT INTO @BUCKET
SELECT 1,2 UNION ALL
SELECT 2,2 UNION ALL
SELECT 3,2 UNION ALL
SELECT 4,4 UNION ALL
SELECT 5,4 UNION ALL
SELECT 6,7 UNION ALL
SELECT 7,9 UNION ALL
SELECT 8, 11 UNION ALL
SELECT 9, 11 UNION ALL
SELECT 10,12
INSERT INTO @ITEM
SELECT 1,2 UNION ALL
SELECT 2,2 UNION ALL
SELECT 3,5 UNION ALL
SELECT 4,11 UNION ALL
SELECT 5,12;
WITH TOTAL_BUCKETS
AS (
SELECT MAX(BUCKETID) CNT
FROM @BUCKET
) -- TO GET THE TOTAL BUCKETS COUNT TO HALT THE RECURSION
, CTE
AS (
--INVOCATION PART
SELECT BUCKETID
, (
SELECT MIN(ITEMID)
FROM @ITEM I2
WHERE I2.SIZE <= (
SELECT SIZE
FROM @BUCKET
WHERE BUCKETID = (1)
)
) ITEMID --PICKS THE FIRST ITEM ID MATCH FOR THE BUCKET SIZE
, BUCKETID + 1 NEXT_BUCKETID --INCREMENT FOR NEXT BUCKET ID
, (
SELECT ISNULL(MIN(ITEMID), 0)
FROM @ITEM I2
WHERE I2.SIZE <= (
SELECT SIZE
FROM @BUCKET
WHERE BUCKETID = (1)
)
) --PICK FIRST ITEM ID MATCH
+ (
CASE
WHEN (
SELECT ISNULL(MIN(ITEMID), 0)
FROM @ITEM I3
WHERE I3.SIZE <= (
SELECT SIZE
FROM @BUCKET
WHERE BUCKETID = (1)
)
) IS NOT NULL
THEN 1
ELSE 0
END
) NEXT_ITEMID --IF THE ITEM IS PLACED IN THE BUCKET THEN INCREMENTS THE FIRST ITEM ID
, (
SELECT SIZE
FROM @BUCKET
WHERE BUCKETID = (1 + 1)
) NEXT_BUCKET_SIZE --STATES THE NEXT BUCKET SIZE
FROM @BUCKET B
WHERE BUCKETID = 1
UNION ALL
--RECURSIVE PART
SELECT NEXT_BUCKETID BUCKETID
, (
SELECT ITEMID
FROM @ITEM I2
WHERE I2.SIZE <= NEXT_BUCKET_SIZE
AND I2.ITEMID = NEXT_ITEMID
) ITEMID -- PICKS THE ITEM ID IF IT IS PLACED IN THE BUCKET
, NEXT_BUCKETID + 1 NEXT_BUCKETID --INCREMENT FOR NEXT BUCKET ID
, NEXT_ITEMID + (
CASE
WHEN (
SELECT I3.ITEMID
FROM @ITEM I3
WHERE I3.SIZE <= NEXT_BUCKET_SIZE
AND I3.ITEMID = NEXT_ITEMID
) IS NOT NULL
THEN 1
ELSE 0
END
) NEXT_ITEMID --IF THE ITEM IS PLACED IN THE BUCKET THEN INCREMENTS THE CURRENT ITEM ID
, (
SELECT SIZE
FROM @BUCKET
WHERE BUCKETID = (NEXT_BUCKETID + 1)
) NEXT_BUCKET_SIZE --STATES THE NEXT BUCKET SIZE
FROM CTE
WHERE NEXT_BUCKETID <= (
SELECT CNT
FROM TOTAL_BUCKETS
) --HALTS THE RECURSION
)
SELECT
BUCKETID
, ITEMID
FROM CTE
答案 1 :(得分:3)
我说单个SQL查询可能不是工作的工具,因为存储桶被消耗了&#34;通过分配项目给他们。您可以使用SQL,但不能使用单个查询。以下伪代码的建议:
Have a cursor on ITEM:
within the FETCH loop for that {
SELECT in BUCKET the bucket with minimum bucket id and bucket size >= item size
INSERT bucket id, item id to MAPPING
}
If you need the NULL (unoccupied) buckets, you can locate them via a further
INSERT into MAPPING (....)
SELECT <bucket id>, NULL
from BUCKET
where <bucket id> not in (SELECT <bucket id> from MAPPING);
答案 2 :(得分:2)
使用@SoulTrain中的表定义(但需要提前对数据进行排序):
; WITH ORDERED_PAIRINGS AS (
SELECT i.ITEMID, b.BUCKETID, ROW_NUMBER() OVER (ORDER BY i.SIZE, i.ITEMID, b.SIZE, b.BUCKETID) AS ORDERING, DENSE_RANK() OVER (ORDER BY b.SIZE, b.BUCKETID) AS BUCKET_ORDER, DENSE_RANK() OVER (PARTITION BY b.BUCKETID ORDER BY i.SIZE, i.ITEMID) AS ITEM_ORDER
FROM @ITEM i
JOIN @BUCKET b
ON i.SIZE <= b.SIZE
), ITEM_PLACED AS (
SELECT ITEMID, BUCKETID, ORDERING, BUCKET_ORDER, ITEM_ORDER, CAST(1 as int) AS SELECTION
FROM ORDERED_PAIRINGS
WHERE ORDERING = 1
UNION ALL
SELECT *
FROM (
SELECT op.ITEMID, op.BUCKETID, op.ORDERING, op.BUCKET_ORDER, op.ITEM_ORDER, CAST(ROW_NUMBER() OVER(ORDER BY op.BUCKET_ORDER) as int) as SELECTION
FROM ORDERED_PAIRINGS op
JOIN ITEM_PLACED ip
ON op.ITEM_ORDER = ip.ITEM_ORDER + 1
AND op.BUCKET_ORDER > ip.BUCKET_ORDER
) AS sq
WHERE SELECTION = 1
)
SELECT *
FROM ITEM_PLACED