在网格中查找空位置

Find empty positions in a grid

Contents table 中,项目存储在 X、Y 坐标中:

Contents
-------
id
parent_id
pos_x
pos_y

假设容器大小为 3 x 3。我想找出给定容器中的哪些位置是空闲的。到目前为止,我已经生成了一个二维矩阵:

SELECT *
FROM 
    (SELECT rownum X FROM dual  CONNECT BY LEVEL <= 3 ) xaxis
INNER JOIN 
    (SELECT rownum Y FROM dual  CONNECT BY LEVEL <=3 ORDER BY 1) yaxis
ON xaxis.X <> yaxis.Y OR xaxis.X = yaxis.Y

然后我尝试 JOIN 一起查询,排除 Contents:

中存在的 X,Y 位置
SELECT X, Y
FROM 
    (SELECT rownum X FROM dual  CONNECT BY LEVEL <= 3 ) xaxis
INNER JOIN 
    (SELECT rownum Y FROM dual  CONNECT BY LEVEL <=3 ORDER BY 1) yaxis
ON xaxis.X <> yaxis.Y OR xaxis.X = yaxis.Y

INNER JOIN (
    SELECT pos_x, pos_y FROM Contents WHERE parent_id = ?) items
ON items.posx <> xaxis.X AND items.posy <> yaxis.Y;

这不会将每一对都视为唯一的,并且如果某个位置已被占用,则会从 所有 行中排除值。比如假设(2, 2)被占用,上面的returns:

X   Y
-----
1   1
1   3
3   1
3   3

本质上,我是想找出两组的不同之处。任何帮助表示赞赏。

我在 post 提出问题之前想出了答案,所以我认为我会 post 同时回答它。将问题描述为 得到两组的差异 让我朝着正确的方向前进。

答案是MINUS运算符。将最后的 JOIN 替换为 MINUS,您将获得预期的结果:

select X, Y
from 
    (select rownum X from dual  CONNECT BY LEVEL <= 3 ) xaxis
inner join 
    (select rownum Y from dual  CONNECT BY LEVEL <=3 order by 1) yaxis
on xaxis.X <> yaxis.Y OR xaxis.X = yaxis.Y

MINUS

select pos_x, pos_y FROM Contents WHERE parent_id = ?;

哪个 returns 预期结果(注意缺少 (2, 2)):

X   Y
-----
1   1
1   2
1   3
2   1
2   3
3   1
3   2
3   3

今天是个好日子

您可以使用外部联接而不是减号来执行此操作(尽管您必须测试两者以找出哪个对您的数据性能更高!)。

如果你一次只为一个 parent_id 做,你会做:

WITH CONTENTS AS (SELECT 1 parent_id, 2 pos_x, 2 pos_y FROM dual UNION ALL
                  SELECT 2 parent_id, 2 pos_x, 1 pos_y FROM dual)
SELECT xaxis.x,
       yaxis.y
FROM   ((SELECT LEVEL x FROM dual CONNECT BY LEVEL <= 3) xaxis
        CROSS JOIN (SELECT LEVEL y FROM dual CONNECT BY LEVEL <= 3) yaxis)
       LEFT OUTER JOIN CONTENTS c ON c.pos_x = xaxis.x AND c.pos_y = yaxis.y AND c.parent_id = 1
WHERE  c.parent_id IS NULL
ORDER BY x, y;

         X          Y
---------- ----------
         1          1
         1          2
         1          3
         2          1
         2          3
         3          1
         3          2
         3          3

或者,如果你想 运行 所有 parent_id s,你可以使用 partitioned outer join 像这样:

WITH CONTENTS AS (SELECT 1 parent_id, 2 pos_x, 2 pos_y FROM dual UNION ALL
                  SELECT 2 parent_id, 2 pos_x, 1 pos_y FROM dual)
SELECT c.parent_id,
       xaxis.x,
       yaxis.y
FROM   ((SELECT LEVEL x FROM dual CONNECT BY LEVEL <= 3) xaxis
        CROSS JOIN (SELECT LEVEL y FROM dual CONNECT BY LEVEL <= 3) yaxis)
       LEFT OUTER JOIN CONTENTS c PARTITION BY (c.parent_id) ON c.pos_x = xaxis.x AND c.pos_y = yaxis.y
WHERE  c.pos_x IS NULL
AND    c.pos_y IS NULL
ORDER BY c.parent_id,
         xaxis.x,
         yaxis.y;

 PARENT_ID          X          Y
---------- ---------- ----------
         1          1          1
         1          1          2
         1          1          3
         1          2          1
         1          2          3
         1          3          1
         1          3          2
         1          3          3
         2          1          1
         2          1          2
         2          1          3
         2          2          2
         2          2          3
         2          3          1
         2          3          2
         2          3          3