【问题标题】:How to return rows from three tables at once?如何一次从三个表中返回行?
【发布时间】:2017-12-15 22:51:24
【问题描述】:

如何将这三个查询合并为一个?

1.

SELECT "Skills"."name", "Skills"."id", "TrainerScores"."fellow_uid", MIN("TrainerScores"."score") AS "score"
FROM "TrainerScores"
INNER JOIN "Skills" ON "TrainerScores"."skill_id" = "Skills"."id"
WHERE "TrainerScores"."fellow_uid" = 'google:105697533513134511631'
AND DATE("TrainerScores"."created_at") BETWEEN '2015-10-01' AND '2015-10-30'
GROUP BY "Skills"."name", "Skills"."id", "TrainerScores"."fellow_uid"

2.

Select "Skills"."name", "Skills"."id", MIN("PeerScores"."score") AS "score"
FROM "PeerScores"
LEFT OUTER JOIN "Skills" ON "PeerScores"."skill_id" = "Skills"."id"
WHERE "PeerScores"."evaluatee_uid" = 'google:105697533513134511631'
AND DATE("PeerScores"."created_at") BETWEEN '2015-10-01' AND '2015-10-30'
GROUP BY "Skills"."name", "Skills"."id"

3.

Select "Skills"."name", "Skills"."id", MIN("SelfScores"."score") AS "score"
FROM "SelfScores"
LEFT OUTER JOIN "Skills" ON "SelfScores"."skill_id" = "Skills"."id"
WHERE "SelfScores"."fellow_uid" = 'google:105697533513134511631'
AND DATE("SelfScores"."created_at") BETWEEN '2015-10-01' AND '2015-10-30'
GROUP BY "Skills"."name", "Skills"."id"

我想将其用作报告,并且我不想在任何时候都调用每个查询来获取数据。

【问题讨论】:

  • @AyoolaSolmon 我不确定是否可以加入这三个查询,但是使用 sequelize 可以使用回调在同一个函数中获得所有三个查询的结果。
  • @LucasCosta 你觉得我可以用续集来解决这个问题
  • 为什么不想只使用这三个查询呢?您希望通过组合它们来解决什么问题?
  • @CraigRinger 我想将每个表的最低分数表示为水平条形图,我想使用一次而不是三次获取数据
  • 简单地联合所有?在 LEFT JOIN 之前的三个分数表之间。或者一个大(双)UNION ALL。

标签: sql node.js postgresql sequelize.js


【解决方案1】:

基本上,使用UNION ALL 就像@jarlh already provided
详情见手册"Combining Queries"一章。

但还有更多。我有根据的猜测,你真的想要这个:

WITH vals AS (SELECT timestamp '2015-10-01 00:00' AS ts_low  -- incl. lower bound
                   , timestamp '2015-10-31 00:00' AS ts_hi   -- excl. upper bound
                   , text 'google:105697533513134511631' AS uid)
SELECT s.name, sub.*
FROM  (
   SELECT skill_id AS id, min(score) AS score, 'T' AS source
   FROM   "TrainerScores", vals v
   WHERE  fellow_uid =  v.uid
   AND    created_at >= v.ts_low
   AND    created_at <  v.ts_hi
   GROUP  BY 1

   UNION ALL
   SELECT skill_id, min(score), 'P'
   FROM   "PeerScores", vals v
   WHERE  evaluatee_uid = v.uid
   AND    created_at >= v.ts_low
   AND    created_at <  v.ts_hi
   GROUP  BY 1

   UNION ALL
   SELECT skill_id, min(score), 'S'
   FROM   "SelfScores", vals v
   WHERE  fellow_uid =  v.uid
   AND    created_at >= v.ts_low
   AND    created_at <  v.ts_hi
   GROUP  BY 1
   ) sub
JOIN   "Skills" s USING (id);

要点

  • 首先,我从您的语法(可能由您的 ORM 产生)中修剪了杂音,使其易于阅读:删除多余的双引号、添加表别名、修剪杂音……

    李>
  • 您对LEFT [OUTER] JOIN 的使用被破坏了,因为您过滤了左表的列,这抵消了LEFT JOIN。替换为[INNER] JOIN

  • WHERE 子句中使用sargable 表达式,否则您的查询不能使用普通索引,并且对于大表来说会很慢。相关:

  • 在 CTE(WITH 子句)中提供参数 一次 - 在传递 uid、@ 的准备好的语句中不需要987654333@ 和 ts_hi 改为参数。

  • 我从您的第一个查询的输出中删除了 "TrainerScores"."fellow_uid" 以简化查询。无论如何,这只是您的输入参数。

  • 您可以在加入"Skills"一次之前汇总各自的主表。

  • 我添加了一列source 来表示每一行的来源。

顺便说一句:您似乎想匹配整个 2015 年 10 月,但随后排除了 10 月 31 日。这是故意的吗?

