Select only rows where min(column1) and min (column2) - php

I have two tables. The first table shows the id_product and product_price_value. Below I will show you one example (in my database there are many rows)
TABLE: main_product
ID_product: product_price_value:
119, Product1
TABLE: value_product
product_price_value: width_from: width_to: height_from: height_to: price:
Product1 , 10, 20, 5, 15, 100
Product1 , 10, 20, 10, 30, 200
Product1 , 20, 30, 5, 45, 300
Product1 , 30, 30, 20, 30, 400
As you can see one product can have multiple dimensions. I want to get the price with the lowest width and height combined. In my example it should be the first row (width from -> 10, height from -> 5).
I used the following code:
$sql = "SELECT value_product.price FROM value_product INNER JOIN main_product
ON (main_product.product_price_value = value_product.product_price_value
AND (
value_product.width_from = (SELECT MIN(value_product.width_from) FROM value_product)
AND value_product.height_from = (SELECT MIN(value_product.height_from) FROM value_product)
)
);";
In this way I thought I was gonna get the price for the lowest width/height for each product. But the only results I get is when the width_from OR height_from contains a value of 0. If either width or height has more than 0 then it doesn't return anything.
Am I doing something wrong in my query?
Is there any way to get the price with the lowest 'width_from' and 'height_from' columns?
Thanks

If you only want such a price for one product, you can simply sort and limit:
SELECT price
FROM value_product
WHERE product_price_value = ?
ORDER BY width_from + height_from
LIMIT 1
Otherwise you're after the group-wise minimum, which can be obtained by joining the table back to a grouped version of itself:
SELECT v.product_price_value,
v.price
FROM value_product v
JOIN (
SELECT product_price_value,
MIN(width_from + height_from) min_dimension
FROM value_product
GROUP BY product_price_value
) t
ON t.product_price_value = v.product_price_value
AND t.min_dimension = v.width_from + v.height_from
In both cases I have assumed that there is only ever one record with the minimal dimensions. Should there be multiple such records, the first query will pick one indeterminately; and the second query will list them all. If this is not your desired behaviour, you will have to clarify what you would like to occur instead.

Related

Count SQL results depending on two rows

Here an example of my SQL structure:
magazines
magazines.id
magazines.templateId
magazines.userId
I would like to count the number of times a user have used a specific template id.
Considering 3 templates id : 100, 101 and 102 and considering 5 users (10, 20, 30, 40, 50). here is an example of the SQL datas:
magazines
1, 100, 10
2, 100, 20
3, 100, 30
Here, 3 differents users have the template #100. But the result of the query should be [100 = 1] because it's different users. On the other hand, if I have :
magazines
1, 100, 10
2, 100, 10
I should have templateId [100 = 2].
I tried multiple queries with GROUP BY and UNIQUE, by results are completely wrong. If someone can help, thanks.
If you want the maximum number of times that a user uses a template:
select templateid, userid, count(*)
from magazines m
where temploateid = X
group by templateid, userid
order by count(*) desc
limit 1;
EDIT:
You can get what you want by aggregating two times:
select templateid, cnt, count(*) as numUsers
from (select templateid, userid, count(*) as cnt
from magazines m
where temploateid = X
group by templateid, userid
) tu
group by templateid, cnt
order by templateid, cnt;
This will get you the amount of times each user has used a particular template:
SELECT
templateId,
userId,
COUNT(id) as cnt
FROM
Magazines GROUP BY templateId, userId
If your table had the following data:
(1, 100, 10),
(2, 100, 10),
(3, 100, 20),
(4, 100, 30)
Then you result set would be:
templateId userId cnt
100 10 2
100 20 1
100 30 1

How to JOIN tables to get all of Table A, and most of Table B

