How to select limited rows based on column - mysql

I'm new in MySQL. This may be easy, but out of my logic
I have a product_table which has 5 columns
id
product_name
product_image
description
category_id
in this table category_id has multiple rows with the same id
for example
category field has 300 rows with category_id '4' and 100 rows with category_id '3' so on....
I want to select only 5 rows per category_id
for example
5 rows for category_id 4 and 5 rows for category_id 3 like this.

SELECT * FROM product_table WHERE category_id = '1' LIMIT 5
UNION
SELECT * FROM product_table WHERE category_id = '2' LIMIT 5
UNION
SELECT * FROM product_table WHERE category_id = '3' LIMIT 5
.
.
.
If there are just a few category, then this should be fine.

select *
from
(SELECT category_id,
id,
CASE WHEN #gr=category_id THEN #rn:=#rn+1 ELSE #rn:=0 END as row_number,
#gr:=category_id
FROM the_table, (select #rn:=0, #gr:=null) as sess
order by category_id, id) sub
WHERE sub.row_number<5
You can introduce session variables and order your data counting member in each group. Then leave only members with group number less than 5

Related

MySQL Query to select from table specific rows

I have a table which looks has the following values:
product_id
custom_id
custom_value
1
10
A
1
9
V
2
10
B
3
3
Q
I am looking for a mysql query to get all values from product_id once and select the row which has custom_id = "10" in case it is available. Nevertheless in case custom_id = 10 is not available for a product_id I would still like to return the product_id but also only once.
So the result I am looking for is
product_id
custom_id
custom_value
1
10
A
2
10
B
3
NULL
NULL
Could please someone direct me in the right direction.
select product_id, custom_id, custom_value from table where custom_id = 10
does of course only return the values for product_id "1" and "2"
You can select the first set of rows, then union by a distinct of all the other product id's
select product_id, custom_id, custom_value from table where custom_id = 10
union
select distinct product_id, NULL as custom_id, NULL as custom_value where custom_id <> 10
You can first generate a ROW_NUMBER to get the first element for each "product_id", then transform to NULL values for which "product_id" does not match your value 10, using the IF function.
WITH cte AS (
SELECT *, ROW_NUMBER() OVER(PARTITION BY product_id ORDER BY custom_id = 10 DESC) AS rn
FROM tab
)
SELECT product_id,
IF(custom_id=10, custom_id, NULL) AS custom_id,
IF(custom_id=10, custom_value, NULL) AS custom_value
FROM cte
WHERE rn = 1
Check the demo here.

Select items from distinct categories, including articles with no category

This seems like it would be pretty simple to do. I have a table of articles that has the following fields relevant to this question:
id - INTEGER(11) AUTO_INCREMENT
category_id - INTEGER(11) DEFAULT(-1)
When an article has a category, its ID goes in the category_id field. When it has no category, the column's value is -1.
What I want to do is to select three random articles of distinct categories from this articles table. This alone is pretty simple to do:
SELECT id FROM articles GROUP BY category_id ORDER BY RAND() LIMIT 3;
However, I don't want to group articles with no category into one single category, like the previous query would do. That is, I want to treat each article with a category_id of -1 as being in a separate category. How can I do this?
You can use union to create a derived table that contains
1 article id per non -1 category
All article ids for -1 category
And then select 3 random ids from that table
select id from (
select id from articles
where category_id <> -1
group by category_id
union all
select id from articles
where category_id = -1
) t order by rand() limit 3;
As pointed out in the comments, the query above will likely return the same article id per category id. If that's an issue you can try the query below but it might run slowly since it's ordering the tables by rand() twice.
select id from (
select id from (
select id from articles
where category_id <> -1
order by rand()
) t
group by category_id
union all
select id from articles
where category_id = -1
) t order by rand() limit 3;

MYSQL - Group By / Order By not working

I have the following data inside a table:
id person_id item_id price
1 1 1 10
2 1 1 20
3 1 3 50
Now what I want to do is group by the item ID, select the id that has the highest value and take the price.
E.g. the sum would be: (20 + 50) and ignore the 10.
I am using the following:
SELECT SUM(`price`)
FROM
(SELECT id, person_id, item_id, price
FROM `table` tbl
INNER JOIN person p USING (person_id)
WHERE p.person_id = 1
ORDER BY id DESC) x
GROUP BY item_id
However, this query is still adding (10 + 20 + 50), which is obviously not what I need to have.
Any ideas to where I am going wrong?
Here is what you are trying to achieve. First you need grouping in a subquery and not in outer query. In outer query you need only sum:
SELECT SUM(`price`)
FROM
(SELECT MAX(price) as price
FROM `table` tbl
INNER JOIN person p USING (person_id)
WHERE p.person_id = 1
GROUP BY item_id) x
http://sqlfiddle.com/#!9/40803/5
SELECT SUM(t1.price)
FROM tbl t1
LEFT JOIN tbl t2
ON t1.person_id= t2.person_id
AND t1.item_id = t2.item_id
AND t1.id<t2.id
WHERE t1.person_id = 1
AND t2.id IS NULL;
I'm not sure if this is the only requirement you have. If so, try this.
SELECT SUM(price)
FROM
(SELECT MAX(price)
FROM table
WHERE person_id = 1
GROUP BY item_id)
First of all - you don't need the person table, because the other table already contains the person_id. So i removed it from the examples.
Your query returns a sum of prices for each item.
If you replace SELECT SUM(price) with SELECT item_id, SUM(price) you wil get
item_id SUM(`price`)
1 30
3 50
But that is not what you want. Neither is it what you wrote in the question " (10 + 20 + 50)".
Now replacing the first line with SELECT id, item_id, SUM(price) you will get one row for each item with the highest id.
id item_id price
2 1 20
3 3 50
This works because of the "undocumented feature" of MySQL, wich allows you to select columns that are not listed in the GROUP BY clause and get the first row from the subselect each group (each item in this case).
Now you only need to sum the price column in an additional outer select
SELECT SUM(price)
FROM (
SELECT id, item_id ,price
FROM (
SELECT id, person_id, item_id, price
FROM `table` tbl
WHERE tbl.person_id = 1
ORDER BY id DESC ) x
GROUP BY item_id
) y
However i do not recomend to use that "feature". While it still works on MySQL 5.6, you never know if that will work with newer versions. It already doesn't work on MariaDB.
Instead you can determite the MAX(id) for each item in an subselect, select only the rows with the determined ids and get the summed price of them.
SELECT SUM(`price`)
FROM `table` tbl
WHERE tbl.id IN (
SELECT MAX(tbl2.id)
FROM `table` tbl2
WHERE tbl2.person_id = 1
GROUP BY tbl2.item_id
)
Another solution (wich internaly does the same) is
SELECT SUM(`price`)
FROM `table` tbl
JOIN (
SELECT MAX(tbl2.id) as id
FROM `table` tbl2
WHERE tbl2.person_id = 1
GROUP BY tbl2.item_id
) x ON x.id = tbl.id
Alex's solution also works fine, if the groups (number of rows per person and item) are rather small.
You have used group by in main query, but it is on subquery like
SELECT id, person_id, item_id, SUM(`price`) FROM ( SELECT MAX(price) FROM `table` tbl WHERE p.person_id = 1 GROUP BY item_id ) AS x

Mysql select last 2 elements ascending followed by 1st element

I want to select the last two elements in ascending order followed by the first element. Here is my code
SELECT products.*, locations.logo FROM
(SELECT products.* FROM
(SELECT products.* FROM products AS products ORDER BY products.id DESC )
AS products LEFT JOIN users ON users.id=products.userid WHERE users.hide=0)
AS products LEFT JOIN locations ON products.location=locations.id LIMIT 2
UNION SELECT products.*, locations.logo FROM
(SELECT products.* FROM
(SELECT products.* FROM products AS products ORDER BY products.id ASC )
AS products LEFT JOIN users ON users.id=products.userid WHERE users.hide=0)
AS products LEFT JOIN locations ON products.location=locations.id LIMIT 3
E.g. for 20 products now I'm getting
20, 19, 1 (ordered by id).
I'm trying to get 19, 20, 1.
At this moment the above statement works according to the E.g. I know I have to put an ORDER BYclause but I don't know where cause in my trials I'm getting error
"Incorrect usage of UNION and ORDER BY"
Can anybody help me with that?
You can do something like this
SELECT id
FROM
(
(
SELECT id, 0 sort_order
FROM Table1
ORDER BY id DESC
LIMIT 2
)
UNION ALL
(
SELECT id, 1 sort_order
FROM Table1
ORDER BY id
LIMIT 1
)
) q
ORDER BY sort_order, id
Output:
| ID |
|----|
| 19 |
| 20 |
| 1 |
Here is SQLFiddle demo

MySQL fetch X columns for each category_id

I don't know if this is possible in mysql via ONE query .
assuming I have a table "products" that has "id","category_id","product_name","price"
Case 1 : I want to fetch 5 products from each category where price is more than 100$
Case 2: I want to fetch
1-"3 products from category 1"
2- "5 products from category 2"
3- "3 products from category 2 where the price is more than 100 and is not fetched in point 2 above"
each case in ONE query , is that possible ?
PS : The table has about 100K rows ...
I found this method : it is VERY fast and gave me exactly what i wanted :
SELECT l.*
FROM (
SELECT category,
COALESCE(
(
SELECT id
FROM writings li
WHERE li.category= dlo.category
ORDER BY
li.category, li.id
LIMIT 15, 1
), CAST(0xFFFFFFFF AS DECIMAL)) AS mid
FROM (
SELECT id as category
FROM cats dl
) dlo
) lo, writings l
WHERE l.category>= lo.category
AND l.category<= lo.category
AND l.id <= lo.mid
I think that you could do this using nested select statements.
http://dev.mysql.com/tech-resources/articles/4.1/subqueries.html
Although, I'm not sure how you would utilize them as seperate select statements after you've gotten your query. I mean, they will all be one record set.
Case 2 I think I've got...
(
select * from products where category_id = 1 limit 3
)
union
(
select * from products where category_id = 2 limit 5
)
union (
select * from products where category_id = 2 and price > 100 limit 3
)
Note that union will eliminate duplicates, union all will allow duplicates (and is therefor faster).
Have you seen?
mySQL Returning the top 5 of each category
That looks like what you're asking...