mysql, update foreach category - mysql

it is a really simple question. it is only 1 table!
i have a table of books which each book have a category and a number.
SO I WANNA TRANSFORM THIS
+--------------------------+
| book_id | category | num |
+--------------------------+
| 1 | a | 7 |
| 2 | a | 5 |
| 3 | a | 3 |
| 4 | b | 9 |
| 5 | b | 8 |
| 6 | b | 1 |
+--------------------------+
INTO THIS,
+--------------------------+
| book_id | category | num |
+--------------------------+
| 3 | a | 3 |
| 2 | a | 5 |
| 1 | a | 7 |
| 6 | b | 1 |
| 5 | b | 8 |
| 4 | b | 9 |
+--------------------------+
AND THEN THIS!
+--------------------------+
| book_id | category | num |
+--------------------------+
| 3 | a | 1 |
| 2 | a | 2 |
| 1 | a | 3 |
| 6 | b | 1 |
| 5 | b | 2 |
| 4 | b | 3 |
+--------------------------+
BUT HOW?!?!?!?!
script to create table...
drop table if exists books;
CREATE TABLE books(
id int AUTO_INCREMENT,
category varchar(30),
num int,
PRIMARY KEY (id)
);
insert into books (category,num)
values
('a',7),
('a',5),
('a',3),
('b',9),
('b',8),
('b',1);

You can use user variables to generate the sequence numbers within each category in the order of increasing id.
If you just want to query the table, use:
select
b.id,
b.category,
#rn := if(#category = category, #rn + 1, if (#category := category, 1, 1)) num
from books b, (select #category := null, #rn := 0) t2
order by b.category, b.id
If you want to update your table, use:
update books b1
join (
select
b.id,
#rn := if(#category = category, #rn + 1, if (#category := category, 1, 1)) num
from books b, (select #category := null, #rn := 0) t2
order by b.category, b.id
) b2 on b1.id = b2.id
set b1.num = b2.num;
Demo
EDIT:
As per the edited question, you can use order by b.category, b.num instead.

Related

mysql sequence number by value coloumn (query UPDATE)

example:
I have a table with the columns
______________________
|field_id|Code|seq_num|
| 1 | a | 1 |
| 1 | a | 2 |
| 1 | a | 3 |
| 2 | a | 4 |
| 2 | a | 5 |
| 3 | a | 6 |
| 3 | a | 7 |
| 3 | a | 8 |
how to query it, so sequence number look like this
_____________________
|field_id|Code|seq_num|
| 1 | a | 1 |
| 1 | a | 2 |
| 1 | a | 3 |
| 2 | a | 1 |
| 2 | a | 2 |
| 3 | a | 1 |
| 3 | a | 2 |
| 3 | a | 3 |
please help!!
One method is to get the minimum sequence for the field:
select t.field_id, t.code,
(seq_num - min_seqnum + 1) as seqnum
from t join
(select field_id, min(seq_num) as min_seq_num
from t
group by field_id
) f
on t.field_id = f.field_id;
You can also do this using variables, if you don't trust the current sequence numbers to have no gaps:
select . . .,
(#rn := if(#f = field_id, #rn + 1,
if(#f := field_id, 1, 1)
)
) as seq_no
from (select t.*
from t
order by field_id, seq_no
) t cross join
(select #f := '', #rn := 0) params;

Counting and limiting number of rows per field value not working

I'm trying to limit the number of rows per field value of a given query. I've found this answered question:
here
As in the first answer of the link, I've created the following table:
create table mytab (
id int not null auto_increment primary key,
first_column int,
second_column int
) engine = myisam;
Inserted this data:
insert into mytab (first_column,second_column) values
(1,1),
(1,4),
(2,10),
(3,4),
(1,4),
(2,5),
(1,6);
And finally run this query
select
id,
first_column,
second_column,
row_num
from
(select
*,
#num := if(#first_column = first_column, #num + 1, 1) as row_num,
#first_column:=first_column as c
from mytab
order by first_column,id) as t,
(select #first_column:='',#num:=0) as r;
But instead of getting this result, where the row_num increases whenever first_column is repeated,
+----+--------------+---------------+---------+
| id | first_column | second_column | row_num |
+----+--------------+---------------+---------+
| 1 | 1 | 1 | 1 |
| 2 | 1 | 4 | 2 |
| 5 | 1 | 4 | 3 |
| 7 | 1 | 6 | 4 |
| 3 | 2 | 10 | 1 |
| 6 | 2 | 5 | 2 |
| 4 | 3 | 4 | 1 |
+----+--------------+---------------+---------+
I get this result:
+----+--------------+---------------+---------+
| id | first_column | second_column | row_num |
+----+--------------+---------------+---------+
| 1 | 1 | 1 | 1 |
| 2 | 1 | 4 | 1 |
| 5 | 1 | 4 | 1 |
| 7 | 1 | 6 | 1 |
| 3 | 2 | 10 | 1 |
| 6 | 2 | 5 | 1 |
| 4 | 3 | 4 | 1 |
+----+--------------+---------------+---------+
I literally copied the code from the link. I checked in SQL Fiddle and code works fine. I'm using XAMPP. Could that be the reason? If it is, is there any workaround to get something like the above working?
I'd really appreciate some help. Thanks in advance.
The variable assignment has to be in the sub-query.
select
id,
first_column,
second_column,
row_num
from
(select
m.*,
#num := if(#first_column = first_column, #num + 1, 1) as row_num,
#first_column:=first_column as c
from mytab m
cross join (select #first_column:='',#num:=0) r --this was in the outer query previously
order by first_column,id
) t

Order by logic and get mixed results?

I have this table structure
id | status
-----+------+
1 | a |
2 | b |
3 | b |
4 | b |
5 | a |
6 | a |
7 | b |
8 | a |
I have two statuses "a" and "b".
I need to order this by logic that says this: for each two "a", show one "b".
So something like this (the "a" is more important, so if there are a lot of "b"s they will just be left in the end)
id | status
-----+------+
1 | a |
5 | a |
2 | b |
6 | a |
8 | a |
3 | b |
4 | b |
7 | b |
Is there a way to do this?
select id
,status
from (select status
,id
,#prev_status := #status
,#status := status
,#rn := case when #prev_status = status
then #rn + 1
else 1
end as rn
from mytable t1,(select #status:=null,#rn:=0) x
order by status
,id
) t
order by floor((rn-1) / case status when 'a' then 2 else 1 end)
,case status when 'a' then 1 else 2 end
,rn
;
+----+--------+
| id | status |
+----+--------+
| 1 | a |
| 5 | a |
| 2 | b |
| 6 | a |
| 8 | a |
| 3 | b |
| 4 | b |
| 7 | b |
+----+--------+
This will help you to understand the solution:
(group_id = floor((rn-1) / case status when 'a' then 2 else 1 end))
+--------+----+----+----------+
| status | id | rn | group_id |
+--------+----+----+----------+
| a | 1 | 1 | 0 |
| a | 5 | 2 | 0 |
| a | 6 | 3 | 1 |
| a | 8 | 4 | 1 |
| b | 2 | 1 | 0 |
| b | 3 | 2 | 1 |
| b | 4 | 3 | 2 |
| b | 7 | 4 | 3 |
+--------+----+----+----------+
Use variables to enumerate the values and then some simple logic:
select id, status
from (select t.*,
(#rn := if(#s = status, #rn + 1,
if(#s := status, 1, 1)
)
) as rn
from t cross join
(select #rn := 0, #s := '') params
where status in ('a', 'b')
order by status, id
) ab
order by (case when status = a then floor( (rn - 1) / 2) else (rn - 1) end),
status, id;

mysql split row to multiple columns and rows

I have a table with the following structure and these values in column assoc.
| id | assoc |
| 1 | |3|-1|107|-4|146|-6| |
| 2 | |19|-3|107|-5| |
| 3 | |42|-1| |
You can see it here
This is a wrong mysql table structure. So I think that the right structure it must be:
| id | assoc | attrib | order |
| 1 | 3 | 1 | 1 |
| 1 | 107 | 4 | 2 |
| 1 | 146 | 6 | 3 |
| 2 | 19 | 3 | 1 |
| 2 | 107 | 5 | 2 |
| 3 | 42 | 1 | 1 |
Is possible to do with a mysql script on phpmyadmin?
SET #prev := null;
SET #cnt := 0;
SELECT id,blah,mah,IF(#prev <> id, #cnt := 1, #cnt := #cnt + 1) AS rank, #prev := id
FROM(
SELECT id,REPLACE(SUBSTRING_INDEX(assoc,'|',2),'|','')*1 as blah,
SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(assoc,'-0'),'|',3),'-',-1)as mah FROM table1
UNION ALL
SELECT id,
SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(assoc,'|0'),'|',4),'|',-1)*1 as blah,
SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(assoc,'-0'),'|',5),'-',-1)as mah FROM table1
UNION ALL
SELECT id,
SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(assoc,'|0'),'|',6),'|',-1)*1 as blah,
SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(assoc,'-0'),'|',7),'-',-1)as mah FROM table1
)x
WHERE x.mah !='0'
ORDER BY x.id ,x.blah
FIDDLE

