Mysql with join statement - mysql

I have the following tables
1) discounts
+--------------------------------------------+
| ID discount_description discount_type |
+--------------------------------------------+
| 17 20% off PERCENT |
| 19 Citric ABSOLUTE |
+--------------------------------------------+
Table 2 - Products Included
+------+--------------+------------------+
| ID | discount_id | product_id |
+------+--------------+------------------+
| 2 | 17 | 52238403 |
| 3 | 17 | 52238409 |
| 4 | 19 | 52238408 |
+------+--------------+------------------+
Table 3 - Products Excluded
+---------------------------------+---------------+
| ID discount_id | product_id |
+---------------------------------+---------------+
| 2 17 | 52238411 |
| 3 17 | 52238408 |
+---------------------------------+---------------+
I need the query to fetch all the discounts based on product ID's from included table and excluded table. And it should also include the rows from discounts which is not present in products included/excluded table.
Below is a sample one to fetch discounts based on products 52238408,52238403.
SELECT
discounts.id as ID1,
discount_products.product_id as p1,
exclude_discount_products.product_id as p2
FROM discounts
LEFT JOIN `discount_products` ON 1=1
AND discounts.id = discount_products.discount_id
LEFT JOIN `exclude_discount_products` ON 1=1
AND discounts.id = exclude_discount_products.discount_id
WHERE discount_products.product_id IN (52238408,52238403)
AND exclude_discount_products.product_id NOT IN (52238408,52238403)
The query is just a basic version and i am pretty sure it's incorrect. But basically i wanted to check both the tables for product ID.
I am suppose to get discount 19 as the output since products excluded has 52238408, So 17 should ignored. But it gives 17 instead. Not sure what i am missing, any help is much appreciated.
Thanks

Not sure if you want this you didnt include the expected output.
Create table/insert data
CREATE TABLE discounts
(`ID` INT, `discount_description` VARCHAR(7), `discount_type` VARCHAR(8))
;
INSERT INTO discounts
(`ID`, `discount_description`, `discount_type`)
VALUES
(17, '20% off', 'PERCENT'),
(19, 'Citric', 'ABSOLUTE')
;
CREATE TABLE discount_products
(`ID` INT, `discount_id` INT, `product_id` INT)
;
INSERT INTO discount_products
(`ID`, `discount_id`, `product_id`)
VALUES
(2, 17, 52238403),
(3, 17, 52238409),
(4, 19, 52238408)
;
CREATE TABLE exclude_discount_products
(`ID` INT, `discount_id` INT, `product_id` INT)
;
INSERT INTO exclude_discount_products
(`ID`, `discount_id`, `product_id`)
VALUES
(2, 17, 52238411),
(3, 17, 52238408)
;
Query
SELECT
discounts.id AS ID1
, product_id AS p1
, (
# SELECT matching product_id FROM exclude_discount_products based on product_id (checks excludes)
SELECT
product_id
FROM
exclude_discount_products
WHERE
product_id = discount_products_NOT_IN_exclude_discount_products.product_id
)
AS p2
FROM (
# SELECT all discount_products that dont have the same discount_id, product_id as in exclude_discount_products
SELECT
*
FROM
discount_products
WHERE (
discount_id
, product_id
)
NOT IN (
SELECT
discount_id
, product_id
FROM
exclude_discount_products
)
)
AS
discount_products_NOT_IN_exclude_discount_products
INNER JOIN
discounts
ON
discount_products_NOT_IN_exclude_discount_products.discount_id = discounts.id
WHERE
product_id IN(
52238408
, 52238403
)
Result
ID1 p1 p2
------ -------- ----------
17 52238403 (NULL)
19 52238408 52238408

Related

How to show multiple results in single columns whith subquery

