MySQL Calculating the sum of each digit of an Text value - mysql

How to calculate the sum of each digit of an Text value in MySQL?
for example
SET #Chars = CONCAT('1','2','33');
-- #Chars = '1233'
-- and result should be 1+2+3+3 = 9.

Yeah, not pretty to do, but here's a way to do it. I'm like you -- had to do it for some spot checking. We do check digit calculations and I needed some specific accounts where the digits would sum in various ways.
select (substring('123456', 1, 1) +
substring('123456', 2, 1) +
substring('123456', 3, 1) +
substring('123456', 4, 1) +
substring('123456', 5, 1) +
substring('123456', 6, 1) +
substring('123456', 7, 1) +
substring('123456', 8, 1) +
substring('123456', 9, 1) +
substring('123456', 10, 1) +
substring('123456', 11, 1) +
substring('123456', 12, 1)) as sumOfDigits;
So just replace '123456' with your string and then make sure that the 1..12 in this example is enough to cover the length of the max string you are expecting to calculate a total for.
Here's my real world example:
select (substring(a.member_number, 1, 1) +
substring(a.member_number, 2, 1) +
substring(a.member_number, 3, 1) +
substring(a.member_number, 4, 1) +
substring(a.member_number, 5, 1) +
substring(a.member_number, 6, 1) +
substring(a.member_number, 7, 1) +
substring(a.member_number, 8, 1) +
substring(a.member_number, 9, 1) +
substring(a.member_number, 10, 1) +
substring(a.member_number, 11, 1) +
substring(a.member_number, 12, 1)) as sumOfDigits,
a.member_number from account a
left join account a2 on a2.member_number = a.member_number and a2.discriminator = 'D'
where a.discriminator = 'S'
and a2.account_id is null having sumOfDigits = 16;

According to https://en.wikipedia.org/wiki/Digital_root there is very simple formula to do it.
In MySQL in can be specified this way:
#Chars - 9 * FLOOR( (#Chars-1 ) / 9 ).
Or any field / expression can be used instead #Chars.

Related

mysql how to show order number in result

so here is the query
select * from test order by pow(c1 - 8, 2) + pow(c2 - 5, 2) limit 3
is there any way to show order section -> "pow(c1 - 8, 2) + pow(c2 - 5, 2)" for each record in result ?
Just include it in the select:
select t.*, pow(c1 - 8, 2) + pow(c2 - 5, 2) as distance_squared
from test t
order by distance_squared
limit 3

INSERT Trigger for a rating system: Division by zero error

