A simple mySQL statement to ask - php

I've a question to ask about mysql, I am creating a supplier page which requires list down all the suppliers details and their products (randomly choose 3 product for each supplier), so we have to join 2 tables together, one is suppliers, another one is products, may I know how to present them in single mysql statement?
table1 - suppliers
id corp_name product_count
---------------------------------------
AD0728 John Smith 187
AD0729 JayJay 1983
table2 - products
pid cid p_name quantity
---------------------------------------
1 AD0728 Body Shampoo 10
2 AD0728 glass 10
3 AD0728 pen 10
4 AD0728 pencil 10
5 AD0728 eraser 10
6 AD0728 speaker 10
7 AD0729 monitor 10
8 AD0729 keyboard 10
9 AD0729 mouse 10
10 AD0729 processor 10
11 AD0729 box 10
12 AD0729 sunglass 10
Output
id corp_name pid p_name quantity
----------------------------------------------------------------
AD0728 John Smith 1 Body Shampoo 10
AD0728 John Smith 2 glass 10
AD0728 John Smith 3 pen 10
AD0729 JayJay 10 processor 10
AD0729 JayJay 11 box 10
AD0729 JayJay 12 sunglass 10
Thanks everyone.

For SQL Server 2005 or greater you can use ORDER BY NEWID() for a random order and you can use ROW_NUMBER to get your partion:
WITH CTE AS
(
SELECT
s.id, s.corp_name, p.pid, p.p_name, p.quantity
, RN = ROW_NUMBER() OVER (PARTITION BY s.id ORDER BY NEWID())
FROM suppliers s
INNER JOIN products p ON s.id = p.cid
)
SELECT id, corp_name, pid, p_name, quantity
FROM CTE WHERE RN <= 3
Here's the fiddle (thanks to #MahmoudGamal): http://www.sqlfiddle.com/#!3/a26c6/17/0

Check this SQL Fiddle code. It will return as requested the first 3 products for each supplier. It will work for SQL2005 and greater as it uses CTE (Common Table Expressions), introduced with SQL2005.
Either ways, here is the code:
;WITH myCTE AS (
SELECT p.*, ROW_NUMBER() OVER (PARTITION BY p.cid ORDER BY NEWID()) AS r
FROM products p)
SELECT sup.id,sup.corp_name,c.pid,c.p_name,c.quantity
FROM myCTE c JOIN suppliers sup ON c.cid = sup.id
WHERE c.r <= 3

In MYSQL this works: http://sqlfiddle.com/#!2/8b291/10
SET #rank=0;
SELECT *, #rank:=#rank+1 AS rank
FROM suppliers s
LEFT JOIN products p ON p.cid = s.id
GROUP BY s.id, #rank % 3

Related

How to count a Column data using join over 3 tables

I have three tables
tbl_product
item_id | item_name
company_details
v_id | item_id | company_name
user_ratings
r_id | rate | v_id
I want to count the rate and also get the rate of the company. Here is my query
SELECT company_details.v_id,
company_details.company_name,
COUNT(user_ratings.rate) as vote,
user_ratings.rate,
tbl_product.item_name
FROM company_details
LEFT JOIN tbl_product ON tbl_product.item_id = company_details.item_id
LEFT JOIN user_ratings ON user_ratings.v_id = company_details.v_id
GROUP BY company_details.v_id, user_ratings.rate
This is Whts i am Getting after this query:
v_id company_name vote rate item_name
1 The Oberoi Udaivilas 1 4 5 Star Hotels
1 The Oberoi Udaivilas 1 5 5 Star Hotels
2 The Taj Mahal Palace 2 5 4 Star Hotels
3 Rambagh Palace 1 5 3 Star Hotels
4 Taj Lake Palace 1 5 5 Star Hotels
5 Windflower Hall 1 3 2 Star Hotels
5 Windflower Hall 1 5 2 Star Hotels
6 Leela Palace Kempinski 0 n 4 Star Hotels
7 Umaid Bhawan Palace 0 n 4 Star Hotels
8 Hotel Ratan Vilas 0 n 4 Star Hotels
9 The Leela Palace 0 n 4 Star Hotels
10 The Imperial Hotel 0 n 3 Star Hotels
You can see vote column is not counting.
This is what I am expecting
v_id company_name vote rate item_name
1 The Oberoi Udaivilas 2 5 5 Star Hotels
2 The Taj Mahal Palace 2 5 4 Star Hotels
But this query is not counting the rate from user_ratings table, because of I also want to get the rate, if I remove the user_ratings.rate from select clause, then this query works, but when I add the user_ratings.rate in the select clause, then this query is not counting the rates as(vote), and it is returning as one rows in every count.
You should remove user_ratings.rate from group by clause and add tbl_product.item_name.
SELECT
company_details.v_id,
company_details.company_name,
tbl_product.item_name,
COUNT(user_ratings.rate) as vote,
avg(user_ratings.rate) as rate
FROM
company_details
LEFT JOIN tbl_product ON tbl_product.item_id = company_details.item_id
LEFT JOIN user_ratings ON user_ratings.v_id = company_details.v_id
GROUP BY company_details.v_id, company_details.company_name, tbl_product.item_name;
Seems to me that your query is only missing an aggregation on the rate column, and from the expected output I'd say it's a max. You also should fix your group by. Try this one
SELECT company_details.v_id,
company_details.company_name,
COUNT(user_ratings.rate) as vote,
MAX(user_ratings.rate) as rate,
tbl_product.item_name
FROM company_details
LEFT JOIN tbl_product ON tbl_product.item_id = company_details.item_id
LEFT JOIN user_ratings ON user_ratings.v_id = company_details.v_id
GROUP BY company_details.v_id,
company_details.company_name,
tbl_product.item_name
Query and result are both correct. Look at the rate
you can try this query it will return your exactly output according to your given 3 tables.
"select c.v_id , c.company_name , u.rate , p.item_name from company_details c join tbl_product p on p.item_id = c.item_id join user_details u on c.v_id = u.v_id group by c.company_name ";

