Update:
Here is what I have learned from the answers below...
SQLite supports defining default values on columns. However, it does not support any form of DEFAULT keyword or function in queries. So, in effect, it is not possible to do a MySQL style bulk insert where a column is set explicitly in some rows, and the column default is used in other rows.
There are several creative workarounds listed below, but for simplicity's sake I most prefer Shawn's suggestion of using an INSERT statement per row, possibly all wrapped inside a BEGIN TRANSACTION statement. Only this will allow you to utilize column defaults.
Original Question:
I have a simple SQLite table named todo with this schema...
I can insert rows into this table with this syntax...
INSERT INTO 'todo' ('title', 'complete', 'notes') VALUES
('Pickup dry cleaning', true, 'They close at 7pm'),
('Clean apartment', false, 'Floors have already been swept'),
('Finish homework', true, 'Chapters 3 and 4');
However,
I can't figure out how to omit certain values and let the database use the default for that column.
For example, the complete column has a default of 0. It would be nice to be able to pass undefined as one of the values in my query, thus signaling to SQLite that I'd like to use whatever the column default is, but I get a Result: no such column: undefined error when I try this.
null would not work in this case either, because sometimes you want to explicitly set the value of a cell to null. You can't just plain omit the value either because the parameters are positional.
You just leave it out of the columns being inserted:
INSERT INTO todo(title, notes) VALUES ('Music practice', 'Not Elvis');
I can't figure out how to omit certain values and let the database use the default for that column.
As of this writing, SQLite does not support the default(_) function, but you could use triggers to achieve the effect you want, though doing so is a bit cumbersome.
Here's an example:
CREATE TRIGGER IF NOT EXISTS onnull
AFTER INSERT ON mytable
FOR EACH ROW
WHEN NEW.column = 'undefined'
BEGIN
UPDATE mytable
SET column = 'mydefault' ;
END;
this works if you are using better-sqlite3, and I think it will equally work for sqlite3.
db.prepare("INSERT INTO bugs (title, notes) VALUES (?, ?)").run(["Good Book", "nice test"]);
What Shawn said plus the bulk issue can be solved with DEFAULT value, that is if you are inserting all records manually, if you want it dynamically through a loop for example you must declare a variable as default and change to the specific value if the specific value exists, and use this variable in the query.
String var='DEFAULT';
if (Value exists) {var=Value;}
sql=inset into x(x,y,z) values (a,b,var);
Related
I'm trying to write the value of a parameter in Anylogic into a specific cell of a Database table. The parameter is declared in my main and gets its value through a specific calculation in function. Now I want to store the calculated value in the Database table.
I tried using the
INSERT INTO query (executeStatement("INSERT INTO eu (country, capital) VALUES ('Croatia', 'Zagreb')"); --> example from help)
…but I'm not able to use the specific parameter in the query/as a VALUE. I can only write direct input (like 'Croatia'), but not the parameter. At the end I want the table to get the current value from the parameter and insert the value in the table.
I found an Insert connectivity tool in the help function, but unfortunately it's only available in the professional edition.
Does anyone have an idea how to handle this?
Thank you and have a great weekend!
I don't know if I fully understood what you need but if what you want to do is simply insert values into a table, then what you've done inside your "executeStatement" is actually enough:
INSERT INTO eu (country, capital) VALUES ('Croatia', 'Zagreb')
If what you mean by "specific cell" is actually replacing the content of an existing field in a given row, you want to use UPDATE instead:
UPDATE eu SET country='New country', capital='New capital' WHERE <some criteria matching targeted row>
WARNING: don't forget the WHERE statement or every row of your table will be clobbered with these same new values.
And if what you want to do is inserting one or more new rows filled with something that already exists in the database, then you can construct your dataset directly from a SELECT query:
INSERT INTO eu (country, capital)
SELECT country_field, capital_field
FROM some_table
WHERE some_criteria
The SELECT query following the UPDATE statement can be absolutely anything. It may also refer to the same table if needed. The only requirement is to form rows that have the same structure and same type (at least compatible) than the targeted fields.
You need to adjust your String that defines the SQL statement.
Instead of
"INSERT INTO eu (country, capital) VALUES ('Croatia', 'Zagreb')"
you write
"INSERT INTO eu (country, capital) VALUES ('"+myParam1+", '"+myParam2+"')"
Assuming you have 2 params of type String that hold string text.
Your dbase column type must match the parameter (or variable) type you want to write
Be VERY careful with the ' and " signs as above
In a statement like this:
update tab1 set (col1,col2)=(val1,val2) returning "?"
I send whole row for update on new values, RETURNING * gives back the whole row, but is there a way to check which exactly column has changed when others remained the same?
I understand that UPDATE rewrites the values, but maybe there is some built-in function for such comparison?
Basically, you need the pre-UPDATE values of updated rows to compare. That's kind of hard as RETURNING only returns post-UPDATE state. But can be worked around. See:
Return pre-UPDATE column values using SQL only
So this does the basic trick:
WITH input(col1, col2) AS (
SELECT 1, text 'post_up' -- "whole row"
)
, pre_upd AS (
UPDATE tab1 x
SET (col1, col2) = (i.col1, i.col2)
FROM input i
JOIN tab1 y USING (col1)
WHERE x.col1 = y.col1
RETURNING y.*
)
TABLE pre_upd
UNION ALL
TABLE input;
db<>fiddle here
This is assuming that col1 in your example is the PRIMARY KEY. We need some way to identify rows unambiguously.
Note that this is not safe against race conditions between concurrent writes. You need to do more to be safe. See related answer above.
The explicit cast to text I added in the CTE above is redundant as text is the default type for string literals anyway. (Like integer is the default for simple numeric literals.) For other data types, explicit casting may be necessary. See:
Casting NULL type when updating multiple rows
Also be aware that all updates write a new row version, even if nothing changes at all. Typically, you'd want to suppress such costly empty updates with appropriate WHERE clauses. See:
How do I (or can I) SELECT DISTINCT on multiple columns?
While "passing whole rows", you'll have to check on all columns that might change, to achieve that.
i have created a table with 3 columns in which 2 are defined as NOT NULL in SQL, now i created a new column i wanted to insert values to only the new variable, but i'm having an error while using Insert into statement
If you are using MySQL, here is your answer:
Inserting NULL into a column that has been declared NOT NULL. For multiple-row INSERT statements or INSERT INTO ... SELECT statements, the column is set to the implicit default value for the column data type. This is 0 for numeric types, the empty string ('') for string types, and the “zero” value for date and time types. INSERT INTO ... SELECT statements are handled the same way as multiple-row inserts because the server does not examine the result set from the SELECT to see whether it returns a single row. (For a single-row INSERT, no warning occurs when NULL is inserted into a NOT NULL column. Instead, the statement fails with an error.)
The documentation is here: MySQL 5.7 Reference Manual: Insert
As said above, just use ' ' and 0 depending on the type of column you have. I believe it's the same for any other DBMS system anyone could use.
Disregarding that this normally indicates you should rethink your database design, some database engines will let you do this through temporary schema manipulation (removing and afterwards re-adding the 'not null' constraint).
Alternatively you could either define default values for the not null columns, or pass in through the insert command the appropriate "default" values, or select a database mode where either the checks are not enforced (thus allowing nulls) or default values are automatically generated (eg mysql in non strict mode which replaces the nulls with a calculated default value).
The only valid use case I can think of is replicating the situation where a database has rows with nulls in certain fields, and then the schema is changed to make those columns not null. Mysql for instance will allow you to add 'not null' constraints to columns that already have data with nulls in that column.
The INSERT syntax I've been using is this
INSERT INTO TableName VALUES (...)
The UPDATE syntax I've been using is
UPDATE TableName SET ColumnName=Value WHERE ...
So in all my code, I have to generate 2 strings, which would result in something like this
insertStr = "(27, 'John Brown', 102)";
updateStr = "ID=27, Name='John Brown', ItemID=102";
and then use them separately
"UPDATE TableName SET " + updateStr + " WHERE ID=27 " +
"IF ##ROWCOUNT=0 "+
"INSERT INTO TableName VALUES (" + insertStr + ")"
It starts bothering me when I am working with tables with like 30 columns.
Can't we generate just one string to use on both INSERT and UPDATE?
eg. using insertStr above on UPDATE statement or updateStr on INSERT statement, or a whole new way?
I think you need a whole new approach. You are open to SQL Injection. Provide us with some sample code as to how you are getting your data inputs and sending the statements to the database.
alt text http://goose.ycp.edu/~weddins/440/S09%20IFS440%20Bobby%20Drop%20Tables.PNG
As far as I'm aware, what you're describing isn't possible in ANSI SQL, or any extension of it that I know. However, I'm mostly familiar with MySQL, and it likely depends completely upon what RDBMS you're using. For example, MySQL has "INSERT ... ON DUPLICATE KEY UPDATE ... " syntax, which is similar to what you've posted there, and combines an INSERT query with an UPDATE query. The upside is that you are combining two possible operations into a single query, however, the INSERT and UPDATE portions of the query are admittedly different.
Generally, this kind of thing can be abstracted away with an ORM layer in your application. As far as raw SQL goes, I'd be interested in any syntax that worked the way you describe.
Some DBMS' have an extension to do this but why don't you just provide a function to do it for you? We've actually done this before.
I'm not sure what language you're using but it's probably got associative arrays where you can wrote something like:
pk{"ID"} = "27"
val{"Name"} = "'John Brown'"
val{"ItemID"} = "102"
upsert ("MyTable", pk, val)
and, if it doesn't have associative arrays, you can emulate them with multiple integer-based arrays of strings.
In our upsert() function, we just constructed a string (update, then insert if the update failed) and passed it to our DBMS. We kept the primary keys separate from our other fields since that made construction of the update statement a lot easier (primary key columns went in the where clause, other columns were just set).
The result of the calls above would result in the following SQL (we had a different check for failed update but I've put your ##rowcount in for this example):
update MyTable set
Name = 'John Brown',
ItemID = 102
where ID = 27
if ##rowcount=0
insert into MyTable (ID, Name, ItemID) values (
27,
'John Brown',
102
)
That's one solution which worked well for us. No doubt there are others.
Well, how about no statements? You might want to look into an ORM to handle this for you...
Some databases have proprietary extensions that do exactly this.
I agree that the syntax of INSERT and UPDATE could be more consistent, but this is just a fact of life now -- it ain't gonna change now. For many scenarios, the best option is your "whole new way": use an object-relational mapping library (or even a weak-tea layer like .NET DataSets) to abstract away the differences, and stop worrying about the low-level SQL syntax. Not a viable option for every application, of course, but it would allow you to just construct or update an object, call a Save method and have the library figure out the SQL syntax for you.
If you think about it, INSERT and UPDATE are exactly the same thing. They map field names to values, except the UPDATE has a filter.
By creating an associative array, where the key is the field name and the value is the value you want to assign to the field, you have your mapping. You just need to convert it to a the proper string format depending on INSERT or UPDATE.
You just need to create a function that will handle the conversion based on the parameters given.
SQL Server 2008:
MERGE dbo.MyTable AS T
USING
(SELECT
#mykey AS MyKey
#myval AS MyVal
) AS S
ON (T.MyKey = S.MyKey)
WHEN MATCHED THEN
UPDATE SET
T.MyVal = S.MyVal
WHEN NOT MATCHED THEN
INSERT (MyKey, MyVal)
VALUES (S.MyKey, S.MyVal)
MySQL:
INSERT (MyKey, MyVal)
INTO MyTable
VALUES({$myKey}, {$myVal})
ON DUPLICATE KEY UPDATE myVal = {$myVal}
I have a table that looks a bit like this actors(forename, surname, stage_name);
I want to update stage_name to have a default value of
forename." ".surname
So that
insert into actors(forename, surname) values ('Stack', 'Overflow');
would produce the record
'Stack' 'Overflow' 'Stack Overflow'
Is this possible?
Thanks :)
MySQL does not support computed columns or expressions in the DEFAULT option of a column definition.
You can do this in a trigger (MySQL 5.0 or greater required):
CREATE TRIGGER format_stage_name
BEFORE INSERT ON actors
FOR EACH ROW
BEGIN
SET NEW.stage_name = CONCAT(NEW.forename, ' ', NEW.surname);
END
You may also want to create a similar trigger BEFORE UPDATE.
Watch out for NULL in forename and surname, because concat of a NULL with any other string produces a NULL. Use COALESCE() on each column or on the concatenated string as appropriate.
edit: The following example sets stage_name only if it's NULL. Otherwise you can specify the stage_name in your INSERT statement, and it'll be preserved.
CREATE TRIGGER format_stage_name
BEFORE INSERT ON actors
FOR EACH ROW
BEGIN
IF (NEW.stage_name IS NULL) THEN
SET NEW.stage_name = CONCAT(NEW.forename, ' ', NEW.surname);
END IF;
END
According to 10.1.4. Data Type Default Values no, you can't do that. You can only use a constant or CURRENT_TIMESTAMP.
OTOH if you're pretty up-to-date, you could probably use a trigger to accomplish the same thing.
My first thought is if you have the two values in other fields what is the compelling need for redundantly storing them in a third field? It flies in the face of normalization and efficiency.
If you simply want to store the concatenated value then you can simply create a view (or IMSNHO even better a stored procedure) that concatenates the values into a pseudo actor field and perform your reads from the view/sproc instead of the table directly.
If you absolutely must store the concatenated value you could handle this in two ways:
1) Use a stored procedure to do your inserts instead of straight SQL. This way you can receive the values and construct a value for the field you wish to populate then build the insert statement including a concatenated value for the actors field.
2) So I don't draw too many flames, treat this suggestion with kid gloves. Use only as a last resort. You could hack this behavior by adding a trigger to build the value if it is left null. Generally, triggers are not good. They add unseen cost and interactions to fairly simple interactions. You can, though, use the CREATE TRIGGER to update the actors field after a record is inserted or updated. Here is the reference page.
As of MySQL 8.0.13, you can use DEFAULT clause for a column which can be a literal constant or an expression.
If you want to use an expression then, simply enclose the required expression within parentheses.
(concat(forename," ",surname))
There are two ways to accomplish what you are trying to do as per my knowledge:
(important: consider backing up your table first before running below queries)
1- Drop the column "stage_name" all together and create a new one with DEFAULT constraint.
ALTER TABLE actors ADD COLUMN stage_name VARCHAR(20) DEFAULT (concat(forename," ",surname))
2- This will update newer entries in the column "stage_name" but not the old ones.
ALTER TABLE actors alter stage_name set DEFAULT (concat(forename," ",surname));
After that, if you need to update the previous values in the column "stage_name" then simply run:
UPDATE actors SET stage_name=(concat(forename," ",surname));
I believe this should solve your problem.