Select MAX value from SQL table to apply in another table - php

My site is a Prestashop (1.5.6.2).
Some of my product can have a lower price according to the number of product ordered. And I'd like to mention somewhere the minimum price of the product (so I need the maximum amount of reduction to make this happen).
Table 1 (my price is in this table)
+------------+-------+
| id.product | price |
+------------+-------+
| 1 | 1000 |
+------------+-------+
Table 2 (my reduction is in this table)
+------------+--------+-----------+
| id.product | amount | reduction |
+------------+--------+-----------+
| 1 | 2 | 100 |
| 1 | 3 | 200 |
| 1 | 4 | 300 |
+------------+--------+-----------+
According to this is example, I would like to display:
Product 1 from 700 euros
1000 - 300 (which is the maximum reduction on product.id 1) = 700
(I'd like to UPDATE the existing price because this is a second field which I create actually called price_from but i didn't want to make the example too complicate)
This is my code so far:
UPDATE table1
INNER JOIN table2 ON (table1.id_product = table2.id_product )
SET table1.price = table1.price - (SELECT MAX(table2.reduction) FROM table2 GROUP BY id_product)
Any ideas?

If you only want to display the modified price use this:
select t1.id_product, (price - max_reduction) as new_price
from table1 t1 inner join (
select id_product, max(reduction) max_reduction FROM table2
GROUP BY id_product
) t2 on t1.id_product = t2.id_product
If you want to modify the price try this:
update table1 t1, (
select id_product, MAX(t2.reduction) as max_reduction
FROM table2 t2
GROUP BY id_product) t2
SET t1.price = t1.price - t2.max_reduction
WHERE t1.id_product = t2.id_product;

Try this:
update table1
inner join (SELECT max(`reduction`) as maxprice, id FROM table2 group by id ) t
on
table1.id = t.id
SET
table1.price = table1.price - t.maxprice

Related

select first row against an id from table mysql query with php [duplicate]