Min and Max from every group

I have two table
location:
location_id
address
itransfile:
id
transactionNumber
location_id
itemName
quantity
I want to get maximum and minimum sold items by locations.
HighestItemName HighQauntity LowestItemName LowQuantity LocationName
Chicken Burger 50 Tako 5 Gulshan
Chicken Burger 100 Tikka 10 Nipa
Pasta 150 Cheese Burger 12 Liyari
Pizza 200 Chicken Burger 3 F.B.Area
The query I've done so far:
SELECT t.itemName as HighestItemName, sum(t.quantity) as HighQuantity, l.address LocationName
from itransfile as t join locations as l
on t.location_id = l.location_id
where t.location_id IN(1,2,3,4)
group by t.location_id
I don't know how will get max and min items from every group.
Sample Data:
ID TransNumber ItemName Quantity location_id
1 1234 Chicken Burger 3 1
2 1234 Cheese Burger 1 1
3 1235 Sandwich 4 2
4 1332 Salad 1 4
5 14537 Tikka 1 3
6 1236 Roll 3 2
7 1333 Biryani 2 4
location_id address
1 Gulshan
2 Nipa
3 Liyari
4 F.B.Area
This is what you may be looking for if you need it in one query (SQLFiddle):
select
l.address,
imax.itemName max_item, max_min.max_q,
imin.itemName min_item, max_min.min_q
FROM
(select
i.location_id, MAX(i.quantity) max_q, MIN(i.quantity) min_q
FROM
itransfile i
GROUP BY
i.location_id) as max_min
LEFT JOIN itransfile imax ON (max_min.max_q = imax.quantity)
LEFT JOIN itransfile imin ON (max_min.min_q = imin.quantity)
LEFT JOIN location l ON (max_min.location_id = l.location_id)
GROUP BY
l.location_id
It looks for min/max values and then looks up the item name and location address. The GROUP_CONCAT makes sure that when there are more items with the same min/max quantity, you get all of them.
Alternatively you can get rid of the GROUP BY and GROUP_CONCAT and get all the items in rows if you need to further process them.

mysql query to get the min values according to the latesttime stamp