I'm pretty sure I need a LEFT JOIN for this, but I have a snag. I need to pull one column from table B dependant on a column in table A.
TABLE A = list
list_id
user_id
operator_id
operator_name
operator_level
TABLE B = operators
operators_id
type
image
skill1
skill2
skill3
1
2
3
...
10
Here is the SQL Query that I have now:
SELECT * FROM list l
LEFT JOIN operators o ON l.operator_id = o.operators_id
WHERE l.user_id=1
ORDER BY o.10 DESC
It returns all of Table A, which I want, and also returns all of Table B, which I don't need.
The columns 1-10 contain INT values, and those columns correspond to the operator_level in Table A.
So really what I need is to create a temp column, and put whatever INT is in the 1-10 column that corresponds to the operator_level, or only return that column for that row. I have no idea how to do that though.
Here is some sample data, and expected results:
list list_id, user_id, operator_id, operator_name, operator_level
1, 1, 2, Johnson, Bob, 1
2, 1, 3, Mouse, Mickey, 9
3, 1, 2, Duck, Donald, 5
operators operator_id, type, image, skill1, skill2, skill3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
1, pilot, image.jpg, F16, B32, , 50, 60, 70, 80, 90, 100, 110, 120, 130, 140
2, medic, image.jpg, first aid, trauma, general surgery, 100, 105, 110, 115, 120, 125, 130, 135, 140, 145
3, kitchen, image.jpg, knife, soup, , 20, 22, 24, 26, 28, 30, 32, 34, 36, 38
Expected Results list_id, user_id, operator_id, operator_name, operator_level, type, image, skill1, skill2, skill3, op_rate
1, 1, 2, Johnson, Bob, 1, pilot, image.jpg, F16, B32, 50
2, 1, 3, Mouse, Mickey, 9, medic, image.jpg, first aid, trauma, general surgery, 140
3, 1, 2, Duck, Donald, 5, kitchen, image.jpg, knife, soup, 28
Sorry, it lined up well in my editor, but lost the alignment. when I pasted.
Added sample data to sqlfiddle, don't know how to add expected results there.
http://sqlfiddle.com/#!2/390d8/1
You have the basics right withe the outer join, but what you can do is limit the fields you select in your query like this:
select
l.list_id,
l.user_id,
l.operator_id,
l.operator_name,
l.operator_level,
`1`+`2`+`3`+...+`10` as opLevel
FROM
list l
LEFT JOIN operators o
ON l.operator_id = o.operators_id
WHERE
l.user_id=1
ORDER BY
o.10 DESC
This is based on the fact that it would seem you have 0 for and or a value for the right operator.
Having said that, why on earth would you have ten fields to store one bit of information? Your table should only have the ONE field called something like "OperatorLevel" and have the value in it - unless I am missing something.
Alternately, you could also use a greatest() function in your query if you have multiple values and you want the highest one:
select
l.list_id,
l.user_id,
l.operator_id,
l.operator_name,
l.operator_level,
greatest(`1`, `2`, ... `10`) as opLevel
FROM
list l
LEFT JOIN operators o
ON l.operator_id = o.operators_id
WHERE
l.user_id=1
ORDER BY
o.10 DESC
Edit: Okay, based on additional information you can use the following (ick ick ick) statement:
select
l.list_id,
l.user_id,
l.operator_id,
l.operator_name,
l.operator_level,
case
when l.operator_level=1 then o.`1`
when l.operator_level=2 then o.`2`
when l.operator_level=3 then o.`3`
// etc etc yuck!
when l.operator_level=10 then o.`10`
end as yicky
FROM
list l
LEFT JOIN operators o
ON l.operator_id = o.operators_id
WHERE
l.user_id=1
ORDER BY
o.10 DESC
Edit 2:
I would very much suggest a data normalisation.
Given what you have as data, it seems that you are duplicating data to all sorts of users. This kinda sounds like a nightmare to update. From what I understand, it seems that there is a pay scale for each "skill" and it goes up based on the level of the operator?
I would make a table with the following structure (assumptions based on user 1):
skill level value
B32 1 25
B32 2 30
.....
B32 10 75
F16 1 25
F16 2 30
...
F16 10 75
Then you could simply perform a link from the "list" table to the "operators" table, and then link to the "grades" table based on data within then.
This would make for a much simpler query.
Having said ALL THAT, I would actually look at normalizing your "operators" table down to skills only. Move the "type" and "image" into the "list" table (and call it users while you are at it).
Now the "operators" table should be renamed to a skillset table with data like this:
userID skill Level
1 F16 4
1 B32 8
2 F_Aid 2
This would allow you to have users with more than 3 skills as well as allowing you to easily record each skill level of the user. They might be a superhero at B32, but only mediocre at F16.
Assuming only one int column has value maybe something like this?
SELECT l.*, COALESCE(1,2,3,4,5,6,7,8,9,10) opLevel
FROM list l
LEFT JOIN operators o ON l.operator_id = o.operators_id
WHERE l.user_id=1
ORDER BY COALESCE(1,2,3,4,5,6,7,8,9,10) DESC

