调查测验问卷db设计SQL显示问题或根据以前的答案选择django数据库

时间:2014-07-03 05:50:10

标签: sql database django survey

我有一个调查应用,我需要根据用户以前的答案限制问题的可用答案选择。

为了实现这一点,我认为引入一个依赖表是个好主意。

我觉得自己几乎就在那里,但我似乎无法弄清楚如何为我想要的结果构建正确的查询,或者如果我以最简单的方式解决问题。

我的表是:

  • 用户
  • 问题
  • 选择
  • 答案
  • 依赖
  • Choice_Dependencies(选择和依赖的多对多)

依赖项引用一个问题和一个选项。每个依赖项都有一个描述字段,如“is male”,因此如果问题1是“你是男性还是女性?”问题1有两个选择,“男性”choice_id = 1,“女性”choice_id = 2。然后,该依赖记录将引用问题1和选择1。

然后,您可以将该依赖关系与许多选择相关联。

因此,如果问题2是“你最喜欢哪一件事?”可用的选择记录是化妆品,裙子,汽车,油脂,

汽车和油脂与“男性”的依赖性有关,化妆和裙子与“是女性”有关

我需要弄清楚如何编写一个查询,以获得满足每个依赖关系的所有选择。

我认为的另一种方式是获得任何不满足的选择,并将其排除在可用选项之外。也许是NOT IN子查询?

问题也与他们的选择有关。但我可以处理查询的那一部分。

这是一个django应用程序,但我不在乎我是否必须使用原始sql。下面是我在SQL和django模型语法中解释的表结构。

SQL

CREATE TABLE public.auth_user (
    id serial NOT NULL,
    password varchar(128) NOT NULL,
    last_login timestamp with time zone NOT NULL,
    is_superuser boolean NOT NULL,
    username varchar(30) NOT NULL,
    first_name varchar(30) NOT NULL,
    last_name varchar(30) NOT NULL,
    email varchar(75) NOT NULL,
    is_staff boolean NOT NULL,
    is_active boolean NOT NULL,
    date_joined timestamp with time zone NOT NULL
)

CREATE TABLE public.bny_question (
    id serial NOT NULL,
    section_id integer NOT NULL,
    input_type_id integer NOT NULL,
    "order" integer NOT NULL,
    text text NOT NULL
)

CREATE TABLE public.bny_dependency (
    id serial NOT NULL,
    question_id integer,
    choice_id integer,
    description varchar(255) NOT NULL
)

CREATE TABLE public.bny_choice_dependencies (
    id serial NOT NULL,
    choice_id integer NOT NULL,
    dependency_id integer NOT NULL
)

CREATE TABLE public.bny_choice (
    id serial NOT NULL,
    question_id integer NOT NULL,
    text varchar(255) NOT NULL,
    value varchar(255) NOT NULL,
    blurb text NOT NULL,
    "order" integer NOT NULL
)

CREATE TABLE public.bny_answer (
    id serial NOT NULL,
    user_id integer NOT NULL,
    question_id integer NOT NULL,
    choice_id integer NOT NULL
)

Django的

class Question(models.Model):
    section = models.ForeignKey('Section')
    input_type = models.ForeignKey('Input_type')
    order = models.IntegerField()
    text = models.TextField()

    class Meta:
        ordering = ['order']

    def get_next(self):
        next = Question.objects.filter(id__gt=self.id)
        if next:
          return next[0]
        return None

    def __unicode__(self):
        return u"%s" % (self.text)

class Dependency(models.Model):
    question = models.ForeignKey('Question', null=True)
    choice = models.ForeignKey('Choice', null=True)
    description = models.CharField(max_length=255, blank=True)

    class Meta:
        verbose_name_plural = "dependencies"

    def __unicode__(self):
        return u"%s [%s - %s]" % (self.description, self.question, self.choice)

class Choice(models.Model):    
    question = models.ForeignKey('Question', related_name='choices')
    dependencies = models.ManyToManyField('Dependency', related_name='dependent_choices', null=True)    
    text = models.CharField(max_length=255)
    value = models.CharField(max_length=255)
    blurb = models.TextField()
    order = models.IntegerField()

    class Meta:
        ordering = ['order']

    def __unicode__(self):
        return u"%s" % (self.value)

class Answer(models.Model):
    user = models.ForeignKey(User, related_name='answers')
    question = models.ForeignKey('Question')
    choice = models.ForeignKey('Choice')

    class Meta:
        unique_together = ('user', 'question',)

    def __unicode__(self):
        return u"%s - %s" % (self.question, self.choice)

1 个答案:

答案 0 :(得分:0)

哇,好吧,这真让我大吃一惊,我已经花了好几个小时。如果有人知道更好的方式让我知道。

通过使用以下使用NOT IN子查询的SQL查询,我能够获得我想要的确切结果。