Hi I have try to find a solution for a query to get the minvalues along with latest time stamp.
I have following tables
Table1
sno user_id name subject rank Timestamp
1 10 SS maths1 1 2014-12-05 17:24:33
2 10 SS maths2 2 2014-12-05 17:24:33
3 10 PH phy1 3 2014-12-05 17:24:33
4 10 PH phy2 4 2014-12-05 17:24:33
5 10 SS maths1 2 2014-12-04 17:24:33
6 10 SS maths2 1 2014-12-04 17:24:33
7 10 PH phy1 3 2014-12-04 17:24:33
8 10 PH phy2 4 2014-12-04 17:24:33
Tabe12
sno name Status
1 SS Active
2 PH ACtive
3 So Inactive
So I am trying to get the following result
sno userid name subject rank timpestamp
1 10 SS maths1 1 2014-12-05 17:24:33
2 10 PH phy1 3 2014-12-05 17:24:33
So far I tried this query I am able to get the latest time and least rank values for subjects with Distinct name.
but they are not connected to each other means I getting the latesttime stamp and least value from some other row. Please help me How to solve it.
SELECT DISTINCT(T1.`name`),
T1.`user_id`,
T1.`subject`,
MAX(T1.`Timestamp`) as latest_Timestamp,
MIN(T1.`rank `) as rank
FROM Table1 T1,Table2 T2
where T1.`user_id`='10'
AND T2.`status` = 'Active'
AND T1.`name` = T2.`name`
GROUP BY T1.`exercise_id`
ORDER BY T1.`quality_id`, T1.`Timestamp` ASC
Relational databases work well on sets. So think of the data in different sets
You need a set that combines t1 and t2
You need a subset of t1 that only consists of the min rank and max timestamp for each user_Id and name.
So T1 and T2 are joined generating the universe of data and columns you need. We then join to the subquery contianing the subset to filter out the non max timestamp and non-min rank you want.
SELECT T1values.name,
T1values.user_id,
T1values.subject,
T1values.rank,
T1values.timestamp
FROM Table2 T2
INNER JOIN table1 t1Values
ON t1values.sno = t2.sno
and t2.status='Active'
INNER JOIN (SELECT user_Id, name max(timestamp) maxTime, min(rank) Mrank
FROM Table1
GROUP BY user_Id, Name) T1
ON t1.name = T1values.name
and t1.user_Id = t1values.user_ID
and t1.maxTime = T1Values.timestamp
and t1.rank = T1values.mrank
WHERE T1values.user_id='10'
ORDER BY T1values.quality_id, T1values.Timestamp ASC

How to formulate the query to join more than one table

