Subquery as generated column in mysql? - mysql

Can I create a generated column in table A which sums up a column in table B with a tableA_id of the row in table A?
Suppose I have a table of of families, and a table of children. I want a sum of the ages of the children for each family.
ALTER TABLE people.families
ADD COLUMN sumofages DECIMAL(10,2) GENERATED ALWAYS AS
(SELECT SUM(age) FROM people.children WHERE family_id = people.families.id) STORED;
ERROR 3102: Expression of generated column 'sumofages' contains a disallowed function.
I can't save it as type VIRTUAL either. What am I doing wrong here?
ALTER TABLE people.families
ADD COLUMN sumofages DECIMAL(10,2) GENERATED ALWAYS AS
(SELECT SUM(age) FROM people.children WHERE family_id = people.families.id) VIRTUAL;
ERROR 3102: Expression of generated column 'sumofages' contains a disallowed function.
I don't know which function is disallowed. SUM doesn't seem to be it. Maybe SELECT?

https://dev.mysql.com/doc/refman/5.7/en/create-table-generated-columns.html
Generated column expressions must adhere to the following rules. An
error occurs if an expression contains disallowed constructs.
Subqueries, parameters, variables, stored functions, and user-defined
functions are not permitted.
It's reasonable that the expression for a generated column can reference only columns within the same row. The generated column cannot use subqueries, or reference other tables, or functions with non-deterministic output.
Suppose generated columns did support cross-table references. Particularly consider the case of STORED generated columns.
If you update a table, MySQL would also have to update any references in generated columns elsewhere in the database, if they reference the row you updated. It would be complex and expensive for MySQL to track down all those references.
Then consider add indirect references through stored functions.
Then consider that your update is to an InnoDB table in a transaction, but the generated column may be in a non-transaction (MyISAM, MEMORY, ARCHIVE, etc.) table. Should your update be reflected in those generated columns when you make it? What if you roll back? Should your update be reflected at the time you commit? Then how should MySQL "queue up" changes to apply to those tables? What if multiple transactions commit updates that affect the generated column reference? Which one should win, the one that applied the change last or the one that committed last?
For these reasons, it's not practical or efficient to allow generated columns to reference anything other than the columns of the same row in the same table.

The idea of a computed column is to derive data from the other columns in the record, e.g. combine the country code with the zip code, so you'd store DE and 12345 and you'd get DE-12345 which you could use in an address.
What you are trying to do, however, is something completely different. You are accessing data from another table. But that table's data may change, so when accessing the same record you might suddenly get a completely different result. Computed columns should contain deterministic values, so they don't change as long as your record's data doesn't change. I don't know about MySQL in this regard, but it probably forbids non-deterministic data, such as your subquery.
What you are actually looking for is a view. A view can combine selects from different tables, just as you want it to happen. So use
create view familydata as
(
select f.*, sum(c.age) as sumofages
from families f
join children c on c.family_id = f.id
group by f.id
);
or
create view familydata as
(
select f.*,
(
select sum(age)
from children c
where c.family_id = f.id
) as sumofages
from families f
);
I hope I got the syntax right.

Related

Is is possible to use a query to specify a data type MySQL?

For example, I want a column in table A to be of type char(n) where n = the number of entries in table B.
I'm aware that the motivations for doing this can be mitigated via relational database normalization, but sometimes you want to maintain simplicity/readability for simple projects.
In my case, B is a const reference table. I'm essentially using it to replicate the function of an enum without using the set type. I simply want to avoid having to change the int literal in char(n) every time I add a new entry to B ("add" as in "append another value to the insert statement which IMMEDIATELY follows the table definition in the SAME FILE, the ONLY place where values are inserted into B).

MySQL Trigger to update corresponding column in another table

I'm trying to write a MySQL trigger for a table update (and a similar one for insert) that will take the updated columns and update corresponding columns in another table.
My set-up is this: I have one table (A) with several columns of numerical values and a record number Primary Key. I have another table (B) with identical column names but with short text descriptors that relate to each numerical value and also a record number as a Foreign Key referring to table A. Both of these tables may grow over time to include more columns - always matching each other - each with a simple predictable name (sticking with integers for now). All records are 1:1.
My hope was that I could write triggers for both update and insert on table A that would look at the numbers and, based on some simple logic, assign a descriptor to the corresponding record in table B (inserting that record in the case of the insert trigger). It got rather complicated quickly because I had to query INFORMATION_SCHEMA.COLUMNS to identify all current column names in table A, check each OLD vs NEW to verify that column was updated (for the update trigger anyway), do some logic to determine the appropriate descriptor, then INSERT/UPDATE the corresponding column in table B. I can't figure out how to set up a procedure/trigger that doesn't require storing column names in a variable to dynamically build an SQL statement. This is, of course, not allowed in a trigger and I have made some attempts at getting around this by moving the dynamic SQL statement into a separate stored procedure. None of this has worked and I've run into so many roadblocks, I'm coming to the conclusion that I'm going about this in entirely the wrong way.
Since I'm very new to database design, I just don't know what question to ask at this point other than, is there a better way or alternatively, is there a fix to my approach outlined above?
As always, I've searched thoroughly and not found any questions that answer mine but, if you see one that does, please point me that way!

