Simple Life

和这个世界交手这许多年   你是否光彩依旧,兴致盎然...

您现在的位置是:首页 爱折腾 爱折腾详情

django中mysql报错You can't specify target table

发布时间:2015-9-06 作者:Felix 浏览(1166)

      在写项目的时候遇到了一个错误,mysql报错 OperationalError: (1093, "You can't specify target table 'user_nsuser' for update in FROM clause"),查到了错误的原因也很快找到了解决的方法。但还是把问题的分析和解决方法总结下。

    首先看下我的数据库设计吧,因为是用django写的,也就不看schema了,直接看下model清晰点,这里截取部分。

class NsUser(BaseModel, AbstractBaseUser, PermissionsMixin):
  username = models.CharField(u'用户名', max_length=30)
  real_name = models.CharField(u'真实姓名', max_length=30, blank=True, null=True)
  mobile = models.CharField(u'手机号', max_length=11, blank=True, null=True)
  usb_key = models.CharField(u'USB KEY序列号', blank=True, null=True, max_length=100)
  created_by = models.ForeignKey('self', verbose_name=u'创建者', blank=True, null=True)

    用户表中有个created_by字段是指向自身的。

    然后在后端逻辑处理的时候,有一段逻辑这么写的:

nsuser_id_list = NsUser.objects.filter(created_by=users).values_list('id', flat=True)
if user_change == 'T':
    NsUser.objects.filter(id__in=nsuser_id_list).update(is_active=True)

    然后测试的时候mysql报错了,OperationalError: (1093, "You can't specify target table 'user_nsuser' for update in FROM clause")

    

    原因

    首先说下报错原因,django中QuerySet是lazy query,如上写法,执行的sql语句如下:

UPDATE `user_nsuser` SET `is_active` = 0 WHERE `user_nsuser`.`id` IN (SELECT V0.`id` 
FROM `user_nsuser` V0 
WHERE (V0.`created_by_id`) 
IN (SELECT U0.`id` FROM `user_nsuser` U0 WHERE U0.`id` IN (6)))

    然而,mysql中复合语句中updateselect不能操作同一个表,所以报错了。

文档解释如下:
In general, you cannot modify a table and select from the same table in a subquery. 
For example, this limitation applies to statements of the following forms:
  DELETE FROM t WHERE ... (SELECT ... FROM t ...);
  UPDATE t ... WHERE col = (SELECT ... FROM t ...);
  {INSERT|REPLACE} INTO t (SELECT ... FROM t ...);

    解决

    问题已经很明显,解决方法很简单,我们先执行子查询的不就行了,而且sql并不是写的越复杂越好,mysql的子查询实现的非常糟糕,最糟糕的一类查询是where条件中包含IN()的子查询语句,这个mysql查询执行中的查询优化器有关,之前也碰到过坑,这里就不展开说了,自己可以查下资料。这里说白了是lazy query好心办坏事,我们改下写法:

nsuser_id_list = NsUser.objects.filter(created_by=users).values_list('id', flat=True)
if user_change == 'T':
  NsUser.objects.filter(id__in=list(nsuser_id_list)).update(is_active=True)

    这样django就会先执行后面子查询,这下解决了问题。

SELECT `user_nsuser`.`id` FROM `user_nsuser` 

WHERE (`user_nsuser`.`created_by_id`) IN (SELECT U0.`id` FROM `user_nsuser` U0 WHERE U0.`id` IN (6));
UPDATE `user_nsuser` SET `is_active` = 0 WHERE `user_nsuser`.`id` IN (8);

    拓展

    那在django中哪些地方会执行真正查询,然不是lazy query呢,这里总结下。

    1. 迭代     

        for e in Entry.objects.all():

            print(e.headline)

    2. 分片

        Entry.objects.all()[:10]

    3. 持久化或缓存

    4. repr()

    5. len()

        print len(Entry.objects.all())

    6. bool()

        if Entry.objects.all():

            print 'exists'


基于 Django 搭建

服务器采用的 阿里云

域名来自 万网

苏ICP备16015443号

©2015-2016 felixglow.com.

GitHub

Design by Felix