我在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 |
[请忽略
job
和action
列均未标准化的事实,这不是问题的一部分,而只是示例的一部分。
例如,如果我现在想为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 Screw
和drill
动作,我将Charge Drill
的顺序设置为等于Get Screw
的顺序。但随后,其他两个动作将无法正确排序。我看到的选项是:
一种在后续行中“级联”排序的方式。我想不出一个纯SQL方法,它可能需要通过在表上运行transaction
的脚本来处理。
代替sequence
,该列为behind
。在此列中的任何一行都位于id
。但是我不知道SQL将如何对它进行排序(即在ORDER BY
中),实际上仍然存在与上述相同的问题(如果两行指向相同的id
,该怎么办)。
如果可能,我正在寻找更标准的纯SQL方法。
考虑这样做是一个两步过程,因此,首先将新操作添加(插入)到表中,并使用空序列号。然后将其“移动”到正确的位置(更新),因此将其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 |
+----+-------+----------------+----------+
规范化是关于从数据库中删除冗余和逻辑错误。冗余通常会导致逻辑错误,尽管在大多数情况下,这些错误是无设计方法或直接跳入物理设计的结果。
这里是一种可能的逻辑设计的例子。
-- 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 |
+----------+------+----------------+