mysql union or join just one query

I need to find a better way to get the discount for each article price in our web shop depending on which pricelist or pricelist/discount list a customer has. I think this is possible to do in just one query instead of the 5 I have today, but I really do not know where to start.
All our customers have a pricelist and some have both pricelist and one extra discount list. Today we have about 25 different pricelists and about 100 extra discount lists.
All the pricelists are structured in the same manner; they have a price group and a discount in percent.
For example pricelist 01 could look like
A 20
B 35
C 20
The extra discount list is structured in a different manner and can have a fixed price or percentage. It also has three different priority levels: discount based on the article code (has priority 1), based on category (has priority 2) and based on price group (has priority 3).
Discount list 0013 could look like:
In the article tab
PL-2344-444 40 (%)
P-0455-23 200 (SEK)
In the category tab
C12 50 (%)
N12 35 (%)
Today I have three different queries to see if I get a hit in the discount list:
First I check to see if I get a hit in priority 1: (FIXED returns f and PERCENTAGE r)
SELECT DISCOUNT, FIXED, PERCENTAGE FROM PUPRIREG
WHERE ARTCODE = 'JO-23455' AND DISCOUNTLIST = '0013'
If the above returns 0, I do the second query, priority 2:
SELECT DISCOUNT, FIXED, PERCENTAGE FROM PUPRIREG
WHERE CATEGORY = 'C15' AND DISCOUNTLIST = '0013'
And the last one priority 3:
SELECT DISCOUNT, FIXED, PERCENTAGE FROM PUPRIREG
WHERE PRICEGROUP = 'F' AND DISCOUNTLIST = '0013'
If none of the extra discount lists returns 0 I get the discount from the pricelist
SELECT DISCOUNT FROM PUPRIREG WHERE PRICELIST = '01' AND PRICEGROUP = 'F'
I call the function like follows
$discount = discount($articlecode, $category, $pricegroup);
function discount($articlecode, $category, $pricegroup){
$articlecode = sanitizingData($articlecode);
$category = sanitizingData($category);
$pricegroup = sanitizingData($pricegroup);
// do priority 1
// prio 2
// prio 3
// pricelist
return $discount;
}
I would be so happy if someone could show me how to do this. I am using mysqli and php.
Many thanks
Best regards linda
You can do the queries with a union:
(SELECT DISCOUNT, FIXED, PERCENTAGE, 1 priority FROM PUPRIREG
WHERE ARTCODE = 'JO-23455' AND DISCOUNTLIST = '0013')
union
(SELECT DISCOUNT, FIXED, PERCENTAGE, 2 priority FROM PUPRIREG
WHERE CATEGORY = 'C15' AND DISCOUNTLIST = '0013')
union
(SELECT DISCOUNT, FIXED, PERCENTAGE, 3 priority FROM PUPRIREG
WHERE PRICEGROUP = 'F' AND DISCOUNTLIST = '0013')
union
(SELECT DISCOUNT, 0 fixed, 0 percentage, 4 priority FROM PUPRIREG
WHERE PRICELIST = '01' AND PRICEGROUP = 'F')
order by priority;
The additional artificial priority column and order by ensures, that you get the discounts properly sorted.

PHP MySQL inventory likeness percentage

