Modify column values to that they are in order - mysql

I got a very special problem that I'd like to solve in SQL. I need to make sure that optionOrder for the same questionID goes from 0-[any number].
So for example the rows with questionID = 18386, their optionOrder are right now 1,2,3,4. They need to be 0,1,2,3.
Also if the rows are like this 1,2,4, it needs to be 0,1,2
I'm sorry for the incorrect grammars.

In MySQL, you can do this with variables:
set #rn := 0;
set #q := -1;
update table t
set optionorder = (case when #q = questionid then (#rn := #rn + 1)
when (#q := questionid) is not null then (#rn := 0)
else -1 -- should never happen
end)
order by questionid, optionorder;
Because of the order by, you need to set the variables outside the update.

Related

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

MySQL: Empty string with wildcards do not return any results

I have a procedure that looks something below.
It returns results only if the _keyword is set for some reason.
But if the _keyword is an empty string, it returns nothing.
★ _keyword is a dynamic variable to this procedure and not NULL
What could be the cause of this behavior?
set #cart_count := 0;
set #fav_count := 0;
set #access_count := 0;
set #i := 0;
set #orderCol := 'itemId';
set #offset := 1;
set #maxLimit := 30;
set #keyword := concat('%', _keyword, '%');
set #targetDate := '2018-01-05';
select
t.item_id,
t.item_title,
t.item_url,
t.item_status,
t.cart_count,
t.favourite_count,
t.access_count,
UNIX_TIMESTAMP(t.created_date) as created_date
from (
select
t1.item_id,
t1.item_title,
t1.item_url,
t1.item_status,
t1.created_date,
#val1 := if(#item_id1=t1.item_id, t1.cart_count - #cart_count, t1.cart_count) as cart_count,
#val1 := if(#item_id1=t1.item_id, t1.favourite_count - #fav_count, t1.favourite_count) as favourite_count,
#val1 := if(#item_id1=t1.item_id, t1.access_count - #access_count, t1.access_count) as access_count,
#cart_count := t1.cart_count,
#fav_count := t1.favourite_count,
#access_count := t1.access_count,
#item_id1 := t1.item_id
from some_table t1
where date(t1.created_date) = #targetDate AND (t1.item_title like #keyword or t1.item_id like #keyword)
) t
where (#i := #i+1) BETWEEN #offset and #maxLimit
and t.cart_count > 0
I don't know where/how you are setting the value of _keyword, but I propose that the empty string value you think you have is actually NULL. Consider the following two queries:
select 'empty string matches' from dual where 'hello' like '%%';
select 'null matches' from dual where 'hello' like concat('%', NULL, '%');
Demo
Only empty string matches will appear in the output. The reason why CONCAT('%', NULL, '%') does not match anything is that NULL means unknown. Hence, we cannot say in general that it does or does not match anything.

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 incrementing value

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);