Counting the number of raws with Join and GroupBy - mysql , knex - mysql

I have this table:
table foo
id | bar_id | user_id
-----------------
1 | 3 | 1
2 | 5 | 1
3 | 6 | 2
4 | 5 | 1
5 | 3 | 2
table bar
id | title | description
------------------------
3 | hey | desc1
5 | ola | desc 2
6 | vassup | desc 3
Then I have this query
const basequery = knex
.select('bar.*')
.from('bar')
.join('foo', 'foo.bar_id', 'bar.id')
.groupBy('foo.bar_id')
.whereIn('foo.user_id', 1);
This will return:
id | title | description
-----------------------
3 | hey | desc 1
5 | ola | desc 2
which is correct.
Basically I'm reusing that basequery multiple times on the file.
Then I want to count the number of rows returned by this.
basequery.clone().countDistinct(`bar.id as total`)
What I'm expecting is:
id | title | description | total
--------------------------------
3 | hey | desc 1 | 2
because there are 2 columns on the foo table with id = 1 and grouped by bar_id. I want to count the number of column returned by the first query as "total".
However, it returns this:
id | title | description | total
--------------------------------
3 | hey | desc 1 | 1
5 | ola | desc 2 | 2
Any help would greatly appreciated

I don't know Knex, but if Knex is anything like Laravel, then you would almost certainly need to use some raw query functionality to get the output you want here. The total you want in the output is just the total number of records in thr query itself. Unfortunately, there is no trick we can use in MySQL to get that count without running the actual query. In the raw query below, I use a non correlated subquery to bring in the total.
SELECT
b.*,
(SELECT COUNT(*) FROM (SELECT b.id FROM bar b INNER JOIN foo f ON f.bar_id = b.id
WHERE f.user_id IN (1) GROUP BY f.bar_id) t) total
FROM bar b
INNER JOIN foo f
ON f.bar_id = b.id
WHERE f.user_id IN (1)
GROUP BY f.bar_id
To be clear, the subquery in the above SELECT list is this:
SELECT COUNT(*)
FROM
(
SELECT b.id
FROM bar b
INNER JOIN foo f
ON f.bar_id = b.id
WHERE f.user_id IN (1)
GROUP BY f.bar_id
) t

Related

Querying MySQL tables for a item a user hasnt 'scored' yet

Tables
__________________ ________________________________
|______name________| |____________scores______________|
|___id___|__name___| |_id_|_user-id_|_name-id_|_score_|
| 1 | bob | | 1 | 3 | 1 | 5 |
| 2 | susan | | 2 | 1 | 3 | 4 |
| 3 | geoff | | 3 | 3 | 2 | 3 |
| 4 | larry | | 4 | 2 | 4 | 5 |
| 5 | peter | | 5 | 1 | 1 | 0 |
-------------------- ----------------------------------
Im looking to write a query that returns a RANDOM name from the 'name' table, that the user hasnt scored so far.
So given user '1' for example, it could return 'susan, larry or peter' as user '1' hasnt given them a score yet.
SELECT *
FROM names
LEFT JOIN
votes
ON names.id = votes.name_id
WHERE votes.user_id = 1
AND (votes.score IS NULL);
So far I have this, but it doesnt seem to be working as I would like
(atm it doesnt return a random, but all, but this is wrong)
Any help would be appreciated.
If you are filtering on some field of outer joined table type of join is automatically changed to inner. In your case it's condition
votes.user_id = 1
So you need to move that condition from WHERE to ON
SELECT *
FROM names
LEFT JOIN
votes
ON names.id = votes.name_id and votes.user_id = 1
WHERE (votes.score IS NULL);
Consider moving the condition from WHERE to JOIN ON clause since you are performing an OUTER JOIN else the effect would be same as INNER JOIN
LEFT JOIN votes
ON names.id = votes.name_id
AND votes.user_id = 1
WHERE votes.score IS NULL
ORDER BY RAND();
You could apply :
SELECT name FROM name join scores on name.id=scores.user_id WHERE scores.score=0
You can perform this as a sub-query
SELECT *
FROM names
WHERE id NOT IN (SELECT name_id FROM votes WHERE user_id=1)
ORDER BY RAND()
LIMIT 1

How to ORDER BY CALCULATED SUM with Another Table in MySQL