Here's what I'm trying to do. Let's say I have this table t:
key_id | id | record_date | other_cols
1 | 18 | 2011-04-03 | x
2 | 18 | 2012-05-19 | y
3 | 18 | 2012-08-09 | z
4 | 19 | 2009-06-01 | a
5 | 19 | 2011-04-03 | b
6 | 19 | 2011-10-25 | c
7 | 19 | 2012-08-09 | d
For each id, I want to select the row containing the minimum record_date. So I'd get:
key_id | id | record_date | other_cols
1 | 18 | 2011-04-03 | x
4 | 19 | 2009-06-01 | a
The only solutions I've seen to this problem assume that all record_date entries are distinct, but that is not this case in my data. Using a subquery and an inner join with two conditions would give me duplicate rows for some ids, which I don't want:
key_id | id | record_date | other_cols
1 | 18 | 2011-04-03 | x
5 | 19 | 2011-04-03 | b
4 | 19 | 2009-06-01 | a
How about something like:
SELECT mt.*
FROM MyTable mt INNER JOIN
(
SELECT id, MIN(record_date) AS MinDate
FROM MyTable
GROUP BY id
) t ON mt.id = t.id AND mt.record_date = t.MinDate
This gets the minimum date per ID, and then gets the values based on those values. The only time you would have duplicates is if there are duplicate minimum record_dates for the same ID.
I could get to your expected result just by doing this in mysql:
SELECT id, min(record_date), other_cols
FROM mytable
GROUP BY id
Does this work for you?
To get the cheapest product in each category, you use the MIN() function in a correlated subquery as follows:
SELECT categoryid,
productid,
productName,
unitprice
FROM products a WHERE unitprice = (
SELECT MIN(unitprice)
FROM products b
WHERE b.categoryid = a.categoryid)
The outer query scans all rows in the products table and returns the products that have unit prices match with the lowest price in each category returned by the correlated subquery.
I would like to add to some of the other answers here, if you don't need the first item but say the second number for example you can use rownumber in a subquery and base your result set off of that.
SELECT * FROM
(
SELECT
ROW_NUM() OVER (PARTITION BY Id ORDER BY record_date, other_cols) as rownum,
*
FROM products P
) INNER
WHERE rownum = 2
This also allows you to order off multiple columns in the subquery which may help if two record_dates have identical values. You can also partition off of multiple columns if needed by delimiting them with a comma
This does it simply:
select t2.id,t2.record_date,t2.other_cols
from (select ROW_NUMBER() over(partition by id order by record_date)as rownum,id,record_date,other_cols from MyTable)t2
where t2.rownum = 1
If record_date has no duplicates within a group:
think of it as of filtering. Simpliy get (WHERE) one (MIN(record_date)) row from the current group:
SELECT * FROM t t1 WHERE record_date = (
select MIN(record_date)
from t t2 where t2.group_id = t1.group_id)
If there could be 2+ min record_date within a group:
filter out non-min rows (see above)
then (AND) pick only one from the 2+ min record_date rows, within the given group_id. E.g. pick the one with the min unique key:
AND key_id = (select MIN(key_id)
from t t3 where t3.record_date = t1.record_date
and t3.group_id = t1.group_id)
so
key_id | group_id | record_date | other_cols
1 | 18 | 2011-04-03 | x
4 | 19 | 2009-06-01 | a
8 | 19 | 2009-06-01 | e
will select key_ids: #1 and #4
SELECT p.* FROM tbl p
INNER JOIN(
SELECT t.id, MIN(record_date) AS MinDate
FROM tbl t
GROUP BY t.id
) t ON p.id = t.id AND p.record_date = t.MinDate
GROUP BY p.id
This code eliminates duplicate record_date in case there are same ids with same record_date.
If you want duplicates, remove the last line GROUP BY p.id.
This a old question, but this can useful for someone
In my case i can't using a sub query because i have a big query and i need using min() on my result, if i use sub query the db need reexecute my big query. i'm using Mysql
select t.*
from (select m.*, #g := 0
from MyTable m --here i have a big query
order by id, record_date) t
where (1 = case when #g = 0 or #g <> id then 1 else 0 end )
and (#g := id) IS NOT NULL
Basically I ordered the result and then put a variable in order to get only the first record in each group.
The below query takes the first date for each work order (in a table of showing all status changes):
SELECT
WORKORDERNUM,
MIN(DATE)
FROM
WORKORDERS
WHERE
DATE >= to_date('2015-01-01','YYYY-MM-DD')
GROUP BY
WORKORDERNUM
select
department,
min_salary,
(select s1.last_name from staff s1 where s1.salary=s3.min_salary ) lastname
from
(select department, min (salary) min_salary from staff s2 group by s2.department) s3

How to subtract values of two different columns from two different tables?

Example Table Structure
Table 1
ID | Name | Price
-----------------------------
1 | Casio | 30
2 | Titan | 40
Table 2
ID | Place | Price
-----------------------------
1 | Cali | 30
2 | Mexi | 10
Operation to perform:
Table1(Price) - Table2(Price) for ID = 1
New Table 1
ID | Name | Price
-----------------------------
1 | Casio | 0
2 | Titan | 40
ID matches in both tables
You should consider another database design to handle this case.
But to answer your question, you can create a view :
create view Differences2 as (
select t1.id, t1.price - t2.price
from t1, t2
where t1.id = t2.id
)
As you told both table will have same ID column you can use following query.
SELECT table1.ID, table1.Name, (table1.Price-table2.Price) AS Price
FROM table1
INNER JOIN table2 ON table1.ID = table2.ID
If you want to update record you can use following:
UPDATE table1
INNER JOIN table2 ON table1.ID = table2.ID
SET table1.Price = (table1.Price-table2.Price)

Join MySql Tables and SUM

I'm trying to write JOIN Query with 3 Tables. I need Query to Return Total SUM of each users based Link Value Passed to Query. Added below the Table Structure, required result and Query that i tried.
Table 1: UserDetails
| ID | Name | Link |
| 1 | User A | 12 |
| 2 | User B | 12 |
| 3 | User C | 12 |
Table 2: Saving
|CreatedBy| Amount |
| 3 | 100 |
| 3 | 50 |
| 2 | 75 |
Table 3: Expense
|CreatedBy| Amount |
| 2 | 20 |
| 1 | 15 |
| 3 | 85 |
By Passing Link Value to Query must return Total(savings+expense) for each user.
Result 1: If Passing Link Value 12.
Query Result be
|User Name| Total |
|User A | 15 |
|User C | 235 |
Result 2: If Passing Link Value 11.
Query Result be
|User Name| Total |
|User B | 95 |
Below query Not returning any result.
SELECT
t1.name,
SUM(t2.total_amount+t3.total_amount)
FROM
userdetails t1
LEFT JOIN savings t2
ON t2.created_by=t1.id
LEFT JOIN expense t3
ON t3.created_by=t1.id
WHERE
t1.link=12
AND t1.status=1
AND t2.status=2
AND t3.status=2
GROUP BY
t2.created_by,
t3.created_by
When you make an outer join you cannot place conditions in the where clause otherwise it nullifies the outer join and turns it into a normal inner join. You can however get what you want (based on your where clause) by adding the condition into the join like this:
SELECT
t1.name,
SUM(t2.total_amount+t3.total_amount)
FROM
userdetails t1
LEFT JOIN savings t2
ON t2.created_by=t1.id
AND t2.status=2
LEFT JOIN expense t3
ON t3.created_by=t1.id
AND t3.status=2
WHERE
t1.link=12
AND t1.status=1
GROUP BY
t2.created_by,
t3.created_by
You also don't normally want to group by fields you aren't naming in your select clause:
SELECT
t1.name,
SUM(t2.total_amount+t3.total_amount)
FROM
userdetails t1
LEFT JOIN savings t2
ON t2.created_by=t1.id
AND t2.status=2
LEFT JOIN expense t3
ON t3.created_by=t1.id
AND t3.status=2
WHERE
t1.link=12
AND t1.status=1
GROUP BY
t1.name
select
u.Name,
u.link,
c.total
from user_details u,
(select
"created by",
sum(amount) Total
from
(select
"created by",
amount
from expense
union all
select
"created by",
amount
from savings)
group by "created by") c
where u.ID=c."created by"
and u.link=12;
Try this:
SELECT A.Name,A.link,B.total
FROM user_details A,
(SELECT 'created by', SUM(amount) AS Total
FROM
(SELECT 'created by',amount
FROM expense
UNION ALL
SELECT'created by',amount
FROM savings)
GROUP BY 'created by') B
WHERE A.ID=B.'created by'
AND A.link=12;
You can use field_name as 'Field Name' or 'Column Title' to display it in the results.
Make sure you have a column created by in the table.

mysql getting max min values across multiple tables

I want to get a price range for some products from two tables.
Table1 (products):
pid | products_name | products_model
1 | Product 1.....| model 1
2 | Product 2.....| model 2
Table2 (products_prices):
pid | nerchant_id | price | sale_price | sale_start_date | sale_expire_date
1 | RVTBV | 11.90 | 0 | NULL | NULL
1 | QNUNU | 11.90 | 9.90 | 2013-05-01 | 2013-12-31
1 | YOLOR | 12.90 | 10.90 | 2013-04-01 | 2013-12-31
2 | RVTBV | 20.90 | 0 | NULL | NULL
2 | QNUNU | 29.90 | 25.90 | 2013-04-01 | 2013-12-31
2 | YOLOR | 29.90 | 0 | NULL | NULL
How do I get a result with price range to look like this:
pid | products_name | products_model | min_price | max_price
1 | Product 1.... | model 1 ...... | 10.90 ... | 12.90
2 | Product 2.... | model 2 ...... | 20.90 ... | 29.90
I am using a main query to get products data from table1 then a loop with php foreach product to get the min max values depending on sale start and expiry dates.
It does the work but I don't like subqueries with php. I prefer one MySQL query for performance reasons.
Thanks for helping.
Until now the following statement the best
SELECT p.pid,
p.manufacturers_id,
p.products_image,
p.products_name,
(select min(if(CURRENT_DATE BETWEEN pp.sale_start_date AND pp.sale_expire_date and pp.sale_price>'0', pp.sale_price, pp.price)) from products_prices pp where p.pid = pid) as min_price,
(select max(if(CURRENT_DATE BETWEEN pp.sale_start_date AND pp.sale_expire_date and pp.products_sale_price>'0', pp.sale_price, pp.price)) from products_prices pp where p.pid = pp.pid) as max_price
FROM products p
WHERE p.products_status = '1'
AND p.categories_id = '1'
ORDER BY min_price ASC LIMIT 0, 100
is it possible to optimize it a little bit?
Resumé:
sometimes is the solution so simple that i don´t see it;)
ok the project is an price comparison plattform. Products will be updated hourly or something like that, but not all prices will change. So let´s say 10% will be updated.
But the data must be retrieverd with each visit of the website.
In this case it will be more reads than writes (80-20).
I can add two extra columns to the products table (min_price and max_price) that i update only once if price_data changes.
on one Hand the update will be a little bit more complicated but that´s not a drama. On the other hand the data will be retrieved very fast.
I have testet 3 options based on 15000 products to retrieve 100 rows:
worst: the group by approch (over 1 sec)
good: the approach of arheops (0,12 sec)
best: update once with two extra colums (0,07 sec)
I go with the third option.
thanks for your help anyway!
That depend of you query.
If you query only some values from product, this will be optimal:
select pid,products_name,products_model,
(select min(price) from price where price.pid=product.pid) as min_price,
(select max(price) from price where price.pid=product.pid) as max_price
from product where some_filter_here;
If you need got FULL table, this one is best:
select a.pid,products_name,products_model,min_price,max_price
from product as a
left join (
select pid,min(price) as min_price, max(price) as max_price
from price group by pid
) as b on b.pid=a.pid
SELECT products.*,
MIN(IF(CURRENT_DATE BETWEEN sale_start_date AND sale_expire_date, sale_price, price)) min_price,
MAX(price) max_price
FROM products JOIN products_prices USING (pid)
GROUP BY pid
See it on sqlfiddle.
The below should work for your requirements.
Update:
The first query now also considers the start/end date for the sale price
SELECT
p.pid,
p.products_name,
p.products_model,
pp.price as min_price,
pp.sale_price as max_price
FROM
products p
JOIN products_prices pp ON ( p.pid = pp.pid )
LEFT JOIN products_prices pp2 ON ( pp2.pid = pp.pid AND pp2.price > pp.price AND pp.sale_start_date BETWEEN pp2.sale_start_date AND pp2.sale_expire_date )
WHERE
pp2.pid IS NULL AND NOW() BETWEEN pp.sale_start_date and pp.sale_expire_date
The below one, gets the max, min of the prices avaliable for a product
SELECT
p.pid,
p.products_name,
p.products_model,
MIN( LEAST( pp.price, pp.sale_price) ) as min_price,
MAX( GREATEST( pp.price, pp.sale_price) ) as max_price
FROM
products p
JOIN products_prices pp ON ( p.pid = pp.pid )
WHERE
pp.sale_price <> 0
GROUP BY
p.pid
SQLFIDDLE
I think you're actually after this. I've amended sale_price to NULL in the event that there is no sale which is just as it should be if you're going to insist on including sale information in the products_prices table...
DROP TABLE IF EXISTS products;
CREATE TABLE products
(pid INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,products_name VARCHAR(12) NOT NULL
,products_model VARCHAR(12) NOT NULL
);
INSERT INTO products VALUES
(1 ,'Product 1','model 1'),
(2 ,'Product 2','model 2');
DROP TABLE IF EXISTS products_prices;
CREATE TABLE products_prices
(pid INT NOT NULL
,merchant_id CHAR(5) NOT NULL
,price DECIMAL(5,2)NOT NULL
,sale_price DECIMAL(5,2) NULL
,sale_start_date DATE
,sale_expire_date DATE
,PRIMARY KEY(pid,merchant_id)
);
INSERT INTO products_prices VALUES
(1,'RVTBV',11.90,NULL,NULL,NULL),
(1,'QNUNU',11.90,9.90,'2013-05-01','2013-12-31'),
(1,'YOLOR',12.90,10.90,'2013-04-01','2013-12-31'),
(2,'RVTBV',20.90,NULL,NULL,NULL),
(2,'QNUNU',29.90,25.90,'2013-04-01','2013-12-31'),
(2,'YOLOR',29.90,NULL,NULL,NULL);
SELECT p.*
, MIN(CASE WHEN CURDATE() BETWEEN sale_start_date AND sale_expire_date THEN pp.sale_price ELSE pp.price END) min_price
, MAX(CASE WHEN CURDATE() BETWEEN sale_start_date AND sale_expire_date THEN pp.sale_price ELSE pp.price END) max_price
FROM products p
JOIN products_prices pp
ON pp.pid = p.pid
GROUP
BY p.pid;
+-----+---------------+----------------+-----------+-----------+
| pid | products_name | products_model | min_price | max_price |
+-----+---------------+----------------+-----------+-----------+
| 1 | Product 1 | model 1 | 10.90 | 11.90 |
| 2 | Product 2 | model 2 | 20.90 | 29.90 |
+-----+---------------+----------------+-----------+-----------+

Select row in which the sum of the times the same value came up is less than X

this one been puzzling me for a couple of searching hours.
So I have a campaign table and a vendor Table. The vendor might have several campaigns.
I want to select all campaigns if the vendor has enough credits.
Problem is I don't know how many campaigns are going to be selected from the same vendor which means that the vendor might still have credits for two campaigns but not for the rest of them.
Example
tblvendors
+---------+------------+---------------+
|vendorId | vendorName | vendorCredits |
+---------+------------+---------------+
| 1 | a | 5 |
| 2 | b | 100 |
+---------+------------+---------------+
tblproducts
+-----------+---------------+------------+
| productId | productName | vendorId |
+-----------+---------------+------------+
| 1 | c | 1 |
| 2 | e | 2 |
| 3 | f | 1 |
| 4 | g | 1 |
| 5 | h | 1 |
+-----------+---------------+------------+
tblcampaigns
+------------+---------------+------------+
| campaignId | productId | vendorId |
+------------+---------------+------------+
| 1 | 1 | 1 |
| 2 | 2 | 2 |
| 3 | 3 | 1 |
| 4 | 4 | 1 |
| 5 | 5 | 1 |
+------------+---------------+------------+
Now considering that everytime a row is selected the vendor looses 2 credits since vendor 'a' only has 5 credits left only campaigns 1 2 and 3 should be returned.
My current Query is this:
SET #maxCampaignId = (SELECT MAX(campaignId) FROM tblCampaigns);
SELECT
#maxCampaignId,
t0.campaignId,
t0.productId,
productName,
productDescription,
productImage,
(CASE WHEN campaignId > (SELECT configValue FROM tblconfiguration WHERE configKey = 'lastHomeCampaignId')
THEN campaignId ELSE campaignId + #maxCampaignId END) AS orderField
FROM tblcampaigns AS t0
INNER JOIN tblproducts AS t1 ON t0.productId = t1.productId
INNER JOIN tblvendors AS t2 ON t1.vendorId = t2.vendorId
WHERE
campaignType = 'homeFeature' AND
t0.isActive = 1 AND
t2.vendorCredits > (SELECT configValue FROM tblconfiguration WHERE configKey = 'campaignHomeFeatureCost' LIMIT 1)
ORDER BY orderField ASC
LIMIT 4
The problem as you can see is int the line that compares the vendorCredits. Obviously as is the query selects more campaigns than the vendor can afford.
I wanted to avoid doing this in PHP as I think it should be possible to do this straight out of the database.
Check this post, it may help - group by and having clauses. I'll try to do some test later
Using COUNT(*) in the WHERE clause
UPDATE:
select t2.vendorId, vendorCredits from tblcampaigns AS t0 JOIN tblproducts AS t1 ON t0.productId = t1.productId JOIN tblvendors AS t2 ON t1.vendorId = t2.vendorId group by t2.vendorId having t2.vendorCredits = count(t2.vendorId)
If I correctly understood the question: This query will select all vendors having more campains than credits.
Ok found it.
Thanks to this post: How do I limit the number of rows per field value in SQL?
What I did was Selecting the rows I wanted in the order I wanted as a subquery and its respective row number so that I could reorder it back in the end.
Then I made a second subquery ordered by the vendorId so that I could count the number of times it turned up and returning the row_count to the main query.
Finally in the main query I reordered it back to the row number in the deepest subquery but now I have the value I wanted to compare which is the value of credits per row * the current row number for a particular vendor.
Anyways maybe the code is cleared and here it goes:
SET #creditsCost = (SELECT configValue FROM tblconfiguration WHERE configKey = 'campaignHomeFeatureCost' LIMIT 1);
SET #maxCampaignId = (SELECT MAX(campaignId) FROM tblCampaigns);
SET #curRow = 0;
SELECT * FROM
(
SELECT *,
#num := if(#first_column = vendorId, #num:= #num + 1, 1) as row_num,
#first_column:=vendorId as c
FROM
(SELECT
#curRow := #curRow + 1 AS row_number,
#maxCampaignId,
t0.campaignId,
t0.productId,
t2.vendorId,
t2.vendorCredits,
productName,
productDescription,
productImage,
(CASE WHEN campaignId > (SELECT configValue FROM tblconfiguration WHERE configKey = 'lastHomeCampaignId')
THEN campaignId ELSE campaignId + #maxCampaignId END) AS orderField
FROM tblcampaigns AS t0
INNER JOIN tblproducts AS t1 ON t0.productId = t1.productId
INNER JOIN tblvendors AS t2 ON t1.vendorId = t2.vendorId
WHERE
campaignType = 'homeFeature' AND
t0.isActive = 1
ORDER BY orderField ASC) AS filteredCampaigns
ORDER BY vendorId
) AS creditAllowedCampaigns
WHERE
row_num * #creditsCost <= vendorCredits
ORDER BY row_number
Anyhow I still appreciate Who took the time to answer and try to help, and will be listening to future comments since I think this is not the best way performance wise.

Categories