Count SQL results depending on two rows - php

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

Related

MySQL select multiple id at the same time with optional id with php

I would like to complicate this request by changing the scenario.
Here is the link to the original request.
Here is the link to the original request.
I have the following MySQL table called skills.
id
idUser
idSkill
1
4
1
2
8
4
3
8
9
4
13
9
5
18
2
6
22
1
7
27
2
8
32
4
9
11
2
10
32
9
10
32
7
I need to select, for example, all idUsers that have idSkill 4 and 9 at the same time (mandatory skills).
But I would like to have the possibility to search by optional idSkills (if any).
Mandatory skills are 9 and 4
Optional skill is 7
The result would be idUser 32.
I thought of this query:
SELECT id, idUser, idSkill FROM skills WHERE idSkill IN (9,4,7) GROUP BY idUser HAVING (idSkill IN (9,4))
But it clearly does not work.
Many thanks
You can do it with aggregation and RANK() window function.
This query:
SELECT idUser
FROM skills
WHERE idSkill IN (9, 4, 7)
GROUP BY idUser
HAVING SUM(idSkill IN (9, 4)) = 2 -- for the 2 mandatory skills
returns all the users with at least the 2 mandatory skills 9 and 4.
If you use an ORDER BY clause with LIMIT like this:
SELECT idUser
FROM skills
WHERE idSkill IN (9, 4, 7)
GROUP BY idUser
HAVING SUM(idSkill IN (9, 4)) = 2 -- for the 2 mandatory skills
ORDER BY COUNT(*) DESC
LIMIT 1
you will get from all the users with at least the 2 mandatory skills 9 and 4, only 1 user: the one with the largest number of skills (mandatory and optional).
If you want ties returned, use RANK() window function:
SELECT idUser
FROM (
SELECT idUser, RANK() OVER (ORDER BY COUNT(*) DESC) rnk
FROM skills
WHERE idSkill IN (9, 4, 7)
GROUP BY idUser
HAVING SUM(idSkill IN (9, 4)) = 2 -- for the 2 mandatory skills
) t
WHERE rnk = 1
See the demo.
Your answer will be a simple query.
select * from (select distinct b.idUser, (select 1 from skill a where a.idUser=b.idUser and a.idSkill=4) 'four', (select 1 from skill a where a.idUser=b.idUser and a.idSkill=9) 'nine', (select 1 from skill a where a.idUser=b.idUser and a.idSkill=7) 'seven' from skill b) t where t.four=1 and t.nine=1 and t.seven=1

mysql - order by field not working properly

suppose I've a database , the table contains rows with ides from 1 to 20 .
i want to return 3 rows with id 3,4,1 first and then return the other rows . this is my code :
SELECT id
FROM prod
ORDER BY field( id, 3, 4, 1 )
LIMIT 20
this is the result of this code :
id
13
17
16
15
7
6
5
2
3
4
1
strangely the 3 rows that I need to come first are showing at the end ,
How can I bring these 3 rows to the top of the list ?
Thanks
You can use DESC:
SELECT id
FROM prod
ORDER BY field( id, 3, 4, 1 ) DESC
LIMIT 20
The issue is that MySQL puts NULL values first when you do an ascending order by.
If you actually want the rows in the order 3, 4, 1, then reverse them in the field statement:
SELECT id
FROM prod
ORDER BY field( id, 1, 4, 3 ) DESC
LIMIT 20
Or, if you wanted to be fancy:
ORDER BY - field( id, 3, 4, 1 ) DESC
The other way is to use case-when and giving each id an order value
select * from prod
order by
case
when id = 3 then 0
when id=4 then 1
when id=1 then 2
else 3
end,id
limit 20
;
Try with DESC
SELECT id
FROM prod
ORDER BY field( id, 3, 4, 1 ) DESC
LIMIT 20
It seems your id order is important. Reverse numbers to get correct result
SELECT id
FROM prod
ORDER BY field( id, 1, 4, 3 ) DESC
LIMIT 20
Not tested but you can try
SELECT id,
(
CASE
WHEN id = '3' THEN 0
WHEN id = '4' THEN 1
WHEN id = '1' THEN 2
END
) as rank
FROM prod
ORDER BY rank
LIMIT 20;

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

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.

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