For example, I have 2 table 'meta' and 'log'
in meta table:
| type | score |
|------|-------|
| a | 1 |
| b | 2 |
| c | 3 |
in log table:
| log_id | log_type | object_id |
|--------|----------|-----------|
| 1 | a | 13 |
| 2 | b | 13 |
| 3 | a | 14 |
| 4 | c | 14 |
| 5 | b | 15 |
| 6 | c | 15 |
so we know:
object 13 got score: a+b = 3
object 14 got score: a+c = 4
object 15 got score: b+c = 5
I want to query log table group by object id and order by sum of object score, is it possible?
select log.object_id, sum(meta.score)
from log
left join meta on meta.type = log.log_type
group by log.object_id
order by sum(meta.score) desc
This will produced the desired output
SELECT object_id, sum(score) from log
INNER JOIN meta on meta.type = log.log_type group by object_id ORDER BY sum(score);
But have you got the correct table design? You need to join on the meta.type and log.log_type column but this implies that if the log_type is 'a' the value of 3 is common for all object_ids is this really what you want?
Try this
Select object_id,sum(score) as c FROM `log` as a INNER JOIN `meta` as b on a.log_type=b.type group by object_id order by c desc
SELECT log.object_id, sum(score) total_score
FROM meta
INNER JOIN log on meta.type = log.log_type
GROUP BY log.object_id

MySQL JOIN with LIMIT query results

I have 2 tables, products and origins
Products:
p_id | name | origin_id
------------------------
1 | P1 | 1
2 | P2 | 2
3 | P3 | 1
Origins:
o_id | name
-------------
1 | O1
2 | O2
I am using the following query :
SELECT * FROM `products` LEFT OUTER JOIN `origins`
ON ( `products`.`origin_id` = `origins`.`o_id` ) LIMIT 2
I am getting the below results
p_id | name | origin_id | o_id | name
-----------------------------------------
1 | P1 | 1 | 1 | O1
3 | P3 | 1 | 1 | O1
I was wondering how the LEFT OUTER JOIN affects the result where I am getting the first and the third row rather than the first and the second row?
When you are not using ORDER BY Clause, there is no guarantee of a specific order for your SELECT query.
So we should use ORDER BY when we need any specific order.
See this: MySQL Ref: What is The Default Sort Order of SELECT with no ORDER BY Clause
You don't control the inherent ordering of rows in a table. It behaves like a set. If you want to order it, use order by clause.
SELECT * FROM `products` p LEFT OUTER JOIN `origins` o
ON ( p.`origin_id` = o.`o_id` ) ORDER BY p.`name` LIMIT 2
Output :
p_id | name | origin_id | o_id | name
-----------------------------------------
1 | P1 | 1 | 1 | O1
2 | P2 | 2 | 2 | O2

how to select all of duplicate record in mysql

My records is:
name | id | AVG(point) as point
a | 1 | 6
b | 2 | 6
c | 3 | 5
d | 4 | 5
e | 5 | 4
f | 6 | 3
g | 7 | 2
How to select record below:
1.I want to select top 3 record, result follow:
name | id | AVG(point) as point
a | 1 | 6
b | 2 | 6
c | 3 | 5
d | 4 | 5
e | 5 | 4
2.I want to select record not into top 3, result follow:
name | id | AVG(point) as point
f | 6 | 3
g | 7 | 2
How can I do?
There are several ways to do these. Here's a couple using in and not in.
For the top 3, you can use in:
select *
from yourtable
where point in (select distinct point
from yourtable
order by 1 desc
limit 3)
For the rest, use not in instead:
select *
from yourtable
where point not in (select distinct point
from yourtable
order by 1 desc
limit 3)
Other methods include exists with not exists and distinct with joins.
select *
from yourtable as t1
inner join (select distinct point
from yourtable
order by 1 desc
limit 3) as t2
on t1.point = t2.point
For the second part of your question, do not use
desc

Sql SUM over products of grouped elements

I have the following data structure:
Table 1(groups):
ID | Group
=============
1 | Sample
2 | Data
Table 2(items):
ID | GroupID | Cost | Amount
==============================
1 | 1 | 1 | 12
2 | 1 | 7 | 15
3 | 1 | 3 | 8
4 | 2 | 2 | 12
And would like the following (query) results:
groups.ID | groups.Name | total
1 | Sample | 141
2 | Data | 24
total is the sum over the products of cost and amount of all items in the group i.e. for group 1: 1*12+7*15+3*8=141
Im guessing I have to something with
SELECT g.ID, g.Group, SUM(Products)
FROM groups AS g, items AS i
WHERE g.ID=i.GroupID
GROUP BY i.GroupID
But don't know what exactly.
Doing iit in clientsoftware with loops is no problem, but I am curious (and certain) that this can be done in (my)Sql
SELECT g.ID as ID, g.Group as Name, SUM(i.Cost * i.Amount) as total
FROM groups g
INNER JOIN items i ON i.GroupID = g.ID
GROUP BY g.Group, g.ID
Having a field named "Group" is quite a bad idea in SQL (reserved keyword)