归一化顺序/序列

问题描述 投票:0回答:2

我在google或其他任何地方都找不到解决方案。也许我的术语是错误的。

我正在尝试找出如何规范表中MySQL中的排序的顺序:

| id | job   | action         | sequence |
| :- | :---- | :------------- | :------- |
| 1  | build | Procure Wood   | 1        |
| 2  | build | Grab Hammer    | 2        |
| 3  | build | Hammer         | 3        |
| 4  | drill | Get Screw      | 1        |
| 5  | drill | Charge Drill   | 2        |
| 6  | drill | Start Drilling | 3        |

[请忽略jobaction列均未标准化的事实,这不是问题的一部分,而只是示例的一部分。

例如,如果我现在想为Get Nails作业插入一个build的新动作,该怎么办?这将在Grab Hammer之后但在Hammer之前。如果将其插入(然后是ORDER BY job, sequence),则会得到以下信息:

| id | job   | action         | sequence     |
| :- | :---- | :------------- | :----------- |
| 1  | build | Procure Wood   | 1            |
| 2  | build | Grab Hammer    | 2            |
| 7  | build | Get Nails      | 3            |
| 3  | build | Hammer         | 3 <- problem |
| 4  | drill | Get Screw      | 1            |
| 5  | drill | Charge Drill   | 2            |
| 6  | drill | Start Drilling | 3            |

或者,如果我想在Charge Drill作业中重新安排Get Screwdrill动作,我将Charge Drill的顺序设置为等于Get Screw的顺序。但随后,其他两个动作将无法正确排序。我看到的选项是:

  1. 一种在后续行中“级联”排序的方式。我想不出一个纯SQL方法,它可能需要通过在表上运行transaction的脚本来处理。

  2. 代替sequence,该列为behind。在此列中的任何一行都位于id。但是我不知道SQL将如何对它进行排序(即在ORDER BY中),实际上仍然存在与上述相同的问题(如果两行指向相同的id,该怎么办)。

如果可能,我正在寻找更标准的纯SQL方法。

mysql sql database-design database-normalization
2个回答
0
投票

考虑这样做是一个两步过程,因此,首先将新操作添加(插入)到表中,并使用空序列号。然后将其“移动”到正确的位置(更新),因此将其ID设置为3,并将所有相关动作(大于或等于3)的ID设置为序列+ 1 ...

  DROP TABLE IF EXISTS my_table;

CREATE TABLE my_table  (
      `id` Serial primary key,
      `job` varchar(12),
      `action` VARCHAR(20),
      `sequence` int);

    INSERT INTO my_table 
      (`id`, `job`, `action`, `sequence`)
    VALUES
      ('1', 'build', 'Procure Wood',1),
      ('2', 'build', 'Grab Hammer',2),
      ('3', 'build', 'Hammer',3),
      ('4', 'drill', 'Get Screw',1),
      ('5', 'drill', 'Charge Drill',2),
      ('6', 'drill', 'Start Drilling',3);

SELECT * FROM my_table ORDER BY job,sequence; 
+----+-------+----------------+----------+
| id | job   | action         | sequence |
+----+-------+----------------+----------+
|  1 | build | Procure Wood   |        1 |
|  2 | build | Grab Hammer    |        2 |
|  3 | build | Hammer         |        3 |
|  4 | drill | Get Screw      |        1 |
|  5 | drill | Charge Drill   |        2 |
|  6 | drill | Start Drilling |        3 |
+----+-------+----------------+----------+

      insert into my_table
      (job,action,sequence) values 
      ('build','grab nails',null);

      UPDATE my_table x 
        JOIN 
           ( SELECT a.id
                  , CASE WHEN a.sequence IS NULL THEN 3 
                         WHEN a.sequence >=3 THEN a.sequence +1 ELSE a.sequence 
                     END new_seq     
               FROM my_table a
               JOIN my_table b
                 ON b.job = a.job
              WHERE b.id = LAST_INSERT_ID()
           ) y 
          ON y.id = x.id 
         SET x.sequence = y.new_seq; 

SELECT * FROM my_table ORDER BY job,sequence;
+----+-------+----------------+----------+
| id | job   | action         | sequence |
+----+-------+----------------+----------+
|  1 | build | Procure Wood   |        1 |
|  2 | build | Grab Hammer    |        2 |
|  7 | build | grab nails     |        3 |
|  3 | build | Hammer         |        4 |
|  4 | drill | Get Screw      |        1 |
|  5 | drill | Charge Drill   |        2 |
|  6 | drill | Start Drilling |        3 |
+----+-------+----------------+----------+

https://www.db-fiddle.com/f/mBAYoVB69UNk76GcU1QCrb/1


0
投票

规范化是关于从数据库中删除冗余和逻辑错误。冗余通常会导致逻辑错误,尽管在大多数情况下,这些错误是无设计方法或直接跳入物理设计的结果。

这里是一种可能的逻辑设计的例子。

-- Job named JOB_NAME exists
--
job {JOB_NAME}
 PK {JOB_NAME}


-- Action named ACTION_NAME exists.
--
action {ACTION_NAME}
    PK {ACTION_NAME}


-- Job JOB_NAME involves action ACTION_NAME.
--
job_action {JOB_NAME, ACTION_NAME}
        PK {JOB_NAME, ACTION_NAME}
       FK1 {JOB_NAME}    REFERENCES job    {JOB_NAME}
       FK2 {ACTION_NAME} REFERENCES action {ACTION_NAME}


-- Action ACTION_NAME has associated step number STEP_NO in job JOB_NAME.
--
job_step {JOB_NAME, STEP_NO, ACTION_NAME}
      PK {JOB_NAME, STEP_NO}

FK1 {JOB_NAME, ACTION_NAME} REFERENCES job_action {JOB_NAME, ACTION_NAME}

[此时,您可以开始考虑物理设计:列索引的大小等,并决定添加数字IDs以用于PKs。根据数据库的大小,使用的RDBMS和硬件,您可以决定保留原样。我特意此时避免添加IDs


示例是PostgreSQL(已安装)-应该易于调整以适应MySQL。对于MySQL,请使用varchar(25)而不是TEXT,该示例应该可以正常工作(未经测试)。

CREATE TABLE job (
    JOB_NAME TEXT NOT NULL

  , CONSTRAINT pk_job PRIMARY KEY (JOB_NAME)
);


CREATE TABLE action_ (
    ACTION_NAME TEXT NOT NULL

  , CONSTRAINT pk_action PRIMARY KEY (ACTION_NAME)
);


CREATE TABLE job_action (
    JOB_NAME    TEXT NOT NULL
  , ACTION_NAME TEXT NOT NULL

, CONSTRAINT pk_job_action PRIMARY KEY (JOB_NAME, ACTION_NAME)

, CONSTRAINT fk1_job_action
    FOREIGN KEY (JOB_NAME) REFERENCES
            job (JOB_NAME)

, CONSTRAINT fk2_job_action
    FOREIGN KEY (ACTION_NAME) REFERENCES
        action_ (ACTION_NAME)
);


CREATE TABLE job_step (
      JOB_NAME    TEXT   NOT NULL
    , ACTION_NAME TEXT   NOT NULL
    , STEP_NO     BIGINT NOT NULL

, CONSTRAINT pk_job_step PRIMARY KEY (JOB_NAME, STEP_NO)

, CONSTRAINT fk1_job_step
    FOREIGN KEY (JOB_NAME, ACTION_NAME) REFERENCES
     job_action (JOB_NAME, ACTION_NAME)
);

插入一些测试数据;请注意,步骤编号不是连续的,为以后的中间步骤留有空间。

INSERT INTO job (JOB_NAME)
VALUES
    ('build')
  , ('drill')
;

INSERT INTO action_ (ACTION_NAME)
VALUES
    ('procure wood')
  , ('grab hammer')
  , ('hammer')
  , ('get screw')
  , ('charge drill')
  , ('start drilling')
;

INSERT INTO job_action (JOB_NAME, ACTION_NAME)
VALUES
    ('build','procure wood')
  , ('build','grab hammer')
  , ('build','hammer')
  , ('drill','get screw')
  , ('drill','charge drill')
  , ('drill','start drilling')
;

INSERT INTO job_step (JOB_NAME, ACTION_NAME, STEP_NO)
VALUES
    ('build','procure wood', 1000000)
  , ('build','grab hammer' , 2000000)
  , ('build','hammer'      , 3000000)

  , ('drill','get screw'     , 1000000)
  , ('drill','charge drill'  , 2000000)
  , ('drill','start drilling', 3000000)
;

查询返回正确的结果

SELECT JOB_NAME
     , rank() OVER (PARTITION BY JOB_NAME ORDER BY STEP_NO ASC) AS STEP
     , ACTION_NAME
FROM job_step
ORDER BY JOB_NAME ;
+----------+------+----------------+
| job_name | step | action_name    |
+----------+------+----------------+
| build    | 1    | procure wood   |
| build    | 2    | grab hammer    |
| build    | 3    | hammer         |
| drill    | 1    | get screw      |
| drill    | 2    | charge drill   |
| drill    | 3    | start drilling |
+----------+------+----------------+

并将get nails插入到构建作业中。

-- first define the new action
INSERT INTO action_ (ACTION_NAME) VALUES ('get nails');

-- associate it with the build job
INSERT INTO job_action (JOB_NAME, ACTION_NAME)
VALUES ('build','get nails');

-- insert between grab hammer and hammer
INSERT INTO job_step (JOB_NAME, ACTION_NAME, STEP_NO)
VALUES ('build','get nails', 2500000) ;

现在查询返回:

+----------+------+----------------+
| job_name | step | action_name    |
+----------+------+----------------+
| build    | 1    | procure wood   |
| build    | 2    | grab hammer    |
| build    | 3    | get nails      |
| build    | 4    | hammer         |
| drill    | 1    | get screw      |
| drill    | 2    | charge drill   |
| drill    | 3    | start drilling |
+----------+------+----------------+
© www.soinside.com 2019 - 2024. All rights reserved.