MySQL order by points from 2nd table - mysql

So I have MySQL 3 tables, items (which in this case are lodging properties and the data is simplified below), amenities that the properties might offer, and amenities_index which is a list of item ids and amenity ids for each amenity offered. The end user can select any number of amenities they want and I want to return the results in order of the number of amenities that match what they are looking for. So, if they search for 3 different amenities, I want the items listed that offer all 3, then those that offer 2, 1 and finally the rest of the items. I have a query that I think is working for getting the results in the correct order, but I was hoping that I could also return a point value based on the matches, and that's where I'm running into trouble. My SQL skills are a bit lacking when it comes to more complex queries.
Here is an example query I have that returns the results in the correct order:
SELECT * FROM items
ORDER BY
(
SELECT count(*) AS points
FROM `amenities_index`
WHERE
(amenity_id = 1 || amenity_id = 2)
AND amenities_index.item_id = items.id
) DESC
And here is what the tables are structured like. Any help is appreciated.
items table
id name
1 location 1
2 location 2
3 location 3
4 location 4
amenities table
id name
1 fireplace
2 television
3 handicapped accessible
4 kitchenette
5 phone
amenities_index
item_id amenity_id
1 2
1 3
1 5
2 1
2 2
2 6
3 2
3 3
3 4
3 5

You want to move your expression into the select clause:
SELECT i.*,
(SELECT count(*) AS points
FROM `amenities_index` ai
WHERE amenity_id in (1, 2) AND
ai.item_id = i.id
) as points
FROM items i
ORDER BY points desc;
You can also do this as a join query with aggregation:
SELECT i.*, ai.points
FROM items i join
(select ai.item_id, count(*) as points
from amenities_index ai
where amenity_id in (1, 2)
) ai
on ai.item_id = i.id
ORDER BY ai.points desc;
In most databases, I would prefer this version over the first one. However, MySQL would allow the first in a view but not the second, so it has some strange limitations under some circumstances.

Related

MySQL: AVG when there are no matching rows

I'm creating a website where users can rate items (say books), from 1 to 5 stars. I have created a MySQL database and a table which stores info about each rating (itemid, userid, ratingValue).
Each item has a dedicated webpage, and I'd like to put on this page info about: (1) number of ratings and (2) average rating. I thought that this was best done with a view. In thinking about MySQL queries to use in the view, I came to:
SELECT ItemID, COUNT(ratingValue), IFNULL(AVG(ratingValue),0) FROM reviews GROUP BY ItemID
Problem: if an item is not yet rated, it will not appear on the query. What I would like is that all items appear in the query result (view), and if there is an item with no reviews, it should appear with the information that there are 0 reviews and that the average rating is some arbitrary value, let's say 0.
From the official documentation,
AVG() returns NULL if there were no matching rows
and for that reason I tried to use the IFNULL() function. However, it doesn't work. For example, in a database with items 1,2,3,4,5, where item 5 has no reviews, I'll get:
ItemID COUNT(Rating) IFNULL(AVG(ratingValue),0)
1 4 4.0000
2 2 4.0000
3 2 3.5000
4 3 5.0000
Any ideas on how to overcome this problem?
You would have to have another table that lists all of your items (without regard to whether they have reviews) and left join that table to your reviews table. Without that, there is no way to infer from the reviews table alone which items may exist that have no reviews.
So something like:
SELECT
i.itemID AS itemID,
COUNT(r.rating) AS ratingCount,
AVG(ratingValue) AS averageRating
FROM items AS i
LEFT JOIN reviews AS r
ON i.itemID = r.itemID
GROUP BY i.itemID
This should give results like:
ItemID ratingCount averageRating
1 4 4.0000
2 2 4.0000
3 2 3.5000
4 3 5.0000
5 0 NULL

Count Category products with mysql

