I needed to recommend to the visitor similar items to the one they where looking at. So I asked in another question for a SQL query to get items with the same categories (from a 'categories' table). The next is the correct code I'm actually using:
SELECT c2.itemID
FROM categories c1
JOIN categories c2 ON c1.catID = c2.catID
WHERE c1.itemID = :id
AND c2.itemID <> :id
GROUP BY c2.itemID
ORDER BY count(c2.itemID) DESC;
It returns items ordered by the number of categories matches (from a table containing 'itemIDs' and 'catIDs'). So for example: item 1 have categories 2,3,4,5,6 and item 2 have categories 2,3,4,5,6 and item 3 have categories 3,5,6 then if I compare item 1 (the one the visitor is looking at) to item 2 and 3 I need to get item 2 first and then item 3 because item 2 have more categories matches than item 3.
NOW: I need also to to order the results by highest vote. The items votes are in a different table. The table 'votes' contain two columns: 'itemID' and 'total_value', 'total_value' being the final rate.
So, How to order the results also by the number of categories matches and also by highest vote from a different table???
Update: The number if items in the table is 8000+. I think the better thing to do to recommend the most similar items is to order it first by items with the exact set of categories and then by votes. Any ideas? Thanks!
SELECT c2.itemID
FROM categories c1
JOIN categories c2 ON c1.catID = c2.catID
JOIN votes v ON v.itemID=c1.itemId
WHERE c1.itemID = :id
AND c2.itemID <> :id
GROUP BY c2.itemID
ORDER BY count(c2.itemID), v.total_value DESC;
Related
I have a database with
a company table
a country table
a company_country n:n table which defines which company is available in which country
a product table (each product belongs to one specific categoryId)
and a company_product_country n:n:n table that defines which company offers which product in which country.
The latter has the three primary key columns companyId, productId, countryId and the additional columns val and limitedAvailability. val is an ENUM with the values yes|no|n/a, and limitedAvailability is an ENUM with the values 0|1.
Products within categories 1 or 2 are available in all countries and therefore get countryId = 0. But at the same time, only these very products may have a limitedAvailability = 1.
An SQLFiddle with a test database can be found here: http://www.sqlfiddle.com/#!9/a065a/1/0
It contains five countries, products and companies.
Background information on what I need to select from the database:
A PHP script generates a search form where an arbitrary list of countries and products can be selected. The products are separated by categories (I did not add the category table in the sample database, because it is not needed in this case). For the first category, I can select whether to exclude products with limited availability.
Generating the desired result works fine:
It displays all companies that are available in the selected countries and have at least one of the selected products available. The result offers a column that defines how many of the selected products are available by company.
If the user defines that one or more categories should not contain products with limited availability, then the products within the corresponding categories will not count as a match if the company offers them with limited availability only.
I am pleased with the performance of this query. My original database has got around 15 countries, 100 companies and 150 products. Selecting everything in the search form occupies the MySQL server for around two seconds which is acceptable for me.
The problem:
After generating the result list of companies which matches as many product search criteria as possible, I use PHP to iterate through those companies and run another SQL query that should give me the list of products that the company does not offer corresponding to the search criteria. The following is an example query for companyId 1 to find out which products are not available when
the desired products have the productIds 2, 4 and 5
the product's country availability should be at least one of the countryIds 1, 2 or 3
the product should not have a limitedAvailability when it is from categoryId = 2:
SELECT DISTINCT p.name
FROM `product` p
LEFT JOIN `company_product_country` cpc ON `p`.`productId` = `cpc`.`productId` AND `cpc`.`companyId` = 1
WHERE NOT EXISTS(
SELECT *
FROM company_product_country cpcTmp
WHERE `cpcTmp`.`companyId` = 1
AND cpcTmp.val = 'yes'
AND (
cpcTmp.limitedAvailability = 0
OR p.categoryId NOT IN(2)
)
AND cpcTmp.productId = p.productId
)
AND p.`productId` IN (2,4,5)
AND countryId IN(0,1,2,3);
The database along with this query can be found on the SQLFiddle linked above.
The query generates the correct result, but its performance dramatically decreases with the number of products. My local SQL server needs about 4 seconds per company when searching for 150 products in 15 countries. This is inaccpetable when iterating through 100 companies. Is there any way to improve this query, like avoiding the IN(...) function containing up to 150 products? Or should I maybe split the query into two like so:
First fetch the unmatched products that do not have country Id 0 and are IN the desired countryIds
Then fetch the unmatched products in countryId = 0 and if applicable filter limitedAvailability = 0
?
Your help is gladly appreciated!
I would suggest writing the query like this:
SELECT p.name
FROM product p
WHERE EXISTS (select 1
from company_product_country cpc
where p.productid = cpc.productid and
cpc.companyid = 1 and
cpc.countryid in (1, 2, 3)
) and
NOT EXISTS (select 1
from company_product_country cpcTmp
where cpcTmp.productId = p.productId and
cpcTmp.companyId = 1 and
cpcTmp.val = 'yes' and
cpcTmp.limitedAvailability = 0
) AND
NOT EXISTS (select 1
from company_product_country cpcTmp
where cpcTmp.productId = p.productId and
cpcTmp.companyId = 1 and
cpcTmp.val = 'yes' and
p.categoryId NOT IN (2)
)
p.`productId` IN (2, 4, 5) ;
Then, you want the following indexes:
product(productid, categoryid, name)
company_product_country(productid, companyid, countryid)
company_product_country(productid, companyid, val, limitedavailability)
company_product_country(productid, companyid, val, category)
Note: these indexes completely "cover" the query, meaning that all columns in the query come from the indexes. For most purposes, is probably sufficient to have a single index on company_product_country. Any of the three would do.
Take the query that identifies the products that match the user selection. Subquery it and outer join it to the products table. Exclude the matches.
SQL Fiddle
SELECT p.name
FROM
product p LEFT JOIN
(
SELECT productId
FROM company_product_country cpcTmp
WHERE companyId = 1 AND
countryId IN (0,1,2,3) AND
(
productId IN (4, 5) OR
(productId = 2 AND limitedAvailability = 0)
)
) t
ON p.productId = t.productId
WHERE
t.productId IS NULL AND
p.productId IN (2,4,5)
I want to count all rows base on the category column, example below, category apple has two rows, so get the the category name apple. Thank you.
id user_id category
1 2 apple
2 4 banana
3 6 apple
4 7 berry
//Count all rows, apple has two row, the largest count, so then get the the category name apple
Use Group by to count the category.
Then order the result set in descending order of the count and select the top 1.
Query
select t.category
from
(
select category,
count(category) as cat_count
from fruits
group by category
)t
order by t.cat_count desc limit 1;
SQL Fiddle
If multiple category having same max count. Then,
Query
select t.category
from
(
select category,
count(category) as cat_count
from fruits
group by category
)t
where t.cat_count =
(
select count(category) as cat_count
from fruits
group by category
order by count(category) desc
limit 1
);
SQL Fiddle
Add limit if you need top 1,2,3 like that limit X x is 1,2,3
Select count(*) as total,category from table group by category from table order by count(*) desc
I'm currently using the following to show a list of players
$sql = <<<SQL
SELECT pla.*, tea.*
FROM Players pla
INNER JOIN Teams tea USING (TeamID)
WHERE TeamID = '$TeamID'
SQL;
I'd like to now arrange these results by sorting grouping them in to positions. So for example all of the goalkeepers shown together in alphabetical order, then all of the defenders.
I know that I can
ORDER BY Position
However that will throw "attackers" first and I'd like the positions to display as Goalkeepers, Defenders, Midfielders, Attackers. Would I need to do 4 separate queries?
you can do a conditional order by that will do what you want
ORDER BY
CASE
WHEN position = "Goalkeepers" THEN 1
WHEN position = "Defenders" THEN 2
WHEN position = "Midfielders" THEN 3
WHEN position = "Attackers" THEN 4
ELSE 5
END,
name
I added position at the end to order the results in ascending order after grouping them by their position
I am doing a pretty normal routine, but having a tough time getting my output correct.
I have two tables: *ads_list* (listings) and *ads_cate* (categories).
I am currently displaying my category list like so:
SELECT id, cateName FROM ads_cate ORDER BY cateName
What I am trying to achieve: count of all items in each category in this format:
Category | Number of Ads
categoryName 56
This is my current code, and have been tweaking but getting no output in my array:
SELECT
ads_cate.id,
ads_cate.cateName, // Category Name
ads_list.id,
ads_list.COUNT(title), // Title of ad
ads_list.Category // Relational Category ID INT(11)
FROM
ads_cate,
ads_list
GROUP BY cateName
ORDER BY cateName
I am calling in all required fields and running a COUNT() on my title field (as these are unique for each ad) and then I am grouping by cateName which also seems correct.
See what this gives you. I think it is what you need.
SELECT
ads_cate.cateName, // Category Name
COUNT(ads_list.id), // Title of ad
FROM
ads_cate
INNER JOIN
ads_list
ON ads_cate.id = ads_list.category
GROUP BY cateName
ORDER BY cateName
I have 4 tables:
categories - id, position
subcategories - id, categories_id, position
sub_subcategories - id, subcategories_id, position
product - id, sub_subcategories_id, prod_pos
Now I'm doing tests to find out what's wrong with my query.
So i want to select sub_subcategories, and to get someting like that:
[[1,2,3,4,5,6], [1,2,3,4,5,6,7]], [[1,2,3,4,5,6], [1,2,3,4]]
Each [] means: big - categories, small - subcategory, and the numbers are position in sub_subcategories. I want the [] to order by their "position" field, so query:
SELECT id FROM sub_subcategories_id
WHERE subcategories_id IN (
SELECT id
FROM subcategories_id
WHERE categories_id IN (
SELECT id FROM categories
WHERE id = 'X' ORDER BY position)
ORDER BY position)
ORDER BY position
is somehow wrong, because I get:
1,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,6,6,6,7
Dunno why - does last "ORDER BY position" destroy everything?
You need to apply all of your desired ordering in the outermost query - ORDERing within subqueries doesn't make any sense - the question "is this ID in <this list>?" has the same answer, no matter what order the list is in (indeed, more property, <this list> is a set, which has no order).
So you'll need to get all of the columns you need to order by in your outermost query.
Something like:
SELECT ssi.ID
from
sub_subcategories_id ssi
inner join
subcategories_id si
on
ssi.subcategories_id = si.id
inner join
categories c
on
si.categories_id = c.id
where
c.id = 'X'
order by
c.position,
si.position,
ssi.position
As it stands now, your query would never return a 'set' of numbers as is. If you ignore all the subselects, you're essentially doing:
SELECT id FROM sub_subcategories_id
ORDER BY position
which would only return one column: the sub_sub_categories_id. You'd be better off doing something like:
SELECT cat.id, subcat.id, subsubcat.id
FROM sub_sub_categories AS subsubcat
LEFT JOIN sub_categories AS subcat ON subcat.id = subsubcat.subcategories.id
LEFT JOIN categories AS cat ON cat.id = subcat.category_id
WHERE (cat.id = 'X')
ORDER BY cat.id, subcat.id, subsubcat.id
That'll return 3 columns ordered by the various IDs. If you don't need the individual sub_sub_categories values, and just want them as a single string value, you can mess around with GROUP_CONCAT() and do various bits of grouping:
SELECT cat.id, subcat.id, GROUP_CONCAT(subsubcat.id)
FROM ...
...
WHERE (cat.id = 'X')
GROUP BY cat.id, subcat.id, subsubcat.id
ORDER BY ...