SELECT *
FROM public.bny_choice bc
WHERE bc.question_id = 6
  AND bc.id NOT IN
    ( SELECT bc1.id
     FROM public.bny_choice bc1
     INNER JOIN public.bny_question bq ON (bc1.question_id = bq.id)
     INNER JOIN public.bny_choice_dependencies bcd ON (bc1.id = bcd.choice_id)
     INNER JOIN public.bny_dependency bd ON (bcd.dependency_id = bd.id)
     INNER JOIN public.bny_question bq1 ON (bd.question_id = bq1.id)
     INNER JOIN public.bny_answer ba ON (bq1.id = ba.question_id)
     WHERE ba.user_id = 1
       AND ba.choice_id != bd.choice_id)

为了在本机djagno查询语法中执行此操作,事情变得更加困难。

Django通过在过滤器中使用NOT INChoice.objects.exclude()运算符来支持__in ...很棒...但Django不支持!=运算符。

相反,您可以使用前缀为〜

的Q对象

Choice.objects.filter(~Q(dependencies__choice = dependencies__question__answer__choice)

这不起作用,因为我认为Django期望等式两边的1个项目是一个文字,如果你想将它与同一个表中你需要使用F对象的东西的值进行比较,那么它现在变成了。 ..

Choice.objects.filter(~Q(dependencies__choice = F('dependencies__question__answer__choice'))

这可行但实际上并不是真正的!=它实际上等同于以下SQL ...

SELECT •••
FROM "bny_choice"
INNER JOIN "bny_choice_dependencies" ON ("bny_choice"."id" = "bny_choice_dependencies"."choice_id")
INNER JOIN "bny_dependency" ON ("bny_choice_dependencies"."dependency_id" = "bny_dependency"."id")
INNER JOIN "bny_question" ON ("bny_dependency"."question_id" = "bny_question"."id")
INNER JOIN "bny_answer" ON ("bny_question"."id" = "bny_answer"."question_id")
WHERE NOT ("bny_choice"."id" IN
             (SELECT •••
              FROM "bny_choice" U0
              INNER JOIN "bny_choice_dependencies" U1 ON (U0."id" = U1."choice_id")
              INNER JOIN "bny_dependency" U2 ON (U1."dependency_id" = U2."id")
              INNER JOIN "bny_question" U3 ON (U2."question_id" = U3."id")
              INNER JOIN "bny_answer" U4 ON (U3."id" = U4."question_id")
              WHERE U2."choice_id" = U4."choice_id"))
ORDER BY "bny_choice"."order" ASC

并将exclude与=结合使用,产生完全相同的SQL

Choice.objects.exclude(dependencies__choice = F('dependencies__question__answer__choice'))

SELECT •••
FROM "bny_choice"
INNER JOIN "bny_choice_dependencies" ON ("bny_choice"."id" = "bny_choice_dependencies"."choice_id")
INNER JOIN "bny_dependency" ON ("bny_choice_dependencies"."dependency_id" = "bny_dependency"."id")
INNER JOIN "bny_question" ON ("bny_dependency"."question_id" = "bny_question"."id")
INNER JOIN "bny_answer" ON ("bny_question"."id" = "bny_answer"."question_id")
WHERE NOT ("bny_choice"."id" IN
             (SELECT •••
              FROM "bny_choice" U0
              INNER JOIN "bny_choice_dependencies" U1 ON (U0."id" = U1."choice_id")
              INNER JOIN "bny_dependency" U2 ON (U1."dependency_id" = U2."id")
              INNER JOIN "bny_question" U3 ON (U2."question_id" = U3."id")
              INNER JOIN "bny_answer" U4 ON (U3."id" = U4."question_id")
              WHERE U2."choice_id" = U4."choice_id"))
ORDER BY "bny_choice"."order" ASC

但我从不放弃......

感谢asmoore82,我在https://code.djangoproject.com/ticket/5763

上阅读
  

同样,你几乎可以用一个问号(__ lt)|来近似__ne   Q(__ gt)装置。

最终的Django结果......

x = Choice.objects.filter(
        Q(dependencies__question__answer__user = self.user),
        Q(dependencies__choice__lt = F('dependencies__question__answer__choice')) | Q(dependencies__choice__gt = F('dependencies__question__answer__choice')) # Django workaround for not equals https://code.djangoproject.com/ticket/5763
    ).values('id')        
    self.choices = Choice.objects.filter(question = self.question).exclude(id__in=x)

这给了我所需的确切结果。

Django吐出以下SQL

SELECT •••
FROM "bny_choice"
WHERE ("bny_choice"."question_id" = 6
       AND NOT ("bny_choice"."id" IN
                  (SELECT •••
                   FROM "bny_choice" U0
                   INNER JOIN "bny_choice_dependencies" U1 ON (U0."id" = U1."choice_id")
                   INNER JOIN "bny_dependency" U2 ON (U1."dependency_id" = U2."id")
                   INNER JOIN "bny_question" U3 ON (U2."question_id" = U3."id")
                   INNER JOIN "bny_answer" U4 ON (U3."id" = U4."question_id")
                   WHERE (U4."user_id" = 1
                          AND (U2."choice_id" < U4."choice_id"
                               OR U2."choice_id" > U4."choice_id")))))
ORDER BY "bny_choice"."order" ASC

查询只需要2.89ms和1个查询,而在我进行应用程序之前,检查每个选项是否满足依赖关系,并且在123ms内进行了163次查询