MySQL嵌套表记录编号

时间:2017-06-14 18:43:54

标签: mysql sql variables

我遇到类似于以下示例的MySQL问题。

  • 有两个人名的表,由护照号码标识。第一个表有父母。
  • 第二张表有儿童,与他们的父母护照ID相关联(我只考虑一个父母 - 父亲/母亲 - 每个家庭)。
  • 护照ID值越低意味着年轻人(因此,最小的父母拥有最低的身份证;并且出生在家庭中的第一个孩子的ID在该家庭的孩子中最低)

示例表:

父母

PassportID – ParentNAME

098765432 – Kate
012345678 – John
111222333 – Mary

的子

PassportID – ChildNAME – ParentID

211222333 – Peter – 111222333
222333444 – Gabriel – 012345678
222222222 – Lara – 111222333
233333333 – Angela – 012345678
244444444 – Philip – 012345678
255555555 – Michael – 111222333
277777777 – Betty – 098765432
266666666 – Eleanor – 098765432
299999999 – Amanda – 111222333
288888888 – Robert – 111222333

我知道如何使用原始ID加入和排序这些表:

HTTP:!sqlfiddle.com /#9 / 98b5a / 1/0

由于某种原因,SQLfiddle开始失败,所以我在其他很酷的MySQL测试器中重现了这个例子: http://rextester.com/EYIX74197

但我希望显示简单的增长数字,而不是显示原始ID。所以,问题是:

如何从这两个表中进行选择,并通过以下方式获取所有这些人的列表,其中包含几个自动编号列,这些列显示人员的基于1的年龄等级

  • 最老的父级将具有rank = 1.下一级,rank = 2,等等 在
  • 每个家庭中年龄最大的孩子将排名= 1 ,下一个 一个2,依此类推。

示例结果:

ParentAgeRANK - ParentNAME – ChildBornRANK – ChildNAME

1 – John – 1 – Gabriel
1 – John – 2 – Angela
1 – John – 3 – Philip
2 – Kate – 1 – Eleanor
2 – Kate – 2 – Betty
3 – Mary – 1 – Peter
3 – Mary – 2 – Lara
3 – Mary – 3 – Michael
3 – Mary – 4 – Robert
3 – Mary – 5 – Amanda

我想我需要使用几个mysql变量@ParentAgeRank和@ChildBornRank,将它们的原始值设置为0,然后为每一行加1,但仅当父ID更改时(在第一种情况下)或子ID更改(在第二个中)。但是,当父ID更改时,@ ChildBornRank必须重置为0。 我会知道如何使用PHP脚本来执行此操作,该脚本可以评估PassportID并使用PHP变量增加Rank。

但在这种情况下,我被迫使用纯MySQL语句获得排名列表。

这个想法甚至可能吗?如何在另一个变量改变其值时重置一个变量?

非常感谢你的建议

编辑:

虽然我的例子只有10行,这是打算从两个表中运行,每个表有大约10万条记录

@Parfait提供的第一个解决方案确实适用于几行;但是当我在现实世界中尝试它时,我的服务器就会挂起。

@Parfait的第二个解决方案确实返回了一个完整的磁盘"错误消息,所以我猜这是与此页面中的第一条评论相关的内容(连接和分组语句太多):https://dev.mysql.com/doc/refman/5.7/en/full-disk.html

3 个答案:

答案 0 :(得分:1)

考虑相关计数子查询:

SELECT 
    (SELECT Count(*) FROM Parents sub
     WHERE sub.PassportID <= p.PassportID) AS ParentRANK,

    p.PassportID As ParentID, p.ParentNAME, 

    (SELECT Count(*) FROM Children sub
     WHERE sub.PassportID <= c.PassportID
     AND sub.ParentID = c.ParentID) AS ChildRANK,

    c.PassPortID As ChildID, c.ChildNAME    

FROM Parents p 
INNER JOIN Children c 
  ON p.PassportID = c.ParentID

SQL Fiddle

在聚合查询中使用JOIN表和COUNT(DISTINCT ...))替代排名:

SELECT 
       Count(DISTINCT subP.PassportID) AS ParentRANK,
       p.PassportID As ParentID, p.ParentNAME,        
       Count(DISTINCT subC.PassportID) AS ChildRANK,        
       C.PassPortID As ChildID, c.ChildNAME

FROM Parents p 
INNER JOIN Children c 
  ON p.PassportID = c.ParentID

LEFT JOIN Parents subP
  ON subP.PassportID <= p.PassportID

LEFT JOIN Children subC   
  ON subC.PassportID <= c.PassportID
  AND subC.ParentID = c.ParentID

GROUP BY p.PassportID, p.ParentName, c.PassportID, c.ChildName
ORDER BY 1,4

SQL Fiddle

答案 1 :(得分:1)

SELECT CASE WHEN @prev = x.parent_id THEN @i:=@i ELSE @i:=@i+1 END parentrank 
     , x.parentname
     , CASE WHEN @prev = x.parent_id THEN @j:=@j+1 ELSE @j:=1 END childrank
     , x.childname
     , @prev:=parent_id
  FROM ( SELECT p.passportid parent_id
            , p.parentname
            , c.passportid child_id
            , c.childname
         FROM parents p
         JOIN children c
           ON c.parentid = p.passportid
       ) x
  JOIN ( SELECT @prev:=null,@i:=0,@j:=0) vars
 ORDER 
    BY x.parent_id
     , x.child_id;
+------------+------------+-----------+-----------+------------------+
| parentrank | parentname | childrank | childname | @prev:=parent_id |
+------------+------------+-----------+-----------+------------------+
|          1 | John       |         1 | Gabriel   |         12345678 |
|          1 | John       |         2 | Angela    |         12345678 |
|          1 | John       |         3 | Philip    |         12345678 |
|          2 | Kate       |         1 | Eleanor   |         98765432 |
|          2 | Kate       |         2 | Betty     |         98765432 |
|          3 | Mary       |         1 | Peter     |        111222333 |
|          3 | Mary       |         2 | Lara      |        111222333 |
|          3 | Mary       |         3 | Michael   |        111222333 |
|          3 | Mary       |         4 | Robert    |        111222333 |
|          3 | Mary       |         5 | Amanda    |        111222333 |
+------------+------------+-----------+-----------+------------------+

答案 2 :(得分:0)

这与@Strawberry上面提供的方法基本相同:https://stackoverflow.com/a/44578041/710788

我只是把我的版本放在这里,以便更好地理解我提到的差异:

SET @prank=0, @crank=1, @curp=0, @curc=0;

SELECT 
  if(o.Passportid<>@curp, @prank:=@prank+1, @prank) as 'ParentRANK', 
  o.ParentName,
  if(o.Passportid<>@curp, @crank:=1, @crank:=@crank+1) as 'ChildRANK', 
  o.ChildName, 
  if(o.Passportid<>@curp, @curp:=o.Passportid, @curp) as 'current_P' 
FROM (
  SELECT p.Passportid,p.ParentNAME, c.PassportID as childID,c.ChildNAME,c.ParentID 
  FROM parents AS p INNER JOIN children as c on p.PassportID=c.ParentID 
  ORDER BY p.PASSPORTID,c.PassportID
) AS o;

与553003名父母和1106983名儿童进行速度比较:

上面的解决方案

影响0行,找到1,106,983行。 3次查询的持续时间:7.020秒

@Strawberry解决方案

影响0行,找到1,106,983行。 1个查询的持续时间:6.489秒

注意:这些行比我原先说的需要处理的行多得多。我只是将它们添加到更好的测试速度差异中。