Calculated field using MariaDB - mysql

I'm trying to calculate a person's age using MariaDB, the table is already created and I'm trying to alter the table to add the calculated column, this is my syntax:
ALTER TABLE user_details
ADD Age AS TIMESTAMPDIFF(YEAR, dateOfBirth, CURDATE());
I've got no idea why I get a syntax error.
thanks for your help.

MariaDB requires that the data type of the generated column (in your case presumably an INT) is specified, and that the expression be enclosed in parentheses. You need to use:
ALTER TABLE user_details
ADD Age INT AS (TIMESTAMPDIFF(YEAR, dateOfBirth, CURDATE()))
Unlike MySQL, MariaDB (as of 10.2.1) supports non-deterministic built-in functions (in your case CURDATE) in generated columns, as long as the column is VIRTUAL (the default) and not indexed. See the manual.
Demo on dbfiddle

There are 3 issues in your query:
The expression of generated column must be enclosed with parenthesis.
The column datatype must be specified - even when it is generated column.
The expression of generated column cannot use non-deterministic function CURDATE().
The latter problem cannot be fixed.
I'm trying to calculate a person's age using MariaDB
Do this in a query when you need in the age value really. Or create according view.

You can try the method below. Then add an INSERT trigger for new rows.
ALTER TABLE user_details
ADD Age INT;
SET ##sql_safe_updates = 0;
UPDATE user_details
SET Age = TIMESTAMPDIFF(YEAR, dateOfBirth, CURDATE());
SET ##sql_safe_updates = 1;

Related

SELECT INTO #var in Stored Procedure not working [duplicate]