Sorting MYSQL results by multiple columns

OK, so I have gone through 14 Stack Overflow suggestions that were suggested when I wrote this question, and I have tried everything, and can not figure this out.
I have a gym directory with reviews. I have a script that you can search through those gyms by Zip code, City/State or Neighborhood/City/State.
I have a few fields in the back end and in the database. The ones pertaining to this question are priority (I give it a number 1+ and it should show highest first or lowest first I don't care which preferably highest to lowest), photo (it can have 5 photos), membership includes (a few things that the gym can include in the membership), and reviews (not editable but keeps count of how many review the gym has)
I would like to sort in this order
If the gym has priority, it should show first as it is prioritized. Null Priority should come last, Then sort by photo doesn't matter a-z z-a just null comes last, then sort by membership includes null comes last, then sort by review count 0 or null comes last.
So if I have 4 gyms, A and B with priority, photo, and membership but 0 reviews, C with no priority, no photo, no membership, but highest review count at 2, and D with no priority but has photo and membership, but 1 review: it should sort in this order:
GYM Priority Photo Membership Reviews
A yes has some has count 0
B yes has some has count 0
C no no no memb. 2
D no has some has count 1
Expected sort order results: A B D C
Sorry of thats confusing.
Heres what I have already tried:
SELECT * FROM (SELECT * FROM gym WHERE (city = "Queens") AND (state = "NY") GROUP BY priority ORDER BY photo, member_includes, reviews DESC) x LIMIT 0, 150
SELECT * FROM (SELECT * FROM gym WHERE (city = "Queens") AND (state = "NY") ORDER BY priority, photo, member_includes, reviews DESC) x LIMIT 0, 150
SELECT * FROM (SELECT * FROM gym WHERE (city = "Queens") AND (state = "NY") GROUP BY photo, member_includes, reviews ORDER BY priority DESC) x LIMIT 0, 150
SELECT * FROM gym WHERE (city = "Queens") AND (state = "NY") GROUP BY photo, member_includes, reviews ORDER BY priority DESC LIMIT 0, 150
SELECT * FROM gym WHERE (city = "Queens") AND (state = "NY") ORDER BY priority, photo, member_includes, reviews DESC LIMIT 0, 150
And I have tried all other kinds of combinations with and without ASC but still it does not sort properly. I dont know what I am doing wrong.
Please help!
Thanks,
David
I believe this is what you are looking for:
SELECT * FROM gym
WHERE city = "Queens" AND state = "NY"
ORDER BY
ISNULL(priority), priority,
ISNULL(photo),
ISNULL(member_includes), member_includes,
ISNULL(reviews), reviews DESC
LIMIT 150
Here you go - I think you're missing two key ideas: use ifnull to map any null values to 0 and then sort with DESC so that the zeros (from the NULLs) sort to the end.
How does this work for you?:
create table gym (
id int primary key auto_increment not null,
name varchar(255),
priority int,
photo int,
member_includes int,
reviews int
);
insert into gym
(id, name, priority, photo, member_includes, reviews) values
(DEFAULT, 'A', 1, 2, 3, 0),
(DEFAULT, 'B', 1, 2, 3, 0),
(DEFAULT, 'C', NULL, 0, 3, 0),
(DEFAULT, 'D', NULL, 1, 3, 1);
select name from gym
order by ifnull(priority,0) desc
, ifnull(photo, 0) desc
, ifnull(member_includes, 0) desc
, ifnull(reviews, 0) desc ;
+------+
| name |
+------+
| A |
| B |
| D |
| C |
+------+
4 rows in set (0.00 sec)

Categories