mysql - 我们如何控制表中字段的动态排序?

标签 mysql sql delphi

我不知道这个问题是否符合我的要求,但是:

我在表格中有一组问题,这些问题将按特定顺序询问客户,有时我们需要插入新问题,还需要将问题向下或向上移动。

我做了一个名为 position 的字段,还有一些按钮可以增加和减少它的位置,所以我可以使用 SELECT ... ORDER BY 但它不是很好,因为有时会出现两个或更多问题相同的位置编号,MySQL 选择它们的顺序。

那么使它完美运行的正确方法是什么?

注意:我不能使用索引来做到这一点。对一些人来说这是显而易见的,但对其他人来说,不是......

最佳答案

如果我对您的理解是正确的,您需要一种方法来在插入新问题、更改现有问题的位置或删除问题时正确管理 position 列中的值序列。

假设您有以下问题表的 DDL:

CREATE TABLE `questions` (
    `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
    `question` VARCHAR(256) DEFAULT NULL,
    `position` INT(11) DEFAULT NULL,
    PRIMARY KEY (`id`)
);

和这样的初始数据集

+----+------------+----------+
| id | question   | position |
+----+------------+----------+
|  1 | Question 1 |        1 |
|  2 | Question 2 |        2 |
|  3 | Question 3 |        3 |
+----+------------+----------+

为了得到有序的问题列表你做的很明显

SELECT * 
  FROM questions 
 ORDER BY position;

要在问题列表的末尾插入一个新问题,您可以

INSERT INTO questions (question, position) 
SELECT 'New Question', COALESCE(MAX(position), 0) + 1
  FROM questions;

结果将是:

+----+--------------+----------+
| id | question     | position |
+----+--------------+----------+
|  1 | Question 1   |        1 |
|  2 | Question 2   |        2 |
|  3 | Question 3   |        3 |
|  4 | New Question |        4 |
+----+--------------+----------+

要将新问题插入到列表中的特定位置(假设是位置 3),您可以使用两个查询来完成:

UPDATE questions
   SET position = position + 1
 WHERE position >= 3;

INSERT INTO questions (question, position) 
VALUES ('Another Question', 3);

现在你有

+----+------------------+----------+
| id | question         | position |
+----+------------------+----------+
|  1 | Question 1       |        1 |
|  2 | Question 2       |        2 |
|  5 | Another Question |        3 |
|  3 | Question 3       |        4 |
|  4 | New Question     |        5 |
+----+------------------+----------+

交换两个问题的位置(例如,id 为 2 和 5 的问题)

UPDATE questions AS q1 INNER JOIN 
       questions AS q2 ON q1.id = 2 AND q2.id = 5
   SET q1.position = q2.position,
       q2.position = q1.position

让我们看看我们得到了什么

+----+------------------+----------+
| id | question         | position |
+----+------------------+----------+
|  1 | Question 1       |        1 |
|  5 | Another Question |        2 |
|  2 | Question 2       |        3 |
|  3 | Question 3       |        4 |
|  4 | New Question     |        5 |
+----+------------------+----------+

这正是当用户点击您的向上和向下按钮并提供正确的问题 ID 时您所做的。

现在,如果您想在删除问题时保持职位顺序没有间隙,您可以这样做。

要从列表末尾删除,您可以使用简单删除

DELETE FROM questions WHERE id=4;

结果

+----+------------------+----------+
| id | question         | position |
+----+------------------+----------+
|  1 | Question 1       |        1 |
|  5 | Another Question |        2 |
|  2 | Question 2       |        3 |
|  3 | Question 3       |        4 |
+----+------------------+----------+

删除列表中间(或开头)的问题需要更多工作。假设我们要删除 id=5 的问题

-- Get the current position of question with id=5
SELECT position FROM questions WHERE id=5;
-- Position is 2
-- Now delete the question
DELETE FROM questions WHERE id=5;
-- And update position values
UPDATE questions
   SET position = position - 1
 WHERE position > 2;

终于有了

+----+--------------+----------+
| id | question     | position |
+----+--------------+----------+
|  1 | Question 1   |        1 |
|  2 | Question 2   |        2 |
|  3 | Question 3   |        3 |
+----+--------------+----------+

更新:为了让我们的生活更轻松,我们可以将其全部包装在存储过程中

DELIMITER $$
CREATE PROCEDURE add_question (q VARCHAR(256), p INT)
BEGIN

IF p IS NULL OR p = 0 THEN
    INSERT INTO questions (question, position) 
    SELECT q, COALESCE(MAX(position), 0) + 1
      FROM questions;
ELSE
    UPDATE questions
       SET position = position + 1
     WHERE position >= p;

    INSERT INTO questions (question, position) 
    VALUES (q, p);
END IF;
END$$
DELIMITER ;

DELIMITER $$
CREATE PROCEDURE swap_questions (q1 INT, q2 INT)
BEGIN
    UPDATE questions AS qs1 INNER JOIN 
           questions AS qs2 ON qs1.id = q1 AND qs2.id = q2
       SET qs1.position = qs2.position,
           qs2.position = qs1.position;
END$$
DELIMITER ;

DELIMITER $$
CREATE PROCEDURE delete_question (q INT)
BEGIN
    SELECT position INTO @cur_pos FROM questions WHERE id=q;
    SELECT MAX(position) INTO @max FROM questions;

    DELETE FROM questions WHERE id=q;

IF @cur_pos <> @max THEN 
    UPDATE questions
       SET position = position - 1
     WHERE position > @cur_pos;
END IF;
END$$
DELIMITER ;

并像这样使用它们:

-- Add a question to the end of the list
CALL add_question('How are you today?', 0);
CALL add_question('How are you today?', NULL);

-- Add a question at a specific position
CALL add_question('How do you do today?', 3);

-- Swap questions' positions
CALL swap_questions(1, 7);

-- Delete a question
CALL delete_question(2);

关于mysql - 我们如何控制表中字段的动态排序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14927407/

相关文章:

mysql - mysql中的mid和substring有什么区别?

delphi - 在 TObject 的 "Create"构造函数中使用继承

delphi - 使用DrawThemeBackground绘图时如何从右到左(RTL)方向绘制元素部分?

java - 使用 volley 库搜索特定的 json 字符串

mysql - 作为 root 用户,使用密码 yes 访问 ROOT@LOCALHOST 被拒绝

mysql - 仅选择前两项并订购

sql - 使用 WHERE 和 OR 术语时如何知道哪一个匹配

sql - 在 string_agg 中排序似乎不起作用

Java Hibernate 注解 @JoinTable 关系

delphi - 使用 Indy 执行 IPv6 反向 DNS 查找