I have orders table where i show information about order number, products, and customer.
I need to show list of product names and their qty in column products separated or in new row like below in table:
Expected result
id | order_number | customer | address | total | products
------------------------------------------------------------------------------------
1 | OR00123 | Tom Helks | Test 221 | 1233,2 | 1x iphone
| 2x samsung
| 3x someproduct
Order table
id | order_number | customer_id | total | status_id
------------------------------------------------------------------------------------
1 | OR00123 | 1 | 1233,2 | 1
OrderProducts table
id | order_id | product_id | qty |
------------------------------------------------------
1 | 1 | 5 | 1 |
2 | 1 | 2 | 2 |
3 | 1 | 6 | 3 |
Product table
id | name | price
----------------------------------------
5 | iphone | 1231
2 | samsung | 2322
6 | someproduct | 432
What i try.
I get above expected result but only dont do subquery for listing products and qty for order.
SELECT
order.number,
customer.name,
customer.adress,
SUM(order_products.qty * product.price) as total,
# subquery here
(
SELECT p.name FROM products p WHERE order_products.product_id = p.id
) as products
FROM.orders as order
INNER JOIN customer on customer.customer_id= customer.id
INNER JOIN order_products on order.id = order_products.order_id
ORDER BY dok.created_at;
My imagination stopped and I believe that the problem is in the sub-query, but I can't see it at the moment.
Thanks
You need to join all tables and GROUP BY the unique values.
The GROUP_CONCAT has to be used with then CONCAT of the wanted values
CREATE TABLE orders
(`id` int, `order_number` varchar(7), `customer_id` int, `total` varchar(6), `status_id` int)
;
INSERT INTO orders
(`id`, `order_number`, `customer_id`, `total`, `status_id`)
VALUES
(1, 'OR00123', 1, '1233,2', 1)
;
CREATE TABLE orders_product
(`id` int, `order_id` int, `product_id` int, `qty` int)
;
INSERT INTO orders_product
(`id`, `order_id`, `product_id`, `qty`)
VALUES
(1, 1, 5, 1),
(2, 1, 2, 2),
(3, 1, 6, 3)
;)
Records: 3 Duplicates: 0 Warnings: 0
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ')' at line 1
CREATE TABLE product
(`id` int, `name` varchar(11), `price` int)
;
INSERT INTO product
(`id`, `name`, `price`)
VALUES
(5, 'iphone', 1231),
(2, 'samsung', 2322),
(6, 'someproduct', 432)
;
Records: 3 Duplicates: 0 Warnings: 0
SELECT
order_number, `customer_id`, `total`,GROUP_CONCAT(CONCAT( op.`qty`,'x ',p.`name`) SEPARATOR ' ') products
FROM orders o
JOIN orders_product op ON o.`id` = op.`order_id`
JOIN product p ON op.`product_id` = p.`id`
GROUP BY order_number, `customer_id`, `total`
order_number
customer_id
total
products
OR00123
1
1233,2
1x iphone 2x samsung 3x someproduct
fiddle

SQL - Select rows with common item names and different prices and showing the lowest discount without duplicates

We have a table in which we have the product ID, the price, and the shop ID.
the key is on the couple (product-id/shop-id) so we can have twice the same product in the same shop.
table item_price
Product_ID | Shop_ID | Price
1 | 2 | 25.99
1 | 3 | 40.00
1 | 1 | 10.00
2 | 9 | 32.00
3 | 2 | 78.00
and a table Item_reference which contains the Product_name, product_id and the retail_price_eur
Item reference
product_title | product_id | retail_price_eur
product_1 | 1 | 19.99
product_2 | 2 | 29.99
product_3 | 3 | 79.99
My goal is to select the product 1, for which the discount is the bigger with the following request:
SELECT DISTINCT t1.product_id
, t2.retail_price_eur
, t2.product_title
, t1.price, CEIL ((t1.price-t2.retail_price_eur)/t2.retail_price_eur*100) AS product_discount
FROM item_price AS t1
JOIN item_reference AS t2
ON t1.product_id = t2.product_id
WHERE t1.product_id = 1
AND NOT retail_price_eur = 0
ORDER
BY product_discount DESC
so, I have a couple of issue:
Issue 1)
Even tho I put DISTINCT, I have duplicates of the same product_id...
(but if i only ask for "SELECT DISTINCT t1.product_id" without any other field from t1 i do not have duplicates). So I have an issue I do not understand..
Issue 2)
I do not know how to integrate the code allowing to select the smaller Minimum Discount.
I would assume something along the line of
"WHERE NOT retail_price_eur = 0 AND MIN(product_discount)"...
but this is not the correct answer and i do not know how to put it in.
PS: I use "min()" on product discount, as in some cases, the % of variation might be positive if the shop price is superior to the retail price.
here are the command to create the example:
CREATE TABLE evilbrick.item_price ( product_id INT NOT NULL , shop_id INT NOT NULL , price FLOAT NOT NULL ) ENGINE = MyISAM;"
CREATE TABLE evilbrick.item_reference ( product_title VARCHAR (10) NOT NULL , product_id INT NOT NULL , retail_price_eur FLOAT NOT NULL ) ENGINE = MyISAM;
INSERT INTO item_price (product_id, shop_id, price) VALUES (1, 2, 25.99)
INSERT INTO item_price (product_id, shop_id, price) VALUES (1, 3, 40)
INSERT INTO item_price (product_id, shop_id, price) VALUES (1, 1, 10)
INSERT INTO item_reference (product_title, product_id, retail_price_eur) VALUES ('product_1', 1, 19.99)

