I'd like to use the column's default value in an stored procedure insert, so that I don't have to repeat the default value in multiple places (it could change... DRY principle).
The T-SQL INSERT operation has a handy 'default' keyword that I can use as follows:
Declare #newA varchar(10)
Set #newA = 'Foo2'
-- I can use "default" like so...
Insert into Table_1 (
A,
B)
Values (
#newA,
default)
However, If I need to do something conditional, I can't seem to get the case statement to return 'default'.
-- How do I use 'default' in a case statement?
INSERT INTO Table_1 (
A,
B )
VALUES (
#newA,
CASE WHEN (#newA <> 'Foo2') THEN 'bar' ELSE default END)
-- > yeilds "Incorrect syntax near the keyword 'default'."
I could insert the default, and then update as needed like so:
INSERT INTO Table_1 (
A,
B )
VALUES (
#newA,
default)
UPDATE Table_1
SET B = CASE WHEN (A <> 'Foo2') THEN 'bar' ELSE B END
WHERE ID = SCOPE_IDENTITY()
But I'd really like somebody to tell me "There's a better way..."
Here's a table definition for this example if it helps...
CREATE TABLE dbo.Table_1 (
ID int NOT NULL IDENTITY (1, 1),
A varchar(10) NULL,
B varchar(10) NULL )
GO
ALTER TABLE dbo.Table_1 ADD CONSTRAINT DF_Table_1_A DEFAULT 'A-Def' FOR A
GO
ALTER TABLE dbo.Table_1 ADD CONSTRAINT DF_Table_1_B DEFAULT 'B-Def' FOR B
GO
default only works from within a VALUES() block, which does not seem to be an acceptable value in a CASE statement; you could use an if statement to determine what to insert:
DECLARE #newA varchar(10) = 'Foo2'
IF (#newA <> 'Foo2')
BEGIN
INSERT INTO Table_1 (A, B)
SELECT #newA, 'bar'
END
ELSE
BEGIN
--If you are using default values, you do not have to specify the column
INSERT INTO Table_1 (A)
SELECT #newA
END
I think this is better than updating after an insert, so that you only insert correct data into your table. It also keeps the number of INSERTS/UPDATES to 1. You should also be careful when you using ##IDENTITY due to scoping. Consider looking into SCOPE_IDENTITY().
Related
If I have an SQL table with all default columns (e.g. identity column + any number of columns all with default values), what is the SQL statement to insert a row with no explicit values given?
insert MyTable /* ( doh, no fields! ) */
-- values( doh, no values! )
What's the trick?
This is a part of the INSERT syntax
INSERT INTO TableName DEFAULT VALUES
Read more here:
https://learn.microsoft.com/en-us/sql/t-sql/statements/insert-transact-sql
You can use the DEFAULT keyword.
The accepted answer only works for one row, not for multiple rows.
Let us assume you know how many rows to insert, but you want all default values. You cannot do the following, for instance
INSERT MyTable
SELECT DEFAULT VALUES -- Incorrect syntax near the keyword 'DEFAULT'.
FROM SomeQueryOrView;
-- or
INSERT MyTable
DEFAULT VALUES -- Incorrect syntax near the keyword 'FROM'.
FROM SomeQueryOrView;
Instead we can hack MERGE to do this
MERGE INTO myTable
USING (SELECT SomeValue FROM SomeQueryOrView) s
ON 1 = 0 -- never match
WHEN NOT MATCHED THEN
INSERT DEFAULT VALUES;
A bonus benefit is that we can OUTPUT data from columns which are not being inserted:
MERGE INTO myTable
USING (SELECT SomeValue FROM SomeQueryOrView) s
ON 1 = 0 -- never match
WHEN NOT MATCHED THEN
INSERT DEFAULT VALUES
OUTPUT inserted.Id, s.SomeValue;
I have a MEMBER table and NOTIFICATION table. On client side, I list all of the records in MEMBER table and there is a points column and this is shown as text input. So after I change the values for some members, I can click save button and this will update the records in my MEMBER table that's all right,
But the thing I want to accomplish is for every record whose points value has changed I want to INSERT a record in my notifications table.
I couldn't think of anything, how can I approach to this problem?
For notifications I made 3 tables by following the article in here
Use the output clause instead of trigger, they are bad.
You need the condition "where data_old <> data_new" case if you updated a column with the same value, SQL Server marked it as changed, even if the value hasn't changed
create table #example (id int identity(1,1) not null, data nvarchar(max));
insert into #example (data) values ('value 1'),('value 2'), ('value 3');
create table #audit (id int, data_old nvarchar(max), data_new nvarchar(max), [When] datetime not null default (getdate()));
insert into #audit (id, data_old, data_new)
select id, data_old, data_new
from (
update #example
set data = 'value changed'
output inserted.id, deleted.data as data_old, inserted.data as data_new
where id = 2
)changed (id, data_old, data_new)
where data_old <> data_new
select * from #audit
will result with this in #audit :
You have described what a trigger does.
create trigger trig_member_insert on members after update
as
begin
insert into notifications ( . . . )
select . . ., i.points as new_points u.points as old_points -- what you want to insert
from inserted i join
updated u
on i.member_id = u.member_id
where u.points <> i.points
end;
Storing something called "points" as a string seems like a very poor choice. It sounds like a number.
There is a table with all fields of user defined data types and NOT NULL. There are some defaults created as CREATE DEFAULT and binded to those user defined data types. It is given that way and I may not change it.
I need to insert data into this table from the source where there are some NULLS. How to use those defaults in my insert?
I've tried to get these defaults using defenition from sys.default_constraints, but there are no defaults created as CREATE DEFAULT in sys.default_constraints.
'''SQL
--Code I may not change
CREATE TYPE dt_myType FROM varchar(255) NOT NULL
GO
CREATE DEFAULT myDefault
AS
''
GO
EXEC sp_bindefault 'myDefault', 'dt_myType';
GO
CREATE TABLE myTable (
f1 dt_myType NOT NULL )
GO
--My code
insert into myTable
(f1)
select
case f
when 1 then 'example'
else --I need default here
end
from ...
'''
If you do not have very many columns to test, you could do something like this:
insert into t (<other columns>, a, b)
select <other columns>, x.a, x.b
from x
where x.a is not null and x.b is not null;
insert into t (<other columns>, a )
select <other columns>, x.a
from x
where x.a is not null and x.b is null;
insert into t (<other columns>, b )
select <other columns>, x.b
from x
where x.a is null and x.b is not null;
insert into t (<other columns>)
select <other columns>
from x
where x.a is null and x.b is null;
This doesn't generalize well to many more columns.
Otherwise, here are some options I can think of:
Loop through each record (say using a dreaded cursor) and construct the insert based on the presence of NULL values.
Use an instead of trigger to basically construct the inserts, as described above.
Insert the data so all values are default values and then update them with the values in the data.
Figure out a way to parse the default value in the metadata, represent it as the appropriate type, and include it in your query.
I am trying to use T-SQL Merge to check for the existence of records and update, if not then insert.
The update works fine, but the insert is not working.
Any and all help on this would be gratefully received.
DECLARE
#OperatorID INT = 2,
#CurrentCalendarView VARCHAR(50) = 'month';
WITH CTE AS
(
SELECT *
FROM dbo.OperatorOption
WHERE OperatorID = #OperatorID
)
MERGE INTO OperatorOption AS T
USING CTE S ON T.OperatorID = S.OperatorID
WHEN MATCHED THEN
UPDATE
SET T.CurrentCalendarView = #CurrentCalendarView
WHEN NOT MATCHED BY TARGET THEN
INSERT (OperatorID, PrescriptionPrintingAccountID, CurrentCalendarView)
VALUES (#OperatorID, NULL, #CurrentCalendarView);
When would a row Selected from OperatorOption not already exist in OperatorOption?
If you're saying this code does not insert - you're right it doesn't because the row has to be there to begin with (in which case it won't insert), or the row is not there to begin with, in which case there is nothing in the source dataset to insert.
Does
SELECT *
FROM dbo.OperatorOption
WHERE OperatorID = #OperatorID
return anything or not?
This does not work the way you think it does. There is nothing in the source CTE.
The answer to 'was a blank dataset missing from the target' is 'No' so nothing is inserted
To do this operation, I use this construct:
INSERT INTO dbo.OperatorOption
(OperatorID, PrescriptionPrintingAccountID, CurrentCalendarView)
SELECT #OperatorID, NULL, #CurrentCalendarView
WHERE NOT EXISTS (
SELECT * FROM dbo.OperatorOption
WHERE OperatorID = #OperatorID
)
It does not matter you are inserting values as variables. It thinks there is nothing to insert.
You need to produce data that does not match.
Like this:
DECLARE #OperatorID INT = 3, #CurrentCalendarView VARCHAR(50) = 'month';
declare #t table (operatorID int, CurrentCalendarView varchar(50));
insert into #t values (2, 'year');
MERGE #t AS TARGET
USING (SELECT #OperatorID, #CurrentCalendarView) AS source (operatorID, CurrentCalendarView)
on (TARGET.operatorID = Source.operatorID)
WHEN MATCHED THEN
UPDATE SET TARGET.CurrentCalendarView = #CurrentCalendarView
WHEN NOT MATCHED BY TARGET THEN
INSERT (OperatorID, CurrentCalendarView)
VALUES (source.OperatorID, source.CurrentCalendarView);
select * from #t
Insert probably isn't working because your source CTE does not produce any rows. Depending on how your table is organised, you might need to select from some other source, or use table valued constructor to produce source data.
I'm getting my head around the MERGE statement in SQL server. I generally use it to insert/update a single row, which I realise isn't the only use, but it's something I seem to do quite often.
But what happens if you want to insert a value of 1, or update to increment the value and output the incremented value eg:
CREATE TABLE [Counter] (
[Key] VARCHAR(255) NOT NULL PRIMARY KEY,
[Value] INT NOT NULL
);
DECLARE #paramKey VARCHAR(255);
SET #paramKey = 'String';
MERGE [Counter] AS targt
USING (Values(#paramKey)) AS source ([Key])
ON (targt.[Key] = source.[Key])
WHEN MATCHED THEN
UPDATE SET Value = Value +1
WHEN NOT MATCHED THEN
INSERT ([Key], Value)
VALUES (source.[Key], 1);
-- but now I want the new value!
Is there a way of doing this? I notice the output clause in https://msdn.microsoft.com/en-gb/library/bb510625.aspx but it doesn't seem to work with scalars (I could output to a single row-ed table variable but that seems wrong):
-- using table variables but seems
DECLARE #paramKey VARCHAR(255), #value int;
SET #paramKey = 'String'
DECLARE #Tab table (
[Value] INT
)
MERGE Counter AS targt
USING (Values(#paramKey)) AS source ([Key])
ON (targt.[Key] = source.[Key])
WHEN MATCHED THEN
UPDATE SET Value = Value +1
WHEN NOT MATCHED THEN
INSERT ([Key], Value)
VALUES (source.[Key], 1)
OUTPUT inserted.[Value] INTO #Tab;
-- can now use #Tab as a single rowed table variable
Is there a better option?