MySQL - Storing Default Values for System

I have a few tables storing their corresponding records for my system. For example, there could be a table called templates and logos. But for each table, one of the rows will be a default in the system. I would have normally added a is_default column for each table, but all of the rows except for 1 would have been 0.
Another colleague of mine sees another route, in which there is a system_defaults table. And that table has a column for each table. For example, this table would have a template_id column and a logo_id column. Then that column stores the corresponding default.
Is one way more correct than the other generally? The first way, there are many columns with the same value, except for 1. And the second, I suppose I just have to do a join to get the details, and the table grows sideways whenever I add a new table that has a default.
The solutions mainly differ in the ways to make sure that no more than one default value is assigned for each table.
is_default solution: Here it may happen that more than one record of a table has the value 1. It depends on the SQL dialect of your database whether this can be excluded by a constraint. As far as I understand MySQL, this kind of constraint can't be expressed there.
Separate table solution: Here you can easily make sure by your table design that at most one default is present per table. By assigning not null constraints, you can also force defaults for specific tables, or not. When you introduce a new table, you are extending your database (and the software working on it) anyway, so the additional attribute on the default table won't hurt.
A middle course might be the following: Have a table
Defaults
id
table_name
row_id
with one record per table, identified by the table name. Technically, the problem of more than one default per table may also occur here. But if you only insert records into this table when a new table gets introduced, then your operative software will only need to perform updates on this table, never inserts. You can easily check this via code inspection.

is it possible to mimic or ghost a table?

So the situation is that I have some code that uses a database table and I think I can replace that table with a view from another table.
Potentially I would rename/remove the existing table and then somehow create a view that replicates the table so that the php code thinks it is working with the table.
Perhaps what I should do is create the view and then simply find and replace all instances of the table name in the code?
What is the best way to mimic or ghost a table?
Rename the table with
RENAME TABLE <table_name> TO <new_table_name>;
Then create the view with the same name as the original table_name. Your PHP code will continue to work fine without further ado when it's reading from the table. If your code also has to update, delete or insert into that view, there are certain restrictions.
There must be a one-to-one relationship between the rows in the view
and the rows in the underlying table. There are also certain other
constructs that make a view nonupdatable. To be more specific, a view
is not updatable if it contains any of the following:
Aggregate functions (SUM(), MIN(), MAX(), COUNT(), and so forth)
DISTINCT
GROUP BY
HAVING
UNION or UNION ALL
Subquery in the select list
Certain joins (see additional join discussion later in this section)
Reference to nonupdatable view in the FROM clause
Subquery in the WHERE clause that refers to a table in the FROM clause
Refers only to literal values (in this case, there is no underlying table to update)
ALGORITHM = TEMPTABLE (use of a temporary table always makes a view nonupdatable)
Multiple references to any column of a base table
Read more about it in the manual.

MySQL enum column from anothertable column

I'm sure this is either totally impossible or really easy:
If I'm creating a table and I want one of the columns to have limited options, it seems that I use either the ENUM or SET value type. But I have to define the possible values at that moment. What if I have another table which has two columns, a primary key column and a data column, and I want the ENUM for my new table to be set to the primary key of the already existing column?
I'm sure I can just write in the values long-hand, but ideally what I need is for new values to be entered into the list table and for the table with the enum column to just accept that the value choices will include anything new added to that list table.
Is this possible without needing to manipulate the structure of the new table each time something is added to the list?
i think this link help :
http://dev.mysql.com/doc/refman/5.0/en/enum.html
have a discussion of it
in the user comments
start :
"In MySQL 5.0, you can convert an enum's values into a dynamically-defined table of values, which then provides effectively a language-neutral method to handle this kind of conversion (rather than relying on PHP, Tcl, C, C++, Java, etc. specific code).
"
he do it with stored PROCEDURE
The easiest way is to use a regular column without contraints. If you're interested in all the current values, use DISTINCT to query them:
select distinct YourColumn from YourTable
That way, you don't have any maintenance and can store whatever you like in the table.
The foreign key table you mention is also a good option. The foreign key will limit the original column. Before you do the actual insert, you run a query to expand the "enum" table:
insert into EnumTable (name)
select 'NewEnumValue'
where not exists (select * from EnumTable where name = 'NewEnumValue')
Not sure what exactly you're trying to achieve btw; limit the column, but automatically expand the choices when someone breaks the limit?