Mysql join two tables sum, where and group by

I have two tables in the following structure:
sales
|date |time | name | total |
|2017-04-01 |10:23:59 | aaa | 100 |
|2017-04-01 |10:23:59 | aaa | 150 |
|2017-04-01 |11:33:30 | bbb | 200 |
|2017-04-01 |11:33:30 | bbb | 120 |
|2017-04-02 |10:50:59 | aaa | 70 |
|2017-04-02 |10:30:59 | bbb | 35 |
payment
|date |time | name | amount |
|2017-04-01 |10:23:59 | aaa | 300 |
|2017-04-01 |11:33:30 | bbb | 400 |
|2017-04-02 |10:50:59 | aaa | 425 |
|2017-04-02 |10:30:59 | bbb | 600 |
Terms
sales.time = payment.time
where date = 2017-04-01
sum(sales.total) and sum(payment.amount)
group by time
i want this result
|date |time | name | sum(total) | sum(amount)|
|2017-04-01 |13:23:59 | aaa | 250 | 300 |
|2017-04-01 |12:33:30 | bbb | 320 | 400 |
Table structure
CREATE TABLE `payment` (`id` int(5) NOT NULL,`date` date NOT NULL,`time` time NOT NULL,`name` varchar(10) NOT NULL,`amount` varchar(10) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT;
INSERT INTO `payment` (`id`, `date`, `time`, `name`, `amount`) VALUES(3, '2017-04-01', '10:23:59', 'aaa', '300'),(4, '2017-04-01', '11:33:30', 'bbb', '400'),(5, '2017-04-02', '10:50:59', 'aaa', '425'),(6, '2017-04-02', '10:30:59', 'bbb', '600');
CREATE TABLE `sales` ( `id` int(5) NOT NULL,`date` date NOT NULL,`time` time NOT NULL,`name` varchar(10) NOT NULL,`total` int(10) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `sales` (`id`, `date`, `time`, `name`, `total`) VALUES(1, '2017-04-01', '10:23:59', 'aaa', 100),(2, '2017-04-01', '10:23:59', 'aaa', 150),(3, '2017-04-01', '11:33:30', 'bbb', 200),(4, '2017-04-01', '11:33:30', 'bbb', 120),(5, '2017-04-02', '10:50:59', 'aaa', 70),(6, '2017-04-02', '10:50:59', 'bbb', 35);
ALTER TABLE `payment` ADD PRIMARY KEY (`id`);
ALTER TABLE `sales` ADD PRIMARY KEY (`id`);
ALTER TABLE `payment` MODIFY `id` int(5) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=7;
ALTER TABLE `sales` MODIFY `id` int(5) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=7;
Query
SELECT sales.time,sales.name,
SUM(sales.total),SUM(payment.amount)
FROM sales,payment
WHERE sales.time=payment.time
and sales.date like '2017-04-01%'
GROUP BY sales.time
ORDER BY sales.time;
Result is
10:23:59 aaa 250 600
11:33:30 bbb 320 800
You are multiplying the payments amount with the number of sales records, because you are joining all payments records with all sales records before summing up the amounts.
Aggregate first, and only join then.
In case there can always only be one payments record per date, time and name:
select p.name, p.time, p.name, s.sales_total, p.amount
from payments p
join
(
select date, time, name, sum(total) as total
from sales
group by date, time, name
) s
on s.date = p.date and s.time = p.time and s.name = p.name
where p.date = date '2017-04-01';
Otherwise:
select p.name, p.time, p.name, s.total, p.amount
(
select date, time, name, sum(amount) as amount
from payments
group by date, time, name
) p
join
(
select date, time, name, sum(total) as total
from sales
group by date, time, name
) s
on s.date = p.date and s.time = p.time and s.name = p.name
where p.date = date '2017-04-01';

SQL JOIN : Prefix fields with table name

I have the following tables
CREATE TABLE `constraints` (
`id` int(11),
`name` varchar(64),
`type` varchar(64)
);
CREATE TABLE `groups` (
`id` int(11),
`name` varchar(64)
);
CREATE TABLE `constraints_to_group` (
`groupid` int(11),
`constraintid` int(11)
);
With the following data :
INSERT INTO `groups` (`id`, `name`) VALUES
(1, 'group1'),
(2, 'group2');
INSERT INTO `constraints` (`id`, `name`, `type`) VALUES
(1, 'cons1', 'eq'),
(2, 'cons2', 'inf');
INSERT INTO `constraints_to_group` (`groupid`, `constraintid`) VALUES
(1, 1),
(1, 2),
(2, 2);
I want to get all constraints for all groups, so I do the following :
SELECT groups.*, t.* FROM groups
LEFT JOIN
(SELECT * FROM constraints
LEFT JOIN constraints_to_group
ON constraints.id=constraints_to_group.constraintid) as t
ON t.groupid=groups.id
And get the following result :
id| name | id | name type groupid constraintid
-----------------------------------------------------
1 | group1 | 1 | cons1 | eq | 1 | 1
1 | group1 | 2 | cons2 | inf | 1 | 2
2 | group2 | 2 | cons2 | inf | 2 | 2
What I'd like to get :
group_id | group_name | cons_id | cons_name | cons_type | groupid | constraintid
-------------------------------------------------------------------------------------
1 | group1 | 1 | cons1 | eq | 1 | 1
1 | group1 | 2 | cons2 | inf | 1 | 2
2 | group2 | 2 | cons2 | inf | 2 | 2
This is an example, in my real case my tables have much more columns so using the SELECT groups.name as group_name, ... would lead to queries very hard to maintains.
Try this way
SELECT groups.id as group_id, groups.name as group_name ,
t.id as cons_id, t.name as cons_name, t.type as cons_type,
a.groupid , a.constraintid
FROM constraints_to_group as a
JOIN groups on groups.id=a.groupid
JOIN constraints as t on t.id=a.constraintid
The only difference I see are the names of the columns? Use for that mather an AS-statement.
SELECT
groups.id AS group_id,
groups.name AS group_name,
t.id AS cons_id,
t.name AS cons_name,
t.groupid, t.constraintid
FROM groups
LEFT JOIN
(SELECT * FROM constraints
LEFT JOIN constraints_to_group
ON constraints.id=constraints_to_group.constraintid) as t
ON t.groupid=groups.id
Besides, a better join-construction is:
SELECT G.id AS group_id,
G.name AS group_name,
CG.id AS cons_id,
CG.name AS cons_name,
C.groupid, C.constraintid
FROM constraints_to_group CG
LEFT JOIN constraints C
ON CG.constraintid = C.id
LEFT JOIN groups G
ON CG.groupid = G.id;
Possible duplicate of this issue

how to perform complex sql query using a view, rather than by relying on an intermediate (concrete) table

SQL fans:
I'm trying to get dredge up some of my rusty sql skills by playing with the following use case:
say we have are a cable company, and have db tables that track:
tv shows,
customers who watch our shows, and
watch events (date when a particualr customer watched a particular show).
And let's say we want a report of how many views each show received, and the average age of
the viewer. The key thing I realized here is that if the same person watches a show X twice on different dates
we can't have this person's age contribute two times to the 'average age of viewer for show X' calculation.
First I define my tables and stick some data in them (this is mysql syntax, b.t.w):
drop table if exists shows ;
create table shows (
showid int not null,
showname varchar(256) not null,
primary key (showid)
);
drop table if exists cust ;
create table cust (
custid int not null,
custname varchar(256) not null,
age int not null,
primary key (custid)
);
drop table if exists watched ;
create table watched (
date int not null,
showid int not null,
custid int not null,
primary key (custid, showid, date)
);
insert into shows values (1, 'bingo');
insert into shows values (2, 'animals');
insert into cust values (1, 'joe', 20);
insert into cust values (2, 'bob', 30);
insert into cust values (3, 'mary', 40);
insert into cust values (4, 'lou', 20);
# date / show / cust
insert into watched values (1, 1, 1);
insert into watched values (1, 1, 2);
insert into watched values (1, 1, 3);
insert into watched values (2, 2, 2);
insert into watched values (2, 1, 1);
insert into watched values (3, 1, 1);
insert into watched values (4, 1, 1);
insert into watched values (1, 1, 4);
now I create a query that joins up the tables and gives me the showname, and age of each customer
who viewed a show.
select date, shows.showid, cust.custid, showname, age from
-> watched
-> inner join
-> cust
-> on cust.custid = watched.custid
-> inner join
-> shows
-> on shows.showid = watched.showid ;
+------+--------+--------+----------+-----+
| date | showid | custid | showname | age |
+------+--------+--------+----------+-----+
| 1 | 1 | 1 | bingo | 20 |
| 2 | 1 | 1 | bingo | 20 |
| 3 | 1 | 1 | bingo | 20 |
| 4 | 1 | 1 | bingo | 20 |
| 1 | 1 | 2 | bingo | 30 |
| 1 | 1 | 3 | bingo | 40 |
| 1 | 1 | 4 | bingo | 20 |
| 2 | 2 | 2 | animals | 30 |
+------+--------+--------+----------+-----+
8 rows in set (0.00 sec)
But note that customer id 1 appears multiple times as the watcher
of the 'bingo' show, and i want him counted only once.
so, i create a query that lists shows and customers who viewed them,
but only counts each customer one time.
mysql> select age, showname, showid, custid from
-> ( select date, shows.showid, cust.custid, showname, age from
-> watched
-> inner join
-> cust
-> on cust.custid = watched.custid
-> inner join
-> shows
-> on shows.showid = watched.showid
-> ) as VIEWS
-> group by custid, showname;
+-----+----------+--------+--------+
| age | showname | showid | custid |
+-----+----------+--------+--------+
| 20 | bingo | 1 | 1 |
| 30 | animals | 2 | 2 |
| 30 | bingo | 1 | 2 |
| 40 | bingo | 1 | 3 |
| 20 | bingo | 1 | 4 |
+-----+----------+--------+--------+
5 rows in set (0.00 sec)
Next -- and (HERE'S THE THING I HOPE YOU CAN ADVISE ME ON)...
I tried to create a view that gives me the name of each show viewed, the average age of those who watched the show, and the show id.
I planned to join that with a query that gave me a count of the views for each show. But the view creation failed as shown:
mysql> create view viewages as
-> select showname, avg(age), showid
-> from
-> (select age, showname, showid, custid from
-> ( select date, shows.showid, cust.custid, showname, age from
-> watched
-> inner join
-> cust
-> on cust.custid = watched.custid
-> inner join
-> shows
-> on shows.showid = watched.showid
-> ) as VIEWS
-> group by custid, showname)
-> as DISTINCT_CUST_VIEWS
-> group by showname;
ERROR 1349 (HY000): View's SELECT contains a subquery in the FROM clause
OK.. so that didnt' work. I got it to work, but the way I did it seems cheesy. I use an intermediate table.
Is there a sql rock star who can show me a better way to do this, without the table.. Maybe with a view, like I tried
to create, or something even better.?
Here is my lame solution:
drop table if exists viewage ;
create table viewage (
showname varchar(256) not null,
avg_age float not null,
showid int not null
);
insert into viewage
select showname, avg(age), showid
from
(select age, showname, showid, custid from
( select date, shows.showid, cust.custid, showname, age from
watched
inner join
cust
on cust.custid = watched.custid
inner join
shows
on shows.showid = watched.showid
) as VIEWS
group by custid, showname)
as DISTINCT_CUST_VIEWS
group by showname;
## Finally join the table with average age for each show with a query that does the count of views for each show:
drop table if exists viewage ;
create table viewage (
showname varchar(256) not null,
avg_age float not null,
showid int not null
);
insert into viewage
select showname, avg(age), showid
from
(select age, showname, showid, custid from
( select date, shows.showid, cust.custid, showname, age from
watched
inner join
cust
on cust.custid = watched.custid
inner join
shows
on shows.showid = watched.showid
) as VIEWS
group by custid, showname)
as DISTINCT_CUST_VIEWS
group by showname;
select count(*), showname, avg_age from
watched
inner join
viewage
on viewage.showid = watched.showid
group by showname;
+----------+----------+---------+
| count(*) | showname | avg_age |
+----------+----------+---------+
| 1 | animals | 30 |
| 7 | bingo | 27.5 |
+----------+----------+---------+
2 rows in set (0.00 sec)
Thanks in advance for your help !
-chris
I tried to create a view that gives me the name of each show viewed,
the average age of those who watched the show, and the show id.
The error message is clear--Subqueries cannot be used in the FROM clause of a view.
Here's one way to approach this particular problem.
-- Age of each show's customers.
create or replace view show_cust_ages as
select distinct watched.showid, cust.custid, cust.age
from watched
inner join cust on cust.custid = watched.custid;
-- Average age of show's customers. This queries the previous view.
create or replace view show_avg_ages as
select showid, avg(age) avg_age
from show_cust_ages
group by showid;
-- Your goal.
create or replace view show_name_avg_ages as
select t1.showid, t2.showname, t1.avg_age
from show_avg_ages t1
inner join shows t2 on t2.showid = t1.showid;
In production, I'd spend more time thinking about the names of things than I did here.
You should know that in MySQL, views based on views might perform poorly on big tables.