如何将字符串中的定界列表解析为行?

问题描述 投票:1回答:1

这是我以前的问题:How can I merge two strings of comma-separated numbers in MySQL?

我出于以下原因尝试使用定界列表:

  1. 数据超过2000行。
  2. 我只有一天。
  3. 这不是生产级别
  4. 我需要用手分析数据。

我以为我别无选择。但我发现了这一点:SQL split values to multiple rows

所以,我从中获得了一些希望。但是将它应用于我的表是非常困难的。

  1. 我的是另一种形式。我有多列,但我想避免重复行。
  2. 我更喜欢UPDATE和INSERT,而不是SELECT,因为更改表的格式将更易于管理,原因如下:Is storing a delimited list in a database column really that bad?
  3. 我需要找到未列出的数字。例如,如果时间值为1,3,8然后我需要2,4,5,6,7,9,10,11,12,13(介于1和13之间)

我的示例表如下:

+-----+------+--------+------+-------+-----------+-------------+
| cid | sid  | type   | day  | time  | building  | room_number |
+-----+------+--------+------+-------+-----------+-------------+
|   1 |    1 | daytime   | mon   | 6,7,8 | sky    | 507         |
|   2 |    2 | daytime   | thu   | 2,3,4 | nuri    | 906         |
|   3 |    3 | daytime   | tue   | 6,7,8 | nuri    | 906         |
|   4 |    4 | daytime   | thu   | 6,7   | sky    | 1003        |
|   5 |    5 | daytime   | mon   | 2,3,4 | sky    | 507         |
|   6 |    6 | daytime   | wed   | 6,7,8 | belief    | 1003        |
|   7 |    7 | daytime   | mon   | 2,3,4 | belief    | 905         |
|   8 |    8 | daytime   | fri   | 6,7,8 | truth    | 905         |
|   9 |    9 | daytime   | tue   | 6,7,8 | truth    | 905         |
|  10 |   10 | daytime   | fri   | 2,3,4 | truth    | 905         |
|  11 |   11 | daytime   | wed   | 6,7,8 | truth    | 905         |
|  12 |   12 | daytime   | fri   | 2,3,4 | truth    | 1003        |
|  13 |   13 | daytime   | mon   | 6,7,8 | truth    | 905         |
|  14 |   14 | daytime   | tue   | 2,3,4 | truth    | 905         |
|  15 |   15 | daytime   | tue   | 6,7,8 | sky    | 208         |
|  16 |   16 | daytime   | tue   | 2,3,4 | sky    | 208         |
|  17 |   17 | daytime   | tue   | 2,3,4 | truth    | 1004        |
|  18 |   19 | daytime   | mon   | 2,3,4 | sky    | 208         |
|  19 |   20 | daytime   | thu   | 2,3,4 | truth    | 1003        |
|  20 |   21 | daytime   | wed   | 6,7,8 | sky    | 208         |
|  21 |   22 | night   | tue   | 4,5,6 | nuri    | 405         |
|  22 |   23 | night   | tue   | 1,2,3 | nuri    | 405         |
|  23 |   24 | night   | tue   | 1,2,3 | nuri    | 306         |
|  24 |   25 | night   | thu   | 1,2,3 | nuri    | 205         |
|  25 |   26 | night   | thu   | 4,5,6 | sky    | 306         |
|  26 |   27 | night   | wed   | 1,2,3 | nuri    | 306         |
|  27 |   28 | night   | wed   | 4,5,6 | sky    | 309         |
|  28 |   29 | night   | wed   | 4,5,6 | nuri    | 407         |
|  29 |   30 | night   | tue   | 4,5,6 | nuri    | 306         |
|  30 |   31 | night   | thu   | 1,2,3 | nuri    | 307         |
| 31 |    0 | always   |      |      | sky    | 201         |
| 32 |    0 | always   |      |      | sky    | 202         |
| 33 |    0 | always   |      |      | sky    | 203         |
| 34 |    0 | always   |      |      | sky    | 204         |
| 35 |    0 | always   |      |      | nuri    | 205         |
| 36 |    0 | always   |      |      | nuri    | 206         |
| 37 |    0 | always   |      |      | truth    | 207         |
| ... |    ... | ...   |      |      | ...    | ...         |
| 2000 |    0 | always   |      |      | belief    | 1101         |
+-----+------+--------+------+-------+-----------+-------------+
  1. 507 |周一的天空建筑:已安排2,3,4,6,7,8。因此,将添加1,5,9,10,11,12,13。
  2. 906 | nuri大楼在星期二,星期二有时间表。这是不同的一天。因此,它们不会互相影响。因此,周四为1,5,6,7,8,9,10,11,12,13,周二为1,2,3,4,5,9,10,11,12,13。
  3. 如果有重复的行,则不会影响。

我对更改表的期望是这样:


