How to specify a conditional DEFAULT constraint in SQL Server? - sql

I am adding a column as a foreign key which cannot be NULL and so need to have a DEFAULT value.
ALTER TABLE location
ADD [retailer_brand_id] INT NOT NULL DEFAULT (SELECT retailer_id from retailer),
FOREIGN KEY(retailer_brand_id) REFERENCES retailer_brand(retailer_brand_id);
What I want to achieve is, get the retailer_id from SELECT retailer_id from retailer and if it is equal to 12 then set it to 0, otherwise set to the retailer_id returned by the select query.
When I use the above query, I get an error message
Subqueries are not allowed in this context. Only scalar expressions are allowed.

I recommend a calculated column instead....so you don't also have to have this case statement in application logic as well as the table definition...don't want it in 2 spots...and don't have to worry about when retailerid changes...calc column would take care of that

I needed similar functionality. Calculated column is not an option for me, since my value should be changeble later by user, so I went with trigger on insert.
Described for example here: Trigger to update table column after insert?

Related

Why is UPDATE statement not working in SQL code?

I am trying to update one row of the storeID column. When I do the first code below, it runs but will not affect any row. However, the bottom two are producing the following error " The UPDATE statement conflicted with the REFERENCE constraint "Products_FK". The conflict occurred in database "group7", table "dbo.Products", column 'storeID'."
Could anyone help? Thanks!
Table in SQL
UPDATE Store
SET storeID='E50'
WHERE storeID='D50'
AND storeID is Null;
UPDATE Store
SET storeID='E50'
WHERE storeID='D50'
UPDATE Store
SET storeID='E50'
WHERE storeName='A Plus Cables'
Above is the code that I have tried and nothing is being updated.
The first one can't possibly match any rows, because a single value can't equal two different things at the same time:
WHERE storeID='D50'
AND storeID is Null
So that one is kind of a moot point. You'd need to update the WHERE clause to target the record(s) you want to target.
For the latter two, the error is telling you what's wrong. You're trying to write this value to a column:
SET storeID='E50'
But the error is telling you:
That column is a foreign key to another table. (Products?)
The value you're writing isn't presentin that other table.
So your options are:
Use a value that is present in the other table.
Use NULL (and update the column to allow NULL if necessary).
Remove the foreign key constraint to that other table.

Inserting new rows and generate a new id based on the current last row

The primary key of my table is an Identity column of an ID. I want to be able to insert a new row and have it know what the last ID in the table currently is and add one to it. I know I can use Scope Identity to get the last inserted column from my code, but I am worried about people manually adding entries in the database, because they do this quite often. Is there a way I can look at the last ID in the table and not just the last ID my code inserted?
With a SQL Identity column, you don't need to do anything special. This is the default behavior. SQL Server will handle making sure you don't have collisions regardless of where the inserts come from.
The ##Identity will pull the latest identity, and scope_identity will grab the identity from the current scope.
A scope is a module: a stored procedure, trigger, function, or batch. Therefore, if two statements are in the same stored procedure, function, or batch, they are in the same scope.
If you don't want to allow manual entries to the primary column, then you can add Identity constraint to it along with primary key constraint.
Example, while creating a table,
CREATE Table t_Temp(RowID Int Primary Key Identity(1,1), Name Varchar(50))
INSERT Into t_Temp values ('UserName')
INSERT Into t_Temp values ('UserName1')
SELECT * from t_Temp
You can query the table and get the next available code in one SQL query:
SELECT COALESCE(MAX(CAST("RowID" AS INT)),0) +1 as 'NextRowID' from <tableName>
The "0" here is a default, meaning if there are no rows found, the first code returned would be (0+1) =1
Generally I have 999 instead of the 0 as I like my RowID/primary key etc. to start at 1000.

Efficiently add a column with a value

I am trying to add a column to a tsql table, i do this using SMO in c#. Altering the table is fine but i want to set the column to have a value. The table contains 650 million rows and the update query is taking over a day and a half to run.
Update [TempDatabase].[dbo].[table1] set RawSource = 'DTP'
This is the query I am running above.
Can anyone think of a more efficient way of doing this?
Thanks in advance.
Sometimes, it is more efficient to copy the table with the new value and re-create the table in a single command. Also, you might want to be sure that you have minimal logging for these operations.
Probably the best solution is to use a default value when you create the column:
alter table table1 add RawSource varchar(255) not null default 'DTP';
If you don't want the default moving forward, you can remove it after the column is added.
Another method uses computed columns, but basically does the same thing:
alter table table1 add _RawSource varchar(255);
alter table1 add RawSource as (coalesce(_RawSource, 'DTP'));
at the time of addition of column to table only we can set a default value which will applies for all rows
Note:U should keep not null compulsory because if not all rows are applicable with nulls
alter table table_name
add row_source nvarchar(5) not null default(N'DTP')

sql when set default value getdate(), does it set value when run update statement?

