MySQL incrementing value - mysql

Is there a way to make a value increment with every insert if having multiple inserts? (I dont speak of the primary key that autoincrements)
Lets say I have a structure like this:
|ID_PRODUCT|ID_CATEGORY|NAME|POSITION|
So I have individual product ids, each produt belongs to a category and has a different position in this category. I want to do something like this:
INSERT INTO products
( SELECT id_product, id_category, name, MY_POSITION++
FROM db2.products WHERE id_category = xxx )
So there should be a variable MY_POSITION that starts with 1 and increments every insert.
It would be really easy to do this all just with a scripting-language like php or python, but I want to get better with SQL :)

Yes: Use a user defined variable:
SET #position := 0; -- Define a variable
INSERT INTO products
SELECT id_product, id_category, name, (#position := #position + 1)
FROM db2.products
WHERE id_category = xxx;
The result of increment to #position is the value used for the insert.
Edit:
You can skip the declaration of the variable by handling the initial value in-line:
...
SELECT ..., (#position := ifnull(#position, 0) + 1)
...
This can be particularly handy when executing the query using a driver that does not allow multiple commands (separated by semicolons).

You will need to ORDER BY id_category and use two user variables so you can track the previous category id -
SET #position := 0;
SET #prev_cat := 0;
INSERT INTO products
SELECT id_product, id_category, name, position
FROM (
SELECT
id_product,
id_category,
name,
IF(#prev_cat = id_category, #position := #position + 1, #position := 1) AS position,
#prev_cat := id_category
FROM db2.products
ORDER BY id_category ASC, id_product ASC
) AS tmp;
This will allow you to do all categories in one query instead of category by category.

Purely to add to #Bohemians answer - I wanted the counter to reset every so often and this can be done like such:
SELECT *,(#position := IF (#position >= 15,1,#position + 1))
Where 15 is obviously the maximum number before reset.

Try setting a value using a subquery like this
(SELECT MAX(position) FROM products AS T2)+1
Or
(SELECT MAX(position) FROM products AS T2 WHERE id_category = 'product category')+1

Hope this will work.
update <tbl_name> set <column_name>=<column_name>+1 where <unique_column/s>='1'";

For those who are looking for example of update query, here it is:
SET #i := 0;
UPDATE products SET id = (#i := #i + 1);

Related

How to split count() query into 10 groups in my sql

I am currently using mysql and I have to split the data into 10 groups.
For example, if the total count of data is 90, it should go like
1~9,
10~18,
19~27,
28~36,
37~45,
46~54,
55~63,
64~72,
73~81,
82~90.
if the total count of data is 100, it should go like
1~10,
11~20,
21~30,
31~40,
41~50,
51~60,
61~70,
71~80,
81~90,
91~100.
Can anyone give me a clue to split the data into 10 groups. I used rownum, but it did not work....
select total.row_num,
total.name,
total.reg,
total.id,
total.motspd
from(
select
(#row_num:=#row_num+1) AS row_num,
cg.group_name as name,
td.reg_date as reg,
td.car_id as id,
td.mcu_motspd as motspd
from
cartracker.tracker_data td
left join car c on (c.car_device_no = td.car_id)
left join car_group cg on (c.car_group_no = cg.car_group_no)
where cg.car_group_no = "1"
group by DATE_FORMAT(td.reg_date, "%Y%M%d%h%m")
)total
This is a result of a query, but it shows wrong row numbers.I want row_num goes from 0 to the end number of the data. but, in the picture, it starts from 44,713. can anyone help me to fix row num as it starts from 0 to the end number of the data.
attached image
I am going by the question in the title:
I am currently using mysql and I have to split the data into 10 groups.
That is exactly what the function ntile() does. So:
select t.*,
ntile(10) over (order by <whatever>) as tile
from t;
I have no idea what the query has to do with this question.
Haven't realize OP want with dynamic table BEFORE re-edit, this is not cleaver and require a lot of time to process though. switch a with OP table name and create rownumber first.
DECLARE #TotalNum INT;
DECLARE #Num INT;
DECLARE #NUm2 INT;
DECLARE #COUNTS INT;
SET #COUNTS = (select count(rownum) from a)/10
SET #TotalNum = 10
SET #Num =1
SET #Num2 =0
WHILE #Num <= #TotalNum
BEGIN
update a
set a.flag = #Num
where a.rownum >= (#COUNTS)*#NUM2+1 and a.rownum <= #NUM*(#COUNTS)
SET #Num = #NUM + 1
SET #Num2 = #NUM2 + 1
END

How to add series number value on my sql columns

I have a table and I need to put a series number(without duplicate) on each value of the columns.
I do it manually by editing each columns using this,|
UPDATE mytable where column = '123'
SET table_column1 = '955';
then next
UPDATE mytable where column = '124'
SET table_column2 = '956';
but this one takes time for me.
There's any solution or fastest way to do this??
I'm thinking of I will add another auto increment but 2 increment is advisable ?
Here's the screenshot of my data below
You can't add another auto-increment. But you can do it with one query if you don't care about a specific order
UPDATE mytable
cross join (select #rank := 0) r
SET table_column2 = 956 + (#rank := #rank + 1)
SQLFiddle demo
If you need a specific order then you can define the #rank variable outside the query and add an order by
set #rank := 0;
UPDATE mytable
SET table_column2 = 956 + (#rank := #rank + 1)
order by id
SQLFiddle demo

optimization sql query update depend of another table

I did a sql query for specific need, but I am not a professional of SQL, so i need help to "improve" the query.
Firstly, I have 3 tables :
user (id, username, ...)
action (id, point, limitPerDay)
history (id, user_id, action_id, created_at, is_used_for_score)
Each time user do a specific action, it is log in history table (is_used_for_score will be true or false if limit has been reached or not)
But I need query for update all is_used_for_score because under certain conditions is_used_for_score can be pass from false to true
My query actually is
SET #num := 0, #user_id := 0, #type := '', #date := '';
UPDATE history SET is_used_for_score = 1 WHERE history.id IN (
SELECT history_id FROM (
SELECT
history.id AS history_id,
action.limitPerDay AS limit_per_day,
action.point,
#num := IF(
#type = action_id,
IF(
#date = CONCAT(YEAR(history.created_at), '-', MONTH(history.created_at), '-', DAY(history.created_at)),
IF (
#user_id = user_id,
#num + 1,
1
),
1
),
1
) AS row_number,
#user_id := user_id AS user_id,
#type := action_id AS action_id,
#date := CONCAT(YEAR(history.created_at), '-', MONTH(history.created_at), '-', DAY(history.created_at)) AS `date`
FROM history
LEFT JOIN `action` ON action.id = action_id
HAVING (row_number <= limit_per_day) OR (limit_per_day IS NULL)
ORDER BY history.created_at ASC, user_id, action_id
) AS history_id_count_for_vote
);
But, I am pretty sure is probably not the best way to do that. Did you have some suggestion which can improve the query ?
Thank you
On the issues like this, I'm repeating relentlessly, again and again: SQL is a DECLARATIVE language, do NOT forget this (slipping into imperativeness, just like you do)!
That means, that you're going to define, DECLARE the set(s) you going to work with - in other words, tell the Engine WHAT you want, leaving HOWs at its discretion.
That's the cornerstone.
So try to think through it like this: "I need to update SOME set of records [ones fell under '... certain conditions is_used_for_score can be pass from false to true'], so let's define that set first"
And that is what you lacking here - so please go ahead, define these conditions and update your question with it, cause it's quite a time-eater trying to extract it from that mess you've posted.

Insert sequential numbers based on another field - MySQL

There is a similar question
Insert sequential number in MySQL
I want to insert sequential numbers to the table, but based on another field. I have two columns page_numner and parent, so the rows with same parent should have page_number as consequtive numbers. If parent changes, the page should start from 1 again and increase by one.
I was thinking to use smth like this
SELECT #i:=0;
SELECT #p:=0;
UPDATE my_table AS t SET page_number = CASE
WHEN #p = t.`parent` THEN #i:=#i+1
ELSE 1 -- assign current parent to #p ??
END
but, it cant figure out how to assign the new parent into #p for the else case.
Please note, that I am trying to achieve this with pure mysql (if possible of course)
Thanks
You can do what you want with this code:
set #p := -1;
set #i := 0;
UPDATE my_table t
SET page_number = (CASE WHEN #p = t.`parent` THEN #i := #i+ 1
WHEN (#p := t.parent) = NULL THEN NULL -- never happens
ELSE #i := 1
END)
ORDER BY t.parent;
Unfortunately, MySQL doesn't allow both ORDER BY and JOIN in the same UPDATE query. If it did, you could initialize the variables in the query.
Note the second condition just does the assignment. = NULL never returns TRUE.

Mysql, tree, Hierarchical query, performance

My question is based on the following article (the table and the function hierarchy_connect_by_parent_eq_prior_id) http://explainextended.com/2009/03/17/hierarchical-queries-in-mysql/
Lets assume that the table t_hierarchy has two additional fields (beside id and parent) typ1(char) and time(int). the field typ1 can have two values A and B.
My goal is to display the whole tree as described in the article but I need an extra field in the result that displays the time of the current node (if typ1 = B) and of all of its descendants (if typ1 = B). So I need the sum of all descendants' times for a certain node (including itself) when typ1=B.
I have the following solution but it is way too slow:
main query:
SELECT CONCAT(REPEAT(' ', level - 1), hi.id) AS treeitem, get_usertime_of_current_node_and_descendants(hi.id) as B_time,
hierarchy_sys_connect_by_path('/', hi.id) AS path,
parent, level
FROM (
SELECT hierarchy_connect_by_parent_eq_prior_id(id) AS id,
CAST(#level AS SIGNED) AS level
FROM (
SELECT #start_with := 0,
#id := #start_with,
#level := 0
) vars, t_hierarchy
WHERE #id IS NOT NULL
) ho
JOIN t_hierarchy hi
ON hi.id = ho.id
The function get_usertime_of_current_node_and_descendants(input int):
BEGIN
DECLARE _id INT;
DECLARE _desctime INT;
DECLARE _nodetime INT;
SET _id = input;
select COALESCE((select sum(time) from (
SELECT hi.id, time,typ1
FROM (
SELECT hierarchy_connect_by_parent_eq_prior_id_2(id) AS id, #levela AS level
FROM (
SELECT #start_witha := _id,
#ida := #start_witha,
#levela := 0,
) vars, t_hierarchy a
WHERE #ida IS NOT NULL
) ho
JOIN t_hierarchy hi
ON hi.id = ho.id
) q where typ1 = 'B'), 0) into _desctime;
select COALESCE((select time from t_hierarchy where id = _id and typ1='B'), 0) into _nodetime;
return _desctime + _nodetime;
END $$
The function hierarchy_connect_by_parent_eq_prior_id_2 is the same as in the article and as the one above hierarchy_connect_by_parent_eq_prior_id but it has differently named global variables so it won't interfere with the the ones used in the main query.
The above solution works as desired but it is way too slow (especially when working with large datasets). Can you offer a better solution or can you suggest how to improve the query? Thank you in advance for your time and help!
I solved the problem retrieving the time of the descendants outside of mysql (before inserting the entries into the table).