SQL 查询 - 双重否定

Posted

技术标签:

【中文标题】SQL 查询 - 双重否定【英文标题】:SQL query - double negation 【发布时间】:2015-03-20 00:38:29 【问题描述】:

我有下表,里面有一些学术小测验的答案记录:

回复

-SEMESTRE_ID(测验学期的外键)

-PERGUNTA_ID(测验问题的外键)

-DISCIPLINA_ID(被评估主题的外键)

-DOCENTE_ID(被评价教师的外键)

-QUIZ_ID(回答测验的每个人都有一个唯一的 quiz_id)

-TIPOAULA(类的类型,与此上下文无关)

-DATA(日期,与上下文无关)

-ID(答案的唯一 ID)

-RESPOSTA(答案的值,从 1 到 5)

因此,一个给定学期的测验有多个不同科目的问题,可以由人们评估(不一定需要对所有问题进行投票)。

我要实现的查询是:

id 21 学期测验每题至少5分的科目的id和缩写是什么?

我已经用计数策略解决了:

SELECT DISCIPLINA.DISCIPLINA_ID, SIGLA
FROM RESPOSTAS, DISCIPLINA
WHERE RESPOSTAS.DISCIPLINA_ID = DISCIPLINA.DISCIPLINA_ID AND SEMESTRE_ID = 21 AND RESPOSTA = 5
GROUP BY DISCIPLINA.DISCIPLINA_ID, SIGLA
HAVING COUNT(DISTINCT PERGUNTA_ID) = (SELECT COUNT(DISTINCT PERGUNTA_ID) FROM RESPOSTAS WHERE SEMESTRE_ID = 21);

但这是一个作业,我们也被要求使用双重否定策略来解决它,我似乎无法理解如何达到预期的结果。

【问题讨论】:

可能重复? ***.com/questions/15681935/double-negation-sql-query @JoelGregory 真实的故事,看到了,但不幸的是没有解决方案(至少使用双重否定)。看来任务还是一样的,我不是第一个遇到困难的人:)谢谢。 你能定义一下“双重否定策略”吗?这不是我以前听过的术语。你可以把semestre_id = 21 变成not( semestre_id != 21)——这是双重否定,但这可能不是你被要求的。 @JustinCave 我真的觉得很奇怪,Google 对此也不太了解 xD 它在查询中使用了两次NOT IN 【参考方案1】:

这看起来像一个关系除法查询(如果我没记错的话),您正在寻找的双重否定示例是下面的查询。基本上它可以被解读为向我展示所有不存在问题的科目,而不是在分数为 5 或以上的问题集中

SELECT DISCIPLINA_ID, SIGLA 
FROM XDISCIPLINA XD
WHERE NOT EXISTS ( -- there can not be any questions ...
    SELECT 1 FROM XRESPOSTAS XR
    WHERE SEMESTRE_ID = 21 
    AND NOT EXISTS ( -- that are not in the set of 5+
       SELECT 1 FROM XRESPOSTAS 
       WHERE PERGUNTA_ID = XR.PERGUNTA_ID 
         AND SEMESTRE_ID = 21
         AND RESPOSTA >= 5
         AND XD.DISCIPLINA_ID = DISCIPLINA_ID
    )
)

关于这方面的一篇好文章是:Divided We Stand: The SQL of Relational Division,作者是 Joe Celko。非常值得一读。

编辑:

我认为由于缺少连接,上面的查询执行得非常糟糕。这个版本的性能应该会好很多:

SELECT DISCIPLINA_ID, SIGLA 
FROM XDISCIPLINA XD
WHERE SEMESTRE_ID = 21 
  AND NOT EXISTS (
    SELECT 1 FROM XRESPOSTAS XR
    WHERE SEMESTRE_ID = 21 AND
    XD.DISCIPLINA_ID = DISCIPLINA_ID AND
      NOT EXISTS (
       SELECT 1 FROM XRESPOSTAS 
       WHERE PERGUNTA_ID = XR.PERGUNTA_ID 
         AND SEMESTRE_ID = 21
         AND RESPOSTA > 4
         AND XD.DISCIPLINA_ID = DISCIPLINA_ID
    )
)

实现相同结果的另一种方法似乎表现得更好:

SELECT DISCIPLINA_ID, SIGLA 
FROM XDISCIPLINA XD
WHERE NOT EXISTS (
  SELECT PERGUNTA_ID FROM XRESPOSTAS XR 
    WHERE SEMESTRE_ID = 21 AND XD.DISCIPLINA_ID = DISCIPLINA_ID 
  INTERSECT
  SELECT PERGUNTA_ID FROM XRESPOSTAS XR 
    WHERE SEMESTRE_ID = 21 AND XD.DISCIPLINA_ID = DISCIPLINA_ID AND RESPOSTA < 5
    )

【讨论】:

由于某种原因,这个查询似乎是无限的什么的,它并没有停止执行。我明天再仔细看看。 @HugoSousa 我没有测试它,因为我没有时间设置合适的测试数据,所以它可能有一些错误 - 当我做这种类型的时候我会感到困惑查询,因为它非常不直观。如果您在问题中添加一些测试数据,我可以对其进行测试。 它实际上似乎在工作 (sqlfiddle.com/#!4/5fad4/2),但在我的环境中它并没有停止执行。这很奇怪...... 知道它可能是什么吗?我的猜测是这可能是一个巨大的性能问题,因为数据比示例中的要多得多。我让查询运行了一个多小时没有结果。 这个赋值的目的实际上是分析执行计划,但我什至不能确定这个查询会提供正确的结果。这在某处似乎是错误的!或者也许这正是预期的行为,我觉得这很奇怪。无论如何,请接受我的 +1。我有带索引和不带索引的相同表,并尝试了两者(RESPOSTASEMESTRE_IDDISCIPLINA_IDPERGUNTA_ID 列上的索引)。

以上是关于SQL 查询 - 双重否定的主要内容,如果未能解决你的问题,请参考以下文章

EXISTS/NOT EXISTS实现全称量词的查询(双重否定)

如何双重加入sql查询?

SQL查询从双重返回N行

sql解决问题思路

具有双重嵌套级别的 SQL 查询

SQL:双重 if 语句/数据库查询问题