I know when you insert a value into db, it set that column value as current datetime,
does it apply to it when you run a update statement?
e.g.
table schema:
Id, Name, CreatedDate(getdate())
when i insert into table id = 1 , name = 'john' it will set createdDate = current date
if i run an update statement
update table set name="john2" where id =1
Will it update the createdDate?
No, a DEFAULT CONSTRAINT is only invoked on INSERT, and only when (a) combined with a NOT NULL constraint or (b) using DEFAULT VALUES. For an UPDATE, SQL Server is not going to look at your DEFAULT CONSTRAINT at all. Currently you need a trigger ( see How do I add a "last updated" column in a SQL Server 2008 R2 table? ), but there have been multiple requests for this functionality to be built in.
I've blogged about a way to trick SQL Server into doing this using temporal tables:
Maintaining LastModified Without Triggers
But this is full of caveats and limitations and was really only making light of multiple other similar posts:
A System-Maintained LastModifiedDate Column
Tracking Row Changes With Temporal
Columns
How to add “created” and “updated” timestamps without triggers
Need a datetime column that automatically updates
wow - hard to understand...
i think NO based on the clues.
if you insert a record with a NULL in a column, and that column has a default value defined, then the default value will be stored instead of null.
update will only update the columns specified in the statement.
UNLESS you have a trigger that does the special logic - in which case, you need to look at the trigger code to know the answer.
if your update statement tell to update a column with getfate() it will, but if you just update a name for example and you have a createdate column (which was inserted with getdate()), this columns wont be affected.
You can achieve this using DEFAULT constraint like i did it with OrderDate field in below statement.
CREATE TABLE Orders
(
O_Id int NOT NULL,
OrderNo int NOT NULL,
P_Id int,
OrderDate date DEFAULT GETDATE()
)

Intervals: How can I make sure there is just one row with a null value in a timstamp column in table?

I have a table with a column which contains a 'valid until' Date and I want to make sure that this can only be set to null in a single row within the table. Is there an easy way to do this?
My table looks like this (postgres):
CREATE TABLE 123.myTable(
some_id integer NOT NULL,
valid_from timestamp without time zone NOT NULL DEFAULT now(),
valid_until timestamp without time zone,
someString character varying)
some_id and valid_from is my PK. I want nobody to enter a line with a null value in column valid_until if there is already a line with null for this PK.
Thank you
In PostgreSQL, you have two basic approaches.
Use 'infinity' instead of null. Then your unique constraint works as expected. Or if you cannot do that:
CREATE UNIQUE INDEX null_valid_from ON mytable(someid) where valid_until IS NULL
I have used both approaches. I find usually the first approach is cleaner and it allows you to use range types and exclude constraints in newer versions of PostgreSQL better (to ensure no two time ranges overlap based on a given given someid), bt the second approach often is useful where the first cannot be done.
Depending on the database, you can't have null in a primary key (I don't know about all databases, but in sql server you can't). The easiest way around this I can think of is to set the date time to the minimum value, and then add a unique constraint on it, or set it to be the primary key.
I suppose another way would be to set up a trigger to check the other values in the table to see if another entry is null, and if there is one, don't allow the insert.
As Kevin said in his answer, you can set up a database trigger to stop someone from inserting more than one row where the valid until date is NULL.
The SQL statement that checks for this condition is:
SELECT COUNT(*)
FROM TABLE
WHERE valid until IS NULL;
If the count is not equal to 1, then your table has a problem.
The process that adds a row to this table has to perform the following:
Find the row where the valid until value is NULL
Update the valid until value to the current date, or some other meaningful date
Insert the new row with the valid until value set to NULL
I'm assuming you are Storing Effective-dated-records and are also using a valid from date.
If so, You could use CRUD stored procedures to enforce this compliance. E.G the insert closes off any null valid dates before inserting a new record with a null valid date.
You probably need other stored procedure validation to avoid overlapping records and to allow deleting and editing records. It may be more efficient (in terms of where clauses / faster queries) to use a date far in the future rather than using null.
I know only Oracle in sufficient detail, but the same might work in other databases:
create another column which always contains a fixed value (say '0') include this column in your unique key.
Don't use NULL but a specific very high or low value. I many cases this is actually easier to use then a NULL value
Make a function based unique key on a function converting the date including the null value to some other value (e.g. a string representation for dates and 'x' for null)
make a materialized view which gets updated on every change on your main table and put a constraint on that view.
select count(*) cnt from table where valid_until is NULL
might work as the select statement. And a check constraint limiting the cnt value to the values 0 and 1
I would suggest inserting to that table through an SP and putting your constraint in there, as triggers are quite hidden and will likely be forgotten about. If that's not an option, the following trigger will work:
CREATE TABLE dbo.TESTTRIGGER
(
YourDate Date NULL
)
CREATE TRIGGER DupNullDates
ON dbo.TESTTRIGGER
FOR INSERT, UPDATE
AS
DECLARE #nullCount int
SELECT #nullCount = (SELECT COUNT(*) FROM TESTTRIGGER WHERE YourDate IS NULL)
IF(#NullCount > 1)
BEGIN
RAISERROR('Cannot have Multiple Nulls', 16, 1)
ROLLBACK TRAN
END
GO
Well if you use MS SQL you can just add a unique Index on that column. That will allow only one NULL. I guess that if you use other RDBMS, this will still function.