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 |
Related
I'm trying to find a performant and easy-to-read query to get a distinct value from one column, if all rows in the table matches a certain criteria.
I have a table that tracks e-commerce orders and whether they're delivered on time, contents and schema as following:
> select * from orders;
+----+--------------------+-------------+
| id | delivered_on_time | customer_id |
+----+--------------------+-------------+
| 1 | 1 | 9 |
| 2 | 0 | 9 |
| 3 | 1 | 10 |
| 4 | 1 | 10 |
| 5 | 0 | 11 |
+----+--------------------+-------------+
I would like to get all distinct customer_id's which have had all their orders delivered on time. I.e. I would like an output like this:
+-------------+
| customer_id |
+-------------+
| 10 |
+-------------+
What's the best way to do this?
I've found a solution, but it's a bit hard to read and I doubt it's the most efficient way to do it (using double CTE's):
> with hits_all as (
select memberid,count(*) as count from orders group by memberid
),
hits_true as
(select memberid,count(*) as count from orders where hit = true group by memberid)
select
*
from
hits_true
inner join
hits_all on
hits_all.memberid = hits_true.memberid
and hits_all.count = hits_true.count;
+----------+-------+----------+-------+
| memberid | count | memberid | count |
+----------+-------+----------+-------+
| 10 | 2 | 10 | 2 |
+----------+-------+----------+-------+
You use group by and having as follows:
select customer_id
from orders
group by customer_id
having sum(delivered_on_time) = count(*)
This works because an ontime delivery is identified by delivered_on_time = 1. So you can just ensure that the sum of delivered_on_time is equal to the number of records for the customer.
You can use aggregation and having:
select customer_id
from orders
group by customer_id
having min(delivered_on_time) = max(delivered_on_time);
I have two tables, each set up like this
-------------------------- ---------------------------
| DVD | | Sells |
-------------------------- ---------------------------
| dvdid | title | length | | dvdid | storeid | price |
-------------------------- ---------------------------
| 1 | Alpha | 27 | | 1 | 100 | 11.99 |
-------------------------- ---------------------------
I'm trying to select the average of Sells.price for each DVD.title, and select both. Right now I have the following:
SELECT AVG(Sells.price), DVD.title
FROM Sells inner join DVD on Sells.dvdid = DVD.dvdid;
Unfortunately this does not work, I receive the error "Not a single-group group function. I assume this means I can't SELECT a function like AVG and then a single column to go along with it. Any ideas?
You need to add DVD.title in Group By
SELECT AVG(Sells.price), DVD.title
FROM Sells inner join DVD on Sells.dvdid = DVD.dvdid
Group by DVD.title --Here
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
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
I got an interview question where there's a Car sale modeled in a DB. Each Car represents a physical car in a Car sale which refers to a Make and a Model table. A Sale table keeps track of each Car that is sold. A Sale only consists of one Car, so there's a record in Sale per every unique Car that had been sold.
The question was to find-out the name of the most sold Model in the car sale. I answered with a 3-level nested query. The interviewer specifically asked for a solution using joins where I only succeeded in just joining the tables without the aggregates.
How would you join 3 tables as below (Car, Make, Sale) while using two other aggregates?
Here's a rough sketch of the schema. The most sold Model here should return 'Corolla'
Car
| carid| modid | etc...
_________________
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 2 |
| 5 | 2 |
Make
| mkid | name |
_________________
| 1 | Toyota |
| 2 | Nissan |
| 3 | Chevy |
| 4 | Merc |
| 5 | Ford |
Model
| modid| name | mkid |
________________________
| 1 | Corolla| 1
| 2 | Sunny | 2
| 3 | Carina | 1
| 4 | Skyline| 2
| 5 | Focus | 5
Sale
| sid | carid | etc...
_________________
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 4 |
| 5 | 5 |
Edit:
Using MS SQL Server 2008
Output needed:
Model Name | Count
_____________________
Corolla | 3
i.e. The model of the Car that has been sold the most.
Notice only 3 Corollas and 2 Sunnys are in the Car table while Sale table corresponds to each of those with other sales detail. The 5 Sale records are actually Corolla, Corolla, Corolla, Sunnnu and Sunny.
Since you are using SQL Server 2008, make use of Common Table Expression and Window Function.
WITH recordList
AS
(
SELECT c.name, COUNT(*) [Count],
DENSE_RANK() OVER (ORDER BY COUNT(*) DESC) rn
FROM Sale a
INNER JOIN Car b
ON a.carid = b.carID
INNER JOIN Model c
ON b.modID = c.modID
GROUP BY c.Name
)
SELECT name, [Count]
FROM recordList
WHERE rn = 1
SQLFiddle Demo
When interviewers ask for this they usually want you to say that you'd use windowed functions. You could give each sale a unique ascending number partitioned by model and the highest sale number you'd get would be the max count.
http://www.postgresql.org/docs/9.1/static/tutorial-window.html
Following query works on oracle 11g . here's fiddle link
SELECT name FROM (
SELECT model.name AS name FROM car , sale , model
WHERE car.carid=sale.carid
AND car.modid=model.modid
GROUP BY model.name
ORDER BY count(*) DESC )
WHERE rownum = 1;
Or
SELECT name FROM (
SELECT model.name AS name FROM car natural join sale natural join model
GROUP BY model.name
ORDER BY count(*) DESC )
WHERE rownum = 1;
OUTPUT
| NAME |
-----------
| Corolla |
Based on your newly added SQL Server 2008 tag. If you are using a different RDBMS you'll probably need to use limit instead of top and place it at the end of the top_sold_car subquery.
select Make.name as Make, Model.name as Model
from (
select top 1 count(*) as num_sold
from Car
group by modid
order by num_sold desc) as top_sold_car
join Model
on (top_sold_car.modid = Model.modid)
join Make
on (Model.mkid = Make.mkid)