I have followed this paper which suggests what is claimed to be a good approach to 5 star rating systems.
The schema is:
CREATE TABLE `ratings` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`product_id` int(11) DEFAULT NULL,
`positive` float NOT NULL DEFAULT 0,
`negative` float NOT NULL DEFAULT 0,
`stars` float DEFAULT 0,
`total` int(11) NOT NULL,
`lower_bound` float DEFAULT NULL,
PRIMARY KEY (`id`)
)
I setup the INSERT trigger which is:
CREATE
TRIGGER `insert_rating` BEFORE INSERT ON `ratings`
FOR EACH ROW
SET new.total = new.positive + new.negative,
new.stars = ROUND( (((new.positive / new.total) * 4) + 1) * 2, 0) / 2,
new.lower_bound = ((new.positive + 1.9208) / (new.positive + new.negative) - 1.96 * SQRT((new.positive * new.negative) / (new.positive + new.negative) + 0.9604) / (new.positive + new.negative)) / (1 + 3.8416 / (new.positive + new.negative))
but upon my first insert of (Also taken from the paper):
INSERT into ratings (product_id, positive, negative) VALUES (1, 0, 0)
I got a Division by zero error. Is there an SQL error in the design of this method or have I done something wrong? Would like to know how to fix.
There's also an update trigger but I don't think I will ever need to update it with zeros:
CREATE
TRIGGER `update_rating` BEFORE UPDATE ON `ratings`
FOR EACH ROW
SET new.total = new.positive + new.negative,
new.stars = ROUND( (((new.positive / new.total) * 4) + 1) * 2, 0) / 2,
new.lower_bound = ((new.positive + 1.9208) / (new.positive + new.negative) - 1.96 * SQRT((new.positive * new.negative) / (new.positive + new.negative) + 0.9604) / (new.positive + new.negative)) / (1 + 3.8416 / (new.positive + new.negative))
Gordon's answer will prevent the error.
The trigger expects either positive or negative to be non-zero, which makes sense. Your insert statement adds NO actual ratings (either positive or negative); so what's the point?
More importantly, why not save yourself some headaches and change the columns to do the work for you?
In your table definition, change the columns to
...
total INT GENERATED ALWAYS AS (positive+negative) STORED,
stars INT GENERATED ALWAYS AS (ROUND((((positive/NULLIF(positive+negative,0))*4)+1) *2, 0)/2) STORED,
lower_bound DOUBLE GENERATED ALWAYS AS ((positive + 1.9208) / NULLIF(positive + negative, 0) - 1.96 * SQRT((positive * negative) / (positive + negative) + 0.9604) / NULLIF(positive + negative, 0))) / (1 + 3.8416 / NULLIF(positive + negative, 0)) STORED,
...
(And remove all the triggers, of course)
The following line sets new.total to 0
SET new.total = new.positive + new.negative
The next line has new.positive / new.total. Since you just set new.total to 0 you get a divide by 0. I would try testing for this condition and setting new.stars = 0 where is happens.
It is like really hard to tell which division is causing the issue. But the solution is NULLIF():
SET new.total = new.positive + new.negative,
new.stars = ROUND( (((new.positive / NULLIF(new.total, 0)) * 4) + 1) * 2, 0) / 2,
n new.lower_bound = ((new.positive + 1.9208) / NULLIF(new.positive + new.negative, 0) - 1.96 * SQRT((new.positive * new.negative) / NULLIF(new.positive + new.negative, 0) + 0.9604) / NULLIF(new.positive + new.negative, 0))) / (1 + 3.8416 / NULLIF(new.positive + new.negative, 0)
This turns 0s into NULLs, so the result is NULL rather than an error.

MySQL event not working (insert statement)

I am using PHP to execute the SQL query, the event can be created but no random data is inserted into the database. The global event scheduler is set on.
Is there anything wrong with my code?
What I try to do is to insert random data into the database every second once the event is created.
Below are my code :
<?php
$hostname="localhost";
$username="root";
$password='xxx';
$dbname="database";
$conn= mysqli_connect($hostname,$username,$password,$dbname);
if (mysqli_connect_errno($conn)) {
echo "Failed to connect to database: " . mysqli_connect_error();
}
else {
$query="CREATE EVENT XXX
ON SCHEDULE
EVERY 1 SECOND
STARTS '2019-12-28 9:45:20' ON COMPLETION PRESERVE ENABLE
DO
INSERT INTO water(Id,datetime,ph1,ec1,wt1,
ph2,ec2,wt2,
ph3,ec3,wt3,
ph4,ec4,wt4)
VALUES(NOW(),ROUND(FLOOR(21 * RAND()) / 100, 2) + 7.40,
ROUND(FLOOR(101 * RAND()) / 10, 1) + 650,
ROUND(FLOOR(21 * RAND()) / 10, 1) + 23,
ROUND(FLOOR(21 * RAND()) / 100, 2) + 7.40,
ROUND(FLOOR(101 * RAND()) / 10, 1) + 650,
ROUND(FLOOR(21 * RAND()) / 10, 1) + 23,
ROUND(FLOOR(21 * RAND()) / 100, 2) + 7.40,
ROUND(FLOOR(101 * RAND()) / 10, 1) + 650,
ROUND(FLOOR(21 * RAND()) / 10, 1) + 23,
ROUND(FLOOR(21 * RAND()) / 100, 2) + 7.40,
ROUND(FLOOR(101 * RAND()) / 10, 1) + 650,
ROUND(FLOOR(21 * RAND()) / 10, 1) + 23);";
if(mysqli_query($conn, $query)){
echo "successful";
}else{
echo ("error :". mysqli_error($conn));
}
}
mysqli_close($conn);
?>

SSRS Case Statement

I have created a table in SSRS of various grades, but I need to look at the value of 2 grades together and assign it an alternate grade. I cannot put this as a CASE in the SELECT as the way the database is designed the values are not stored in one row, but in multiple rows, therefore it cannot combine the data in a new column. For example, this is one students grades represented in the DB
634 Attainment *#1#2#3#4#N/A NULL 1 2
636 Effort A*#A#B#C#N/A NULL A 2
637 Focus EX#ME#WB#N/A NULL EX 1
638 Participation EX#ME#WB#N/A NULL ME 2
639 Groupwork EX#ME#WB#N/A NULL ME 2
640 Rigour EX#ME#WB#N/A NULL ME 2
641 Curiosity EX#ME#WB#N/A NULL ME 2
642 Initiative EX#ME#WB#N/A NULL ME 2
643 Self Organisation EX#ME#WB#N/A NULL ME 2
644 Perseverance EX#ME#WB#N/A NULL ME 2
I have created a table that has grouped the grades based on the pupil ID and it is now represented as one row and column headings for each grade (effort, Focus etc).
I have tried to do a sum using the ReportItems!Textbox1.Value but I can't use this method as it is not an aggregate function. What I wanted to do was
IF (ReportItems!Textbox104.Value + ReportItems!Textbox105.Value = 2) THEN 5
Is there a way to do this?
ADDITIONAL:
I have just tried:
=SWITCH(ReportItems!Textbox104.Value + ReportItems!Textbox105.Value = 2, 5,
ReportItems!Textbox104.Value + ReportItems!Textbox105.Value = 3, 4,
ReportItems!Textbox104.Value + ReportItems!Textbox105.Value = 4, 3,
ReportItems!Textbox104.Value + ReportItems!Textbox105.Value = 5, 2,
ReportItems!Textbox104.Value + ReportItems!Textbox105.Value = 6, 1,
ReportItems!Textbox104.Value + ReportItems!Textbox105.Value = 7, 0,
"NULL"
)
This is returning an Error.
I finally resolved this by using this expression:
=IIF(ReportItems!Textbox104.Value + (ReportItems!Textbox105.Value = 2), 5,
IIF(ReportItems!Textbox104.Value + (ReportItems!Textbox105.Value = 3), 4,
IIF(ReportItems!Textbox104.Value + (ReportItems!Textbox105.Value = 4), 3,
IIF(ReportItems!Textbox104.Value + (ReportItems!Textbox105.Value = 5), 2,
IIF(ReportItems!Textbox104.Value + (ReportItems!Textbox105.Value = 6), 1,
IIF(ReportItems!Textbox104.Value + ReportItems!Textbox105.Value = 7, 0,
"NULL"))))))

Does mysql query cache the dynamically calculated columns

I have a mysql query:
SELECT my_table.* WHERE SOUNDEX(my_col)='whatever' OR SUBSTR(SOUNDEX(my_col),4)='whatever' ORDER BY SUBSTR(SOUNDEX(my_col),4)='whatever',SOUNDEX(my_col)='whatever'
How many times will the substring function and soundex functions will actually be called? I mean for exactly same inputs will mysql cache the results over the span of one query?
If not, how can I make the change in the query so that each function is called minimum times possible.
MySQL would call this function four times for every returned row, to avoid this you can use a subquery, so instead of
SELECT *
FROM song
ORDER BY Substr(pre_calculated_soundex, 1, 1) =
Substr(Soundex("aaaa"), 1, 1)
+ Substr(pre_calculated_soundex
, 2, 1) =
Substr
(Soundex("aaaa"), 2, 1)
+ Substr(pre_calculated_soundex, 3, 1)
= Substr(Soundex("aaaa"), 3, 1)
+ Substr(pre_calculated_soundex, 4, 1
)
= Substr(Soundex("aaaa"), 4, 1)
You can do
SELECT * from (select *, Soundex("aaaa") as current_soundex from song)
ORDER BY
Substr(pre_calculated_soundex, 1, 1) = Substr(current_soundex , 1, 1)
+ Substr(pre_calculated_soundex, 2, 1) = Substr(current_soundex , 2, 1)
+ Substr(pre_calculated_soundex, 3, 1) = Substr(current_soundex , 3, 1)
+ Substr(pre_calculated_soundex, 4, 1) = Substr(current_soundex , 4, 1)