Let's say a have a stored procedure SetCustomerName which has an input parameter Name, and I have a table customers with column Name.
So inside my stored procedure I want to set customer's name. If I write
UPDATE customers SET Name = Name;
this is incorrect and I see 2 other ways:
UPDATE customers SET Name = `Name`;
UPDATE customers SET customers.Name = Name;
First one works, but I didn't find in documentation that I can wrap parameters inside ` characters. Or did I miss it in the documentation (link is appreciated in this case).
What other ways are there and what is the standard way for such a case? Renaming input parameter is not good for me (because I have automatic object-relational mapping if you know what I mean).
UPDATE:
So, there is a link about backticks (http://dev.mysql.com/doc/refman/5.0/en/identifiers.html) but it's not explained deep enough how to use them (how to use them with parameters and column names).
And there is a very strange thing (at least for me): You can use backticks either way:
UPDATE customers SET Name = `Name`;
//or
UPDATE customers SET `Name` = Name;
//or even
UPDATE customers SET `Name` = `Name`;
and they all work absolutely the same way.
Don't you think this is strange? Is this strange behavior explained somewhere?
Simplest way to distinguished between your parameter and column (if both name is same) is to add table name in your column name.
UPDATE customers SET customers.Name = Name;
Even you can also add database prefix like
UPDATE yourdb.customers SET yourdb.customers.Name = Name;
By adding database name you can perform action on more than 1 database from single store procedure.
I think that your first example is actually backwards. If you're trying to set the "Name" column to the "Name" input parameter, I believe it should be:
UPDATE customers SET `Name` = Name;
And for the second example, you can set table aliases the same way that you do in all other statements:
UPDATE customers AS c SET c.Name = Name;
Not necessarily correct, but a fair way to better argument/parameter management, as well readability with easier understanding, especially while working with the SQL;
DROP PROCEDURE IF EXISTS spTerminalDataDailyStatistics; DELIMITER $$
CREATE PROCEDURE spTerminalDataDailyStatistics(
IN TimeFrom DATETIME,
IN DayCount INT(10),
IN CustomerID BIGINT(20)
) COMMENT 'Daily Terminal data statistics in a date range' BEGIN
# Validate argument
SET #TimeFrom := IF(TimeFrom IS NULL, DATE_FORMAT(NOW(), '%Y-%m-01 00:00:00'), TimeFrom);
SET #DayCount := IF(DayCount IS NULL, 5, DayCount);
SET #CustomerID := CustomerID;
# Determine parameter
SET #TimeTo = DATE_ADD(DATE_ADD(#TimeFrom, INTERVAL #DayCount DAY), INTERVAL -1 SECOND);
# Do the job
SELECT DATE_FORMAT(TD.TerminalDataTime, '%Y-%m-%d') AS DataPeriod,
COUNT(0) AS DataCount,
MIN(TD.TerminalDataTime) AS Earliest,
MAX(TD.TerminalDataTime) AS Latest
FROM pnl_terminaldata AS TD
WHERE TD.TerminalDataTime BETWEEN #TimeFrom AND #TimeTo
AND (#CustomerID IS NULL OR TD.CustomerID = #CustomerID)
GROUP BY DataPeriod
ORDER BY DataPeriod ASC;
END $$
DELIMITER ;
CALL spTerminalDataDailyStatistics('2021-12-01', 2, 1801);
Using backticks in MySQL query syntax is documented here:
http://dev.mysql.com/doc/refman/5.0/en/identifiers.html
So yes, your first example (using backticks) is correct.
Here is the link you are asking for:
http://dev.mysql.com/doc/refman/5.0/en/identifiers.html
The backticks are called "identifier quote" in MySql

Renaming a column with the current date in MySQL

Is there a way to change the name of a column to the current date? I don't need it to dynamically update as the date changes -- just the date when the code was executed. I tried the below code but get a syntax error
ALTER TABLE table_name CHANGE old_column_name CURDATE() DATE;
In the ALTER TABLE statement, the new column name must be supplied like any other identifier in the SQL text.
The new name for the column cannot be supplied as the return from a function, or as bind placeholder. It has to be supplied as an identifier.
That is, the SQL statement you submit to the database will need to have the new column name actually spelled out, as part of the statement:
ALTER TABLE table_name CHANGE old_column_name new_column_name DATE
So, the short answer to your question is no, it can't be done in a single SQL statement.
Obviously, you can perform operations in separate steps, to get the current date, and to create a string containing SQL statement you want to execute. IT seems like you would also need to identify the current name of the column you want to change.
Beyond the question that was asked...
I'm having difficulty fathoming a use case where something like this would be an appropriate solution.
What problem is this type of functionality attempting to solve? Why would you need the name of the column changed. Any SQL statements that reference the column will also need to be changed. Could you store this "date" as a value in a row of another table?
The only thing I can think why someone would want to do this would be a misguided attempt to specify a column name in a resultset from a SELECT * query.
While it is probably a bad idea, what you are going to do - it is possible by using a prepared statement:
SET #stmt := CONCAT('ALTER TABLE table_name CHANGE old_column_name `', CURDATE(), '` DATE;');
PREPARE stmt from #stmt;
EXECUTE stmt;

Unique index not based on words order?

Is it possibile to have a mysql UNIQUE index on a varchar field not based on the words order?
I mean if there is a row with key1 key2, and I try to insert key2 key1 mysql should throw an error.
You could achieve this effect by adding before insert and before update triggers to the table; they could check whether a row with the fields reversed exists or not before inserting/updating if it doesn't, or forcing an error if it does.
See here for more information.
If you set the indexed to UNIQUE, you will not be able to enter the identical data twice - numbers, words or otherwise.
If you wish to achieve something otherwise, you'll have to set a new non-unique field in your database then you'll be able achieve it using external code and queries (PHP, C#).
Create a trigger to do this.
i.e.
CREATE TRIGGER trigger_name BEFORE INSERT ON table_name
FOR EACH ROW
BEGIN
IF ((SELECT COUNT(*) FROM table WHERE col2=NEW.col1 OR col1=NEW.col2) <> 0)
THEN
CALL this_procedure_does_not_exist();
END IF
END

Column calculated from another column?

Given the following table:
id | value
--------------
1 6
2 70
Is there a way to add a column that is automatically calculated based on another column in the same table? Like a VIEW, but part of the same table. As an example, calculated would be half of value. Calculated should be automatically updated when value changes, just like a VIEW would be.
The result would be:
id | value | calculated
-----------------------
1 6 3
2 70 35
Generated Column is one of the good approach for MySql version which is 5.7.6 and above.
There are two kinds of Generated Columns:
Virtual (default) - column will be calculated on the fly when a
record is read from a table
Stored - column will be calculated when a
new record is written/updated in the table
Both types can have NOT NULL restrictions, but only a stored Generated Column can be a part of an index.
For current case, we are going to use stored generated column. To implement I have considered that both of the values required for calculation are present in table
CREATE TABLE order_details (price DOUBLE, quantity INT, amount DOUBLE AS (price * quantity));
INSERT INTO order_details (price, quantity) VALUES(100,1),(300,4),(60,8);
amount will automatically pop up in table and you can access it directly, also please note that whenever you will update any of the columns, amount will also get updated.
If it is a selection, you can do it as:
SELECT id, value, (value/2) AS calculated FROM mytable
Else, you can also first alter the table to add the missing column and then do an UPDATE query to compute the values for the new column as:
UPDATE mytable SET calculated = value/2;
If it must be automatic, and your MySQL version allows it, you can try with triggers
MySQL 5.7 supports computed columns. They call it "Generated Columns" and the syntax is a little weird, but it supports the same options I see in other databases.
https://dev.mysql.com/doc/refman/5.7/en/create-table.html#create-table-generated-columns
#krtek's answer is in the right direction, but has a couple of issues.
The bad news is that using UPDATE in a trigger on the same table won't work. The good news is that it's not necessary; there is a NEW object that you can operate on before the table is even touched.
The trigger becomes:
CREATE TRIGGER halfcolumn_update BEFORE UPDATE ON my_table
FOR EACH ROW BEGIN
SET NEW.calculated = NEW.value/2;
END;
Note also that the BEGIN...END; syntax has to be parsed with a different delimiter in effect. The whole shebang becomes:
DELIMITER |
CREATE TRIGGER halfcolumn_insert BEFORE INSERT ON my_table
FOR EACH ROW BEGIN
SET NEW.calculated = NEW.value/2;
END;
|
CREATE TRIGGER halfcolumn_update BEFORE UPDATE ON my_table
FOR EACH ROW BEGIN
SET NEW.calculated = NEW.value/2;
END;
|
DELIMITER ;
You can use generated columns from MYSQL 5.7.
Example Usage:
ALTER TABLE tbl_test
ADD COLUMN calc_val INT
GENERATED ALWAYS AS (((`column1` - 1) * 16) + `column2`) STORED;
VIRTUAL / STORED
Virtual: calculated on the fly when a record is read from a table (default)
Stored: calculated when a new record is inserted/updated within the
table
If you want to add a column to your table which is automatically updated to half of some other column, you can do that with a trigger.
But I think the already proposed answer are a better way to do this.
Dry coded trigger :
CREATE TRIGGER halfcolumn_insert AFTER INSERT ON table
FOR EACH ROW BEGIN
UPDATE table SET calculated = value / 2 WHERE id = NEW.id;
END;
CREATE TRIGGER halfcolumn_update AFTER UPDATE ON table
FOR EACH ROW BEGIN
UPDATE table SET calculated = value / 2 WHERE id = NEW.id;
END;
I don't think you can make only one trigger, since the event we must respond to are different.
I hope this still helps someone as many people might get to this article. If you need a computed column, why not just expose your desired columns in a view ? Don't just save data or overload the performance with triggers... simply expose the data you need already formatted/calculated in a view.
Hope this helps...

MySQL : When stored procedure parameter name is the same as table column name

Let's say a have a stored procedure SetCustomerName which has an input parameter Name, and I have a table customers with column Name.
So inside my stored procedure I want to set customer's name. If I write
UPDATE customers SET Name = Name;
this is incorrect and I see 2 other ways:
UPDATE customers SET Name = `Name`;
UPDATE customers SET customers.Name = Name;
First one works, but I didn't find in documentation that I can wrap parameters inside ` characters. Or did I miss it in the documentation (link is appreciated in this case).
What other ways are there and what is the standard way for such a case? Renaming input parameter is not good for me (because I have automatic object-relational mapping if you know what I mean).
UPDATE:
So, there is a link about backticks (http://dev.mysql.com/doc/refman/5.0/en/identifiers.html) but it's not explained deep enough how to use them (how to use them with parameters and column names).
And there is a very strange thing (at least for me): You can use backticks either way:
UPDATE customers SET Name = `Name`;
//or
UPDATE customers SET `Name` = Name;
//or even
UPDATE customers SET `Name` = `Name`;
and they all work absolutely the same way.
Don't you think this is strange? Is this strange behavior explained somewhere?
Simplest way to distinguished between your parameter and column (if both name is same) is to add table name in your column name.
UPDATE customers SET customers.Name = Name;
Even you can also add database prefix like
UPDATE yourdb.customers SET yourdb.customers.Name = Name;
By adding database name you can perform action on more than 1 database from single store procedure.
I think that your first example is actually backwards. If you're trying to set the "Name" column to the "Name" input parameter, I believe it should be:
UPDATE customers SET `Name` = Name;
And for the second example, you can set table aliases the same way that you do in all other statements:
UPDATE customers AS c SET c.Name = Name;
Not necessarily correct, but a fair way to better argument/parameter management, as well readability with easier understanding, especially while working with the SQL;
DROP PROCEDURE IF EXISTS spTerminalDataDailyStatistics; DELIMITER $$
CREATE PROCEDURE spTerminalDataDailyStatistics(
IN TimeFrom DATETIME,
IN DayCount INT(10),
IN CustomerID BIGINT(20)
) COMMENT 'Daily Terminal data statistics in a date range' BEGIN
# Validate argument
SET #TimeFrom := IF(TimeFrom IS NULL, DATE_FORMAT(NOW(), '%Y-%m-01 00:00:00'), TimeFrom);
SET #DayCount := IF(DayCount IS NULL, 5, DayCount);
SET #CustomerID := CustomerID;
# Determine parameter
SET #TimeTo = DATE_ADD(DATE_ADD(#TimeFrom, INTERVAL #DayCount DAY), INTERVAL -1 SECOND);
# Do the job
SELECT DATE_FORMAT(TD.TerminalDataTime, '%Y-%m-%d') AS DataPeriod,
COUNT(0) AS DataCount,
MIN(TD.TerminalDataTime) AS Earliest,
MAX(TD.TerminalDataTime) AS Latest
FROM pnl_terminaldata AS TD
WHERE TD.TerminalDataTime BETWEEN #TimeFrom AND #TimeTo
AND (#CustomerID IS NULL OR TD.CustomerID = #CustomerID)
GROUP BY DataPeriod
ORDER BY DataPeriod ASC;
END $$
DELIMITER ;
CALL spTerminalDataDailyStatistics('2021-12-01', 2, 1801);
Using backticks in MySQL query syntax is documented here:
http://dev.mysql.com/doc/refman/5.0/en/identifiers.html
So yes, your first example (using backticks) is correct.
Here is the link you are asking for:
http://dev.mysql.com/doc/refman/5.0/en/identifiers.html
The backticks are called "identifier quote" in MySql