【讨论】:

  • 感谢@Erwin 你拯救了我的一天
  • 最后一件事@Erwin Brandstetter,我希望时间戳包含 ts_hi
  • @AyoolaSolomon:你确定吗? created_at &lt; timestamp '2015-10-31 00:00'date(created_at) &lt;= date '2015-10-30' 做同样的事情(更聪明)。 (我在最初的回答中错过了 10 月 31 日,现在解决了这个问题!)在处理时间戳时,您通常希望包括下限和排除上限。日期不同,因为没有小数位。
  • 我确信这一点,因为我希望能够在从数据库中获取数据时包含创建数据的日期...谢谢
  • 针对您的回答...我只是以此为例..希望您理解
【解决方案2】:

替代方案 1,只是一个巨大的UNION ALL

SELECT "Skills"."name", "Skills"."id", "TrainerScores"."fellow_uid", MIN("TrainerScores"."score") AS "score"
FROM "TrainerScores"
INNER JOIN "Skills" ON "TrainerScores"."skill_id" = "Skills"."id"
WHERE "TrainerScores"."fellow_uid" = 'google:105697533513134511631'
AND DATE("TrainerScores"."created_at") BETWEEN '2015-10-01' AND '2015-10-30'
GROUP BY "Skills"."name", "Skills"."id", "TrainerScores"."fellow_uid"

UNION ALL

Select "Skills"."name", "Skills"."id", NULL, MIN("PeerScores"."score") AS "score"
FROM "PeerScores"
LEFT OUTER JOIN "Skills" ON "PeerScores"."skill_id" = "Skills"."id"
WHERE "PeerScores"."evaluatee_uid" = 'google:105697533513134511631'
AND DATE("PeerScores"."created_at") BETWEEN '2015-10-01' AND '2015-10-30'
GROUP BY "Skills"."name", "Skills"."id"

UNION ALL

Select "Skills"."name", "Skills"."id", NULL, MIN("SelfScores"."score") AS "score"
FROM "SelfScores"
LEFT OUTER JOIN "Skills" ON "SelfScores"."skill_id" = "Skills"."id"
WHERE "SelfScores"."fellow_uid" = 'google:105697533513134511631'
AND DATE("SelfScores"."created_at") BETWEEN '2015-10-01' AND '2015-10-30'
GROUP BY "Skills"."name", "Skills"."id"

【讨论】:

  • @jarih 联盟将失败。我希望能够从各个表中分离出每个数据。
  • @AyoolaSolomon,为每个选择添加不同的文字值怎么样?选择'q1',... UNION ALL 选择'q2',... UNION ALL 选择'q3'?然后你就会知道每一行是从哪个选择开始的。
【解决方案3】:

我提出了一个完全符合您要求的解决方案,但它确实有效。使用raw 查询,您可以运行并获取多个查询的结果,如下所示:

var sequelize = require('./libs/pg_db_connect');

var query = "SELECT Skills.name, Skills.id, TrainerScores.fellow_uid, MIN(TrainerScores.score) AS score
FROM TrainerScores
INNER JOIN Skills ON TrainerScores.skill_id = Skills.id
WHERE TrainerScores.fellow_uid = 'google:105697533513134511631' AND DATE(TrainerScores.created_at) BETWEEN '2015-10-01' AND '2015-10-30'
GROUP BY Skills.name, Skills.id, TrainerScores.fellow_uid";


sequelize.query(query, {
   type: sequelize.QueryTypes.SELECT
}).success(function (query1) {
   done = _.after(query1.length, function () {
      callback(query1)
   })

   query = "Select Skills.name, Skills.id, MIN(PeerScores.score) AS score
        FROM PeerScores
        LEFT OUTER JOIN Skills ON PeerScores.skill_id = Skills.id
        WHERE PeerScores.evaluatee_uid = 'google:105697533513134511631' AND DATE(PeerScores.created_at) BETWEEN '2015-10-01' AND '2015-10-30'
        GROUP BY Skills.name, Skills.id";

   sequelize.query(query, {
      type: sequelize.QueryTypes.SELECT
   }).success(function (query2) {

      query = "Select Skills.name, Skills.id, MIN(SelfScores.score) AS score
        FROM SelfScores
        LEFT OUTER JOIN Skills ON SelfScores.skill_id = Skills.id
        WHERE SelfScores.fellow_uid = 'google:105697533513134511631' AND DATE(SelfScores.created_at) BETWEEN '2015-10-01' AND '2015-10-30'
        GROUP BY Skills.name, Skills.id";

      sequelize.query(query, {
         type: sequelize.QueryTypes.SELECT
      }).success(function (query3) {
         console.log(query1); // show the returns of query 1
         console.log(query2); // show the returns of query 2
         console.log(query3); // show the returns of query 3
      });

sequelize.querysuccess函数的结果也可以存储在json变量中。

【讨论】:

  • 我可以用这个...但是有没有办法让我的查询更清晰?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-02-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多