mysql - 仅 JOIN 每行一次 — 将第 1 行排列(分布)到第 1 行

标签 mysql sql join row-number

我有两个表要连接,并且我希望每行仅连接一次。这是示例数据:

CREATE TABLE A (id smallint, val varchar(1) );
CREATE TABLE B (id smallint, val varchar(1) );
INSERT INTO A VALUES (1, 'a'), (2, 'b'), (3, 'c'), (3, 'd');
INSERT INTO B VALUES (2, 'x'), (3, 'y'), (4, 'z'), (3, 'k');

当我们加入 id 时,我们获得:

mysql> SELECT * FROM A JOIN B ON A.id = B.id;
+------+------+------+------+
| id   | val  | id   | val  |
+------+------+------+------+
|    2 | b    |    2 | x    |
|    3 | c    |    3 | y    |
|    3 | d    |    3 | y    |
|    3 | c    |    3 | k    |
|    3 | d    |    3 | k    |
+------+------+------+------+

我想要的是:

+------+------+------+------+            +------+------+------+------+
| id   | val  | id   | val  |            | id   | val  | id   | val  |
+------+------+------+------+     or     +------+------+------+------+
|    2 | b    |    2 | x    |            |    2 | b    |    2 | x    |
|    3 | c    |    3 | y    |            |    3 | d    |    3 | y    |
|    3 | d    |    3 | k    |            |    3 | c    |    3 | k    |
+------+------+------+------+            +------+------+------+------+

顺序和排列并不重要。

这可能吗?怎么办?

根据this answer我需要指定如何选择匹配的行。在这种情况下,我想如果已使用连接表的行,则需要检查子查询;或者一种与 id 相关的计数器...但我不知道如何写这个。

编辑:

为了澄清,我希望 id 为 3 的每一行与连接表中的另一行映射,例如每一行仅映射一次(我也有兴趣知道当具有相同 id 的行数不同时会发生什么在两个表中):

(3, c) -> (3, y) [join only with the first row such as B.id = 3]
(3, d) -> (3, k) [the first row has been used, so map with (and only with) the second row such as B.id = 3]

但正如我所说,映射可以采用任何其他顺序(例如,以相反的顺序映射行)。

最佳答案

SQL Fiddle

MySQL 5.6 架构设置:

CREATE TABLE A (id smallint, val varchar(1) );
CREATE TABLE B (id smallint, val varchar(1) );
INSERT INTO A VALUES (1, 'a'), (2, 'b'), (3, 'c'), (3, 'd');
INSERT INTO B VALUES (2, 'x'), (3, 'y'), (4, 'z'), (3, 'k');

查询 1:

select
        aa.id  as aid
      , aa.val as aval
      , bb.id  as bid
      , bb.val as bval
from (
      select
            @row_num :=IF(@prev_value=a.id,@row_num+1,1)AS RowInGroup
          , a.id
          , a.val
          , @prev_value := a.id
      from (
            SELECT id, val 
            FROM A
            group by id, val
            /* order by ?? */
              ) a
            CROSS JOIN (
                        SELECT @row_num :=1,  @prev_value :=''
                       ) vars
      ) aa
INNER JOIN (
          select
                @row_num :=IF(@prev_value=b.id,@row_num+1,1)AS RowInGroup
              , b.id
              , b.val
              , @prev_value := b.id
          from (
                SELECT id, val 
                FROM B
                group by id, val
                /* order by ?? */
                  ) b
                CROSS JOIN (
                            SELECT @row_num :=1,  @prev_value :=''
                           ) vars
          ) bb on aa.id = bb.id and aa.RowInGroup = bb.RowInGroup
order by
        aa.id
      , aa.val

<强> Results :

| id | val | id | val |
|----|-----|----|-----|
|  2 |   b |  2 |   x |
|  3 |   c |  3 |   k |
|  3 |   d |  3 |   y |

nb:您可以通过在 group by id, val 的子查询中引入 order by 来影响最终结果,其中序列 RowInGroup 已计算。

关于mysql - 仅 JOIN 每行一次 — 将第 1 行排列(分布)到第 1 行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28984409/

相关文章:

php - 使用动态创建的表单创建 MySQL 表

mysql - SQL 中的字符串操作

mysql - 我的 sql 查询中的操作数应包含 1 列

mysql - 如何根据特定列计算重复行?

mysql - 尝试通过 SQL JOIN 将 "add"一列添加到表中

mysql - phpmyadmin权限表版本问题

Mysql查询多条件错误

sql - GRANT EXECUTE 所需的权限

c# - 使用图像作为数据库表列

sql - 将 SQLite FTS3 与 INTEGER 列一起使用