mysql中的订单字段的默认值

时间:2014-10-20 13:23:21

标签: mysql

在给定的表格中,我有一个字段(field_order),用于定义显示表格行的自定义顺序。插入新记录时 我想设置该特定字段与该表中的行数加一个

因此,如果表格有3行,则在插入新行时,field_order的默认值应为4.

设定该值的最佳方法是什么?

insert语句中的简单选择计数?

CURRENT_TIMESTAMP数据类型的TIMESTAMP常量是否会返回该值?

编辑:这背后的原因是能够按该特定字段对表进行排序;并且该字段将由客户端的用户使用jQuery的可排序

进行操作

4 个答案:

答案 0 :(得分:5)

好的,围绕这个问题的解决方案实际上涉及一些细微差别。走在前面并决定回答,但也想解决一些评论尚未解决的细微差别/细节。

首先,我强烈建议你不要在主键上使用auto_increment,如果没有其他原因,那些自动增量ID很容易被抛弃(例如,回滚事务会干扰它们) MySQL AUTO_INCREMENT does not ROLLBACK。删除就像@Sebas提到的那样。

其次,您必须考虑您的存储引擎。如果您使用的是MyISAM,则可以非常快速地获取表的COUNT(*)(因为MyISAM始终知道每个表中有多少行)。如果你正在使用INNODB,情况并非如此。根据您对此表的需求,您可以使用MyISAM。它不是默认引擎,但您可能会遇到MyISAM更好选择的要求。

你应该问自己的第三件事是,“为什么?”为什么需要以这种方式存储数据?这实际上给你了什么?你实际上在SQL中需要那些信息吗?在同一个SQL表的同一个表中?

如果“为什么”有一个合理使用它的答案,那么我要问的最后一件事是“怎么样?”特别是,您将如何处理并发插入?你打算怎么处理删除或回滚?

鉴于您的要求,基本上有必要做表的计数星......但即便如此,还是有一些细微差别(删除,回滚,并发)以及一些决定(存储引擎做什么)你使用;你能不能使用MyISAM,这对于数星来说会更快?)。

但最重要的是,我会问我为什么首先需要这个。也许你真的这么做......但这是一个非常奇怪的要求。

在你的编辑中:

  

编辑:这背后的原因是能够对表进行排序   那个特定的领域;并且该字段将由用户操纵   在客户端使用jQuery的可排序

基本上你要求的是关于你的表的元数据。我建议将这些元数据存储在单独的表中,或者单独存储在一个单独的服务中(Elastic Search,Redis等)。您需要定期更新该单独的表(或键值存储)。如果您在SQL中执行此操作,则可以使用触发器。或者您使用了类似Elastic Search的东西,您可以同时将数据插入SQL和ES。无论哪种方式,您都需要应对一些棘手的问题(例如,最终的一致性,并发性,以及在MySQL中使用触发器时可能适得其反的所有光荣事物)。

如果是我,我会注意到两件事。其中一个,甚至谷歌都没有提供始终最新的COUNT(*)。 “显示大约XYZ的1-10行。”他们这样做的部分原因是因为他们有更多我想象你会做的数据,部分原因是因为计算表格的精确COUNT(*)实际上是不切实际的(并且很快变得不可行和禁止)并且保持不变始终保持日期。

所以,要么我完全改变我的要求并利用我可以快速获得的统计数据(如果你使用MyISAM进行存储,请继续使用count( * ) ......它会非常快)或者我我会考虑维护我的表的计数星的索引,这些索引通过某个过程(cron作业,触发器,等等)每隔几个小时或每天定期更新一次,或者沿着那些行继续更新。

在这个问题上获得奖励......对这个问题永远不会有单一的,规范的答案。无论你如何决定管理它,都需要做出权衡。它们可能在一致性,延迟,可扩展性,精确与近似解决方案,丢失INNODB以换取MyISAM方面进行权衡......但会有权衡。最终决定归结为您愿意交易的东西以满足您的要求。

如果是我,我可能会弯曲我的要求。如果我这样做,我可能最终会在弹性搜索中对其进行索引,并确保它每隔几个小时左右更新一次。这是你应该做的吗?那要看。它肯定不是一个“正确的答案”,因为如果我可以和我的count(*)一起过时的话,它就是一个答案(在众多中)。

你应该使用Elastic Search吗?那要看。但是,无论你走多远,你都会处理权衡。这不取决于。而且你需要决定你愿意放弃什么才能得到你想要的东西。如果它不重要,请弯曲要求。

答案 1 :(得分:3)

可能有更好的方法,但我现在想到的只是创建一个包含所需值的第二个表,并使用触发器进行适当的插入/删除:

以下是一个例子:

-- Let's say this is your table
create table tbl_test(
    id int unsigned not null auto_increment primary key,
    text varchar(50)
);

