INSERT语句性能中的SQL相关子查询

时间:2010-06-29 02:00:10

标签: mysql

我正在使用以下查询生成一个唯一的新用户:

INSERT INTO `users`
SET `display_name` = CONCAT(
    'user',
    (
        IF(
            EXISTS(
                SELECT COUNT(`id`) FROM (
                    SELECT `id` FROM `users`
                ) AS `A`),
            (SELECT COUNT(`id`) + 1 FROM (
                SELECT `id` FROM `users`
                ) AS `A`),
            1
        )
    )
);

id是INT和AUTO_INCREMENT PRIMARY KEY。列display_name是VARCHAR并且不是NULL。此查询插入具有顺序显示名称的新用户,例如user1,user2,user3,user4,...

DESCRIBE返回了这个:

id    select_type    table    type    possible_keys    key       key_len    ref     rows    Extra
1     PRIMARY        NULL     NULL    NULL             NULL      NULL       NULL    NULL    No tables used
2     SUBQUERY       NULL     NULL    NULL             NULL      NULL       NULL    NULL    Select tables optimized away
3     DERIVED        users    index   NULL             UNIQUE    302        NULL    NULL    Using index
4     SUBQUERY       NULL     NULL    NULL             NULL      NULL       NULL    NULL    Select tables optimized away
5     DERIVED        users    index   NULL             UNIQUE    302        NULL    NULL    Using index

这个查询有效吗?如果没有,什么是更有效的方法?

2 个答案:

答案 0 :(得分:1)

将效率暂时搁置一秒,删除用户行后查询将无效。在这种情况下,Count将减少,您将重用旧的display_name。

话虽如此,MAX()可能是更好地为您的逻辑提供服务的功能,并允许您摆脱EXISTS检查和第二次查询。从而使更快

然而,如果你的逻辑变得复杂。我建议根据您的MySQL版本查看用户定义的函数或过程。这些都是经过编译的,速度更快,看起来更清晰,即

INSERT INTO users SET display_name = UNIQUE_DISPLAY_NAME();

答案 1 :(得分:1)

您的查询不是一项好技术,因为它可能会导致种族异常并删除异常。

例如,如果两个客户端同时创建用户,则他们可以统计15个现有用户并且都分配用户16。

此外,如果您有15个用户,但删除了user10,那么count()将报告14,您将重新分配user15。

相反,我会将id设为自动生成的主键。 MySQL在事务隔离规则之外分配id值,因此保证并发客户端永远不会分配相同的值。

编辑:遗憾的是,您无法使用触发器,因为生成的键值在before触发器中不可用,并且您无法在after触发器中更改display_name的值。所以你必须在插入中执行此操作,然后进行更新。

INSERT INTO users DEFAULT VALUES;
UPADTE users SET display_name = CONCAT('user', LAST_INSERT_ID()) 
WHERE id = LAST_INSERT_ID();

不要担心差距。也就是说,如果user15永远不会被删除,回滚或其他什么,那也没关系。不要落入Pseudokey Neat-Freak antipattern