Edit:
I removed the confusing stuff and I wanted to simplify what I want to accomplish:
Let's say John had 2 baskets and 1 of them contained 1 stone, 2 marbles and 2 sticks, the other one contained 2 stones and 2 sticks.
Eric had 1 basket which contained 3 bottles, 2 caps and 1 stone.
Meanwhile Jack had 1 basket which contained 1 stick. Also, Jack has a stone somewhere but it's not inside his basket yet.
For the purpose of this query, let's say we're focusing on Jack's items. So we would go over everybody's baskets and see which basket looks pretty much the same as all the items he has overall (a) without thinking whether Jack's items are inside a basket or not and (b) only compare to Jack's items with items that are inside other people's baskets, not outside.
This would be the ideal end result when searching which baskets Jack can match the easiest with all his items regardless of where his items are currently:
Fact:
Jack has in total 1 stick and 1 stone.
Result:
1st: John's second basket containing 2 stones and 2 sticks
2nd: John's first basket containing 1 stone, 2 marbles and 2 sticks
3rd: Eric's basket containing 3 bottles, 2 caps and 1 stone
This will do what you asked for a single basket:
SELECT SUM(item.qty/(SELECT sum(qty) total FROM basket WHERE bid = xxx)
* 1/all_item.qty) likeness, basket.bid, all_basket.bid all_bid
FROM basket JOIN item USING (bid)
LEFT JOIN
(basket all_basket JOIN item all_item USING (bid))
USING (iid)
WHERE basket.bid = xxx
GROUP BY basket.bid, all_basket.bid
ORDER BY likeness DESC
It's probably possible to join this to the users table to do all his baskets at once. Then wrap it in yet another query to find the best match.
Try this first, and let me know if it works. If it does I'll see if I can do that part tomorrow.
Whilst I'm not sure that there's a simple way to calculate basket likeness as a percentage using SQL, I assume you are simply trying to product product recommendations.
You could formulate a query to produce product recommendations based on the current basket. You can do this by:
Finding any basket that contains at least one item from the current customer's basket.
Limiting the results to products that are in the same category.
You can fetch the appropriate information with the following query:
SELECT b1.basket_id, b1.product_id, b1.quantity, p1.product_category FROM baskets b1
INNER JOIN products p1 ON p1.product_id = b1.product_id
WHERE b1.product_id IN
(
SELECT b2.product_id FROM baskets b2
INNER JOIN products p2 ON p2.product_id = b2.product_id
WHERE b2.basket_id = $basket
AND p2.product_category = p1.product_category
)
AND b1.basket_id != $basket;
This will return the basket ID, product ID, quantity and product category ID for all items in all other baskets that share at least one product with your selected basket. It also filters the results so that the suggested products are always in the same category as the source product.
Here's a sample:
baskets table:
basket_id | product_id | quantity
1, 2, 1
1, 1, 2
1, 3, 5
2, 5, 1
2, 6, 1
3, 1, 1
3, 2, 1
3, 4, 1
products table:
product_id | product_name | product_price | product_category
1, 'cat1 prod1', 14, 1
2, 'cat1 prod2', 1.5 1
3, 'cat1 prod3', 2, 1
4, 'cat2 prod1', 22, 2
5, 'cat2 prod2', 6, 2
6, 'cat2 prod3', 45, 2
7, 'cat3 prod1', 24, 3
8, 'cat3 prod2', 55.4, 3
9, 'cat3 prod3', 22, 3
result of query:
basket_id | product_id | quantity | product_category
3, 1, 1, 1
3, 2, 1, 1
Hopefully that's of some use to you.

Pull smallest value

I know mysql has the MIN() function pulls out the smallest value located in a specific column.
I was wondering is there a way to pull out the smallest value for each corresponding id and exclude the values that equal 0?
For example, the 2 ids have multiple prices that were entered. I need to exclude '0', and then pull out the min for 1, and then the same for 2, etc etc.
id price
=============
1 0
1 33.0
1 21.7
2 0
2 99.22
Should be something like
select id, min(price) from t where price > 0 group by id;
select id, min(price)
from table
where price > 0
group by id

Categories