php-sql limit query per category - mysql

I wanted to limit my query per category, I've seen a lot of same topic here but too complicated so I will ask another.
for example I have
id title category
1 one number
2 two number
3 three number
4 four number
5 a letter
6 b letter
7 c letter
and I wanted to limit my query, let say 2 per category so I have on my output like these
one
two
a
b

I got answer from diff topic
I'll post it here for others who will drop in this same question
SELECT * FROM (
SELECT
table.*,
#rn := CASE WHEN #category=category THEN #rn + 1 ELSE 1 END AS rn,
#category := category
FROM table, (SELECT #rn := 0, #category := NULL) AS vars
ORDER BY category
) AS T1
WHERE rn <= 2

Created using this link, there's an explanation there. This also works for a large number of categories, but beware, it can become very slow without the right keys defined.
set #num := 0, #category := '';
select title, category, #num := if(#category = category, #num +1, 1) as row_number,
#category := category as dummy
from test
group by category, title
having row_number <=2;

Tell me if i'm getting you right -
That's the sql -
SELECT * FROM `categories` ORDER BY `id` ASC LIMIT 0,2
What I did is this: Select all the items in categories table and order it by the id row, limit it for two results only and order it from the beggining

Related

MYSQL count distinct datas depends on if condition

I have really different problem about database query. There is a little bit different scenarios:
I have a table created with 3 columns. They have ID, ItemId, TypeId columns. I need a count query, it should count ItemId and TypeId together but except duplicate columns. For example;
Id ItemId TypeId
-- ------ ------
1 1 1 -> count +1
2 1 1 -> ignore
3 1 2 -> count -1
4 1 2 -> ignore
5 1 1 -> count +1
result count = 1
In the end, if distinct row repeated, count ignore that row. But TypeId data changed for one specific Item it should increase or decrease count. TypeId equals to 1 count +=1, equals to 2 count -=1.
In MySQL, you would seemingly use count(distinct):
select count(distinct itemId, typeId)
from t;
However, you really have a gaps-and-islands problem. You are looking at the ordering to see where things change.
If I trust that the id has no gaps, you can do:
select count(*)
from t left join
t tprev
on t.id = tprev.id + 1
where not ((t.itemId, t.typeid) <=> (tprev.itemId, t.prev.id))
Try the following query. This employs User-defined session variables. It will work in all the cases (including gaps in Id):
SELECT
SUM(dt.factor) AS total_count
FROM
( SELECT
#factor := IF(#item = ItemId AND
#type = TypeId,
0,
IF(TypeID = 2, -1, 1)
) AS factor,
#item := ItemId,
#type := TypeId
FROM your_table
CROSS JOIN (SELECT #item := 0,
#type := 0,
#factor := 0) AS user_init_vars
ORDER BY Id
) AS dt
DB Fiddle DEMO

MySQL delete records between 'n' rows

I am trying to select records between let's say 2nd and 5th row. My id's are not in sequentially order so I tried to retrieve the row_number this way:
SELECT #curRow := #curRow + 1 AS row_number
FROM product_image p
JOIN (SELECT #curRow := 0) r
WHERE row_number BETWEEN 2 AND 5
My table is kind of:
id name
23 A
42 B
98 C
102 D
109 E
What I get is row_number column doesn't exist and it really does not exist. But how can retrieve the records between 2 and 5 ( 3th and 4th )? I red similar post but didn't understand the query well. Thank you in advance!
The reason the query fails is that you can't use column aliases in a WHERE clause. You can however use them in a GROUP BY and HAVING:
SELECT *, #curRow := #curRow + 1 AS row_number
FROM product_image p
JOIN (SELECT #curRow := 0) r
GROUP BY row_number
HAVING row_number between 2 and 5
Since you don't really need to select the row number, there is no need to rely on undocumented "features" like #curRow := #curRow + 1. Just use LIMIT and OFFSET. To select the rows from 2 to 5 you would just need
select id
from product_image
order by name, id
limit 4
offset 1
Note that you need a well defined ORDER BY clause. Otherwise the result can depend on what index has been used by the engine.
To delete those rows, use a DELETE .. JOIN statement
delete product_image
from product_image
join (
select id
from product_image
order by name, id
limit 4
offset 1
)x using(id)
Demo: http://sqlfiddle.com/#!9/66fa4/1
No tricks. No complex queries.
You can do a DELETE with JOIN:
DELETE p
FROM product_image p
INNER JOIN (
SELECT id, #curRow := #curRow + 1 AS row_number
FROM product_image p
CROSS JOIN (SELECT #curRow := 0) r
ORDER BY id
) AS t ON p.id = t.id
WHERE t.row_number IN (2,3)
Derived tabe t is created using your query. You can join to this table in order to identify any records you want and delete them.
Demo here
Check this -
http://sqlfiddle.com/#!9/c462d/5
select * from
(
SELECT #curRow := #curRow + 1 AS row_number,p.*
FROM product_image p
JOIN (SELECT #curRow := 0) r)
a
WHERE a. row_number BETWEEN 2 AND 5
To add a synthetic row number to a query, you need a subquery. Like so.
SELECT id FROM (
SELECT p.*, #curRow := #curRow + 1 AS row_number
FROM product_image p
JOIN (SELECT #curRow := 0) r
) q
WHERE row_number BETWEEN 2 AND 5
Then, you can, if you wish, use the result to drive a delete operation.
DELETE FROM product_image
WHERE id IN (
SELECT id FROM (
SELECT p.*, #curRow := #curRow + 1 AS row_number
FROM product_image p
JOIN (SELECT #curRow := 0) r
) q
WHERE row_number BETWEEN 2 AND 5
)
But this is an extremely bad thing to do. Why? You're relying on a particular order in the result set of your inner query. The rows in a SQL result set are, without any ORDER BY clause, returned in an unpredictable order. Server optimizers exploit this. Unpredictable is like random, but worse. Random implies that it's unlikely the order will be the same each time you run the query. Random here is good because you'll catch your problem during testing. Unpredictable, on the other hand, means the order remains the same until it doesn't. If you're not sure why that's bad, look up Murphy's law.

mySQL create a result set of two table alternating rows

I have a query that uses union to join two sub queries e.g.
SELECT * FROM posts WHERE postTypeId=1 (e.g. blog)
UNION
SELECT * FROM posts WHERE postTypeId=2 (e.g. news)
The result set that this approach generates positions the two sub-sets sequentially ("blog" then "news").
I want to create a a result set which interleaves the two, alternating between rows from the "blog" sub-set and the "news" sub-set.
I feel that there must be a simple way to do this, but I have failed to find it.
Any suggestions would be greatly appreciated.
Nick
This is solution that best works for me. It's not identical to any of the current proposals, so I have added it independently. #a, #b and #c are used to create row numbers per sub-set, meaning that in the combined results, 3 rows will share the same row number (a "row set"). This is used as the first order sort, and second order sort then orders the rows within the "row set".
SET #a = 0;
SET #b = 0;
SET #c = 0;
SELECT * FROM(
SELECT #a := #a + 1 AS sortOne, 1 AS sortTwo, posts.* FROM posts WHERE posts.postTypeId=3
UNION
SELECT #b := #b + 1 AS sortOne, 2 AS sortTwo, posts.* FROM posts WHERE posts.postTypeId=2
UNION
SELECT #c := #c + 1 AS sortOne, 3 AS sortTwo, posts.* FROM posts WHERE posts.postTypeId=1
) AS result ORDER BY sortOne, sortTwo
This solutions is derived/inspired by submitted solutions, but I don't think it appropriate to mark any of them as being an accepted solution in itself. So, credit where it's due to Thomas Clayson, Tony Hopkinson and rmunoz whose answers I've voted up. Cheers!
Well, here is a novel way I can think of (not tested, but you'll get the gist):
SELECT * FROM (
SELECT 1 AS query, #n := #n + 1 AS rowNumber, posts.* FROM (select #n:=0), posts WHERE posts.postTypeId=1
UNION
SELECT 2 AS query, #n := #n + 1 AS rowNumber, posts.* FROM (select #n:=0), posts WHERE posts.postTypeId=2
) ORDER BY rowNumber, query;
So this will do the two queries and then order by first rowNumber and then by query. What you'll end up with is something like:
rowNumber | query
1 | 1
1 | 2
2 | 1
2 | 2
etc...
SELECT #n=:0 resets the global variable n to 0 for the query and then the #n := #n + 1 increments the value for each row.
If you need any more explanation let me know. I hope this works! :)
That's easy, you can add a parameter in your subquery, the parameter "rank", this way:
SET #rank=0;
SELECT #rank:=#rank+2 AS rank FROM posts WHERE postTypeId=1 (e.g. blog)
Then, you get:
0, ...
2, ...
If you do the same in the other query but init initializating rank to 1, you will get:
1, ...
3, ...
Finally "ORDER BY rank" and you will get posts and news mixed.
Try
Set #current_row = 0
SELECT * FROM (
SELECT #current_row := #current_row + 1 as Position, posts.*
FROM posts
WHERE postTypeId=1
UNION
SELECT #current_row, posts.*
FROM posts
WHERE postTypeId=2
) dummyTableName
ORDER BY position, postTypeId
maybe

Getting mysql query results based on a column value

I have a MySql table with two columns namely category and name. I have 4 unique values of category and there are thousands of records in this table. But all these records fall into either of the 4 categories present in the table.
Now, What I want is that as output, I should get 2 results of each category i.e. 2 results of first category, then 2 results of next category and so on.
Is it possible with a single query ?
set #num := 0, #cat := '';
select category,name
#num := if(#cat = category, #num + 1, 1) as row_number,
#cat := category as dummy
from MyTable
group by cateogry, name
having row_number <= 2;
What about this?
SELECT * FROM your_table WHERE category = 1 LIMIT 2
UNION
SELECT * FROM your_table WHERE category = 2 LIMIT 2
UNION
SELECT * FROM your_table WHERE category = 3 LIMIT 2
UNION
SELECT * FROM your_table WHERE category = 4 LIMIT 2

Fetching multiple random values for each values of one given field

I use MySql and I have a table like that:
| id | category | field 1 | field 2 |
Values in the category field are not unique.
I would like to fetch 5 random rows for each category value
I can't find a solution in one query for that.
Could you help me on this one?
Thank you
EDIT:
I've found this:
SELECT t.*
FROM (
SELECT #lim := 5,
#cg := -1
) vars,
table_example t
WHERE CASE WHEN #cg <> category THEN #r := #lim ELSE 1 END > 0
AND (#r := #r - 1) >= 0
AND (#cg := category) IS NOT NULL
ORDER BY
category , id
But in this case, it fetches 5 rows for each category value (but it's ordered by id). I've tried to tweak that to order this randomly but I couldn't find anything satisfying...
I am not sure that this works, just a hunch:
SET #rownum=1;
SELECT t.id,CASE WHEN #rownum > 5 THEN #rownum:=0 ELSE #rownum=#rownum+1
FROM table_example t WHERE #rownum<=5 GROUP BY t.category,t.id ORDER BY RAND()