I currently use the following query which works perfectly:
SELECT * FROM `items` WHERE `id` IN
(SELECT `item_id` FROM `categories_items`
WHERE `category_id` IN (1, 2) GROUP BY `item_id`
HAVING COUNT(`item_id`) = 2);
It selects all the items that are in all the selected (checkboxes) categories.
The problem is that most items are in many categories and a few items are only in two categories and when I only check those two, I still get a list of hundreds of items, making it nearly impossible to find the items that are in few categories.
My first idea was to add an ORDER BY "number_of_total_categories_that_the_selected_item_is_in" ASC somewhere in the query, but since I even got help with the current one and there would probably be a lot of calculations/subqueries for it to work, I thought of an extra column in the items table that would hold the number of categories it's in!
Is it possible to add an effective ORDER BY clause to the query and if so, what would it look like?
Do you have any other ideas? Would "an inverted" foreign key solution work here? Not a chance, right? :p
If not, all I can think of is to manually update a category_count column in items whenever it's needed.
Edit: Table field that holds row count from another table looks interesting, but I have no idea whether it would work in MySQL.
You want to use join rather than in. The following query filters for items that only have the two categories you want. It also counts the total number of categories, which can be used for the order by:
SELECT i.*
FROM `items` i JOIN
(SELECT `item_id`, COUNT(*) as cnt
FROM `categories_items`
WHERE `category_id` IN (1, 2)
GROUP BY `item_id`
HAVING COUNT(DISTINCT CASE WHEN category_id IN (1, 2) THEN category_id END) = 2
) c
ON i.id = c.item_id
ORDER BY cnt ASC;
EDIT:
If you want to count all the categories, then get rid of the where. It is not really doing anything:
SELECT i.*
FROM `items` i JOIN
(SELECT `item_id`, COUNT(*) as cnt
FROM `categories_items`
GROUP BY `item_id`
HAVING COUNT(DISTINCT CASE WHEN category_id IN (1, 2) THEN category_id END) = 2
) c
ON i.id = c.item_id
ORDER BY cnt ASC;
Related
I want to design voting system with two tables.
First table contains candidates' index and name.
The other one contains index, voter and candidate's index whom the voter support.
One voter can support multiple candidates.
I want a sql query that shows candidates' name with number of its supporters.
So the result looks like
John 12, Bob 8, David 3...
SELECT `name`, COUNT(table2.voter) AS `count`
FROM `table1`
LEFT JOIN `table2`
ON table1.idx = table2.support
ORDER BY COUNT(table2.voter) DESC;
The above query gave only one row with total number of voter.
Can anyone give me any hints?
SELECT `name`, COUNT(table2.voter) AS `count`
FROM `table1`
LEFT JOIN `table2` ON table1.idx = table2.support
GROUP BY `name`
ORDER BY COUNT(table2.voter) DESC;
You were missing a group by and hence getting only the first result.
You need to GROUP BY the non-aggregate column (name), otherwise the query will default to one group; the entire result set; and pick an arbitrary name:
SELECT `name`, COUNT(table2.voter) AS `count`
FROM `table1`
LEFT JOIN `table2`
ON table1.idx = table2.support
GROUP BY `name`
ORDER BY count DESC;
You can use column aliases in an ORDER BY, so I have update this also
I have a table with duplicate skus.
skua
skua
skub
skub
skub
skuc
skuc
skud
SELECT sku, COUNT(1) AS `Count` FROM products GROUP BY sku;
shows me all the skus that have duplicates and the number of duplicates
skua 2
skub 3
skuc 2
skud 1
I am trying to find how many there are with 2 duplicates, 3 duplicates etc.
i.e.
duplicated count
1 1 (skud)
2 2 (skua, and skuc)
3 1 (skub)
and I don't know how to write the sql. I imagine it needs a subselect...
thanks
Just use your current query as an inline view, and use the rows from that just like it was from a table.
e.g.
SELECT t.Count AS `duplicated`
, COUNT(1) AS `count`
FROM ( SELECT sku, COUNT(1) AS `Count` FROM products GROUP BY sku ) t
GROUP BY t.Count
MySQL refers to an inline view as a "derived table", and that name makes sense, when we understand how MySQL actually processes that. MySQL runs that inner query, and creates a temporary MyISAM table; once that is done, MySQL runs the outer query, using the temporary MyISAM table. (You'll see that if you run an EXPLAIN on the query.)
Above, I left your query just as you formatted it; I'd tend to reformat your query, so that entire query looks like this:
SELECT t.Count AS `duplicated'
, COUNT(1) AS `count`
FROM ( SELECT p.sku
, COUNT(1) AS `Count`
FROM products p
GROUP BY p.sku
) t
GROUP BY t.Count
(Just makes it easier for me to see the inner query, and easier to extract it and run it separately. And qualifying all column references (with a table alias or table name) is a best practice.)
select dup_count as duplicated,
count(*) as `count`,
group_concat(sku) as skus
from
(
SELECT sku, COUNT(1) AS dup_count
FROM products
GROUP BY sku
) tmp_tbl
group by dup_count
I have an ordering system that can have multiple receipts related to one order. I recently ran into a query as follows that produced an undesirable result.
SELECT info FROM orders WHERE id IN (1, 2, 2) ORDER BY FIELD (id, 1, 2, 2);
Is there a way to return the row for order #2 twice? As of right now the query returns row one then row two as expected; however, in this particular instance returning row #2 twice is needed.
The tables are roughly as follows (I know it isnt totally valid MySQL, just for illustration):
CREATE TABLE orders (
id int(),
info VARCHAR(),
)
CREATE TABLE links (
orderid int(),
receiptid int()
)
CREATE TABLE receipts (
id int(),
otherinfo VARCHAR(),
)
If I'm understanding the situation correctly, you have two entries in the orders table
but orderId 2 is listed twice in the links table. If that is correct, then what you want is:
select o.info from orders o
inner join links l on o.id = l.orderid
If you need to return the row twice, then filtering in the where clause is not what you want. You can do this by filtering using a join:
SELECT o.info
FROM orders o join
(select 1 as id union all select 2 union all select 2
) ids
on o.id = ids.id
ORDER BY FIELD (o.id, 1, 2, 2);
Well, you coul make use of a UNION ALL
Something like
SELECT info FROM orders WHERE id IN (1, 2)
UNION ALL
SELECT info FROM orders WHERE id IN (2)
I have the following mysql query:
SELECT id, sum(views) as total_views
FROM table
WHERE id IN (1,2,3)
GROUP BY id
ORDER BY total_views ASC
If only id 1,3 are found in the database, i still want id 2 to appear, with total_views being set to 0.
Is there any way to do that? This cannot use any other table.
This query hard-codes the list of possible IDs using a sub-query consisting of unions... it then left joins this set of ids to the table containing the information to be counted.
This will preserve an ID in your results even if there are no occurrences:
SELECT ids.id, sum(views) as total_views
FROM (
SELECT 1 AS ID
UNION ALL SELECT 2 AS ID
UNION ALL SELECT 3 AS ID
) ids
LEFT JOIN table
ON table.ID = ids.ID
GROUP BY ids.id
ORDER BY total_views ASC
Alternately, if you had a numbers table, you could do the following query:
SELECT numbers.number, sum(views) as total_views
FROM
numbers
LEFT JOIN table
ON table.ID = ids.ID
WHERE numbers.number IN (1, 2, 3)
GROUP BY numbers.number
ORDER BY total_views ASC
Here's an alternative to Micheal's solution (not a bad solution, mind you -- even with "a lot" of ID's), so long as you're not querying against a cluster.
create temporary table __ids (
id int unsigned primary key
) engine=MEMORY;
insert into __ids (id) values
(1),
(2),
(3)
;
SELECT table.id, sum(views) as total_views
FROM __ids left join table using (id)
GROUP BY table.id
ORDER BY total_views ASC
And if your query becomes complex, I could even conceive of it running more efficiently this way. But, if I were you, I'd benchmark this option with Michael's ad-hoc UNION'ed table option using real data.
in #Michael's answer, if you do have a table with the ids you care about, you can use it as "ids" in place of Michael's in-line data.
Check this fiddle... http://www.sqlfiddle.com/#!2/a9392/3
Select B.ID, sum(A.views) sum from tableB B
left outer join tableA A
on B.ID = A.ID
group by A.ID
also check
http://www.sqlfiddle.com/#!2/a1bb7/1
try this
SELECT id
(CASE 1
IF EXISTS THEN views = mytable.views END
IF NOT EXIST THEN views = 0 END
CASE 2
IF EXISTS THEN views = mytable.views END
IF NOT EXIST THEN views = 0 END
CASE 3
IF EXISTS THEN views = mytable.views END
IF NOT EXIST THEN views = 0 END), sum(views) as total_views
FROM mytable
WHERE id IN (1,2,3)
GROUP BY id
ORDER BY total_views ASC
Does it have to be rows or could you pivot the data to give you one row and a column for every id?
SELECT
SUM(IF (id=1, views, 0)) views_1,
SUM(IF (id=2, views, 0)) views_2,
SUM(IF (id=3, views, 0)) views_3
FROM table
Sorry for the vague topic but I'm having a hard time explaining this. What I'm trying to do is fetching an ID representing a post which can be posted in different categories, and I want my post to belong to all three categories to match the criteria. The table looks like this
id category_id
1 3
1 4
1 8
What I wanna do is fetch an id that belongs to all 3 of these categories, but since they're on different rows I can't use
SELECT id FROM table WHERE category_id = '3' AND category_id = '4' AND category_id = '8';
This will of course return nothing at all, since no row matches that criteria. I've also tried with the WHERE IN clause
SELECT id from table WHERE category_id IN (3, 4, 8);
This returns any post in any of these categories, the post I want returned has to be in all three of these categories.
So the question becomes, is there any good way to look for an id that belongs to all three of these categories, or do I have to use the WHERE IN clause and see if I get 3 rows with the id 1, then I'll know that it occured three times, therefor belongs to all three categories, seems like a bad solution.
Thanks in advance. I appreciate the help. :)
You could use group_concat to get a comma separated string of your category id's and check if that contains all the categories you're filtering
SELECT id, GROUP_CONCAT(t2.category_id) as categories
FROM table AS t1
INNER JOIN table AS t2 ON t1.id = t2.id
WHERE
FIND_IN_SET('3', categories) IS NOT NULL AND
FIND_IN_SET('4', categories) IS NOT NULL AND
FIND_IN_SET('8', categories) IS NOT NULL
Update
SELECT t1.id, GROUP_CONCAT(t2.category_id) as `categories`
FROM `table` AS t1
INNER JOIN `table` AS t2 ON t1.id = t2.id
HAVING
FIND_IN_SET('3', `categories`) IS NOT NULL AND
FIND_IN_SET('4', `categories`) IS NOT NULL AND
FIND_IN_SET('8', `categories`) IS NOT NULL
The last query would not have worked, since it is a group function the value cannot be used in the WHERE clause but can be used in the HAVING clause.
I think you need to say GROUP BY id like this:
SELECT id FROM table WHERE category_id = '3' OR category_id = '4' OR category_id = '8'; GROUP BY id;
Hope that works for you.
i believe you need to establish the amount of categories a post appears in.
this can be done by applying COUNT and HAVING to the SQL query as follows:
SELECT id,
COUNT(category_id) AS categories
FROM `table`
GROUP BY id
HAVING categories > 3
and if you wish the number of categories in your site to change dynamically you can always have an inner SELECT statement like so:
SELECT id,
COUNT(category_id) AS categories
FROM `table`
GROUP BY id
HAVING categories > (
SELECT COUNT(category_id)
FROM `categories`
)
where categories is the table you store all your categories information