I have to count products inside each category like I have a category A that have two childs B and C.
In B category have 10 products and C category have 5 Products.
I want to write a query that count product like A = 15 B = 10 C = 5.
I have wrote a query that count only for child categories.
My query is given below
SELECT
c.`id`,
c.`parent_id`,
c.`name`,
c.`slug`,
c.`route_id`,
c.`description`,
c.`excerpt`,
c.`sequence`,
c.`image`,
c.`seo_title`,
c.`meta`,
c.`enabled`,
(SELECT
COUNT(`product_id`)
FROM
HM_category_products
WHERE
HM_category_products.category_id IN (SELECT
HM_categories.`id`
FROM
HM_categories
WHERE
(id = c.`id` OR id = c.`parent_id`))) AS count
FROM
`HM_categories` AS c
EDITED :- Below is my tables structure. This is just an example.
Category Table
id parent_id name slug enabled
1 0 Mobiles & Tablets mobiles-tablets 1
2 1 Mobile Phones mobile-phones 1
3 1 Tablets tablets 1
4 1 Mobile Accessories mobile-accessories 1
5 0 Electronics & Computers electronics-computers 1
6 5 Cameras & Accessories cameras-accessories 1
7 5 TV - Video - Audio tv-video-audio 1
Category Product Table
product_id category_id
1 2
2 2
3 2
4 2
5 3
6 2
7 3
8 3
9 3
10 2
11 3
12 2
13 2
14 2
15 2
You hav to use the "Group by" with something like:
SELECT
category_id, COUNT(*)
FROM
HM_category_products
GROUP BY category_id
This would get your table HM_category_products Grouped by category_id so will know how mane rows of each category its in the table, whats mean you will know the number of product of each category. Then you can join this resulting table with category one to get the category info. (Sorry for my english)
The GROUP BY statement is used in conjunction with the aggregate
functions to group the result-set by one or more columns. SQL GROUP BY Statement
for more info abaut "Group by" read
12.16.2 GROUP BY Modifiers and
12.16.1 GROUP BY (Aggregate) Functions
EDIT: I see now what you whant. I already give you the direction but here its a step furder: DISCLAIMER: THIS QUERY ITS JUST EXAMPLE, IT DOSNT WORK
CREATE VIEW view_2 AS
SELECT
category_id, COUNT(*) AS product_sum
FROM
HM_category_products
GROUP BY category_id
CREATE VIEW view_1 AS
SELECT
*
FROM
HM_categories
LEFT JOIN
view_2 AS a ON HM_categories.id = a.category_id;
SELECT
id,
name,
(SELECT
SUM(product_sum)
FROM
view_1
WHERE
parent_id = final.id) as product_count
FROM
HM_categories AS final;
that way you will get the product of a parent category, your only missing a CASE on the last select "product_count" to the child categorys, but that sum its easy because you already hav it on the view_1 (again sorry for my english)

SQL Count subcategories

I am trying to figure out how to use the count function in order to count how many subcategories each of my categories have.
My original query is as follows(where name = the category name):
select
catid,
name,
pcatid
from category
I simply want to add a column (which is not included in my DB anywhere) called 'SubCategoryCount'. This column will count for each catid how many times it is being referenced my the pcatid column, if that makes sense.
A simple example DB would be
catid name pcatid
1 Base NULL
2 Computers 1
3 Phones 1
4 Laptops 2
5 Dell 4
And i would like to produce
catid name pcatid SubCategoryCount
1 Base NULL 2
2 Computers 1 1
3 Phones 1 0
4 Laptops 2 1
5 Dell 4 0
But how would i achive this?
Thanks for your help
John
You need to join the table itself using LEFT JOIN so it will display all records in the table even if it does not have subcategory.
SELECT a.catid,
a.NAME,
a.pcatid,
COUNT(b.pcatid) SubCategoryCount
FROM category a
LEFT JOIN category b
ON a.catid = b.pcatid
GROUP BY a.catid, a.NAME, a.pcatid
SQLFiddle Demo
PS: This query is just a projection of result without changing the original schema of the table. it doesn't physically add new column on table category.

MySQL query, COUNT and SUM with two joined tables

