I have created 3 sample tables which I want to link together, as well as a URL to the case in DB Fiddle with mySQL 8.0.
https://www.db-fiddle.com/f/6mmFrw3JTWkVVznaH5juNp/0
The goal is that I get the prices for the stores "Amazon" and "Ebay" for all items from table "products_info". As soon as no prices are available in my "merchants_products" table, the SKU for the store should still be displayed. So at the end not 3 rows, but 4 rows should be displayed.
Table 1:
merchants contains all locations where my items are being sold.
Table2:
merchants_products contains all the items plus prices that I sell in the different locations.
Table3:
products_info contains all the items I want to get my information about.
create table merchants_products
(
SKU char(10) null,
MERCHANT_ID int null,
PRICE int null
);
INSERT INTO merchants_products (SKU, MERCHANT_ID, PRICE) VALUES ('13749461', 1, 3);
INSERT INTO merchants_products (SKU, MERCHANT_ID, PRICE) VALUES ('13753824', 1, 5);
INSERT INTO merchants_products (SKU, MERCHANT_ID, PRICE) VALUES ('13749461', 2, null);
INSERT INTO merchants_products (SKU, MERCHANT_ID, PRICE) VALUES ('13753770', 2, 4);
INSERT INTO merchants_products (SKU, MERCHANT_ID, PRICE) VALUES ('13749461', 3, 3);
INSERT INTO merchants_products (SKU, MERCHANT_ID, PRICE) VALUES ('13753770', 3, 4);
INSERT INTO merchants_products (SKU, MERCHANT_ID, PRICE) VALUES ('13753824', 3, 5);
create table merchants
(
ID int null,
NAME varchar(255) null
);
INSERT INTO merchants (ID, NAME) VALUES (1, 'amazon');
INSERT INTO merchants (ID, NAME) VALUES (2, 'ebay');
INSERT INTO merchants (ID, NAME) VALUES (3, 'tesco');
create table product_info
(
SKU char(10) null,
NAME varchar(255) null
);
INSERT INTO product_info (SKU, NAME) VALUES ('13749461', 'Artikel1');
INSERT INTO product_info (SKU, NAME) VALUES ('13753770', 'Artikel2');
SELECT
pi.SKU,
mp.PRICE,
mc.NAME
FROM product_info pi
LEFT JOIN merchants_products mp
ON mp.SKU= pi.SKU and mp.MERCHANT_ID IN (1,2)
LEFT JOIN merchants mc
ON mc.ID = mp.MERCHANT_ID
I have 5 tables related to sales. Three of them are like this:
Product_table_image
They are called product_a, product_b, product_c
The other tables are the time_id table which contains the reference for the date_id and the customers' table, which contains the details of customers.
time_id table
and
customer_table
The 3 tables refer to the sales of different products, but the products are not important in this context, because what I need is to sum up all the values per month per customer. There are cases when one or more customers might not have made a purchase of a certain product, which means not all customers ids will be in all products tables and that's what I can't figure out how to solve. It seems that my code is only able to fetch and sum when the clients have made purchases in all 3 tables.
So this is what I was able to come up with:
SELECT C.customer_id, ROUND((A.pa + B.pa + C.pc)* 1, 2) AS total,C.month_id
FROM (SELECT customer.customer_id, SUM(product_a.amount) AS pa , time_id.month_id FROM customer
INNER JOIN product_a on customer.customer_id = product_a.customer_id
INNER JOIN time_id on product_a.date_id = time_id.date_id
GROUP BY customer.customer_id, time_id.month_id) AS A
CROSS JOIN
(SELECT customer.customer_id, SUM(product_a.amount) AS pb , time_id.month_id FROM customer
INNER JOIN product_b on customer.customer_id = product_b.customer_id
INNER JOIN time_id on product_b.date_id = time_id.date_id
GROUP BY customer.customer_id, time_id.month_id) AS B
CROSS JOIN
(SELECT customer.customer_id, SUM(product_a.amount) AS pc , time_id.month_id FROM customer
INNER JOIN product_c on customer.customer_id = product_c.customer_id
INNER JOIN time_id on product_c.date_id = time_id.date_id
GROUP BY customer.customer_id, time_id.month_id) AS C
GROUP BY C.month_id, C.customer_id
ORDER BY C.month_id;
I've been stuck in it for a while, so any help is appreciated!
I have setup the tables and some sample data to make it more real.
create table month(id int, name varchar(20), primary key (id));
insert into month (id, name) values (1, 'January'),(2,'February'),(3,'March'),(4,'April');
create table year(id int, name varchar(4), primary key (id));
insert into year(id, name) values (2019, '2019'),(2020,'2020'),(2021,'2021');
create table time (id int, month_id int, year_id int, primary key (id));
alter table time add constraint fk_month FOREIGN KEY (month_id) REFERENCES month (id);
alter table time add constraint fk_year FOREIGN KEY (year_id) REFERENCES year (id);
insert into time (id, year_id, month_id) values
(1, 2019, 1),(2, 2019, 2),(3,2019,3),(4,2019,4),
(5, 2020, 1),(6, 2020, 2),(7,2020,3),(8,2020,4),
(9, 2021, 1),(10, 2021, 2),(11,2021,3),(12,2021,4);
create table customers (id int, name varchar(100), city varchar(100), country varchar(100), primary key (id));
insert into customers (id, name, city, country) values
(1, 'Google', 'San Francisco', 'US'),
(2, 'Ambev', 'São Paulo', 'BR'),
(3, 'Merck', 'Darmstadt', 'GE');
create table sales_of_product_a (id int, customer_id int, date_id int, amount decimal(10,2), primary key (id));
alter table sales_of_product_a add constraint fk_pa_time FOREIGN KEY (date_id) REFERENCES time (id);
-- only customer 1 - Google and 3 - Merck purchased product A
insert into sales_of_product_a (id, customer_id, date_id, amount) values
(1, 1, 1, 100.10),(2,1,2,200.20),(3,1,3,300.30),(4,1,4,400.40),
(5, 1, 5, 500.50),(6,1,6,600.60),(7,1,7,700.70),(8,1,8,800.80),
(9, 3, 1, 130.10),(10,3,2,230.20),(11,3,3,330.30),(12,3,4,430.40),
(13, 3, 5, 530.50),(14,3,6,630.60),(15,3,7,730.70),(16,3,8,830.80);
create table sales_of_product_b (id int, customer_id int, date_id int, amount decimal(10,2), primary key (id));
alter table sales_of_product_b add constraint fk_pb_time FOREIGN KEY (date_id) REFERENCES time (id);
-- only customer 1 - Google purchased product B
insert into sales_of_product_b (id, customer_id, date_id, amount) values
(1, 1, 1, 100.10),(2,1,2,200.20),(3,1,3,300.30),(4,1,4,400.40),
(5, 1, 5, 500.50),(6,1,6,600.60),(7,1,7,700.70),(8,1,8,800.80),
(9, 1, 9, 900.90),(10,1,10,1000.01),(11,1,11,1100.11),(12,1,12,1200.12);
create table sales_of_product_c (id int, customer_id int, date_id int, amount decimal(10,2), primary key (id));
alter table sales_of_product_c add constraint fk_pc_time FOREIGN KEY (date_id) REFERENCES time (id);
-- only customer 3 - Merck purchased product C
insert into sales_of_product_c (id, customer_id, date_id, amount) values
(1, 3, 1, 130.10),(2,3,2,230.20),(3,3,3,330.30),(4,3,4,430.40),
(5, 3, 5, 530.50),(6,3,6,630.60),(7,3,7,730.70),(8,3,8,830.80),
(9, 3, 9, 930.90),(10,3,10,1030.01),(11,3,11,1130.11),(12,3,12,1230.12);
The SQL you might be looking for would be something like.
with all_sales as (
select pa.customer_id, tt.month_id, sum(pa.amount) as amount from sales_of_product_a pa inner join time tt on (pa.date_id = tt.id) where tt.year_id = 2019 group by pa.customer_id, tt.month_id
union all
select pb.customer_id, tt.month_id, sum(pb.amount) as amount from sales_of_product_b pb inner join time tt on (pb.date_id = tt.id) where tt.year_id = 2019 group by pb.customer_id, tt.month_id
union all
select pc.customer_id, tt.month_id, sum(pc.amount) as amount from sales_of_product_c pc inner join time tt on (pc.date_id = tt.id) where tt.year_id = 2019 group by pc.customer_id, tt.month_id
),
sales_per_customer_per_month as (
-- summary of all sales of all products per customer per month
select customer_id, month_id, sum(amount) as amount from all_sales group by customer_id, month_id
),
customers_month as (
select c.id, c.name, c.city, c.country, m.id as month_id, m.name as month_name from customers c inner join month m on true
)
select c.id,c.name,c.city,c.country,c.month_id, coalesce(s.amount,0) as amount, sum(coalesce(s.amount,0)) over (partition by c.id order by c.id,c.month_id) as total
from customers_month c
left join sales_per_customer_per_month s on (s.customer_id = c.id and s.month_id = c.month_id)
order by c.id,c.month_id;
The result of query above is following.
The concepts used are linked below.
Window Functions:
https://dev.mysql.com/doc/refman/8.0/en/window-functions.html
Common Table Expressions (CTE):
https://www.mysqltutorial.org/mysql-cte/
There are three related tables:
operations (id, name)
pricelists (id, operations_id (link to operations table), cost)
accounting (id, pricelists_id (link to pricelists table), quantity)
How to get table, like
NAME SUMMARY_COST SUMMARY_QUANTITY
milling result of 2*750 2
threading result of 1*444 1
... ... ...
overall 2*750+1*444+... 2+1+...
I trying to group for two tables at a start:
select operations.name, sum(pricelists.cost) total
from operations
left join pricelists on pricelists.operations_id*accounting_quantity = operations.id*accounting.quantity
group by operations.id
but it is not worked yet
A little database:
CREATE TABLE operations (id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR (100));
CREATE TABLE pricelists (id INT PRIMARY KEY, operations_id INT NOT NULL, cost DECIMAL(10,2),
FOREIGN KEY (operations_id) REFERENCES operations (id));
CREATE TABLE accounting (id INT PRIMARY KEY, pricelists_id INT NOT NULL, quantity INT,
FOREIGN KEY (pricelists_id) REFERENCES pricelists (id));
INSERT INTO operations (name) VALUES
('milling'),
('threading'),
('grinding'),
('welding'),
('brazing'),
('soldering'),
('riveting');
INSERT INTO pricelists (id, operations_id, cost) VALUES
(1, 2, 750),
(2, 1, 444),
(3, 3, 123),
(4, 4, 450),
(5, 5, 375),
(6, 6, 250),
(7, 7, 232);
INSERT INTO accounting (id, pricelists_id, quantity) VALUES
(1, 7, 2),
(2, 2, 5),
(3, 4, 2),
(4, 1, 1),
(5, 3, 4);
Consder:
select o.name, sum(a.quantity * pl.cost) total, sum(a.quantity) quantity
from operations o
left join pricelists pl on pl.operations_id = o.id
left join accounting a on a.pricelists_id = pl.id
group by o.name
You can generate the summary row by just adding with rollup at the very end of the query.
I have following tables
movies
- id
details
- user_id
- movie_id
- rating
users
- id
detail belongs to user and movie
I want to find the the diff between the rating of two users say id 3,10
simply I want answer of this
sum(10-(user1.rating - user2.rating))
where rating is > 0
i.e both users should have given at-least non zero rating
select
d1.movie_id
, d1.rating as user1Rating
, d2.rating as user2Rating
, abs(d1.rating - d2.rating)
from
details d1
inner join details d2 on d1.movie_id = d2.movie_id
where d1.user_id = 1
and d2.user_id = 2
See it working live in an sqlfiddle.
CREATE TABLE ratings
( userId int, movieId int, rating int)
INSERT INTO ratings (userId, movieId, rating) VALUES
(3, 1, 5), (10, 1, 8),
(3, 2, 10), (10, 2, 3)
SELECT r1.movieId, (r1.rating - r2.rating) FROM ratings as r1
INNER JOIN ratings as r2 on r1.movieId = r2.movieId
WHERE r1.userId = 3 and r2.userId = 10
DECLARE #UserRating1 int
DECLARE #UserRating2 int
DECLARE #UserID1 int
DECLARE #UserID2 int
DECLARE #MovieID int
SET #MovieID = 99
SET #UserID1 = 3
SET #UserID2 = 10
SET #UserRating1 = (SELECT Rating FROM details WHERE movie_id = #MovieID AND user_id = #UserID1 AND Rating > 0)
SET #UserRating2 = (SELECT Rating FROM details WHERE movie_id = #MovieID AND user_id = #UserID2 AND Rating > 0)
PRINT #UserRating1 - #UserRating2
Update: I replace the syntax from MSSQL to MySql
You can check this on SQL Fiddle:
create table movies (id int);
insert into movies (id) values (1);
insert into movies (id) values (2);
insert into movies (id) values (3);
insert into movies (id) values (4);
insert into movies (id) values (5);
create table users (id int);
insert into users (id) values (1);
insert into users (id) values (2);
insert into users (id) values (3);
insert into users (id) values (4);
insert into users (id) values (5);
insert into users (id) values (10);
create table details (user_id int, movie_id int, rating int);
insert into details (user_id, movie_id, rating) values (3,1,1);
insert into details (user_id, movie_id, rating) values (3,2,2);
insert into details (user_id, movie_id, rating) values (3,3,3);
insert into details (user_id, movie_id, rating) values (3,4,1);
insert into details (user_id, movie_id, rating) values (3,5,4);
insert into details (user_id, movie_id, rating) values (10,1,3);
insert into details (user_id, movie_id, rating) values (10,2,1);
insert into details (user_id, movie_id, rating) values (10,3,2);
insert into details (user_id, movie_id, rating) values (10,4,1);
insert into details (user_id, movie_id, rating) values (10,5,5);
and
select
sum(10-(details1.rating - details2.rating))
from
movies
inner join details details1 on movies.id = details1.movie_id
inner join users user1 on details1.user_id = user1.id
inner join details details2 on movies.id = details2.movie_id
inner join users user2 on details2.user_id = user2.id
where
(user1.id=3)
and
(user2.id=10)
and
(details1.rating is not null) and (details1.rating > 0)
and
(details2.rating is not null) and (details2.rating > 0)
Is there a way to select columns other the one specified in the group by clause?
Let's say I have the following schema:
Student(id, name, age), Course(id, name, credit), Enrollment(student_id, course_id, grade)
I want to query for each course the following: course's name, student_count.
I came up with workaround, but I was wondering if there's a cleaner way to do this:
SELECT MAX(c.name), COUNT(distinct e.student_id)
FROM Enrollment e
INNER JOIN Course c ON c.id = e.course_id
GROUP BY e.course_id;
You might want to copy this DDL, adjust it to match your schema, and paste it into your question.
create table Student(
student_id integer primary key,
student_name varchar(35) not null,
age int not null default 20
);
create table Course(
course_id integer primary key,
course_name varchar(35) not null,
credit integer not null default 3
);
create table Enrollment(
student_id integer not null references Student (student_id),
course_id integer not null references Course (course_id),
primary key (student_id, course_id),
grade char(1) not null
);
insert into student values
(1, 'a', 20),
(2, 'b', 20),
(3, 'c', 20);
insert into course values
(1, 'course 1', 3),
(2, 'course 2', 3),
(3, 'course 3', 3);
insert into enrollment values
(1, 1, 'b'),
(2, 1, 'b'),
(3, 1, 'b'),
(1, 2, 'b'),
(2, 2, 'b'),
(3, 3, 'b');
Now, you can get the number of students enrolled in each course by querying only the "enrollment" table.
select course_id, count(student_id) num_students
from enrollment
group by course_id
order by course_id;
course_id num_students
--
1 3
2 2
3 1
All that remains is to get the corresponding course name. To do that, you just join the table "Course" with the query we just wrote.
select course.course_name, course_enrollment.num_students
from course
inner join (select course_id, count(student_id) num_students
from enrollment
group by course_id) course_enrollment
on course.course_id = course_enrollment.course_id;
course_name num_students
--
course 1 3
course 3 1
course 2 2
No, you can't. But you can extend GROUP BY with c.name:
SELECT MAX(c.name), COUNT(distinct e.student_id)
FROM Enrollment e
INNER JOIN Course c ON c.id = e.course_id
GROUP BY e.course_id, c.name
Because e.course_id is unique, it won't change results.