INSERT INTO ... SELECT if destination column has a generated column - mysql

Have some tables:
CREATE TABLE `asource` (
`id` int(10) unsigned NOT NULL DEFAULT '0'
);
CREATE TABLE `adestination` (
`id` int(10) unsigned NOT NULL DEFAULT '0',
`generated` tinyint(1) GENERATED ALWAYS AS (id = 2) STORED NOT NULL
);
I copy a row from asource to adestination:
INSERT INTO adestination
SELECT asource.*
FROM asource;
The above generates an error:
Error Code: 1136. Column count doesn't match value count at row 1
Ok, quite strange to require me to mention generated query. But ok, I add that column to the query:
INSERT INTO adestination
SELECT asource.*, NULL AS `generated`
FROM asource;
This has worked fine in 5.7.10. However, it generates an error in 5.7.11 (due to a fix:
Error Code: 3105. The value specified for generated column 'generated' in table 'adestination' is not allowed.
Ok, next try:
INSERT INTO adestination
SELECT asource.*, 1 AS `generated`
FROM asource;
But still the same error. I have tried 0, TRUE, FALSE but the error persists.
The DEFAULT value which is stated as the only allowed value (specs or docs). However, the following generates a syntax error (DEFAULT is not supported there):
INSERT INTO adestination
SELECT asource.*, DEFAULT AS `generated`
FROM asource;
So, how can I copy a row from one table to another using INSERT INTO ... SELECT if the destination table adds some columns where some of them are GENERATED?
The code calling this query is generic and has no knowledge what columns that particular tables have. It just knows which extra columns the destination table has. The source table is a live table, the destination table is a historical version of the source table. It has few columns extra like user id made the change, what type of the change it is (insert, update, delete) when etc.

Sadly this is just how MySQL works now to "conform to SQL standards".
The only value that the generated column can accept in an update, insert, etc. is DEFAULT, or the other option is to omit the column altogether.
My poor mans work around for these are to just disable the generated column while I'm working with the data (like for importing a dump) and then go back and add the generated column expression afterwards.

You must declare the columns
Insert into adestination (id, generated)
select id, 1
from asource;

It is best practice to list out the columns, and use null as field1 for the auto incremented id field.
INSERT INTO adestination
(id,
field1,
field2)
SELECT
null AS generated,
asource.field1,
asource.field2
FROM asource;

Related

EMPTY TABLE Duplicate entry '1' for key 'PRIMARY'

I have a strange problem with my MariaDB database. I create an empty table with the following code:
drop table if exists Subject;
CREATE TABLE Subject (
id integer primary key auto_increment,
code varchar(100) unique not null,
name text not null
);
Query executed OK, 0 rows affected.
I try to insert some data into the table:
INSERT INTO Subject (id, code, name) VALUES
(0,'KMI/AIdb/PHW/15','Počítačový hardvér'),
(1,'KMI/AIdb/DBA/15','Tvorba databázových aplikácií'),
(2,'KMI/SPRVdb/INF/16','Informatika a základy správy databáz'),
(3,'KMI/AIdb/PR4/15','Programovanie 4 - Objektové programovanie'),
(4,'KMI/AIdb/DBS/15','Databázové informačné systémy');
Error in query (1062): Duplicate entry '1' for key 'PRIMARY'
If I run the same query one more time:
INSERT INTO Subject (id, code, name) VALUES
(0,'KMI/AIdb/PHW/15','Počítačový hardvér'),
(1,'KMI/AIdb/DBA/15','Tvorba databázových aplikácií'),
(2,'KMI/SPRVdb/INF/16','Informatika a základy správy databáz'),
(3,'KMI/AIdb/PR4/15','Programovanie 4 - Objektové programovanie'),
(4,'KMI/AIdb/DBS/15','Databázové informačné systémy');
Query executed OK, 5 rows affected.
I believe it has something to do with the auto_increment, but I have a huge database dump that I would like to insert. Is this a bug, or is this an expected behavior?
AUTO_INCREMENT attribute can be used to generate a unique identity for new rows.
You can also explicitly assign 0 to the column to generate sequence numbers unless the NO_AUTO_VALUE_ON_ZERO SQL mode is enabled.
Read here for more details
The first insert created id=1. This is because "0" (or NULL) is treated specially to mean "give me the next id". Then the second row tried to explicitly insert id=1 and got a "duplicate".
Did your dump include a row with id=0, as you imply in a Comment. That sounds wrong.
Using id autoincrement don't insert id
INSERT INTO Subject (code, name) VALUES
('KMI/AIdb/PHW/15','Počítačový hardvér'),
('KMI/AIdb/DBA/15','Tvorba databázových aplikácií'),
('KMI/SPRVdb/INF/16','Informatika a základy správy databáz'),
('KMI/AIdb/PR4/15','Programovanie 4 - Objektové programovanie'),
('KMI/AIdb/DBS/15','Databázové informačné systémy');
overall don't insert 0 for id

Updating the timestamp of a row when copied from another table

I've been working at this for a while now and I haven't been able to come up with a solution, so i'm not sure if this may even be possible but any help is greatly appreciated.
I have two tables with a very basic structure as follows:
CREATE TABLE test ( time timestamp NULL DEFAULT NULL );
CREATE TABLE test_copy_to ( time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP );
I then create an entry in the "test" table that has a NULL value i.e. no given time:
INSERT INTO test VALUES (NULL);
I then would like to copy the NULL value from the "test" table to the "test_copy_to" table as follows:
INSERT INTO test_copy_to SELECT * FROM test;
The first problem is that the "test_copy_to" won't accept NULL values which is what is initialized in the first table. However what I would like to achieve is being able to copy the value from "test" (while having the null value) to "test_copy_to" such that the time-stamp is updated to the time at which it was copied from one table to the other. I've tried using UPDATE on the variable time however that does not seem to work. Is there anyway I can create the table such that it behaves in this manner?
Instead use COALESCE() function like below to provide some default values where the value is null
INSERT INTO test_copy_to
SELECT coalesce(`time`, CURRENT_TIMESTAMP ) FROM test;

MySql Basic table creation/handing

I'm trying to create a simple table where I insert field and I do some checks in MySql. I've used Microsoft SQL relatively easy. Instead, MySql give evrrytime query errors without even specifying what's going on. Poor MySql software design apart, here's what I'm trying to do:
1 table with 4 fields with an autoincremental autogenerated number to det an ID as primary key
CREATE TABLE `my_db`.`Patients_table` (
`ID_Patient` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`Patient_name` VARCHAR( 200 ) NOT NULL ,
`Recovery_Date` DATETIME NOT NULL ,
`Recovery_count` INT NOT NULL
) ENGINE = MYISAM
a simple stored procedure to insert such fields and check if something exist before inserting:
CREATE PROCEDURE nameInsert(IN nome, IN data)
INSERT INTO Patients_table (Patient_name,Recovery_Date) values (nome,data)
IF (EXISTS (SELECT Recovery_count FROM Tabella_nomi) = 0) THEN
INSERT INTO (Patients_table (Recovery_count)
ELSE
SET Recovery_count = select Recovery_count+1 from Patients_table
END
this seems wrong on many levels and MySQL useless syntax checker does not help.
How can I do this? Thanks.
There seems to be a lot wrong with this block of code. (No offense intended!)
First, Procedures need to be wrapped with BEGIN and END:
CREATE PROCEDURE nameInsert(IN nome, IN data)
BEGIN
...[actually do stuff here]
END
Second, since your table is declared with all fields as NOT NULL, you must insert all fields with an INSERT statement (this includes the Recovery_Date column, and excludes the AUTO_INCREMENT column). You can add DEFAULT CURRENT_TIMESTAMP to the date column if you want it to be set automatically.
INSERT INTO Patients_table (Patient_name,Recovery_Date) values (nome,data)
Third, what exactly is your IF predicate doing?
EXISTS (SELECT Recovery_count FROM Tabella_nomi) = 0
If you want to check if a row exists, don't put the = 0 at the end. Also, Tabella_nomi isn't declared anywhere in that procedure. Also, your SELECT statement should have a WHERE clause, since I'm assuming you want to select a specific row (this is going to select a result set of all recovery_counts).
Fourth, the second INSERT statement seems a little messy. It should look more like the first INSERT, and keep the point I made above in mind.
INSERT INTO (Patients_table (Recovery_count)
Fifth, the ELSE statement
SET Recovery_count = select Recovery_count+1 from Patients_table
Has some problems too. SET is meant for setting variables, not values in rows. I'm not 100% sure what your intent is from this statement, but it looks like you meant to increment the Recovery_count column of a certain row if it already exists. In which case, you meant to do something like this:
UPDATE Patients_table SET Recovery_count = Recovery_count+1 WHERE <conditional predicate>
Where the conditional predicate is something like this:
Patients_name = nome
Try these things, and look at the errors it gives you when you try to execute the CREATE STATEMENT. I bet they're more useful then you think!

SSIS Inserts not inserting the computed columns

I am using SSIS to insert a Excel file into a Sql Server Table. I believe it uses the Bulk insert, and as a result it doesn't insert into the 'CreationDate' and the 'ModificationDate' columns (both of which are computed columns with getdate() as the default).
Is there a way to get around this problem?
Also, just to be clear - both these date columns are not a part of excel. Here is the exact scenario:
My excel has two columns - code and description. My SQL Server table has 4 columns Code, Description, CreationDate, ModificationDate.
So, when the SSIS copies the data, it copies Code and Description, but the CreationDate and ModificationDate (which are SQL Server Computed Columns) are both empty.
You should use a normal column with a default constraint if you want to log creation
A computed column defined as GETDATE() will change every time you query it.
It is also impossible for a computed column to not be populated
So, assuming you mean "normal column with default", then you need stop sending NULL from SSIS which overrides the default
This is all demonstrated here:
CREATE TABLE #foo (
bar int NOT NULL,
testCol1Null datetime NULL DEFAULT GETDATE(),
testCol1NotNull datetime NOT NULL DEFAULT GETDATE(),
testCol2 AS GETDATE()
);
INSERT #foo (bar, testCol1Null) VALUES (1, NULL);
SELECT * FROM #foo;
WAITFOR DELAY '00:00:00.100';
SELECT * FROM #foo;
WAITFOR DELAY '00:00:00.100';
SELECT * FROM #foo;
DROP TABLE #foo;
Assuming you are using the Bulk Insert Task in SSIS, then you need to set "Keep nulls = off/unchecked" in the options page
You should have a default constraint on the column(s) that specifies get
col1 datetime default getdate()
There should also be an option for the bulk insert KEEPNULLS which should be turned off.
From Bulk Insert on MSDN:
Specifies that empty columns should retain a null value during the bulk-import operation, instead of having any default values for the
columns inserted. For more information, see Keeping Nulls or Using
Default Values During Bulk Import.
KEEPNULLS is also documented: http://msdn.microsoft.com/en-us/library/ms187887.aspx
Put in a Derived Column in your dataflow and populate the two missing columns with the values you want.
The value on a computed column doesn't physically exists on the database, it is calculated every time SQL Server needs to access it, that's why you can't inform a value to it on a insert.
What you need is a default column, which is a column that has a default value that's inserted if you don't inform any other value.
CreationDate datetime default getdate()
ModificationDate datetime default getdate()

MySQL inserting data only if table doesn't exist

Using strictly SQL (no PHP or anything else), is it possible to create a table and insert default data into that table only if that table doesn't exist?
Use the CREATE TABLE ... SELECT format:
create table if not exists tablename as
select * from defaultdata;
Here is one way of doing it:
CREATE TABLE IF NOT EXISTS T (
ID int(10) unsigned NOT NULL primary key,
NAME varchar(255) NOT NULL
);
REPLACE INTO T SELECT 1, 'John Doe';
REPLACE INTO T SELECT 2, 'Jane Doe';
REPLACE is a MySQL extension to the SQL standard that either inserts, or deletes and inserts.
You might do a select on the one of the meta data tables
if(not exists select * from whatever_meta where table_name = "whatever)
begin
...
end
You would have to do some research to figure out how exactly...
Can you store the table status as a variable, then use that variable to determine whether to insert data? Ex:
#status = SHOW TABLES LIKE 'my_table';
INSERT INTO my_table VALUES (1,'hello'),(2,'world') WHERE #status <> false;
The problem with Paul Morgan's answer is that it expects data to already exist in another table. Jonas' answer would be extremely resource exhaustive, especially if there's a lot of REPLACES (which are unnecessary if the table exists).
May be I am missing the point but why can't the default data be a set of insert statements...and what one simply needs to do is create the table if it does not exist followed by insert statements...that ways the default data does not have to exist in a different table.