Mysql join two tables sum, where and group by - mysql

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';

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

Select all orders with the specified status saved in another table (PDO)

At the moment i have all informations of a order in one table, including the order status.
In the future i will have a new table "status" to make a order history.
My tables currently look like this (simplified):
Table "orders":
id
date
name
10001
2021-08-24 16:47:52
Surname Lastname
10002
2021-08-30 17:32:05
Nicename Nicelastname
Table "status":
id
order_id
statusdate
status
1
10001
2021-08-24 16:47:52
new
2
10002
2021-08-30 17:32:05
new
3
10001
2021-08-26 13:44:11
pending
4
10001
2021-09-02 10:01:12
shipped
My problem is: At this moment i can select all orders with status "shipped" to list them like that:
$sql = $pdo->prepare("SELECT * FROM orders WHERE status = ?");
(? is my status, e.g. "shipped")
I know i have to use LEFT JOIN to combine the two tables (correct? or is there a better/easier way?), but i have absolutely no idea how i can select all orders with status X, because the "status" table can have multiple entries per order_id... So the statement must select only the newest entrie!?
Iy you have no 1 timestamps with the same date, you can use the first query the second is in case you can have multiple timestampo for the same order
CREATE TABLE orders
(`id` int, `date` varchar(19), `name` varchar(21))
;
INSERT INTO orders
(`id`, `date`, `name`)
VALUES
(10001, '2021-08-24 16:47:52', 'Surname Lastname'),
(10002, '2021-08-30 17:32:05', 'Nicename Nicelastname')
;
CREATE TABLE status
(`id` int, `order_id` int, `statusdate` varchar(19), `status` varchar(7))
;
INSERT INTO status
(`id`, `order_id`, `statusdate`, `status`)
VALUES
(1, 10001, '2021-08-24 16:47:52', 'new'),
(2, 10002, '2021-08-30 17:32:05', 'new'),
(3, 10001, '2021-08-26 13:44:11', 'pending'),
(4, 10001, '2021-09-02 10:01:12', 'shipped')
,
(5, 10001, '2021-09-02 10:01:13', 'shipped')
;
select o.`id` FROM orders o
| id |
| ----: |
| 10001 |
| 10002 |
select o.`id`,o.`date`, o.`name`,s.`statusdate`
from orders o join status s on o.`id` = s.order_id
where s.`status` = "shipped"
AND s.`statusdate` = (SELECT MAX(`statusdate`) FROM `status` WHERE order_id = o.`id` AND `status` = "shipped")
order by o.`id` desc
id | date | name | statusdate
----: | :------------------ | :--------------- | :------------------
10001 | 2021-08-24 16:47:52 | Surname Lastname | 2021-09-02 10:01:13
SELECT
id, `date`, `name`,`statusdate`
FROM
(SELECT `date`, `name`,`statusdate`,IF( `id` = #id,#rownum := #rownum +1,#rownum :=1) rn, #id := `id` as id
FROM (select o.`id`,o.`date`, o.`name`,s.`statusdate`
from orders o join status s on o.`id` = s.order_id
where s.`status` = "shipped"
order by o.`id` desc,s.`statusdate` DESC) t2 ,(SELECT #id := 0, #rownum:=0) t1) t2
WHERE rn = 1
id | date | name | statusdate
----: | :------------------ | :--------------- | :------------------
10001 | 2021-08-24 16:47:52 | Surname Lastname | 2021-09-02 10:01:13
db<>fiddle here

Get all orders from database - only one row returned

I'm trying to get tables to link together to return the email, Order Number, Date, Total Cost and Number of items returned but of the two rows of data I expect to be returned, only one is. The tables are as follows;
Table: Customer
+------------+---------------------+-----------------+
| CustomerID | EmailAddress | DeliveryAddress |
+------------+---------------------+-----------------+
| BLF1 | FredBloggs#NT.co.uk | NULL |
| SMJ2 | JoeSmith#NT.co.uk | NULL |
+------------+---------------------+-----------------+
Table: Item
+----------+-------------+-----------+
| ItemCode | Description | UnitPrice |
+----------+-------------+-----------+
| 1234 | Ring Binder | 1.5 |
| 3456 | Stapler | 2.99 |
| 8967 | Divider | 0.5 |
| 9684 | Scissors | 1.99 |
+----------+-------------+-----------+
Table: ItemOrder
+-------------+----------+---------------+
| OrderNumber | ItemCode | OrderQuantity |
+-------------+----------+---------------+
| 012367 | 1234 | 3 |
| 012367 | 3456 | 1 |
| 012367 | 8967 | 4 |
| 034231 | 3456 | 4 |
| 034231 | 9684 | 2 |
+-------------+----------+---------------+
Table: OnlineOrder
+-------------+------------+------------+
| OrderNumber | CustomerID | OrderDate |
+-------------+------------+------------+
| 012367 | BLF1 | 2009-05-01 |
| 034231 | SMJ2 | 2009-05-03 |
+-------------+------------+------------+
The query I run is:
SELECT Customer.EmailAddress,
OnlineOrder.OrderNumber,
OnlineOrder.OrderDate,
Count(Item.ItemCode) as TotalItems,
FORMAT(SUM(Item.UnitPrice*ItemOrder.OrderQuantity), 2) as TotalPrice
FROM Customer,
Item,
OnlineOrder,
ItemOrder
WHERE OnlineOrder.OrderNumber = ItemOrder.OrderNumber
AND Customer.CustomerID = OnlineOrder.CustomerID
AND Item.ItemCode = ItemOrder.ItemCode;`
This only returns one record;
+---------------------+-------------+------------+------------+------------+
| EmailAddress | OrderNumber | OrderDate | TotalItems | TotalPrice |
+---------------------+-------------+------------+------------+------------+
| FredBloggs#NT.co.uk | 012367 | 2009-05-01 | 5 | 25.43 |
+---------------------+-------------+------------+------------+------------+
Why won't the query return the second order, with the details and purchaser of that order?
Here is your same query formatted with proper join/on clause. I also changed the alias references for each table. Especially for larger table names, it can significantly simplify the readability.
SELECT
C.EmailAddress,
OO.OrderNumber,
OO.OrderDate,
Count(I.ItemCode) as TotalItems,
FORMAT(SUM(I.UnitPrice * IO.OrderQuantity), 2) as TotalPrice
FROM
OnlineOrder OO
JOIN ItemOrder IO
ON OO.OrderNumber = IO.OrderNumber
JOIN Item I
ON IO.ItemCode = I.ItemCode
JOIN Customer C
ON OO.CustomerID = C.CustomerID
GROUP BY
OO.OrderNumber
The only thing I added was the GROUP BY clause. Since you want the totals on a per-order basis, the group is by the order. If you wanted the total to be per the customer, you would need to remove the explicit order number and date to see ALL the order items and all line item totals.
I reproduced your data and SQL using ANSI joins and got the right answer, two records. I have no idea what you did wrong.
CREATE TABLE customer
(
customerid VARCHAR (10)
, emailaddress VARCHAR (50)
, deliveryaddress VARCHAR (100)
);
INSERT INTO customer (
customerid, emailaddress, deliveryaddress
)
VALUES (
'BLF1', 'FredBloggs#NT.co.uk', NULL
);
INSERT INTO customer (
customerid, emailaddress, deliveryaddress
)
VALUES (
'SMJ2', 'JoeSmith#NT.co.uk', NULL
);
COMMIT;
CREATE TABLE item
(
itemcode NUMBER
, description VARCHAR (50)
, unitprice DECIMAL (10, 2)
);
INSERT INTO item (
itemcode, description, unitprice
)
VALUES (
1234, 'Ring Binder', 1.5
);
INSERT INTO item (
itemcode, description, unitprice
)
VALUES (
3456, 'Stapler', 2.99
);
INSERT INTO item (
itemcode, description, unitprice
)
VALUES (
8967, 'Divider', .5
);
INSERT INTO item (
itemcode, description, unitprice
)
VALUES (
9684, 'Scissors', 1.99
);
COMMIT;
CREATE TABLE itemorder
(
ordernumber VARCHAR (10)
, itemcode INTEGER
, orderquantity INTEGER
);
INSERT INTO itemorder (
ordernumber, itemcode, orderquantity
)
VALUES (
'012367', 1234, 3
);
INSERT INTO itemorder (
ordernumber, itemcode, orderquantity
)
VALUES (
'012367', 3456, 1
);
INSERT INTO itemorder (
ordernumber, itemcode, orderquantity
)
VALUES (
'012367', 8967, 4
);
INSERT INTO itemorder (
ordernumber, itemcode, orderquantity
)
VALUES (
'034231', 3456, 4
);
INSERT INTO itemorder (
ordernumber, itemcode, orderquantity
)
VALUES (
'034231', 9684, 2
);
COMMIT;
CREATE TABLE onlineorder
(
ordernumber VARCHAR (10)
, customerid VARCHAR (10)
, orderdate DATE
);
INSERT INTO onlineorder (
ordernumber, customerid, orderdate
)
VALUES (
'012367', 'BLF1', DATE '2009-05-01'
);
INSERT INTO onlineorder (
ordernumber, customerid, orderdate
)
VALUES (
'034231', 'SMJ2', DATE '2009-05-03'
);
COMMIT;
SELECT customer.emailaddress
, onlineorder.ordernumber
, onlineorder.orderdate
, COUNT (item.itemcode) AS totalitems
, SUM (item.unitprice * itemorder.orderquantity) AS totalprice
FROM customer
INNER JOIN onlineorder ON customer.customerid = onlineorder.customerid
INNER JOIN itemorder ON itemorder.ordernumber = onlineorder.ordernumber
INNER JOIN item ON item.itemcode = itemorder.itemcode
-- WHERE onlineorder.ordernumber = itemorder.ordernumber
-- AND customer.customerid = onlineorder.customerid
-- AND item.itemcode = itemorder.itemcode
GROUP BY customer.emailaddress, onlineorder.ordernumber, onlineorder.orderdate;

Mysql with join statement

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

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