-- Now, here's the table I propose.
-- It will be related to your original table using 'Id'
-- (If you're using InnoDB you can add the appropriate constraint
create table tbl_incremental_values(
    id int unsigned not null primary key,
    incremental_value int unsigned not null default 0
);

-- The triggers that make this work:
delimiter $$
create trigger trig_add_one after insert on tbl_test for each row
begin
    declare n int unsigned default 0;
    set n = (select count(*) from tbl_test);
    insert into tbl_incremental_values
        values (NEW.id, (n));
end $$

-- If you're using InnoDB tables and you've created a constraint that cascades
-- delete operations, skip this trigger
create trigger trig_remove before delete on tbl_test for each row
begin
    delete from tbl_incremental_values where id = OLD.id;
end $$
delimiter ;

现在,让我们测试一下:

insert into tbl_test(text) values ('a'), ('b');

select a.*, b.incremental_value 
from tbl_test as a inner join tbl_incremental_values as b using (id);
-- Result:
--    id | text | incremental_value
--    ---+------+------------------
--    1  | a    | 1
--    2  | b    | 2

delete from tbl_test where text = 'b';
select a.*, b.incremental_value 
from tbl_test as a inner join tbl_incremental_values as b using (id);
-- Result:
--    id | text | incremental_value
--    ---+------+------------------
--    1  | a    | 1

insert into tbl_test(text) values ('c'), ('d');
select a.*, b.incremental_value 
from tbl_test as a inner join tbl_incremental_values as b using (id);
-- Result:
--    id | text | incremental_value
--    ---+------+------------------
--    1  | a    | 1
--    3  | c    | 2
--    4  | d    | 3

这适用于小型数据集,但是evanv says in his answer

  

为什么&#34?;为什么需要以这种方式存储数据?这实际上给你了什么?你实际上在SQL中需要那些信息吗?在同一个SQL表的同一个表中?

如果您只需要输出该结果,那么可以更轻松地完成这项工作:用户变量。

现在让我们说你的桌子是这样的:

create table tbl_test(
    id int unsigned not null auto_increment primary key,
    ts timestamp,
    text varchar(50)
);

insert into tbl_test(text) values('a');
insert into tbl_test(text) values('b');
insert into tbl_test(text) values('c');
insert into tbl_test(text) values('d');
delete from tbl_test where text = 'b';
insert into tbl_test(text) values('e');

ts列将采用插入每行的日期和时间值,因此,如果按该列对其进行排序,您将按插入顺序获取行。但现在:如何添加"增量值"?使用用户变量的小技巧是可能的:

select a.*
     , @n := @n + 1 as incremental_value
--     ^^^^^^^^^^^^ This will update the value of @n on each row
from (select @n := 0) as init -- <-- you need to initialize @n to zero
   , tbl_test as a
order by a.ts;
-- Result:
--   id | ts                  | text | incremental_value
--   ---+---------------------+------+----------------------
--   1  | xxxx-xx-xx xx:xx:xx | a    | 1
--   3  | xxxx-xx-xx xx:xx:xx | c    | 2
--   4  | xxxx-xx-xx xx:xx:xx | d    | 3
--   5  | xxxx-xx-xx xx-xx-xx | e    | 4

但是现在......如何处理大数据集,你可能会使用LIMIT?只需将@n初始化为限制的起始值:

-- A dull example:

prepare stmt from 
    "select a.*, @n := @n + 1 as incremental_value
    from (select @n := ?) as init, tbl_test as a
    order by a.ts
    limit ?, ?";
-- The question marks work as "place holders" for values. If you're working
-- directly on MySQL CLI or MySQL workbench, you'll need to create user variables
-- to hold the values you want to use.
set @first_row = 2, @nrows = 2;
execute stmt using @first_row,   @first_row,   @nrows;
--                 ^^^^^^^^^^    ^^^^^^^^^^    ^^^^^^
--                 Initalizes    The "floor"   The number
--                 the @n        of the        of rows
--                 value         LIMIT         you want
--
-- Set @first_row to zero if you want to get the first @nrows rows
--
-- Result:
--   id | ts                  | text | incremental_value
--   ---+---------------------+------+----------------------
--   4  | xxxx-xx-xx xx:xx:xx | d    | 3
--   5  | xxxx-xx-xx xx-xx-xx | e    | 4
deallocate prepare stmt;

答案 2 :(得分:1)

似乎原始问题是要求在新记录上设置默认排序顺序的简单方法。稍后用户可以调整该“订单字段”值。看起来像DELETES和ROLLBACKS与此无关。

这是一个简单的解决方案。对于排序顺序字段,将默认值设置为0,并使用主键作为辅助排序。只需将查询中的排序顺序更改为DESC即可。如果您希望默认功能为“显示最近添加的第一个”,请使用:

SELECT * from my_table
WHERE user_id = :uid
ORDER BY field_order, primary_id DESC

如果您想“显示最近添加的最后一个”,请使用:

SELECT * from my_table
WHERE user_id = :uid
ORDER BY field_order DESC, primary_id

答案 3 :(得分:-1)

我为避免插入查询中的SELECT COUNT(*) ...所做的工作是field_order列的未排序状态,让我们说一个默认值0

选择查询如下所示:

SELECT * FROM my_table ... ORDER BY id_primary, field_order

只要您不申请自定义订单,您的查询就会按时间顺序生成。

如果您要应用自定义排序field_order,请将其从-X计算到0进行重新设置:

id | sort
---+-----
1  | -2   
2  | -1   
3  |  0   

当发生更改时,自定义排序仍然存在,并且在自定义排序结束时,新行将始终按时间顺序排序:

id | sort
---+-----
1  | -2   
3  |  0   
4  |  0