SQL: Get count of rows w.r.t rows of another table - sql

I want to get the count of rows from a table belonging to a category (which are defined in another table). Kind of like the following.
-----------------------------------------
id | name | category
| |
1 | Name 1 | toddler
2 | Name 2 | toddler
3 | Name 3 | newborn
4 | Name 4 | toddler
5 | Name 5 | adult
And I have another table where all the categories are defined
-----------------------------------------
id | category
|
1 | toddler
2 | newborn
3 | adult
4 | elderly
Now I need an SQL Query on the first table which can give me a return result something like this
-----------------------------------------
category | count
|
toddler | 3
newborn | 1
adult | 1
elderly | 0
I need to count each name from Table 1 with a particular category from Table 2 and return the result.
This seems to have a fairly simple solution but I can't get my mind to work on it. Please help!

This is a simple query with LEFT JOIN and COUNT.
select c.category, COUNT(n.category) as count
from Table2 c
left join Table1 n on c.category = n.category
group by c.category
SQL Fiddle demo

Simple. Use a left join on your Table2 with Table1, then use the count function on category and do group by on category.
select b.category, count(a.category)
from Table1 as a
left join Table2 as b
on a.id = b.id
group by b.category

Related

Finding average of numbers against the ID in SQL Server

Suppose there are two tables, Customer and Limit.
Customer has the following columns:
CustomerId (PK)
Name
And Limit has these columns:
LimitId (PK)
Limitvalue
CustomerId (FK)
This is my sample data:
Customer
CustomerId Name
----------------------
1 xyz
2 abc
3 uio
Limit
LimitValue CustomerId
-------------------------
35303000 1
0 1
3 1
0 2
225140000 2
3 2
Now when I run this query
select a.Limitvalue, b.CustomerId
from limit a
left join Customer b on a.CustomerId = b.CustomerId
It will return data like this:
Here first column is limit value and second is customerid. As you can see there are multiple limit values for one customerid. I want to write a query which shows me the avg of all the limit values against their particular Id.
I tried adding the avg function. The left join here should do the work but it is not working. Can anybody help me by producing some sample data similar to this and writing its query so I can understand the concept ? I would be really thankful.
group by b.CustomerId
This, no?
you need to be specific on how the database should work, so you told it to
make the average "by grouping on" if you don't the database try to make the
AVG on the full table
SQL Fiddle
MS SQL Server 2017 Schema Setup:
Results:
| CustomerId | Name |
|------------|------|
| 1 | bob |
| 2 | jean |
Results:
| LimitId | Limitvalue | CustomerId |
|---------|------------|------------|
| 1 | 0 | 1 |
| 2 | 10 | 1 |
| 3 | 100 | 2 |
Query 3:
select
avg(a.Limitvalue),
b.CustomerId
from limit a
left join Customer b
on a.CustomerId= b.CustomerId
group by b.CustomerId
Results:
| | CustomerId |
|-----|------------|
| 5 | 1 |
| 100 | 2 |

How to count records using an l-tree