Sum up values from rows in MySQL table

I have added a new column 'sums' to my table and I am trying to sum up values from 'vals' column then update 'sums' in the same table, according to the algorithm shown below in the table.
I could write a few loops in PHP but I don't think it would be nice.
Any clue how to write it nicely?
--------------------------------------------------
| id | sets | parts | vals | sums |
|-------|---------|---------|----------|---------|
| 1 | 1 | 1 | 2 | 2 |
|-------|---------|---------|----------|---------|
| 2 | 1 | 2 | 3 | 2+3=5 |
|-------|---------|---------|----------|---------|
| 3 | 1 | 3 | 5 |2+3+5=10 |
|-------|---------|---------|----------|---------|
| 4 | 2 | 1 | 4 | 4 |
|-------|---------|---------|----------|---------|
| 5 | 2 | 2 | 1 | 4+1=5 |
|-------|---------|---------|----------|---------|
| 6 | 2 | 3 | 2 | 4+1+2=7 |
|-------|---------|---------|----------|---------|
| 7 | 3 | 1 | 6 | 6 |
|-------|---------|---------|----------|---------|
This should return the value you want for sums:
SELECT t.id
, IF(t.sets=#prev_sets,#i:=#i+t.vals,#i:=t.vals) AS `sums`
, #prev_sets := t.sets AS prev_sets
FROM mytable t
JOIN (SELECT #prev_sets := NULL, #i := 0 ) i
ORDER BY t.sets, t.parts
You can use this as an inline view in an update statement
UPDATE ( SELECT t.id
, IF(t.sets=#prev_sets,#i:=#i+t.vals,#i:=t.vals) AS `sums`
, #prev_sets := t.sets AS prev_sets
FROM mytable t
JOIN (SELECT #prev_sets := NULL, #i := 0 ) i
ORDER BY t.sets, t.parts
) s
JOIN mytable r
ON r.id = s.id
SET r.sums = s.sums
From the example data, it looks like you want the "groupings" on sets, and you want to order on parts within each group.