mysql update column based on another updated column - mysql

Note, this question is not same to the MySQL update column based on another column data manipulation
table order
| order_number | payment_amount | refund_amount | refund_status |
| 12 | 100 | 50 | 1 |
refund_status:
0: none refunded;
1: part refunded;
2: all refunded;
update order set refund_amount = refund_amount + 50, refund_status = CASE WHEN payment_amount = refund_amount + 50 THEN 2 ELSE 1 END where order_number = 12;
// refund_amount -> 100, refund_status ->1
Cannot get the right result (refund_amount -> 100, refund_status ->2).
But
update order set refund_amount = refund_amount + 50, refund_status = CASE WHEN payment_amount = refund_amount THEN 2 ELSE 1 END where order_number = 12;
// refund_amount -> 100, refund_status ->2
Can get the right result.
It seems update refund_status besed on the updated refund_amount, is it reliable?

use bracket every you use function case for system readable on your logic code.
update order
set refund_amount = (refund_amount + 50),
refund_status = (CASE WHEN payment_amount = refund_amount + 50 THEN 2 ELSE 1 END)
where order_number = 12;

Related

SQL: return the select value in the stored procedure

I have stored procedure with 3 statements to update sta_fk_col_id in the status table. For test porpuses, I want to return the value in sta_fk_col_id without updating the table. Instead of UPDATE status SET sta_fk_col_id = I tried to do something like SET valido = but without seccuss.
SET valido
CREATE DEFINER=`tpcroot`#`%` PROCEDURE `sp_test_store_procedure`(IN functionId INT, out valido int)
BEGIN
-- statement 1.1
SET valido =
(SELECT CASE WHEN MAX(mon_alertlevel) >= 60 THEN 3 WHEN MAX(mon_alertlevel) < 60
AND MAX(mon_alertlevel) >= 30 THEN 2 ELSE 1 END AS color
FROM monitor INNER JOIN monitor_system ON fk_mon_id = mon_funct_id
WHERE fk_dri_id IN (110))
WHERE sta_fk_dri_id = (110);
-- statement 1.2
SET valido =
(SELECT color FROM ( SELECT MAX(sta_fk_col_id) AS color FROM status WHERE sta_fk_mty_id = 1
AND sta_fk_sys_id = 4
AND sta_fk_dri_id IS NOT NULL) helptable)
WHERE sta_fk_sys_id = 4 AND
sta_fk_mty_id = 1 AND sta_fk_dri_id IS NULL;
-- statement 2
SET valido = (SELECT CASE WHEN MAX(mon_alertlevel) >= 60 THEN 3 WHEN MAX(mon_alertlevel) <60 AND MAX(mon_alertlevel) >= 30 THEN 2 ELSE 1 END AS color
FROM monitor_system INNER JOIN monitor ON fk_mon_id = mon_funct_id AND mon_funct_id = functionId)
WHERE sta_fk_mty_id = 1 AND
sta_fk_sys_id = 4 AND
sta_fk_dri_id IS NULL AND
(SELECT countDrilldown FROM (select Count(*) AS countDrilldown FROM status
WHERE sta_fk_mty_id = 1 AND
sta_fk_sys_id = 4 AND sta_fk_dri_id IS NOT NULL) helptable) = 0;
select #valido;
END
I am calling the stored procedure like this:
CALL sp_test_store_procedure(2315, #returnValue);
select #returnValue;

SQL count specific value over multiple columns and rows

I feel as if this should be quite easy, but can't seem to find a solution.
Suppose I have the following table:
|--------||---||---||---||---||---||---||---|
|Company ||q1 ||q2 ||q3 ||q4 ||q5 ||q6 ||q7 |
|--------||---||---||---||---||---||---||---|
|abc ||1 ||2 ||1 ||3 ||2 ||2 ||1 |
|abc ||2 ||2 ||1 ||2 ||3 ||1 ||1 |
|abc ||1 ||1 ||3 ||3 ||1 ||2 ||2 |
|abc ||1 ||2 ||1 ||3 ||0 ||1 ||3 |
I want to count the number of times '1' appears in the table, so the query should, in this case, result with 12. I tried 'hardcoding' it, like the following query. But that just results in the rows containing a 1, so in this case 4. How do I count the number of times '1' occurs, thus resulting in a count of 12?
SELECT COUNT(*)
FROM table
WHERE Company = 'abc'
AND (
q1 = '1'
OR q2 = '1'
OR q3 = '1'
OR q4 = '1'
OR q5 = '1'
OR q6 = '1'
OR q7 = '1'
)
SELECT SUM(
IF(q1 = 1, 1, 0) +
IF(q2 = 1, 1, 0) +
IF(q3 = 1, 1, 0) +
IF(q4 = 1, 1, 0) +
IF(q5 = 1, 1, 0) +
IF(q6 = 1, 1, 0) +
IF(q7 = 1, 1, 0)
)
FROM table
WHERE Company = 'abc'
This is very weird assignment but:
http://sqlfiddle.com/#!9/2e7aa/3
SELECT SUM((q1='1')+(q2='1')+(q3='1')+(q4='1')+(q5='1')+(q6='1')+(q7='1'))
FROM table
WHERE Company = 'abc'
AND '1' IN (q1,q2,q3,q4,q5,q6,q7)
Not so easy, each column needs to be hard-coded. I'd try something using a CASE or DECODE.
SELECT
SUM(
CASE WHEN q1 = 1 THEN 1 ELSE 0 END +
CASE WHEN q2 = 1 THEN 1 ELSE 0 END +
CASE WHEN q3 = 1 THEN 1 ELSE 0 END +
CASE WHEN q4 = 1 THEN 1 ELSE 0 END +
CASE WHEN q5 = 1 THEN 1 ELSE 0 END +
CASE WHEN q6 = 1 THEN 1 ELSE 0 END +
CASE WHEN q7 = 1 THEN 1 ELSE 0 END)
FROM table
WHERE Company = 'abc'
Using a SUM instead of a COUNT will allow the CASE statement to be SUMed.
Use conditional COUNT
SELECT COUNT(case when q1 = '1' then 1 end) +
COUNT(case when q2 = '1' then 1 end) +
COUNT(case when q3 = '1' then 1 end) +
COUNT(case when q4 = '1' then 1 end) +
COUNT(case when q5 = '1' then 1 end) +
COUNT(case when q6 = '1' then 1 end) +
COUNT(case when q7 = '1' then 1 end) as ones_total
FROM table
WHERE Company = 'abc'
For reasons of efficiency I don't recommend actually using this approach; but for learning purposes here's another way you could have broken down the problem along the lines of the way you were thinking about it.
select sum(c) as total_ones
from
(
select count(*) c from table where q1 = 1 union all
select count(*) from table where q2 = 1 union all
select count(*) from table where q3 = 1 union all
select count(*) from table where q4 = 1 union all
select count(*) from table where q5 = 1 union all
select count(*) from table where q6 = 1 union all
select count(*) from table where q7 = 1
) t

Count multiple columns

I have this table:
Teams:
ID | Size1 | Size2 | Size3 | Size4 | ... (other columns)..
----------------------------------------
1 | L | XL | L | M | ...
----------------------------------------
2 | S | L | S | XXL | ...
----------------------------------------
3 | M | XXL | L | M | ...
----------------------------------------
4 | L | XL | XS | XXL | ...
What is the most effective (and simplest) MySQL query to count all L in table?
I'd like to have only one field in result which is the count of all L in all columns.
EDIT:
Just to clarify little more, in my table there is 152 Ls in first column, 2 in second, 151 in third and 3 in fourth and I expect 308 as result
You can use SUM and CASE to do that:
SELECT
sum(case when Size1 = 'L' then 1 else 0 end) +
sum(case when Size2 = 'L' then 1 else 0 end) +
sum(case when Size3 = 'L' then 1 else 0 end) +
sum(case when Size4 = 'L' then 1 else 0 end)
FROM Teams;
Alternatively instead of CASE, you can use IF:
SELECT
sum(IF(Size1 = 'L',1, 0)) +
sum(IF(Size2 = 'L',1, 0)) +
sum(IF(Size3 = 'L',1, 0)) +
sum(IF(Size4 = 'L',1, 0))
FROM Teams;
This is actually the same.
Edit.
According to Andomar's comment there's even simpler solution:
SELECT
sum(Size1 = 'L') +
sum(Size2 = 'L') +
sum(Size3 = 'L') +
sum(Size4 = 'L')
FROM Teams;
This is correct since true is equal to 1 in MySQL. I've just verified this. ;-)
2nd Edit
Next step to simplify this - only one SUM usage:
SELECT sum(
(Size1 = 'L') +
(Size2 = 'L') +
(Size3 = 'L') +
(Size4 = 'L') )
FROM Teams;
Please try the following query:
SELECT sum(
IF(Size1='L', 1, 0) +
IF(Size2='L', 1, 0) +
IF(Size3='L', 1, 0) +
IF(Size4='L', 1, 0)
) as total FROM Teams
Assuming there can only be one 'L' per row:
select count(*)
from YourTable
where 'L' in (Size1, Size2, ..., SizeN)
Or a normalizing solution, which supports multiple 'L''s per row:
select count(*)
from (
select size1 as size
from YourTable
union all
select size2
from YourTable
union all
select size3
from YourTable
union all
...
)
where size = 'L'
Your WHERE condition is going to be quite large but you could use this:
SELECT count(Size1) + count(size2) AS total WHERE size1 = 'L' OR size2 = 'L'
This solution will count all columns and total based on individual column comparisons.

using a CASE within WHERE

I have code where I need to add filter into where clause that varies based on value of variable. As in example below if #X has a value of 0 then I want
to include a filter for OrderID = 10; otherwise I want to add filter for OrderID = 20 with another filter for DepartmentID either been NULL or a value of 30. This could be accomplished using a IF ELSE as below
DECLARE #X INT
-- Retrieve value for #X
IF #X = 0
BEGIN
SELECT *
FROM Customers
WHERE ProductID IS NOT NULL
AND OrderID = 10
END
ELSE
BEGIN
SELECT *
FROM Customers
WHERE ProductID IS NOT NULL
AND OrderID = 20 AND ( DepartmentID IS NULL OR DepartmentID = 30)
END
I wonder if there is someway of doing it using one SQL statement. I thought it's doable using CASE within a WHERE but SQL does not seem to allow below.
SELECT *
FROM Customers
WHERE ProductID IS NOT NULL
AND CASE WHEN #X = 0 THEN OrderID = 10 ELSE OrderID = 20 AND ( DepartmentID IS NULL OR DepartmentID = 30) END
Is there anyway of accomplishing this.
you were close,
AND OrderID = (CASE WHEN #X = 0 THEN 10 ELSE 20 END)
AND DepartmentID = (CASE WHEN #X = 0 THEN DepartmentID ELSE 30 END)
SELECT *
FROM Customers
WHERE ProductID IS NOT NULL
AND OrderID = CASE WHEN #X = 0 THEN 10 ELSE 20 END
AND ISNULL(DepartmentID,30) = CASE WHEN #X = 0 THEN ISNULL(DepartmentID,30) ELSE 30 END
You can do that without a CASE:
SELECT *
FROM Customers
WHERE ProductID IS NOT NULL
AND ((#X = 0 AND OrderID = 10) OR (#X <> 0 AND OrderID = 20 AND DepartmentID = 30))
Edit:
With the condition that you wanted to add:
SELECT *
FROM Customers
WHERE ProductID IS NOT NULL
AND ((#X = 0 AND OrderID = 10) OR (#X <> 0 AND OrderID = 20 AND (DepartmentID IS NULL OR DepartmentID = 30)))

auto increment using 'case' in mysql

I have a simple table in mysql that has different types of records, differentiated by a values in the column ptype
my table looks like this
id1...ptype..usr...item
1.....43.......2......7001
2.....44.......2......8001
3.....43.......2......7002
4.....43.......2......7003
5.....43.......3......7001
When I add a new record, I need my query to insert an auto incremented value in the item column, based upon ptype and specific to usr. i.e. if i insert a new record
id1...ptype..usr...item
6.....43.......3......?
it would add 1 to the highest number existing for ptype=43 and usr=3
id1...ptype..usr...item
6.....43.......3......7002
if we added another record for ptype=44 and usr=2
id1...ptype..usr...item
7.....44.......2......8002
i think i should do this by initially inserting the new record with item blank and then updating that record with information derived from the new record(i.e. #lastid) using the CASE WHEN THEN method, but it's not working.
SET #lastid := LAST_INSERT_ID();
SET #ptype = (SELECT `ptype` FROM a1 WHERE `id1` = #lastid);
SET #item = (SELECT (
CASE
when #ptype = 41 then (SELECT 1 + coalesce((SELECT max(`item`) FROM `a1` WHERE `ptype` = 41 AND `plate`=7 AND `userid` = #userid), 5000))
when #ptype = 42 then (SELECT 1 + coalesce((SELECT max(`item`) FROM `a1` WHERE `ptype` = 42 AND `plate`=7 AND `userid` = #userid), 6000))
when #ptype = 43 then (SELECT 1 + coalesce((SELECT max(`item`) FROM `a1` WHERE `ptype` = 43 AND `plate`=7 AND `userid` = #userid), 7000))
when #ptype = 44 then (SELECT 1 + coalesce((SELECT max(`item`) FROM `a1` WHERE `ptype` = 44 AND `plate`=7 AND `userid` = #userid), 8000))
when #ptype = 45 then (SELECT 1 + coalesce((SELECT max(`item`) FROM `a1` WHERE `ptype` = 45 AND `plate`=7 AND `userid` = #userid), 9000))
when #ptype = 46 then (SELECT 1 + coalesce((SELECT max(`item`) FROM `a1` WHERE `ptype` = 46 AND `plate`=7 AND `userid` = #userid), 10000))
ELSE 0
end) as item
from
a1 WHERE `id1` = #lastid);
UPDATE a1 SET item = #item WHERE id1 = #lastid
as is, #item is returning values of 0 initially, no matter what 'ptype' the new record has, and is incrementing by 1 for subsequent entries.... i need the first record added in each ptype to be 5001 6001, 7001, etc.
First, the answer for which you didn't ask: reverse your idea by creating the rows in their own tables (with an AUTO_INCREMENT as eggyal suggested) and then move the data to this table.
And now the answer:
Your information is a bit mis-matched, which might explain the problem or just be a red herring. For example, you don't describe what 'plate' is, but you use it in your query. You also use #userid, which is not set in your examples.
I created a table that seemed to match your data at the top:
create table a1 (
id1 int primary key auto_increment,
ptype int,
usr int,
item int
);
Then set the variable that you seemed to want:
set #userid = 2;
set #ptype = 43;
and inserted a row:
insert into a1 (ptype, usr) values (#ptype, #userid);
pulled the id back out as you did:
SET #lastid := LAST_INSERT_ID();
Then you can get the max 'item':
select max(item) from a1 WHERE `ptype` = #ptype AND `usr` = #userid;
To handle the initial case, you wanted a default. Since you're separating the ptypes by 1000, you can use that:
SELECT ifnull(max(`item`),(#ptype % 40 + 2)*1000)+1 as next
FROM `a1`
WHERE `ptype` = #ptype
AND `usr` = #userid;
+------+
| next |
+------+
| 5001 |
+------+
Note that this isn't thread safe, so wrap it all in a transaction/trigger.
Hope that helps.
You can use a trigger:
CREATE TRIGGER biA1 BEFORE INSERT ON a1 FOR EACH ROW SET NEW.item = (
  SELECT COALESCE(MAX(item), (NEW.ptype-36)*1000) + 1
FROM a1
WHERE ptype = NEW.ptype AND plate = 7 AND userid = NEW.userid
)