MY tables are as follows
users
id name
1 Michael
2 James
3 John
4 Susie
5 Harvey
products
pid name uploader post_id views exclude groupid
1 learn_java 2 1 21 0 1
2 learn_sql 1 2 8 0 2
3 4 GB DDR3 0 3 5 0 3
4 love jacket 2 4 0 0 5
5 1 TB HDD 3 5 12 1 4
6 kill_ants 3 6 5 0 6
7 2 TB HDD 2 7 2 0 4
8 8 GB DDR3 2 8 18 0 3
9 1 GB DDR2 3 9 7 0 3
product_group
gid name category
1 text 1
2 pdf 1
3 ram 2
4 hdd 2
5 leather 0
6 diy 0
product_category
cid name
1 book
2 electronics
/* forgot about comment field*/
comments
comment_id post_id comment
1 1 ...
2 1 ...
3 2 ...
4 2 ...
5 2 ...
6 3 ...
My Goal:
The product table has 4 types of data.
Products with no uploader( uploader = 0 )
Products that are in review( exclude = 1 )
Products that fall under category = 0(poducts.groupid = product_group.gid AND product_group.category = 0 )
Products that are uploaded by an uploader, not in review and not fall under category = 0( uploader != 0, exclude = 1, poducts.groupid = product_group.gid AND product_group.category != 0)
I only have to consider the 4th type of data. I have to exclude the first three types of data. I have to group these data by their uploader. Say, James have uploaded 3 product, Jones have uploaded 2 product and the rest of the user hasn't uploaded anything.
The query should return this
3 James SUM of views of 3 products
2 Jones SUM of views of 2 products
0 user1 0
0 user2 0
....
....
So if I consider the data of my table, I want to get the data in follwing order
product_num users.id users.name total_views
3 2 James 41(21+18+2)
1 3 Jones 7
1 1 Michael 8
0 5 Harvey 0/NULL
0 4 Susie 0/NULL
I came up with this.
SELECT COUNT(pid) as product_num,
SUM(views) as total_views
users.*
FROM users
INNER JOIN products ON products.uploader = users.id
INNER JOIN product_group ON products.groupid = product_group.category
WHERE exclude = 0
AND product_group.category != 0
Which obviously doesn't work as it doesn't include the users, who hasn't uploaded any product. How to make this work to take these users into account?
EDIT:
SELECT COUNT(pid) as product_num,
SUM(views) as total_views
users.*
FROM users
LEFT JOIN products ON products.uploader = users.id
INNER JOIN product_group ON products.groupid = product_group.category
WHERE exclude = 0
AND product_group.category != 0
GROUP BY users.id
ORDER BY product_num
It also doesn't take users with 0 upload.
Second EDIT:
I have added a comment table(I forgot about it earlier). Is there any way to show the total_number of comments for a user.
Here, James has uploaded product 1, 4, 8. For these post_id is also 1, 4, 8(In real these won't be same). From comments table, these posts have following number of comments 2, 0, 0. So, total number of comment 2.
So, final result should be
product_num users.id users.name total_views total_comments
3 2 James 41(21+18+2) 2
1 3 Jones 7 0/NULL
1 1 Michael 8 3
0 5 Harvey 0/NULL 0/NULL
0 4 Susie 0/NULL 0/NULL
Try this query:
SELECT COUNT(pid) as product_num,
SUM(views) as total_views,
u.*
FROM users u
LEFT JOIN products p
ON p.uploader = u.id
LEFT JOIN product_group pg
ON p.groupid = pg.gid
WHERE p.exclude = 0
AND p.uploader <> 0
AND pg.category != 0
OR p.pid is null
GROUP BY u.id
demo: http://sqlfiddle.com/#!2/c94bef/12
To count a number of comments, please add a dependent subquery to the SELECT clause:
SELECT count(*)
FROM comments c
WHERE c.post_id = p.post_id
in this way:
SELECT COUNT(pid) as product_num,
SUM(views) as total_views,
( SELECT count(*)
FROM comments c
WHERE c.post_id = p.post_id
) As total_comments,
u.*
FROM users u
LEFT JOIN products p
ON p.uploader = u.id
LEFT JOIN product_group pg
ON p.groupid = pg.gid
WHERE p.exclude = 0
AND p.uploader <> 0
AND pg.category != 0
OR p.pid is null
GROUP BY u.id
demo: http://sqlfiddle.com/#!2/5c44f/1

PHP MySQL Highscore table

Im joining 3 tables to present a table with users highest score
My tables
game_log:
---ID---user_ID---score---time---
| 1 52 567 10 |
| 2 53 641 13 |
| 3 52 465 8 |
| 4 53 451 14 |
---------------------------------
users:
---ID---name---countyid---
| 52 Dave 1 |
| 53 John 2 |
------------------------
county:
---countyid---countyname---
| 1 Orange wichit |
| 2 Orange clemts |
--------------------------
SQL:
SELECT * FROM game_log
INNER JOIN users ON game_log.user_ID=users.ID
INNER JOIN county ON users.countyid=county.countyid
ORDER BY game_log.score DESC , game_log.time LIMIT 20";
Above code gives me this result:
Rank---Name--------County------Score---Time
1 John Orange clemts 641 13
2 Dave Orange wichit 567 10
3 John Orange clemts 465 8
4 Dave Orange wichit 451 14
My problem is that I want the highscore table to display the top 20 users with the highest score, not the 20 highest scores.
Like this:
Rank---Name--------County------Score---Time
1 John Orange clemts 641 13
2 Dave Orange wichit 567 10
Need som help with this, not familiar with joining tables ;-)
This approach will show the top 20 users and each user's highest score, and if they have multiple instances of the same score, it'll show the information for the earliest one (lowest time value for that user and score).
SELECT *
FROM game_log gl
INNER JOIN users u
ON gl.user_ID = u.ID
INNER JOIN county c
ON u.countyid = c.countyid
WHERE not exists (select 1
from game_log gl2
where gl2.user_id = gl.user_id
and gl2.score > gl.score)
and not exists (select 1
from game_log gl2
where gl2.user_id = gl.user_id
and gl2.time < gl.time
and gl2.score = gl.score)
ORDER BY gl.score DESC, gl.time LIMIT 20;
Without doing this, if the same user in the top 20 had the same score 2+ times, they would be listed 2+ times, and you would not get back 20 people by using LIMIT 20 because the same person would be taking up N rows out of that 20.
SQL Fiddle here showing data with a tie: http://sqlfiddle.com/#!2/0ac931/5/0
GROUP BY should do the job.
SELECT users.ID, users.name, county.countyname, MAX(game_log.score) AS score, game_log.time
FROM game_log
INNER JOIN users ON game_log.user_ID = users.ID
INNER JOIN county ON users.countyid = county.countyid
GROUP BY game_log.user_ID
ORDER BY game_log.score DESC, game_log.time
LIMIT 20;
Try it out with SQL Fiddle.
I would do this with the not exists approach to get the highest score for each user. The rest of the query is the same:
SELECT *
FROM game_log gl INNER JOIN
users u
ON gl.user_ID = u.ID INNER JOIN
county c
ON u.countyid = c.countyid
WHERE not exists (select 1
from game_log gl2
where gl2.user_id = gl.user_id and gl2.score > gl.score
)
ORDER BY gl.score DESC, gl.time
LIMIT 20;
The where clause is saying "keep this row if no other row for the same user has a higher score".
Another way to do this is with the aggregation approach:
SELECT *
FROM (select user_id, max(score) as maxscore
from game_log gl
group by user_id
) gl INNER JOIN
users u
ON gl.user_ID = u.ID INNER JOIN
county c
ON u.countyid = c.countyid
ORDER BY gl.maxscore DESC
LIMIT 20;
But this method loses the information about time. It is possible to include that, but it makes the query more complicated.

Categories