I need a little help with a MySQL query.
I have two tables one table is a list of backlinks with a is_homepage (bool) flag. The second table is a list of the domains for all of the backlinks, a was link_found (bool) flag, and a url_count column which is the number of rows in the backlinks table that are associated with each domain.
Note that the domain_id column is the foreign key to the domain table id column. Heres some sample data.
backlinks
id domain_id is_homepage page_href
1 1 1 http://ablog.wordpress.com/
2 1 0 http://ablog.wordpress.com/contact/
3 1 0 http://ablog.wordpress.com/archives/
4 2 1 http://www.somewhere.org/
5 2 0 http://www.somewhere.org/page=3
6 3 1 http://www.great-fun-site.com/
7 3 0 http://www.great-fun-site.com/index.html
8 4 0 http://red.blgspot.com/page=7
9 4 0 http://blue.blgspot.com/page=9
domains
id url_count link_found domain_name
1 3 1 wordpress.com
2 2 0 somewhere.org
3 2 1 great-fun-site.com
4 2 1 blgspot.com
The results Im looking to get from the above data would be: count = 2, total = 5.
Im trying to get the count of rows from the domains table (count) and then the sum of the url_count (total) from the domains table WHERE link_found is 1 and where one of the links in the backlink table is_homepage is 1.
Here's the query I'm trying to work with.
SELECT SUM(1) AS count, SUM(`url_count`) total
FROM `domains` AS domain
LEFT JOIN `backlinks` AS link ON link.domain_id = domain.id
WHERE domain.id IN (
SELECT DISTINCT(bl.domain_id)
FROM `backlinks` AS bl
WHERE bl.tablekey_id = 11
AND bl.is_homepage = 1
)
AND domain.link_found = 1
AND link.is_homepage = 1
GROUP BY `domain`.`id`
The problem with this query is that it returns a row for each entry in the domains table. I think I might need one more sub query to add up the returned results but I'm not sure if that's correct. Does anyone see what I'm doing wrong? Thank you!
EDIT:
The problem I'm having is that if there are more than one homepage in the back-links table then its counted multiple times. I need to only count each domain once.
Well, you shouldn't have to do a group by as you are not selecting anything other than aggregated fields. I'm no mysql expert, but this should work:
SELECT count(d.id) as count, sum(d.url_count) as total from domains as d
inner join backlinks as b
on b.domain_id = d.id
Where d.Link_found = 1 and b. is_homepage = 1
The reason you're getting a row for each entry in the domains table is that you're grouping by domain.id. If you want grand totals only, just leave off the GROUP BY piece.
I think a fairly simple query will do the trick:
SELECT COUNT(*), SUM(domains.URL_Count)
FROM domains
WHERE domains.link_found = 1 AND domains.id IN (
SELECT domain_id FROM backlinks WHERE is_homepage = 1)
There's a working SQLFiddle here.
Thanks for the help. Sorry it was so hard to explain I need a MySQL fiddle :)
If anyones interested heres what I ened up with:
SELECT SUM(1) AS count, SUM(total) AS total
FROM
(
SELECT SUM(`url_count`) total
FROM `domains` AS domain
LEFT JOIN `backlinks` AS link ON link.domain_id = domain.id
WHERE domain.id IN (
SELECT DISTINCT(bl.domain_id)
FROM `backlinks` AS bl
WHERE bl.tablekey_id = 11
AND bl.is_homepage = 1
)
AND domain.link_found = 1
AND link.is_homepage = 1
GROUP BY `domain`.`id`
) AS result

better way to implement the following operations in MySQL?

I get a list of options with price like the following:
(it's the result from a select query sort by price asc)
price color quanlity
o_id
1 2 R medium
3 3 G bad
4 4 G good
5 6 B good
2 8 R medium
Now I need to pair those options according to requirements:
e.g. if I need 2 R(red) and 4 G(green)
I'd like to return a list of possible combinations (sort by price asc), like:
R(2) G(4)
c_id o_id o_id total price
1 1 3 16
2 1 4 20
3 2 3 28
4 2 4 32
My current solution for this is to make multiple queries to the DB:
(I'm using Java at the application layer / back end.)
select distinct colors, and store it in a List
In a For loop, select options of each color into a different temp table
join the List of Tables, and calculate the total, sort by total.
But is there a way to condense the above operations into a stored procedure or something more elegant?
You just need a simple self-join:
SELECT R.o_id AS R_id, G.o_id AS G_id, 2*R.price + 4*G.price AS total
FROM mytable R JOIN mytable G ON R.color = 'R' AND G.color = 'G'
ORDER BY total
See it on sqlfiddle.