I have the query below
List the number of students graduated in 2009 by department. The results should show 0 for the departments that do not have any student graduated in 2009.
im having trouble with the 2nd part of the question. as of right now my query only shows the department that have students that graduated. i have no idea how to make the table show the departments that dd not have any students graduate.
my query looks like this
select d.name, count(s.major_id) as students from departments d
right join students s on s.major_id = d.id where extract( year from s.graduation_date ) = 2009
group by d.name
and my table looks like this
name students
Math 2
Drama 1
how can i get it to show the other departments with no students graduating?
database is
create table departments (
id integer primary key,
name varchar(255)
);
insert into departments (id, name) values (10, 'Computer Science');
insert into departments (id, name) values (20, 'Math');
insert into departments (id, name) values (30, 'Drama');
create table faculty (
id integer primary key,
name varchar(255),
department_id integer references departments(id)
);
insert into faculty (id, name, department_id) values (1, 'Turing', 10);
insert into faculty (id, name, department_id) values (2, 'Newton', 20);
insert into faculty (id, name, department_id) values (3, 'Einstein', 20);
insert into faculty (id, name, department_id) values (4, 'Brando', 30);
insert into faculty (id, name, department_id) values (5, 'Joe', 30);
insert into faculty (id, name, department_id) values (6, 'Gray', 10);
create table students (
id integer primary key,
name varchar(255),
graduation_date date,
major_id integer references departments(id)
);
insert into students (id, name, graduation_date, major_id) values
(1, 'Joe', null, 10);
insert into students (id, name, graduation_date, major_id) values
(2, 'Amy', '2009-04-22', 20);
insert into students (id, name, graduation_date, major_id) values
(3, 'Max', null, 10);
insert into students (id, name, graduation_date, major_id) values
(4, 'Sue', '2009-01-10', 20);
insert into students (id, name, graduation_date, major_id) values
(5, 'Bob', '2009-03-05', 30);
insert into students (id, name, graduation_date, major_id) values
(6, 'Kim', null, 20);
insert into students (id, name, graduation_date, major_id) values
(7, 'Art', null, 30);
insert into students (id, name, graduation_date, major_id) values
(8, 'Pat', '2005-07-11', 20);
insert into students (id, name, graduation_date, major_id) values
(9, 'Lee', null, 10);
Try with a LEFT JOIN instead of a RIGHT JOIN:
select d.name, count(s.major_id) as students
from departments d
left join students s on s.major_id = d.id and
extract( year from s.graduation_date ) = 2009
group by d.name
Note that extract( year from s.graduation_date ) = 2009 predicate should be placed in the ON clause, otherwise LEFT JOIN becomes an INNER JOIN.
Output:
name | students
=================+============
Computer Science | 0
Drama | 1
Math | 2
Related
I have an orders data set. I'd like to get email addresses where the count of orders are specific counts for each year. Let's say 2000 = 1, 2001 = 5 or less, 2002 = 3.
select email
from orders
where year in (2000,2001,2002)
That's where I'm stuck. My thought process is pushing me towards using a having clause or a case statement, but I'm at a wall with the condition of considering the counts by year.
In pseudo SQL it'd be:
select email
from orders
where count(year = 2000) = 1
and count(year = 2001) <= 5
and count(year = 2002) = 3
You can't do this in the where clause, you have to group by email and apply your condition in a having clause (or have your group by query as a subquery and use a where condition in an outer query).
select email
from orders
where year in (2000,2001,2003)
group by email
having sum(year = 2000) = 1
and sum(year = 2001) <= 5
and sum(year = 2002) = 3
You can do it as bellow.
Note that you can change the filtred values wthin the where condition for the count value and the associated year.
-- create a table
CREATE TABLE Orders (
id INTEGER PRIMARY KEY,
email VARCHAR(30) NOT NULL,
year int NOT NULL
);
-- insert some values
INSERT INTO Orders VALUES (1, 'test1#mail.com', 2000);
INSERT INTO Orders VALUES (2, 'test2#mail.com', 2001);
INSERT INTO Orders VALUES (3, 'test2#mail.com', 2001);
INSERT INTO Orders VALUES (4, 'test3#mail.com', 2002);
INSERT INTO Orders VALUES (5, 'test2#mail.com', 2001);
INSERT INTO Orders VALUES (6, 'test3#mail.com', 2002);
INSERT INTO Orders VALUES (7, 'test2#mail.com', 2001);
INSERT INTO Orders VALUES (9, 'test2#mail.com', 2001);
INSERT INTO Orders VALUES (10, 'test3#mail.com', 2002);
INSERT INTO Orders VALUES (11, 'test4#mail.com', 2002);
INSERT INTO Orders VALUES (12, 'test4#mail.com', 2001);
INSERT INTO Orders VALUES (13, 'test4#mail.com', 2002);
--sql statement
select result.email from (
select email, year, count(*) As count from Orders where year in (2000,2001,2002)
group by year, email
)result
where
(result.count = 1 and year = 2000)
;
Output:
email
test1#mail.com
i'm doing a query on DB that i want return to me the group concat of the duplicate category for example in this table
I want the result of the concats parent from the same table in MySQL as this way
Hello Please test this:
CREATE TABLE Monopoly (Id INT,Name VARCHAR(30),Platform_ID INT,Parent_Name VARCHAR(30));
INSERT INTO Monopoly VALUES
(1, 'Rosatis', 1113080,'Rosatis Franchase'),
(2, 'Rosatis', 1113080,'Rosatis Global'),
(3, 'Rosatis', 1113080,'Rosatis USA'),
(4, 'Macianos Food', 1125676,'Rosatis Franchase'),
(5, 'Macianos Food', 1125676,'Rosatis Global'),
(6, 'Aurora BAR', 1452355,'Bar Grill Global'),
(7, 'Aurora BAR', 1452355,'USA Group'),
(8, 'Aurora BAR', 1452355,'Food Group');
Full Code:
SELECT M1.Id,M1.Name,M1.Platform_ID,M1.Parent_Name,Family_Name
FROM Monopoly M1
JOIN (SELECT Name, GROUP_CONCAT(DISTINCT Parent_Name) AS Family_Name FROM Monopoly GROUP BY Name) AS M2 ON M1.Name = M2.Name
GROUP BY Id,Name,Platform_ID,Parent_Name;
If we test the code:
I have a problem. How can I write this sql statment without roll up?
I want to get the same result, but without rollup. The result should look like the image below.
my query
select state, city, sum((sales.retail_price - products.wholesale_price) *
sales.quantity) as profit
from products, sales
where sales.product_id = products.product_id
group by state, city WITH ROLLUP
order by state is null, city is null, state, city ;
my schema
-- Create some tables and insert some rows.
create table products (product_id integer, wholesale_price real);
insert into products (product_id, wholesale_price) values
(1, 1.00),
(2, 2.00);
create table sales (product_id integer, retail_price real,
quantity integer, city varchar, state varchar);
insert into sales (product_id, retail_price, quantity, city, state) values
(1, 2.00, 1, 'SF', 'CA'),
(1, 2.00, 2, 'SJ', 'CA'),
(2, 5.00, 4, 'SF', 'CA'),
(2, 5.00, 8, 'SJ', 'CA'),
(2, 5.00, 16, 'Miami', 'FL'),
(2, 5.00, 32, 'Orlando', 'FL'),
(2, 5.00, 64, 'SJ', 'PR');
You must use an UNION operation, as follow:
The first query returns the SUM grouped by state and city, the second only for state, and the third (without group by) for all rows
select state, city, sum((sales.retail_price - products.wholesale_price) *
sales.quantity) as profit
from products, sales
where sales.product_id = products.product_id
group by state, city
UNION ALL
select state, NULL, sum((sales.retail_price - products.wholesale_price) *
sales.quantity) as profit
from products, sales
where sales.product_id = products.product_id
group by state
UNION ALL
select NULL, NULL, sum((sales.retail_price - products.wholesale_price) *
sales.quantity) as profit
from products, sales
where sales.product_id = products.product_id;
I have a pricing table as follows,
Pricing Table
id productId ContractId ageGroup ageFrom ageTo sellingPrice specialPrice
1 1 1 1 0 2 0 0
2 1 1 1 3 13 20 0
3 1 1 2 18 55 80 0
4 1 1 3 56 119 60 0
5 1 1 1 0 2 0 0
6 1 2 2 18 55 85 0
7 2 2 3 55 119 90 0
8 2 2 2 18 55 90 0
I need to find the list of Contract Ids and Ids for given age Group (1-adult or 2-child or 3-senior). For the children the age range (from - to) need to be considered as well.
The following query (1 adult, 2 children with the ages 2 & 4 and 1 senior) seems to be working but returns only the ids matching the age group 1.
SELECT contractId,id
FROM tbl_contract_price cp1
WHERE contractId IN
(SELECT contractId FROM tbl_contract_price cp2
WHERE contractId IN
(SELECT contractId FROM tbl_contract_price cp3
WHERE cp1.ageGroup = 1 AND (cp2.ageGroup = 2 AND cp2.ageFrom <= 2 AND 2 <= cp2.ageTo OR cp2.ageGroup = 2 AND cp2.ageFrom <= 4 AND 4 <= cp2.ageTo ) AND cp3.ageGroup = 3))
Is there anything I am missing?
Based on some assumptions, I have created the following to help you get started. Please note that you will need to enforce your data integrity (i.e., ensuring that for each product, all possible ages are covered by a price, etc.)
I suggest that you use a temporary quote table so that you can have more flexibility on the number of inputs. You can see the data example below. Or, better yet, handle that logic within your Business Logic Layer.
You will need to apply any tie-breaker logic if two contracts yield the same price, etc.
CREATE TABLE Pricing (
ID int not null,
productId int not null,
ContractId int not null,
ageGroup int not null,
ageFrom int not null,
ageTo int not null,
sellingPrice int not null,
PRIMARY KEY (ID)
);
INSERT INTO Pricing (ID, productId, ContractId, ageGroup, ageFrom, ageTo, sellingPrice) Values (1, 1, 1, 1, 0, 2, 0);
INSERT INTO Pricing (id, productId, ContractId, ageGroup, ageFrom, ageTo, sellingPrice) Values (2, 1, 1, 1, 3, 13, 20);
INSERT INTO Pricing (id, productId, ContractId, ageGroup, ageFrom, ageTo, sellingPrice) Values (3, 1, 1, 2, 18, 55, 80);
INSERT INTO Pricing (id, productId, ContractId, ageGroup, ageFrom, ageTo, sellingPrice) Values (4, 1, 1, 3, 56, 119, 60);
INSERT INTO Pricing (id, productId, ContractId, ageGroup, ageFrom, ageTo, sellingPrice) Values (5, 1, 2, 1, 3, 13, 0);
INSERT INTO Pricing (id, productId, ContractId, ageGroup, ageFrom, ageTo, sellingPrice) Values (6, 1, 2, 2, 18, 55, 85);
INSERT INTO Pricing (id, productId, ContractId, ageGroup, ageFrom, ageTo, sellingPrice) Values (7, 2, 2, 3, 55, 119, 90);
INSERT INTO Pricing (id, productId, ContractId, ageGroup, ageFrom, ageTo, sellingPrice) Values (8, 2, 2, 2, 18, 55, 90);
CREATE TABLE ValidDates (
ID int not null,
priceId int not null,
fromDate date not null,
toDate date not null,
PRIMARY KEY (ID)
);
INSERT INTO ValidDates (id, priceId, fromDate, toDate) VALUES (1, 1, '2018-06-01', '2018-06-30');
INSERT INTO ValidDates (id, priceId, fromDate, toDate) VALUES (2, 2, '2018-06-01', '2018-06-30');
INSERT INTO ValidDates (id, priceId, fromDate, toDate) VALUES (3, 2, '2018-07-01', '2018-07-31');
INSERT INTO ValidDates (id, priceId, fromDate, toDate) VALUES (4, 3, '2018-06-01', '2018-06-30');
INSERT INTO ValidDates (id, priceId, fromDate, toDate) VALUES (5, 3, '2018-07-01', '2018-07-31');
INSERT INTO ValidDates (id, priceId, fromDate, toDate) VALUES (6, 4, '2018-06-01', '2018-06-30');
INSERT INTO ValidDates (id, priceId, fromDate, toDate) VALUES (7, 5, '2018-06-01', '2018-06-30');
INSERT INTO ValidDates (id, priceId, fromDate, toDate) VALUES (8, 5, '2018-07-01', '2018-07-31');
INSERT INTO ValidDates (id, priceId, fromDate, toDate) VALUES (9, 6, '2018-06-01', '2018-06-30');
INSERT INTO ValidDates (id, priceId, fromDate, toDate) VALUES (10, 6, '2018-07-01', '2018-07-31');
CREATE TABLE Products (
ID int not null,
PRIMARY KEY (ID)
);
CREATE TABLE Quotes (
ID int not null,
age int
);
INSERT INTO Quotes (Id, age) VALUES (1, 70);
INSERT INTO Quotes (Id, age) VALUES (1, 25);
INSERT INTO Quotes (Id, age) VALUES (1, 1);
INSERT INTO Quotes (Id, age) VALUES (1, 4);
Then, you can use the following query to calculate your total price based on the product id, selected date, and your quote id (which has all the ages for the particular quote)
Scenario: tour date = Jun 22, 2018; product = 1, quote = 1 with age = 1, 4, 25, 70
SELECT #tourdate := '2018-06-22', #productid := 1, #quoteid := 1;
First query to show how the relevant information is retrieved
SELECT productid, contractId, ageGroup, ageFrom, ageTo,
SUM(CASE WHEN age BETWEEN ageFrom AND ageTo THEN 1 ELSE 0 END) AS PAXCount, sellingPrice
FROM ValidDates
LEFT JOIN Pricing
ON priceId = Pricing.ID
LEFT JOIN Products
ON productId = Products.ID
LEFT JOIN Quotes
ON Quotes.ID = #quoteid
WHERE (#tourdate BETWEEN fromDate AND toDate) AND productid = #productid
GROUP BY productid, contractid, ageGroup, ageFrom, ageTo, sellingPrice;
second query is built upon the first query, aggregating the total so that you have the total cost for ranking
SELECT contractId, SUM(sellingPrice * PAXCount) FROM (
SELECT productid, contractId, ageGroup,
SUM(CASE WHEN age BETWEEN ageFrom AND ageTo THEN 1 ELSE 0 END) AS PAXCount, sellingPrice
FROM ValidDates
LEFT JOIN Pricing
ON priceId = Pricing.ID
LEFT JOIN Products
ON productId = Products.ID
LEFT JOIN Quotes
ON Quotes.ID = #quoteid
WHERE (#tourdate BETWEEN fromDate AND toDate) AND productid = #productid
GROUP BY productid, contractid, ageGroup, sellingPrice) P
GROUP BY contractid
ORDER BY SUM(sellingPrice * PAXCount)
#LIMIT 1;
You can uncomment the #Limit 1 to get only the cheapest package, but you need to be aware of the limitation
You will need to ensure that your data integrity is enforced, i.e., for each product and date range, all possible age needs to be covered by
Note that because the child aged 0 and the senior aged 70 were not covered by contract id 2, the $85 total is misleading. You can add logic to check if a contract can fulfil all ages (if input count is 4, check if the contract does indeed include four people, etc.)
You might need to clean up the quotes tables as required. It is not the most efficient approach for sure (but it should work according to your requirements).
For example, change the query to something like this:
SELECT #PAXCount := COUNT(*) FROM Quotes WHERE id = #quoteid;
Or you can probably pass that in from your application fairly easily.
Then, check to make sure that the count matches.
SELECT contractId, SUM(sellingPrice * PAXCount) AS TotalPrice, SUM(PAXCount) AS TotalPAXCOUNT
FROM (
SELECT productid, contractId, ageGroup,
SUM(CASE WHEN age BETWEEN ageFrom AND ageTo THEN 1 ELSE 0 END) AS PAXCount, sellingPrice
FROM ValidDates
LEFT JOIN Pricing
ON priceId = Pricing.ID
LEFT JOIN Products
ON productId = Products.ID
LEFT JOIN Quotes
ON Quotes.ID = #quoteid
WHERE (#tourdate BETWEEN fromDate AND toDate) AND productid = #productid
GROUP BY productid, contractid, ageGroup, sellingPrice) P
GROUP BY contractid
HAVING #PAXCount = SUM(PAXCount)
ORDER BY SUM(sellingPrice * PAXCount)
#LIMIT 1;
This way, only contract id covering all passengers will be shown.
Try it in the DB Fiddler
I'm trying to calculate the DAU average for each country for a time period of 1 month. The job of the query is to:
identify unique users
find all users who logged in during last
month
group them into individual days
segment them into their
respective countries
count the average for each country.
So far I've managed steps 1, 2, 3 and 4, but the last one is proving to be tricky.
The query is supposed to first calculate the subquery where it calculates how many active users opened the app in the last month and then group them into days and countries.
After this, it should calculate the average DAU for each country using all 30 days data it has calculated in the subquery.
The result would then be a list of countries and their average DAU.
query so far looks like this:
SELECT Country, AVG(User_ID)
FROM usersession
WHERE User_ID IN
(SELECT count(distinct us.User_ID)
FROM usersession us
WHERE Opened > current_timestamp - interval 1 month
GROUP BY DAY(Opened), Country)
GROUP BY Country ORDER BY Country;
The subquery does steps 1,2,3,4 but the secondary query outside the subquery isn't just working as intended.
Table is as follows (just a short example of the relevant information):
ID | UserID | Opened | Country
-----------------------------------------------
233231 1 2017-11-20 08:00:00 NA
223214 2 2017-11-20 08:53:00 DK
Expected result (around 230 countries total):
Country | Average
------------------
NA 150354
DK 60345
FI 50242
Actual result:
+---------+--------------+
| Country | AVG(User_ID) |
+---------+--------------+
| NULL | 804397.7297 |
| | 746046.7500 |
| BR | 893252.0000 |
| GB | 935599.0000 |
| RU | 993311.0000 |
| US | 735568.0000 |
+---------+--------------+
I think this is what you want:
select
country,
sum(number_of_users) / count(distinct day_of_month) as daily_average_users
from
(
select
country,
day(opened) as day_of_month,
count(distinct user_id) as number_of_users
from
user_session
where
opened > current_timestamp - interval 1 month
group by
country,
day_of_month
) x
group by
country
order by
country;
I tested this on MySQL 5.7:
create table user_session
(
id int,
user_id int,
opened timestamp,
country varchar(2)
);
insert into user_session (id, user_id, opened, country) values ( 1, 100, '2017-12-20 08:00:00', 'NA');
insert into user_session (id, user_id, opened, country) values ( 2, 100, '2017-12-20 08:00:00', 'NA');
insert into user_session (id, user_id, opened, country) values ( 3, 100, '2017-12-20 08:00:00', 'NA');
insert into user_session (id, user_id, opened, country) values ( 4, 100, '2017-12-21 08:00:00', 'NA');
insert into user_session (id, user_id, opened, country) values ( 5, 100, '2017-12-22 08:00:00', 'NA');
insert into user_session (id, user_id, opened, country) values ( 6, 200, '2017-12-20 08:00:00', 'NA');
insert into user_session (id, user_id, opened, country) values ( 7, 300, '2017-12-21 08:00:00', 'NA');
insert into user_session (id, user_id, opened, country) values ( 8, 400, '2017-12-20 08:00:00', 'NA');
insert into user_session (id, user_id, opened, country) values ( 9, 500, '2017-12-20 08:00:00', 'NA');
insert into user_session (id, user_id, opened, country) values (10, 600, '2017-12-20 08:00:00', 'DK');
insert into user_session (id, user_id, opened, country) values (11, 600, '2017-12-21 08:00:00', 'DK');
insert into user_session (id, user_id, opened, country) values (12, 700, '2017-12-20 08:00:00', 'DK');
insert into user_session (id, user_id, opened, country) values (13, 800, '2017-12-20 08:00:00', 'DK');
insert into user_session (id, user_id, opened, country) values (14, 800, '2017-12-21 08:00:00', 'DK');
insert into user_session (id, user_id, opened, country) values (15, 800, '2017-12-21 08:00:00', 'DK');
insert into user_session (id, user_id, opened, country) values (16, 900, '2017-12-20 08:00:00', 'DK');
insert into user_session (id, user_id, opened, country) values (17, 900, '2017-12-20 08:00:00', 'DK');
insert into user_session (id, user_id, opened, country) values (18, 900, '2017-12-22 08:00:00', 'DK');
insert into user_session (id, user_id, opened, country) values (19, 900, '2017-12-22 08:00:00', 'DK');
insert into user_session (id, user_id, opened, country) values (19, 1000, '2017-12-22 08:00:00', 'DK');
Results:
+---------+---------------------+
| country | daily_average_users |
+---------+---------------------+
| DK | 2.6667 |
| NA | 2.3333 |
+---------+---------------------+
2 rows in set (0.00 sec)
For this to be a proper daily average you would need every day of the month to be represented in the data (otherwise the average is over the number of days represented). If that isn't the case then we need to calculate the number of days in the period being considered.