MYSQL Adding plus one in loop or for each - mysql

I have next quarry:
UPDATE PREDMETIF t
CROSS JOIN (select COALESCE(MAX(predf_aa),0) max_predf_aa from PREDMETIF) m
set t.predf_aa = m.max_predf_aa + 1
where t.strf_ID = '1';
Witch gives me result of:
| predf_aa | strp_ID |
| -------- | ------- |
| 1 | 1 |
| 1 | 1 |
| null | 2 |
And I need:
| predf_aa | strp_ID |
| -------- | ------- |
| 1 | 1 |
| 2 | 1 |
| null | 2 |
I have made Example fiddle
I find that closest thing to for each is PROCEDURE with LOOP but never used that and it seems a bit overkill. What is simplest way to achieve this with given example?
And if LOOP Statement is what I need I would appreciate if someone would write it for me on this example and ill decode it for future references.

I think you want variables:
select #rn := COALESCE(MAX(predf_aa), 0) as max_predf_aa
from PREDMETIF;
update PREDMETIF t
set t.predf_aa = (#rn := #rn + 1)
where t.strf_ID = 1;
That said, if you want a unique id on each row, then perhaps you want an auto_increment column. Of course, you would not have null in that column, so it is not clear if that is a viable solution for you.

Related

MySQL: How do you add new id column to the table, group by the old column?

I am trying to add column new_id to the table in MySQL Workbench, but I want this new_id to be GROUP BY the old_id.
I tried the code below. The new_id is automatic increasing, but it is not group by old_id.
ALTER TABLE candidate
ADD COLUMN new_id int not null auto_increment UNIQUE FIRST,
ADD PRIMARY KEY(old_id, new_id);
Below is what I got:
+----------+--------+
| old_id | new_id |
+----------+--------+
| 00132004 | 1 |
| 00132004 | 2 |
| 00132004 | 3 |
| 00132004 | 4 |
| 00118685 | 5 |
| 00118685 | 6 |
| J99999 | 7 |
| J99999 | 8 |
| J99988 | 9 |
| J99987 | 10 |
+----------+--------+
But this is what I want to get:
+----------+--------+
| old_id | new_id |
+----------+--------+
| 00132004 | 1 |
| 00132004 | 1 |
| 00132004 | 1 |
| 00132004 | 1 |
| 00118685 | 2 |
| 00118685 | 2 |
| J99999 | 3 |
| J99999 | 3 |
| J99988 | 4 |
| J99987 | 5 |
+----------+--------+
What am I missing here....?
Thank you!!!
Your new requirement for new_id will not work making that column auto increment, because then the values will not be unique or incremental.
What you are looking for is something called the dense rank. MySQL does not have built in support for this, but you can simulate it using session variables:
SET #dense_rank = 0;
SET #old_id = NULL;
SELECT
#dense_rank:=CASE WHEN #old_id = old_id
THEN #dense_rank ELSE #dense_rank + 1 END AS dr,
#old_id:=old_id AS old_id,
new_id
FROM candidate
ORDER BY new_id
Note that because MySQL does not support any clean way of automatically having a dense rank maintained, a SELECT query might be your best long term option. This way, you can just compute the dense rank from the latest data whenever you need it, without needing to worry about maintaining it in your actual table.
Output:
Demo here:
Rextester
What you want can (sadly) not be done with an auto-increment column.
An alternative solution could be:
CREATE TABLE mytab (new_id int not null auto_increment,
old_id VARCHAR(200),
FOREIGN KEY old_id (old_id) REFERENCES candidate(old_id));
CREATE UNIQUE INDEX old_id_idx ON mytab(old_id);
This guarantees unique old_ids and gives you a unique new_id in return. You can now join this new_id against the candidate table with an INNER JOIN and get your desired result.
Add the column, then set it using update:
set #old_id = '', #rn = 0;
update candidate
set new_id = if(#old_id = old_id, #rn,
if(#old_id := old_id, #rn := #rn + 1, #rn := #rn + 1)
)
order by old_id;

How to update rows of MySQL without stored function?

My cloud server doesn't allow stored function/procedure, so how to do this?
My table was:
|id |Status |
| 1 | Good |
| 2 | Badd |
| 3 | Good |
| 4 | Good |
I wanted it to be:
| 1 | Good |
| 3 | Badd |
| 4 | Good |
| 5 | Good |
Can this be done with one line of statement? Basically I want to change all id in rows where id>x to id+1. In this case, x = 2.
Update
Forgot to mention that id is unique so how to sort them desc then change?
You can use
UPDATE yourtable SET id = id + 1 WHERE id > 1 ORDER BY ID DESC;
there is a known "bug" or "feature" that you will face if id is unique but order by desc will help you to avoid it.
Try this
UPDATE `table` SET id = id + 1 WHERE id > 1

Sum column only if id is different

Hope everyone is doing well.
I have the following output...
+---------+--------------+--------------+-----------+---------+----------+
| ord_num | signoff_date | program_name | prod_desc | tx_comp | priority |
+---------+--------------+--------------+-----------+---------+----------+
| 1234567 | 2012-08-12 | ilearn | run | 1 | 1 |
| 1234567 | 2012-08-12 | ilearn | plan | 1 | 1 |
| 1234568 | 2012-08-12 | other | run | 1 | 1 |
| 1234569 | 2012-08-12 | other | run | 0 | 1 |
+---------+--------------+--------------+-----------+---------+----------+
What I would like to do is SUM the tx_comp column once per unique "ord_num".
Now I cant use GROUP BY ord_num as I also do a sum on the type of tasks.
Its like I need to know what the previous ord_num was then sum if different?
Any ideas would be greatly appreciated.
Thanks.
* EDIT *
SELECT
signoff_date,
SUM(IF(prod_desc = 'run', 1, 0)) AS run,
SUM(IF(prod_desc = 'plan', 1, 0)) AS plan,
SUM(tx_comp) AS tx_comp
FROM
(
SELECT
ord_num,
signoff_date,
program_name,
prod_desc,
tx_comp,
priority
FROM
test.orders
LEFT JOIN test.tx_comp USING (ord_num)
) AS grp
Obviously not the desired output
+--------------+------+------+---------+
| signoff_date | run | plan | tx_comp |
+--------------+------+------+---------+
| 2012-08-12 | 3 | 1 | 3 |
+--------------+------+------+---------+
I am after...
+--------------+------+------+---------+
| signoff_date | run | plan | tx_comp |
+--------------+------+------+---------+
| 2012-08-12 | 3 | 1 | 2 |
+--------------+------+------+---------+
If the value of tx_comp is always 1 or zero, then we can leverage COUNT(), which may give us more options. For instance, we can count the distinct ord_num where tx_comp is 1:
COUNT(distinct IF(tx_comp, ord_num, NULL))
Which gives me a final query of:
SELECT signoff_date,
SUM(IF(prod_desc = 'run', 1, 0)) AS run,
SUM(IF(prod_desc = 'plan', 1, 0)) AS plan,
COUNT(distinct IF(tx_comp, ord_num, NULL)) as tx_comp
FROM
test.orders
JOIN test.tx_comp USING (ord_num)
GROUP BY signoff_date
And there is no need for the subquery in this case. (edit: updated for your JOIN)
I have tested this with your sample data; the only dependency is on the semantic nature of tx_comp. You have been saying "SUM", and this assumes that the value will be at most 1 (I understand it to be a boolean flag, and in a comment on another answer you mentioned MAX(tx_comp) returning 1, so I think we're good).
Maybe just use MAX instead of SUM on the tx_comp column? I'm not sure about the semantics of your data, but I'm guessing that's what you want. In fact, it may be the same for run and plan as well.
For that matter, what you really want is BIT_OR, as you're working with booleans.

How can I sequence a column within a table using SQL (MySQL)?

I have a list mapped using Hibernate with an index column. The table looks like this:
mysql> select * from chart;
+----+--------+------+-----------------------+
| id | report | indx | name |
+----+--------+------+-----------------------+
| 2 | 1 | 0 | Volume |
| 3 | 2 | 0 | South Africa (Volume) |
| 5 | 2 | 2 | People |
| 6 | 2 | 3 | Platforms |
| 7 | 2 | 4 | People (Gender) |
+----+--------+------+-----------------------+
As you can see chart id=4 for report=2 with indx=1 has been deleted.
I need to eliminate the gaps so all the indx values for a given report run in sequence from 0. I could write Java code to sort this out but SQL solution would be much easier to deploy.
Anyone know how to do this? I am using MySQL.
if this technique works on your version of MYSQL you can try
SET #row := -1;
UPDATE chart
SET indx = #row := #row + 1
WHERE report = 2
ORDER BY indx
but I think better to change design.

MySql complex query - SUM on multiple and variable columns

I have the following table structure (simplified version)
+----------------+ +-----------------+ +------+
| fee_definition | | user_fee | | user |
+----------------+ +-----------------+ +------+
| id | | user_id | | id |
| label | | fee_id | | ... |
| case1 | | case | +------+
| case2 | | manual_override |
| case3 | +-----------------+
| case4 |
| case5 |
+----------------+
Base on a pretty simple algorithm id determine which case fits the user to determine the amount of money they have to pay. A user_fee can be base on 1 to no limit number of fees definitions. which mean i can have the following content in the intersection table
+-----------+----------+--------+-------------------+
| user_id | fee_id | case | manual_override |
+-----------+----------+--------+-------------------+
| 1 | 1 | case1 | |
| 1 | 3 | case1 | |
| 1 | 5 | case1 | 50.22 |
| 2 | 1 | case5 | |
| 3 | 1 | case2 | |
| 3 | 2 | case2 | 18.50 |
+-----------+----------+--------+-------------------+
If a user is setted to have the case 1, all the fees listed under the case 1 where the value is different from 0 get picked. Same goes for the four other cases.
Just for reference on how i did things here is the actual query that I execute which is written in french (sorry for that but since we are a team of french speaking developpers, we mostly write in our code and queries in french).:
SELECT
`etudiant_etu`.*,
`session_etudiant_set`.*,
SUM(ROUND(frais_session_etudiant.fse_frais_manuel*100)/100) AS `fse_frais_manuel`,
`frais_session_etudiant`.`des_colonne`,
SUM(ROUND(definition_frais_des.des_quebecCanada*100)/100) AS `des_quebecCanada`,
SUM(ROUND(definition_frais_des.des_etranger*100)/100) AS `des_etranger`,
SUM(ROUND(definition_frais_des.des_non_credite*100)/100) AS `des_non_credite`,
SUM(ROUND(definition_frais_des.des_visiteur*100)/100) AS `des_visiteur`,
SUM(ROUND(definition_frais_des.des_explore*100)/100) AS `des_explore`,
`type_etudiant_tye`.*,
`type_formation_tyf`.*,
`pays_pys`.*,
`province_prc`.*
FROM `etudiant_etu`
INNER JOIN `session_etudiant_set`
ON session_etudiant_set.etu_id = etudiant_etu.etu_id
INNER JOIN `frais_session_etudiant`
ON frais_session_etudiant.set_id = session_etudiant_set.set_id
INNER JOIN `definition_frais_des`
ON definition_frais_des.des_id = frais_session_etudiant.des_id
LEFT JOIN `type_etudiant_tye`
ON type_etudiant_tye.tye_id = session_etudiant_set.tye_id
LEFT JOIN `type_formation_tyf`
ON type_formation_tyf.tyf_id = session_etudiant_set.tyf_id
LEFT JOIN `pays_pys`
ON pays_pys.pys_code = etudiant_etu.pys_adresse_permanente_code
LEFT JOIN `province_prc`
ON province_prc.prc_code = etudiant_etu.prc_adresse_permanente_code
WHERE (set_session = 'P11')
GROUP BY `session_etudiant_set`.`set_id`
ORDER BY `etu_nom` asc, `etu_prenom` ASC
as for reference from the actual query with the simplified version:
simplified version actual version
fee_definition.id definition_frais_des.des_id
fee_definition.case1 definition_frais_des.des_quebecCanada
fee_definition.case2 definition_frais_des.des_etranger
fee_definition.case3 definition_frais_des.des_non_credite
fee_definition.case4 definition_frais_des.des_visiteur
fee_definition.case5 definition_frais_des.des_explore
user_fee.user_id frais_session_etudiant.set_id
user_fee.fee_id frais_session_etudiant.des_id
user_fee.case frais_session_etudiant.des_colonne
user_fee.manual_override frais_session_etudiant.fes_frais_manuel
user.id session_etudiant_set.set_id
The problem I have is when it comes to handling the manual override setting. What would be the best way of doing this?
I would rather this to be handled in the query itself than in the programmation.
the logic behind what I am looking for goes as follow
get the SUM of the fees to be charged for a user and if an override value as been set, use that value instead of the actual value setted in the fee_definition, else use the value in the fee_definition.
I don't mind to loose the 4 not used cases and only keep the right column
Edited to display final result
This is the query I ended with, five levels of IF's
'IF(`frais_session_etudiant`.des_colonne= "des_quebec_canada",
SUM(IF(`frais_session_etudiant`.fse_frais_manuel > 0,
ROUND(`frais_session_etudiant`.fse_frais_manuel*100)/100,
ROUND(definition_frais_des.des_quebec_canada*100)/100)
),
IF(`frais_session_etudiant`.des_colonne= "des_etranger",
SUM(IF(`frais_session_etudiant`.fse_frais_manuel > 0,
ROUND(`frais_session_etudiant`.fse_frais_manuel*100)/100,
ROUND(definition_frais_des.des_etranger*100)/100)
),
IF(`frais_session_etudiant`.des_colonne= "des_non_credite",
SUM(IF(`frais_session_etudiant`.fse_frais_manuel > 0,
ROUND(`frais_session_etudiant`.fse_frais_manuel*100)/100,
ROUND(definition_frais_des.des_non_credite*100)/100)
),
IF(`frais_session_etudiant`.des_colonne= "des_visiteur",
SUM(IF(`frais_session_etudiant`.fse_frais_manuel > 0,
ROUND(`frais_session_etudiant`.fse_frais_manuel*100)/100,
ROUND(definition_frais_des.des_visiteur*100)/100)
),
IF(`frais_session_etudiant`.des_colonne= "des_explore",
SUM(IF(`frais_session_etudiant`.fse_frais_manuel > 0,
ROUND(`frais_session_etudiant`.fse_frais_manuel*100)/100,
ROUND(definition_frais_des.des_explore*100)/100)
),
0
)
)
)
)
) as frais'
That's a monster! as said by Ted Hopp :D
You can use IFNULL(manual_override,non-override-value)