在给定的表格中,我有一个字段(field_order
),用于定义显示表格行的自定义顺序。插入新记录时
我想设置该特定字段与该表中的行数加一个
因此,如果表格有3行,则在插入新行时,field_order
的默认值应为4.
设定该值的最佳方法是什么?
insert语句中的简单选择计数?
CURRENT_TIMESTAMP
数据类型的TIMESTAMP
常量是否会返回该值?
编辑:这背后的原因是能够按该特定字段对表进行排序;并且该字段将由客户端的用户使用jQuery的可排序
进行操作答案 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