0

How do merge these three queries into one?

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"

I want to use this as a report and I do not want to call each one of the queries any time I want to get the data.

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
Solomon Ayoola
  • 2,639
  • 3
  • 18
  • 21

3 Answers3

3

Alternative 1, simply a huge 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"
Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
jarlh
  • 42,561
  • 8
  • 45
  • 63
  • 1
    @jarih The Union will defeat the purpose. I want to be able to seperate each of the data from the respective tables. – Solomon Ayoola Oct 29 '15 at 15:11
  • @AyoolaSolomon, what about adding different literal values to each select? select 'q1', ... UNION ALL select 'q2', ... UNION ALL select 'q3'? Then you'll know from which select every row origins. – jarlh Oct 29 '15 at 15:14
3

Basically, use UNION ALL like @jarlh already provided.
Details in the manual in the chapter "Combining Queries".

But there is a lot more. My educated guess, you really want this:

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);

Major points

  • First I trimmed the noise from your syntax (probably produced by your ORM) to make it human-readable: remove redundant double-quotes, add table aliases, trim noise words ...

  • Your use of LEFT [OUTER] JOIN was broken, since you filter on columns of the left table, which counteracts the LEFT JOIN. Replace with [INNER] JOIN.

  • Use sargable expressions in the WHERE clause or your query can't use plain indexes and will be very slow for big tables. Related:

  • Provide your parameters once in a CTE (WITH clause) - which is not needed in a prepared statement where you pass uid, ts_low and ts_hi as parameters instead.

  • I removed "TrainerScores"."fellow_uid" from the output in your first query to simplify the query. That's just your input parameter anyway.

  • You can aggregate your respective main tables before you join to "Skills" once.

  • I added a column source to signify the source of each row.

Aside: It seems like you want to match the whole of October 2015, but then you exclude Oct. 31. Is that on purpose?

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
  • Thanks @Erwin you saved my day – Solomon Ayoola Oct 29 '15 at 16:10
  • One last thing @Erwin Brandstetter, i want the timestamp to be inclusive of the ts_hi – Solomon Ayoola Oct 29 '15 at 21:54
  • @AyoolaSolomon: Are you sure of this? `created_at < timestamp '2015-10-31 00:00'` does the same (smarter) as `date(created_at) <= date '2015-10-30'`. (I had missed Oct. 31 in my initial answer and fixed that now!) When dealing with timestamps you typically want to *include* the lower and *exclude* the upper bound. Dates are different because there are no fractional digits. – Erwin Brandstetter Oct 29 '15 at 22:27
  • I am sure about that because i want to be able to include the date i am creating the data when am fetching it from the database... Thanks – Solomon Ayoola Oct 29 '15 at 22:31
  • 1
    In response to your answer ... I am just using that as an example.. Hope you understand – Solomon Ayoola Oct 29 '15 at 22:35
  • @AyoolaSolomon: To *include* a certain date, use `created_at < (date '2015-10-30' + 1)` - which is effectively the same as `created_at < timestamp '2015-10-31 00:00'`. – Erwin Brandstetter Oct 29 '15 at 22:57
  • Thanks @Erwin Bandstetter – Solomon Ayoola Oct 30 '15 at 10:09
1

I propose a solution that does exactly what you want, but it works. Using raw query you can run and get the results of multiple queries, something like this:

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
      });

The results of success function of sequelize.query can be storage in the json variable too.

BrTkCa
  • 4,703
  • 3
  • 24
  • 45