I have two tables, tickets and categories. The categories table has 3 columns of interest: id, name and path. The data looks like this:
id | Name | Path
------------------
1 | ABC | 1
2 | DEF | 1.2
3 | GHI | 1.2.3
4 | JKL | 4
5 | MNO | 4.5
6 | PQR | 4.5.6
9 | STU | 4.5.9
Note that the path column is an l-tree. What this is meant to represent is that the category with id=2 is a subcategory of id=1 and that id=3 is a subcategory of id=2.
In my tickets table, there's a column called category_id which refers to the id column in my categories table. Each ticket can have up to one category assigned to it (category_id may be null).
I'm trying to count all the tickets for each category.
Suppose my tickets table looks like this:
ticket_id | ticket_title | category_id
1 | A | 1
2 | B | 2
3 | C | 3
4 | D | 5
5 | F | 5
6 | G | 6
7 | H | 9
I would like to output:
category_id | count
1 | 3
2 | 2
3 | 1
4 | 4
5 | 4
6 | 1
9 | 1
I've found that I can get all of the tickets which belong to a given category with the following query: select * from tickets where category_id in (select id from categories where path ~ '*.1.*'); (although now that I'm writing this question I'm not convinced this is correct).
I've also attempted to perform the ticket-count-by-category problem and I came up with:
SELECT
categories.id as cid,
COUNT(*) as tickets_count
FROM tickets
LEFT JOIN categories ON tickets.category_id = categories.id
GROUP BY cid;
which outputs the following:
c_id | count
1 | 1
2 | 1
3 | 1
5 | 2
6 | 1
9 | 1
I'm not very good at SQL. Is it possible to achieve what I want?
Try this:
WITH tickets_per_path AS (
SELECT
c.path AS path,
count(*) AS count
FROM tickets t INNER JOIN categories c ON (t.category_id = c.id)
GROUP BY c.path)
SELECT
c.id,
sum(tickets_per_path.count) AS count
FROM categories c LEFT JOIN tickets_per_path ON (c.path #> tickets_per_path.path)
GROUP BY c.id
ORDER BY c.id;
Which yields the following result:
id| count
1 | 3
2 | 2
3 | 1
4 | 4
5 | 4
6 | 1
9 | 1
It roughly works like this:
the WITH clause computes the number of tickets per path (without
including the count of tickets of descendent paths).
the second select clause joins the categories table with the precomputed tickets_per_path view, but instead of an equi-join on path, it
joins by testing whether a record in the left table (categories) is
an ancestor of the right side table (using #> operator). Then it
groups by category id and sums up the ticket counts by category
including the descendant counts.
You are close, but you need a more general join:
SELECT c.id as cid, COUNT(*) as tickets_count
FROM categories c LEFT JOIN
tickets t
ON t.category_id || '.' LIKE c.id || '.%'
GROUP BY c.id;
The '.' in the comparison is just so 1.100 doesn't match 1.1.

Using count my tuples are coming out as 1's when a 0 should be present?

I am trying to count how many times each ID appears in a specific table as follows.
Members
ID | NAME
1 | Bob
2 | John
3 | Sally
4 | Hannah
Department
ID | DEPT
1 | Math
1 | English
3 | Math
4 | Math
4 | English
4 | Science
I would like to display a table as follows:
ID | Name | Count(Dept)
1 | Bob | 2
2 | John | 0
3 | Sally | 1
4 | Hannah | 3
I have made several attempts at this my most recent one being:
select owner
from table
where (select count(distinct letter) from table group by owner) in
(select max(count(distinct letter)) from table group by owner);
I just cannot seem how to get the 0 results to show properly. Honestly, i'm kinda confused as to how to use on ... when you don't want to basically preform a natural join.
edit: I have edited the question to properly show what I am trying to ask. Sorry for any confusion.
When joining tables it is very important to reference WHICH TABLE each column comes from. To help abbreviate the overall SQL a "table alias" may be defined and used instead of the full table name.
select m.id, m.name, count(d.id) as count_d
from members m
left outer join department d on m.id = d.id
group by m.id, m.name
;
In the above I have used a LEFT OUTER JOIN. Once you have that outer join defined there may be some rows where no row from department is matched to a row in membesr and you therefore would get NULLs in all columns related to departmenton that row.
The COUNT() function increments by 1 for every NON-NULL value it encounters, so what you include inside the parentheses is important. COUNT(*) will increment for every row no matter what. COUNT(d.id) may give zeros because some rows might be NULL in that column.
SEE this SQL Fiddle
Results:
| ID | NAME | COUNT_D |
|----|--------|---------|
| 1 | Bob | 2 |
| 2 | John | 0 |
| 3 | Sally | 1 |
| 4 | Hannah | 3 |

How to get number of students per course in sql?

So I have these 3 tables:
t_student which looks like this:
STUDENT_ID| FIRST_NAME |LAST_NAME
-----------------------------------
1 | Ivan | Petrov
2 | Ivan | Ivanov
3 | Georgi | Georgiev
t_course which looks like this:
course_id | NAME |LECTURER_NAME
-----------------------------------
1 | Basics | Vasilev
2 | Photography| Loyns
t_enrolment which looks like this:
enrolment_id| student_fk |course_fk | Avarage_grade
-------------------------------------------------------
1 | 1 | 1 |
2 | 3 | 1 |
3 | 4 | 1 |
4 | 2 | 1 |
5 | 1 | 2 | 5.50
6 | 2 | 2 | 5.40
7 | 5 | 2 | 6.00
I need to make 'select' statement and present the number of students per course. The result should be:
Count_students | Course_name
-----------------------------
4 | Basics
3 | Photography
Select all courses from your course Table, join the enrolment table and group by your course id. With count() you can select the number of Students
SELECT MAX(t_course.NAME) AS Course_name, COUNT(t_enrolment.student_fk) AS Count_students
FROM t_course
LEFT JOIN t_enrolment ON t_enrolment.course_fk = t_course.course_id
GROUP BY t_course.course_id;
If you want to select the same student in one course only once (if more then one enrolment can happen) you can use COUNT(DISTINCT t_enrolment.student_fk)
UPDATE
To make it working not only in mySQL I added an aggregate function to the name column.
Depending on the SQL database you are using you will have to add quotes or backticks.
Is this your homework?
select count(*) Count_students, c.name as course_name from t_enrolment e, t_course c group where e.course_fk = c.course_id by c.name
You need a select statement with a join to the couse table (for the Course_name). Group by 't_course'.name to use the COUNT(*) function
this will work:
SELECT COUNT(*) AS Count_students, c.NAME AS Course_name
FROM t_enrolment e
JOIN course c
ON e.course_fk = c.course_id
GROUP BY c.NAME
More information
Count function
Group by
Join

Postgres: Joining twice on a table

I (admitted SQL noob) have three tables in Postgresql that look like this:
groups
id | name | cat_id
----+--------+--------
1 | group1 | 1
3 | group3 | 1
2 | group2 | 2
4 | group4 | 2
category
id | name
----+------
1 | cat1
2 | cat2
translation
id | source | value | type | res_id
----+--------+---------+----------+--------
1 | group1 | Gruppe1 | groups | 1
2 | group2 | Gruppe2 | groups | 2
3 | group3 | Gruppe3 | groups | 3
4 | group4 | Gruppe4 | groups | 4
5 | cat1 | Kat1 | category | 1
6 | cat2 | Kat2 | category | 2
The translation table is global to the application and references other tables using the "res_id" and "type" fields. So to get the translation for "group1" I need to use "where res_id = 1 and type = 'groups'.
I need to list the groups in the following format:
category | group | translated category | translated group
This query gets me almost there:
select category.name, groups.name, translation.value from groups
join category on groups.cat_id = category.id
join translation on groups.id = translation.res_id
where type = 'groups';
But of course I'm missing the translated category, and have no clue how to get it.
I think you want something like:
select
category.name,
groups.name,
tg.value AS translated_group,
tc.value AS translated_category
from groups
inner join translation tg on (groups.id = tg.res_id AND tg.type = "groups")
inner join category on groups.cat_id = category.id
inner join translation tc on (category.id = tc.res_id AND tc.type = "category");
i.e. join translation twice, alias each copy, and use a join condition that also filters for the type field.
Untested as there's no CREATE TABLE and INSERT-form sample data in the question.
None of this is PostgreSQL specific, it's all just standard SQL.
BTW, it's nicer if you don't mix plural and singular forms for table names.