【问题标题】:Survey quiz questionnaire db design SQL show questi0n or choice based on previous answers django database调查 quiz 问卷 db design SQL show questi0n or selection based on previous answers django 数据库
【发布时间】:2014-07-03 05:50:09
【问题描述】:

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

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

我觉得我快到了,但我无法弄清楚如何为我想要的结果构建正确的查询,或者我是否以最简单的方式解决问题。

我的桌子是:

  • 用户
  • 问题
  • 选择
  • 回答
  • 依赖关系
  • Choice_Dependencies(多对多通过选择和依赖)

依赖项引用一个问题和一个选择。每个依赖项都有一个描述字段,例如“是男性”,因此如果问题 1 是“你是男性还是女性?”问题 1 有 2 个选项,“男”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
)

姜戈

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)

【问题讨论】:

    标签: sql database django survey


    【解决方案1】:

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

    通过使用以下使用 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 通过在过滤器中使用Choice.objects.exclude()__in 运算符来支持NOT 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 上看到的内容

    类似地,你几乎可以用一个 hokey Q(__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.89 毫秒和 1 次查询,而在我让应用程序检查每个选择是否满足依赖关系之前,它在 123 毫秒内需要 163 次查询

    【讨论】:

    • 我应该为我的 PHP 项目应用相同的功能。你有什么建议:在 php 端还是在 mysql 端实现?
    猜你喜欢
    • 1970-01-01
    • 2016-07-26
    • 1970-01-01
    • 2020-06-12
    • 2019-08-25
    • 2012-04-25
    • 2022-12-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多