I have an apartment calendar table for displaying prices to users for each day.
On this table, some of the apartments include rooms (id_room) to rent:
table columns / records example:
id_apart | id_room | date | price | promo_price
1 | 1 | 03-03-2013 | 20.00 | 0
1 | 1 | 04-03-2013 | 20.00 | 0
1 | 2 | 03-03-2013 | 50.00 | 45.00
1 | 2 | 04-03-2013 | 50.00 | 45.00
I want to get as a result, a string that has 'price' and 'promo_price' concatenated with total SUM of the lowest prices found for the apartment / rooms between two dates.
This query is summing all room prices for the apartment and i can't figure out how to use MIN on this one, to sum ONLY the prices of the CHEAPEST room:
select
concat(sum(if(promo_price>0,promo_price,price)),
"---",
sum(price))
from
apart
where
id_apart=215
and date>= "2013-03-03"
and date<"2013-03-05"
The result from this query is:
90---140
Legend: first string number is sum of 'promo_price'; second number sum of 'price'
select concat(promo_price, "--", price) from apart
where id_apart=215 and date >= "2013-03-03" and date < "2013-03-05"
and (promo_price + price)) = (select min(promo_price + price) from apart);
Please try this
select concat(if(promo_price>0, 'true', sum(promo_price)),'---', sum(price)) from aprt where id_apart = 215 and date between "2013-03-03" and "2013-03-05"
i think is this what you looking for
select if(promo_price = 0,concat(sum(price),"---", sum(price)) ,sum(price)) as pri
from apart
where id_apart=1 and `date`>= "03-03-2013" and `date` < "05-03-2013"
and promo_price = 0
OUTPUT.
PRIS
40---40
obs note that i made id_apart=1 from your above example .
Is this answer u looking for?
select
concat(sum(if(promo_price>0,promo_price,price)),
"---",
sum(price)) from apart group by id_room
having sum(price) + sum(promo_price) =
(select min(sum(price) + sum(promo_price)) from apart where id_apart =1
group by id_room);
Output :40 --- 40
Related
I have a table of items that I track inventory on daily basis (mysql)
| item_id | qty | created_at |
| ------- | -------| ---------- |
| 1 | 5 | 2021-04-08 |
| 1 | 30 | 2021-04-07 |
| 1 | 100 | 2021-04-06 |
| 2 | 100 | 2021-04-08 |
| 2 | 0 | 2021-04-07 |
| 2 | 0 | 2021-04-06 |
| 3 | 0 | 2021-04-08 |
| 3 | 50 | 2021-04-07 |
| 3 | 100 | 2021-04-06 |
I wonder how can I write an SQL query that analyze all items in the table and response some insights
for ex.
item_id = 1 ---> item is almost out of stock (5% limit point)
item_id = 2 ---> item just returned to stock (was 0 yesterday new we have 100)
item_id = 3 ---> item is out of stock today
also if there is another ideas for monitor the stock insights I'll be happy to know..
Thanks for all the helpers.
Consider running SQL jobs on regular time schedule.
Item is almost out of stock (5% limit)
Well five percent of what total (I assume it's not always 100 pcs). Create table with item_id and threshold value. If current stock is lower or equal to threshold value report it.
Item just returned to stock.
currentDay
is a function to return actual date like '2021/04/08' I am not familiar with mySql function/syntax
select item_id from myTable where qty > 0 and created_at = currentDay
and item_id IN (select item_id from myTable where qty = 0 and created_at = currentDay-1
Item is out of stock today
select item_id from myTable where qty = 0 and created_at = currentDay
//Edit after comments
1st comment: In case you want to see item that today is out of stock and yesterday it was not, tweak the provided query. Switch qty in conditions.
select item_id from myTable where qty = 0 and created_at = currentDay
and item_id IN (select item_id from myTable where qty > 0 and created_at = currentDay-1
2nd comment: Consider this. If your initial stock is 2pcs at day D, stock increase by 1000 at D+1 and stock increase by 70 at D+2. In case you take your stock qty from day D, your 5% is 0.1 pcs.
First of all that won't happen, so you should round up to nearest whole number (qty = 1) and second the stock qty warning may not be relevant. When your total stock is 1072 pcs your warning will trigger when qty <= 1 since it's based on stock qty from first day.
Just use lag() and some conditional logic:
select t.*,
(case when qty <= 5
then 'Almost out of stock'
when qty > 0 and lag(qty) over (partition by item_id order by created_at) = 0
then 'Just returned to stock'
when qty = 0 and lag(qty) over (partition by item_id order by created_at) > 0
then 'Out of stock today'
when qty = 0
then 'Still out of stock. How long will this go on?'
else 'Okay'
end) as status
from t
I have a table of invoices that can be in multiple currencies that looks like this:
| id | issue_date | total | currency |
|----|------------|-------|----------|
| 1 | 2020-04-20 | 1234 | EUR |
| 2 | 2020-04-26 | 2345 | USD |
| 1 | 2020-04-27 | 9876 | EUR |
| 3 | 2020-04-28 | 3456 | RON |
And i have a currency table that holds currency exchange rates that looks like this:
| id | date | currency_id | rate |
|----|------------|-------------|---------|
| 1 | 2020-04-20 | EUR | 1 |
| 2 | 2020-04-20 | USD | 1.08600 |
| 3 | 2020-04-20 | RON | 4.83560 |
What I would like to achieve is to calculate each invoice price based on its issue_date, currency and a target currency.
All currency exchange rates are based on EUR so its value will always be 1. Currencies are updated daily but there are dates missing (during weekend exchange rates don't update) so calculation needs to be based on most recent exchange rate until invoice.issue_date
So what I tried was this:
SELECT
`i`.`id`,
`i`.`total`,
`i`.`currency`,
`exr1`.`rate` as `invoice_rate`,
`exr2`.`rate` AS `target_rate`,
`i`.`total` * `exr1`.`rate` as `euro_price`,
`i`.`total` * `exr1`.`rate` / `exr2`.`rate` AS `target_price`
FROM `invoices` as `i`
LEFT JOIN `exchange_rates` AS `exr1`
ON
`exr1`.`currency_id` = `i`.`currency` AND
`exr1`.`date` = `i`.`issue_date`
LEFT JOIN `exchange_rates` as `exr2`
ON
`exr2`.`currency_id` = 'RON' AND
`exr2`.`date` = `i`.`issue_date`
GROUP BY
`i`.`id`,
`invoice_rate`,
`target_rate`
ORDER BY `i`.`issue_date` DESC
Problem nr. 1
Because there are no exhange rates for the exact invoice dates I get null values. I tried changing the LEFT JOIN ON to something like exr1.date <= i.issue_date but GROUP BY invoice doesn't work anymore (i get duplicates).
Problem nr. 2
For rows that have exchange rates on that exact day I get wrong values because based on the target currency I need to either multiply or divide:
i.total * exr1.rate * exr2.rate AS usd_price vs i.total * exr1.rate / exr2.rate AS usd_price
https://www.db-fiddle.com/f/e5GnVnry5sAiXwbuScV6JT/19
This is a (rare) case where a dependent subquery is the way to go. Here's the overall query (https://www.db-fiddle.com/f/e5GnVnry5sAiXwbuScV6JT/21)
SELECT id,
total,
currency,
rate,
total / rate euro_price
FROM ( SELECT i.id,
i.total,
i.currency,
(SELECT e.rate
FROM exchange_rates e
WHERE e.currency_id = i.currency
AND e.date <= i.issue_date
ORDER BY e.date DESC
LIMIT 1) rate
FROM invoices i
) d
The dependent subquery is this:
SELECT e.rate
FROM exchange_rates e
WHERE e.currency_id = i.currency
AND e.date <= i.issue_date
ORDER BY e.date DESC
LIMIT 1
It finds the exchange rate for the most recent date equal to or before the issue_date. It's called dependent because it refers to column values in its outer query.
This isn't going to be fast. A covering index on exchange_rates(currency_id, date DESC, rate) will help. Like this.
CREATE INDEX lookup ON exchange_rates(currency_id, date DESC, rate);
I used a nested query so the outer query can simply refer to rate as a column when it needs to, rather than repeating the dependent subquery.
Also note I think you want to divide, not multiply, when computing your euro_price.
I left the second rate lookup to you.
**Pro tip* Only use the backtick marks when your table or column name is a reserved word in the query language. Your queries are MUCH easier to read without them.
I have the following tables:
Let's assume the first table represents a booking made by a customer for a hotel.
+------------+-----------+-----------+--------------+----------------+
| booking_id | client_id | room_type | arrival_date | departure_date |
+------------+-----------+-----------+--------------+----------------+
| 1 | 1 | 1 | 2016-05-30 | 2016-06-03 |
+------------+-----------+-----------+--------------+----------------+
The second table contains the prices for different types of room. The prices vary for different periods of time.
+---------------+-----------+------------+------------+-------+
| room_price_id | room_type | start_date | end_date | price |
+---------------+-----------+------------+------------+-------+
| 1 | 1 | 2016-03-01 | 2016-05-31 | 150 |
| 2 | 1 | 2016-06-01 | 2016-08-31 | 200 |
+---------------+-----------+------------+------------+-------+
My question is: how can I calculate the total cost for a booking if the price is not the same during the whole period of the booking? (in my case the total cost would be 700)
I read the following links: MySQL: Select all dates between date range and get table data matching dates and Select data from date range between two dates, but I don't get it on how to solve my problem since the entries in the table with the prices contain only date ranges and not explicit dates as in the first link.
The trick with your question is in formulating the join condition between the booking and prices tables. Once this is out of the way, a simple aggregation gets you the result you want. The join condition should be true whenever any point in a given price range falls within the range of the arrival or departure date.
Then, to count days we use the greater of the arrival date or the start date, and the lesser of the departure date or the end date.
SELECT
b.booking_id,
SUM((DATEDIFF(LEAST(b.departure_date, p.end_date),
GREATEST(b.arrival_date, p.start_date)) + 1) * p.price) AS total
FROM booking b
INNER JOIN prices p
ON b.room_type = p.room_type AND
(p.start_date BETWEEN b.arrival_date AND b.departure_date OR
p.end_date BETWEEN b.arrival_date AND b.departure_date)
GROUP BY b.booking_id;
The output for your sample data is 900. This is correct because:
2 days x 150 = 300
3 days x 200 = 600
total = 900
Demo here:
SQLFiddle
And here is another demo which highlights the case where a stay overlaps an entire price range.
I have some table in mysql database like this.
id | product | warehouse | price | date_shipping
------------------------------------------------
1 | Salt | 15 | 300 | 2017-03-08
2 | Salt | 15 | 300 | 2017-03-09
I want to SUM that column price with several condition. This is the condition.
From the product salt if the warehouse is have same id I don't want to SUM price value.
This is my second condition.
id | product | warehouse | price | date_shipping
------------------------------------------------
1 | Salt | 15 | 300 | 2017-03-08
2 | Salt | 18 | 300 | 2017-03-09
From the product salt if the warehouse is have different id I want to SUM price value.
This is the result what I want from the query.
From first condition :
salt | 15 | 300
From second condition :
salt | 15,18 | 600
This is query what I have doing.
SELECT product, GROUP_CONCAT(warehouse SEPARATOR ',') as warehouse, SUM(price)
FROM db_product
Somebody can help me with this ? Thank you.
Your problem requires two aggregations. The first one identifies, for each product in a warehouse, the record having the earliest shipping date. Then, a second aggregation is needed to group concatenate the warehouses for each product.
SELECT t.product,
GROUP_CONCAT(t.warehouse) AS warehouses,
SUM(t.price) AS total
FROM
(
SELECT t1.product,
t1.warehouse,
t1.price
FROM db_product t1
INNER JOIN
(
SELECT product, warehouse, MIN(date_shipping) AS min_date_shipping
FROM db_product
GROUP BY product, warehouse
) t2
ON t1.product = t2.product AND
t1.warehouse = t2.warehouse AND
t1.date_shipping = t2.min_date_shipping
) t
GROUP BY t.product
Output:
Demo here:
Rextester
I'm creating a query which will display projected qty versus qty sold on a monthly basis. Below are my two tables and third one is what i want as an output. The two table is almost the same, except that the date and creation date is in different format (i have converted this to output e.g 201301, so no worries about it).
As you have noticed in my output table, qty sold become 25.00 because it adds the qty if the date falls under the same month and year. I also need to consider these three important conditions:
to display rows with projected qty but without sales (ex. item 206)
To display rows without projected qty but with sales (ex. item 312)
to display row with projection and with sales (ex. item001 & 040)
I don't know if i have to used join or union to be able to achieve my desired output.
Table A (Sales Table)
item code Sold date
001 cust001 10.00 2013-01-20
001 cust001 15.00 2013-01-25
040 cust045 16.00 2013-04-07
312 cust001 20.00 2013-03-13
Table B (Projection Table)
item Custcode ProjectedQty Creation Date
001 cust001 20.00 2013-01-01
040 cust045 50.00 2013-04-01
206 cust121 60.00 2013-04-01
Output
item Custcode sold Date ProjectedQty Creation Date
001 cust001 25.00 201301 20.00 201301
312 cust001 20.00 201303 null null
040 cust045 16.00 201304 50.00 201304
206 cust121 null null 60.00 201304
Thanks guys for helping.
Try
SELECT COALESCE(p.item, s.item) item,
COALESCE(p.code, s.code) code,
sold,
CONCAT(s.year, LPAD(s.month, 2, '0')) date,
ProjectedQty,
CONCAT(p.year, LPAD(p.month, 2, '0')) creationdate
FROM
(
SELECT item, code, YEAR(`date`) year, MONTH(`date`) month
FROM sales
UNION
SELECT item, custcode, YEAR(`creationdate`), MONTH(`creationdate`)
FROM projection
) i LEFT JOIN
(
SELECT item, code, SUM(sold) sold, YEAR(`date`) year, MONTH(`date`) month
FROM sales
GROUP BY item, code, YEAR(`date`), MONTH(`date`)
) s ON i.item = s.item
AND i.code = s.code
AND i.year = s.year
AND i.month = s.month LEFT JOIN
(
SELECT item, custcode code, SUM(ProjectedQty) ProjectedQty, YEAR(`creationdate`) year, MONTH(`creationdate`) month
FROM projection
GROUP BY item, custcode, YEAR(`creationdate`), MONTH(`creationdate`)
) p ON i.item = p.item
AND i.code = p.code
AND i.year = p.year
AND i.month = p.month
ORDER BY code, item
Output:
| ITEM | CODE | SOLD | DATE | PROJECTEDQTY | CREATIONDATE |
------------------------------------------------------------------
| 001 | cust001 | 25 | 201301 | 20 | 201301 |
| 312 | cust001 | 20 | 201303 | (null) | (null) |
| 040 | cust045 | 16 | 201304 | 50 | 201304 |
| 206 | cust121 | (null) | (null) | 60 | 201304 |
SQLFiddle
Why not use a simple join for this?
SELECT * FROM `sales`, `projection` WHERE `code`=`custcode`;
Make a view of the query above, and using WHERE clause, you can alter the data.
Create a view using union. and then you can use the below query:
select *, sum(sold) as sold from view group by item
another is:
select *, sum(sold) from (
select item, code as Custcode, sold, date, 'ProjectedQty' as ProjectedQty, 'Creation Date' as Creation Date from table 1 union select item, Custcode, sold, Date, ProjectedQty, Creation Date, from table 2) group by item