+-----+------+--------+------+-------+-----------+-------------+
| cid | sid  | type   | day  | time  | building  | room_number |
+-----+------+--------+------+-------+-----------+-------------+
|   1 |    1 | daytime   | mon   | 1 | sky    | 507         |
|   2 |    1 | daytime   | mon   | 5 | sky    | 507         |
|   3 |    1 | daytime   | mon   | 9 | sky    | 507         |
|   4 |    1 | daytime   | mon   | 10 | sky    | 507         |
|   5 |    1 | daytime   | mon   | 11 | sky    | 507         |
|   6 |    1 | daytime   | mon   | 12 | sky    | 507         |
|   7 |    1 | daytime   | mon   | 13 | sky    | 507         |
|   8 |    2 | daytime   | thu   | 1 | nuri    | 906         |
|   9 |    2 | daytime   | thu   | 5 | nuri    | 906         |
|   10 |    2 | daytime   | thu   | 6 | nuri    | 906         |
|   11 |    2 | daytime   | thu   | 7 | nuri    | 906         |
|   12 |    2 | daytime   | thu   | 8 | nuri    | 906         |
|   13 |    2 | daytime   | thu   | 9 | nuri    | 906         |
|   14 |    2 | daytime   | thu   | 10 | nuri    | 906         |
|   15 |    2 | daytime   | thu   | 11 | nuri    | 906         |
|   16 |    2 | daytime   | thu   | 12 | nuri    | 906         |
|   17 |    2 | daytime   | thu   | 13 | nuri    | 906         |
|   18 |    3 | daytime   | tue   | 1 | nuri    | 906         |
|   19 |    3 | daytime   | tue   | 2 | nuri    | 906         |
|   20 |    3 | daytime   | tue   | 3 | nuri    | 906         |
| ... |    ... | ...   |      |      | ...    | ...         |
| 302 |    0 | always   |      |      | nuri    | 206         |
| 303 |    0 | always   |      |      | truth    | 207         |
| ... |    ... | ...   |      |      | ...    | ...         |
| 4020 |    0 | always   |      |      | belief    | 1101         |
+-----+------+--------+------+-------+-----------+-------------+

我这样做的原因是源数据是课程的时间表。而且我想找到空余时间,以便人们在上课的时候可以使用教室。

mysql sql string database-design recursive-query
1个回答
0
投票

这是一种使用回溯查询将字符串拆分为行的方法。然后,您可以使用此结果集来识别丢失的记录:为此,您可以使用时间列表来cross join原始表,并使用反left join来提取丢失的记录:

with recursive 
    cte as (
        select cid, sid, type, day, room_number,
            substring(time, 1, locate(',', time) - 1) time,
            substring(concat(time, ','), locate(',', time) + 1) rest
        from mytable
        union all
        select cid, sid, type, day, room_number,
            substring(rest, 1, locate(',', rest) - 1),
            substring(rest, locate(',', rest) + 1)
        from cte
        where  locate(',', rest) > 0
    ),
    all_times as (
        select 1 time 
        union all select time + 1 from all_times where time < 13
    )
select  t.cid, t.sid, t.type, t.day, at.time, t.building, t.room_number
from all_times at
cross join mytable t
left join cte c on c.cid = t.cid and c.time = at.time
where c.cid is null
order by t.cid, at.time

Demo on DB Fiddle

样本数据:

cid | sid |类型天时间|建筑|房间号-:| -:| :------ | :-| :---- | :------- | ----------:  1 | 1 |白天|星期一| 6,7,8 |天空| 507  2 | 2 |白天|周四| 2,3,4 |努里906

查询结果:

cid | sid |类型天时间|建筑|房间号-:| -:| :------ | :-| ---: :------- | ----------:  1 | 1 |白天|星期一| 1 |天空| 507  1 | 1 |白天|星期一| 2 |天空| 507  1 | 1 |白天|星期一| 3 |天空| 507  1 | 1 |白天|星期一| 4 |天空| 507  1 | 1 |白天|星期一| 5 |天空| 507  1 | 1 |白天|星期一| 9 |天空| 507  1 | 1 |白天|星期一| 10 |天空| 507  1 | 1 |白天|星期一| 11 |天空| 507  1 | 1 |白天|星期一| 12 |天空| 507  1 | 1 |白天|星期一| 13 |天空| 507  2 | 2 |白天|周四| 1 |努里906  2 | 2 |白天|周四| 5 |努里906  2 | 2 |白天|周四| 6 |努里906  2 | 2 |白天|周四| 7 |努里906  2 | 2 |白天|周四| 8 |努里906  2 | 2 |白天|周四| 9 |努里906  2 | 2 |白天|周四| 10 |努里906  2 | 2 |白天|周四| 11 |努里906  2 | 2 |白天|周四| 12 |努里906  2 | 2 |白天|周四| 13 |努里906

通过在前面加上insert指令,您可以轻松地将其转换为insert查询,例如:

insert into sometable(cid, sid, type, day, time, building, room_number)
with recursive ...
select ...
from ...
where ...

实际上,由于csv列表中的值列表是预定义的,因此可以很好地完成而无需递归。您可以将天数列表与表格合并,然后使用find_in_set()识别差距。好处是它可以在不支持追溯查询的MySQL / MariaDB版本上运行:

select  t.cid, t.sid, t.type, t.day, at.time, t.building, t.room_number
from (
    select 1 time union all select 2 time union all select 3 
    union all select 4 union all select 5 union all select 6 
    union all select 7 union all select 8 union all select 9 
    union all select 10 union all select 11 union all select 12 union all select 13
) at
cross join (
    select distinct cid, sid, type, day, building, room_number 
    from mytable
) t
left join mytable t1 on t1.cid = t.cid and find_in_set(at.time, t1.time)
where t1.cid is null
order by t.cid, at.time

Demo

© www.soinside.com 2019 - 2024. All rights reserved.