Rails MySQL subquery with arithmetic - mysql

I have 3 models:
Product
Daily Log
Click
The tables look like:
Products
id
,
Daily Logs
id | product_id | price
,
Clicks
id | product_id | daily_log_id
For reporting, I would like to get all products that have accrued more than x cost (based on price per click).
In Ruby, the code might look like
products.select do | p |
cost = 0
p.daily_logs.each do | dlog |
cost += dlog.clicks.count * dlog.price
end
cost > x ? true : false
end
How might I write a similar query for mysql?
Thanks!

I think this is what you are looking for:
SELECT dl.product_id AS `product_id`, COUNT(c.id) AS `total_clicks`, SUM(dl.price) AS `total_price`
FROM daily_logs AS dl
INNER JOIN clicks AS c on dl.id = c.daily_log_id
WHERE `total_price` > ?
GROUP BY `product_id`
Note I didn't join the products table as it only has an id field, which is available in the daily_logs table as well. If the products table actually has more fields with information that you need, you could join that table as well and select the needed fields.

Related

MYSQL How find order products matching current storage products (compare two tables for 100% match)

i'm looking for solution to check if multiple rows from one table have match in other table. In my situation i need to check if items from orders are in storage. Currently I use php to check orders - script is taking open orders and foreach one by one to check storage. It's generating quite a lot of queries and it's not efficient at all and i'm looking for solution to do this via sql query.
Desired result should be:
OrderId | Date | Products
1002/02 | 2020/08/16 | 1x Ipod; 2x battery; 9x some item;
0333/4 | 2020/06/22 | 10x shelf
Storage products table
id | id_product | quantity
Orders
id | reference | id_status | created_at
Order Products
Id | id_order | quantity | id_storage_product
I've written some code to generate table visible above but result it's not even close to desired.
select('orders.id', orders.created_at','orders.reference', 'storage_products.id as storageProductId')
->join('order_products', 'orders.id', '=', 'order_products.id_order')
->join('storage_products', 'order_products.id_product', '=', 'storage_products.id_product')
->where('storage_products.quantity', '>=', 'order_products.quantity')
->whereIn('orders.id_status', array(1, 2)) //get new orders/ open
->where('order_products.id_storage_product', null)
->groupBy('orders.id');
Clean sql:
SELECT `orders`.`id`,
`orders`.`created_at`,
`orders`.`reference`,
`storage_products`.`id` AS `storageProductId`,
`order_products`.`id_order`
FROM `orders`
INNER JOIN `order_products`
ON `orders`.`id` =
`order_products`.`id_order`
INNER JOIN `storage_products`
ON `order_products`.`id_product` =
`storage_products`.`id_product`
WHERE `storage_products`.`quantity` >=
'order_products.quantity'
AND `orders`.`id_status` IN ( 1, 2 )
AND `order_products`.`id_storage_product` IS NULL
GROUP BY `orders`.`id`
ORDER BY `orders`.`id` ASC
So code should find open orders (id_status); where storage quantity is equal or greater than product in order; where id_storage_products is null (means product bought on website but it was not in storage when ordered).
Upper query result is wrong because it showed me partial match to storage - even without checking quantity (some products have 0 but still displayed).
For any help many thanks
EDIT: fiddle sample: https://www.db-fiddle.com/f/6jKvKXPYvsLeXgm3Qv1nHu/0
Your query contains the condition:
AND `order_products`.`id_storage_product` IS NULL
but in your sample data all values are 0.
So instead I use COALESCE() to cover both cases.
Also I removed the condition:
AND `orders`.`id_status` IN ( 1, 2 )
because the column id_status is not included in the definition of the table orders in your sample data.
This query works:
SELECT o.id,
o.reference,
o.created_at,
GROUP_CONCAT(op.quantity, 'x', op.id_product separator ' ;') products
FROM orders o
INNER JOIN order_products op ON o.id = op.id_order
INNER JOIN storage_products sp ON op.id_product = sp.id_product
WHERE sp.quantity >= op.quantity AND COALESCE(op.id_storage_product, 0) = 0
GROUP BY o.id, o.reference, o.created_at
ORDER BY o.id ASC
See the demo.
Results:
| id | reference | created_at | products |
| --- | --------- | ------------------- | ------------- |
| 2 | 345554/02 | 2020-08-22 00:00:00 | 3x188 ; 1x155 |
If you also join the table products (I assume there is such a table) you can get the names of the products instead of their ids.
I tried the following query on the db-fiddle link and this works.
SELECT
orders.reference, orders.created_at, order_products.id_product
FROM
storage_products
LEFT JOIN
order_products ON storage_products.id_product = order_products.id_product
LEFT JOIN
orders ON orders.id = order_products.id_order;
What I did in the query is calling all storage_products with the same id_product in order_products and proceed to call all orders in the called order_products.

Is there a way to calculate total from a MySQL Query?

I'm trying to build a cart system. So far, I can pull the user cart data easily with some join and even calculate the total price based on unit price and quantity user has in another column. However, I wanted to know if there is a way to actually compute the total amount owed directly from an SQL query. Allow me to showcase an example. Here are my tables:
products
product_id | product_name | product_price | stock
1 | Pasta | 10 | 50
2 | Bread | 2 | 100
cart
user_id | product_id | quantity
1 | 1 | 3
1 | 2 | 2
My Current Query
SELECT
`cart`.`product_id`,
`cart`.`user_id`,
`cart`.`quantity`,
`products`.`product_price` as `unit_price`,
`products`.`product_name`,
`cart`.`quantity` * `products`.`product_price` as `Total`
FROM `cart`
INNER JOIN `products` ON `products`.`product_id` = `cart`.`product_id`
WHERE `cart`.`u_id` = 1;
As you can see, the query above will work and return me all the products in the cart of a specific user and add the Total column to the result with the total price for each item.
Now, if I want to calculate the gross total, I have to read each row in my PHP code and do the total. Although I can do this, I was wondering if MySQL has a way of returning just the gross total directly through a single query.
Bonus question: Is a cart table structure like the one above good enough?
You can use "SUM" Operator in MYSQL to get the value from database directly.
Here is a documentation where you can get more info:
https://www.mysqltutorial.org/mysql-sum/
Regarding the code here is an example:
SELECT SUM(`cart`.`quantity` * `products`.`product_price`) as `CartTotal`
FROM `cart`
INNER JOIN `products` ON `products`.`product_id` = `cart`.`product_id`
WHERE `cart`.`u_id` = 1;
You can calculate total cart cost using SUM aggregation function.
SELECT SUM(`cart`.`quantity` * `products`.`product_price`) as `CartTotal`
FROM `cart`
INNER JOIN `products` ON `products`.`product_id` = `cart`.`product_id`
WHERE `cart`.`u_id` = 1;
Also you can use UNION construction for combine cart total result to main query like:
-- Get cart content for user
SELECT
`cart`.`product_id`,
`cart`.`user_id`,
`cart`.`quantity`,
`products`.`product_price` as `unit_price`,
`products`.`product_name`,
`cart`.`quantity` * `products`.`product_price` as `Total`
FROM `cart`
INNER JOIN `products` ON `products`.`product_id` = `cart`.`product_id`
WHERE `cart`.`u_id` = 1
UNION -- Union summary row to cart results
SELECT
NULL as `product_id`, -- set NULL for non aggregated field
`cart`.`user_id`,
COUNT(*), -- calculate items count in cart
NULL as `unit_price`, -- set NULL for non aggregated field
'Total' as `product_name`, -- set 'Total' string as product name
SUM(`cart`.`quantity` * `products`.`product_price`) as `CartTotal` -- calculate total cart cost
FROM `cart`
INNER JOIN `products` ON `products`.`product_id` = `cart`.`product_id`
WHERE `cart`.`u_id` = 1;

Find the Maximum number in mysql

How do I find the most popular book among all customers from the table above? (cid = 'customer id')?
I have
select Title, sum(c.quantity) from cart c group by c.ISBN;
which gives me the following results
+-----------------------------------------+-----------------+
| Title | sum(c.quantity) |
+-----------------------------------------+-----------------+
| Writing Skills | 5 |
| Fundamentals of Database Systems | 2 |
| Database Management Systems | 5 |
| Data Mining, Practical Machine Learning | 4 |
+-----------------------------------------+-----------------+
I know the Max() function in mysql can achieve my goal, but I do not know to implement Max() and Sum() together.
Thanks!
To get most popular book/books you can use following query
select c.ISBN,c.Title, sum(c.quantity) soldQuantity
from cart c
group by c.ISBN,c.Title
having soldQuantity = (
select sum(quantity)
from cart
group by ISBN,Title
order by sum(quantity) desc
limit 1
)
Note there can be more than 1 books which share same highest quantity
The following SQL statement should give you the book with the most quantity
SELECT
Title,
sum(c.quantity) AS total_count
FROM
cart c
GROUP BY
c.ISBN
ORDER BY
total_count DESC
LIMIT 1
Note: You really should put the books in a seperate table titled "books" with two columns, "id" and "title". You can then change the "title" column in your original table to "book_id" and make it a foreign key to books.id. This will greatly improve the speed of your SQL calls.
Hope this helps!
Hi one approach would be using sub queries like this:
SELECT TITLE, MAX (SUMMATION)
FROM (SELECT TITLE, SUM (C.QUANTITY) SUMMATION
FROM CART C
GROUP BY TITLE, C.ISBN) LIST
GROUP BY TITLE, SUMMATION

Update table with data from multiple tables

I'm stuck on this one. I have three tables:
Table 1:
**ORDERS**
| ORDER_NO | PRODUCT_NO | CLIENT_NO | UNITS | ORDER_DATE |
Table 2:
**CLIENTS**
| CLIENT_NO | NAME | LOCATION | SELLER_NO | OWES | OVERPAID | CREDIT_LIMIT |
Table 3:
**PRODUCTS**
| PRODUCT_NO | DESCRIPTION | UNIT_PRICE | AVAILABLE_STOCK |
Now, what I have to do is to update column OWES in table CLIENTS so it contains total amount of money of all the orders they made.
This is as far as I got:
update CLIENTS set OWES = (select sum(o.UNITS) from ORDERS o where CLIENTS.CLIENT_NO = o.CLIENT_NO);
That seems to work just fine to get a total number of orders, but than I have to multiply it by the price of given item (whichever the order was for) and I get myself confused.
I tried for example:
update CLIENTS set OWES = ( select sum(o.UNITS) from ORDERS o where CLIENTS.CLIENT_NO = o.CLIENT_NO)*(select UNIT_PRICE from PRODUCTS where PRODUCT_NO= any(select PRODUCT_NO from ORDERS));
But that returns ERROR 1242 (21000): Subquery returns more than 1 row
What am I doing wrong? Would it be better to use update CLIENTS as ( some complicated sub query goes here) ?
Can anyone help me out and be so kind to throw in some explanation why such solution and not some other? It just seem like I didn't get something on more basic level and now I'm struggling.
Thanks in advance.
I think you can just do a join in the subquery and do the appropriate aggregation:
update CLIENTS
set OWES = (select sum(o.UNITS * p.unit_price)
from ORDERS o join
products p
on o.product_no = p.product_no
where CLIENTS.CLIENT_NO = o.CLIENT_NO
);
Your syntax is a little off. The general syntax for updating from other tables is:
UPDATE table1
JOIN table2 ON table2.mycolumn = table1.mycolumn
JOIN (
SELECT foo, SUM(bar) as sumbar FROM table3) table3sum ON table3sum.foo = table1.foo
SET
table1.foo = table2.bar,
table1.baz = table3sum.sumbar

How can I use JOIN in order to retrieve rows according to WHERE statements in distinct tables?

I got a database which controls some orders, each order has many items, naturally, an order is submitted by a user which belongs to a department.
Each user belongs to certain role, so orders are reviewed consecutively by distinct users with distinct higher roles. I control the next user who should do something with the order with a delegate field.
I will describe some of my tables with its relevant fields:
Department:
- id
- name
User:
- id
- name
- department_id
- role
Order:
- id
- user_id
- delegate // Here I got an enum for the multiple roles a user can get
Revision:
- id
- order_id
- user_id
- operation
Query
So what I need is a query for retrieving all orders whose users belong to a given department_id and the delegate field equals one provided argument OR where there are records at revisions table with user_id equal to another provided argument
What I got ...
With the aid of Eloquent ORM I got this style of query for first part of my needed result:
select `orders`.*, `users`.`department_id` from `orders` inner join `users` on `users`.`id` = `orders`.`user_id` where `users`.`department_id` = '1' and `delegate` = 'GERENTE DE DEPARTAMENTO' limit 2 offset 0
In order to include the OR where there are records at revisions table with user_id equal to another provided argument part of the query I tried to append to the query:
JOIN orders on revisions WHERE user_id = 5
But didn't work ...
The whole query I tried was:
select `orders`.*, `users`.`department_id` from `orders` inner join `users` on `users`.`id` = `orders`.`user_id` where `users`.`department_id` = '1' and `delegate` = 'GERENTE DE DEPARTAMENTO' JOIN orders on revisions WHERE user_id = 5
Example
I'm using hardcoded arguments in my queries examples as they are from a real example ... In that case I need to get only the whole orders whose users belong to deparment_id 1, delegate is 'GERENTE DE DEPARTAMENTO' OR if there are rows at revisions table which got the user_id set to 5
Orders:
+----+---------+-------------------------+
| id | user_id | delegate |
+----+---------+-------------------------+
| 1 | 4 | GERENTE DE DEPARTAMENTO |
| 2 | 2 | SUPERVISOR DE COMPRAS |
+----+---------+-------------------------+
Revisions:
+----+---------+----------+
| id | user_id | order_id |
+----+---------+----------+
| 1 | 5 | 2 |
| 2 | 5 | 2 |
+----+---------+----------+
A working query should take orders 1 and 2 only. How can I accomplish it? I'm interested about the MySQL needed and is a PLUS if it's Eloquent based query.
If I get you right, ie. you need:
orders that delegate= $delegate AND have user_id= x if that user got as department_id= $department_id at users, OR revisions by user with id = $userId
then this is the Eloquent way:
Order::where('delegate', $delegate)
->whereHas('user', function ($q) use ($depId) {
$q->where('department_id', $depId);
})->orWhereHas('revisions', function ($q) use ($userId) {
$q->where('user_id', $userId);
})
->get();
You could use left joins.
Your query might look something like this:
select distinct `orders`.*, `users`.`department_id` from `orders`
left join `users` on `order`.`user_id`=`users`.`id`
left join `revisions` on `order`.`id`=`revisions`.`order_id`
and `revisions`.`user_id`=?
where `users`.`department_id`=?
and (`order`.`delegate`=? or `revisions`.`id` is not null)