Update null values when we use Addition assignment (+=) - sql

declare #temp table
(
Id int,
Qty int
)
insert into #temp values(1,null)
update #temp set Qty+=2
select * from #temp
I am trying to update the Qty column however the Qty column is null by default. So because the production database column might be null when I try to update I am getting a null value I need Qty to be 2.

Use coalesce (or isnull) to handle the null case.
update #temp set Qty = coalesce(Qty,0) + 2;

If you want to increment the value, then the column should not allow NULLs.
I would suggest that you default the value to 0. Then don't insert a NULL value explicitly:
declare #temp table (
Id int,
Qty int not null default 0
);
insert into #temp (id) values (1);
update #temp set Qty += 2;
select * from #temp;
You can also express the insert as:
insert into #temp (id, value) values (1, default);

Related

Using the identity column to add a value to a computed column

At times I need to store a temporary value to a field. I have a stored procedure that adds it using:
Insert new record first then
SELECT #Record_Value = SCOPE_IDENTITY();
UPDATE ADMIN_Publication_JSON
SET NonPubID = CAST(#Record_Value as nvarchar(20)) + '_tmp'
WHERE RecID = #Record_Value
It simply takes the identity value and adds an '_tmp' to the end. Is there a way that I can create a default value in the table that would do that automatically if I did not insert a value into that field?
The NonPubID column is just a NVARCHAR(50).
Thanks
You could write a trigger, that replaces NULL with that string upon INSERT.
CREATE TRIGGER admin_publication_json_bi
ON admin_publication_json
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
UPDATE apj
SET apj.nonpubid = concat(convert(varchar(20), i.id), '_tmp')
FROM admin_publication_json apj
INNER JOIN inserted i
ON i.id = apj.id
WHERE i.nonpubid IS NULL;
END;
db<>fiddle
Downside: You cannot explicitly insert NULLs for that column, should that be desired.
Check out NewKey col below:
CREATE TABLE #Table
(
ID INT NOT NULL IDENTITY(1,1) PRIMARY KEY CLUSTERED,
IDValue VARCHAR(1) ,
ModifiedDT DATETIME NULL,
NewKey AS ( CONVERT(VARCHAR(100),ID)+'_Tmp' )
)
INSERT #Table( IDValue, ModifiedDT )
SELECT 'A', GETDATE()
UNION ALL
SELECT 'Y', GETDATE() - 1
UNION ALL
SELECT 'N', GETDATE() - 5
SELECT * FROM #Table

How to create an auto increment column that is segmented by an other column

I need to create a table that will contain a incremental id, but I would like the ids be automatically segmented according to an other column. Here is what I want :
CREATE TABLE dbo.MyTable (
myKey INT IDENTITY PRIMARY KEY,
category INT,
incrementalId INT
);
INSERT INTO dbo.MyTable (category) VALUES (100);
INSERT INTO dbo.MyTable (category) VALUES (200);
INSERT INTO dbo.MyTable (category) VALUES (100);
INSERT INTO dbo.MyTable (category) VALUES (100);
INSERT INTO dbo.MyTable (category) VALUES (100);
INSERT INTO dbo.MyTable (category) VALUES (200);
SELECT *
FROM dbo.MyTable;
I would like this to display something like :
myKey category incrementalId
----------- ----------- -------------
1 100 1
2 200 1
3 100 2
4 100 3
5 100 4
6 200 2
Meaning I want the incrementalId to be automatically incremented per category and restart from 1 for any new category inserted. I want this to be done by itself on any inserts in the table (I don't want to have to remember to do that when I insert in this table).
I think this might be done with window functions and maybe a trigger, but I just can't figure how.
EDIT:
I would like the data to be persisted to avoid incrementalId to be shifted if data deletion happens. Also, ideally the same ID would not be re-given in the event of rows deletion (the same way that sequences or IDENTITY works)
Any idea ?
CREATE TABLE dbo.MyTable (
myKey INT IDENTITY PRIMARY KEY,
category INT,
incrementalId INT
);
GO
create table dbo.nextCategoryID (
category int,
nextidvalue int,
constraint PK_nextCategoryID primary key clustered( category, nextidvalue )
);
GO
create trigger numberByCategory on dbo.MyTable
after insert as
-- Automatically add any net new category
insert into dbo.nextCategoryID ( category, nextidvalue )
select distinct category, 1 as nextidvalue
from inserted
where not exists ( select * from dbo.nextCategoryID s
where s.category = inserted.category );
-- Number the new rows in each incoming category
with numberedrows as (
select
i.myKey,
i.category,
n.nextidvalue - 1 + row_number() over ( partition by i.category order by i.category ) as incrementalId
from inserted i
join dbo.nextCategoryID n on i.category = n.category
)
update m
set incrementalId = n.incrementalId
from dbo.MyTable m
join inserted i on m.myKey = i.myKey
join numberedrows n on n.myKey = i.myKey;
update dbo.nextCategoryID
set nextidvalue = 1 + ( select max( m.incrementalId )
from inserted i
join dbo.MyTable m on i.myKey = m.myKey
where i.category = nextCategoryID.category
)
where exists ( select *
from inserted i
where i.category = nextCategoryID.category
);
GO
-- Test data
INSERT INTO dbo.MyTable (category) VALUES (100);
INSERT INTO dbo.MyTable (category) VALUES (200);
INSERT INTO dbo.MyTable (category) VALUES (100);
INSERT INTO dbo.MyTable (category) VALUES (100);
INSERT INTO dbo.MyTable (category) VALUES (100);
INSERT INTO dbo.MyTable (category) VALUES (200);
insert into dbo.MyTable (category)
values
( 200 ),
( 200 ),
( 100 ),
( 300 ),
( 400 ),
( 400 )
SELECT *
FROM dbo.MyTable;
You can easily achieved this via a trigger:
CREATE TRIGGER dbo.UpdateIncrementalID
ON dbo.MyTable
AFTER INSERT
AS
UPDATE x
SET incrementalId = ROW_NUMBER() OVER(PARTITION BY category ORDER BY myKey DESC)
FROM dbo.MyTable x
I think you don't need to add additional column 'IncrementalID' in your table.
You can make it in your select statement.
SELECT myKey,category,ROW_NUMBER() OVER(PARTITION BY category ORDER BY myKey )incrementalId
FROM MyTable
ORDER BY myKey
sample output.
Else you can create a view from your actual table.
CREATE VIEW dbo.VIEW_MyTable
AS
SELECT myKey,category,ROW_NUMBER() OVER(PARTITION BY category ORDER BY myKey )incrementalId
FROM MyTable
ORDER BY myKey
You can update the same table using below Update query
;with cte as (
select mykey, category, incrementalid, row_number() over (partition by category order by mykey,category) as rn from MyTable
)
update cte
set incrementalId = rn
Extending #Kannan's solution into a UDF that's called from a compute column:
create function dbo.fnsPartId(#mykey int)
returns int
as
begin
declare #Ret int
;
with
enum as
(
select mykey, category, incrementalid,
row_number() over (partition by category order by mykey, category) as rn
from MyTable
)
select #Ret = rn from enum where mykey = #mykey
return #Ret
end
And modify the table as:
CREATE TABLE dbo.MyTable (
myKey INT IDENTITY PRIMARY KEY,
category INT,
incrementalId AS ([dbo].[fnsPartId]([mykey]))
);
Try to create a default constraint on the column. Use a function to generate next value for the row as a default row returned by the function.
Please try this (added after insert trigger on this table)-
create trigger InsertIncrementalID
on dbo.MyTable
after insert
as
begin
update mt
set incrementalId = (select count(mt1.category) from dbo.MyTable mt1 where mt1.category = mt.category)
from dbo.MyTable mt
inner join inserted i on i.myKey = mt.myKey
end
Please remember two points while using trigger -
1. We are updating table from inside trigger so if you have any other trigger(after update) on this table, that trigger will be executed too.
2. While inserting multiple rows in this table with single select query, this trigger will be executed only once.

SQL Server : procedure tables cannot insert value null

I want to add 100 storages in a table.
This is my procedure:
CREATE PROCEDURE [add100*sTORAGE]
AS
DECLARE #i int, #start DATETIME, #end DATETIME
SET #start = GETDATE()
SET #i = 1
WHILE #i < 101
BEGIN
INSERT INTO Storage(storage_name)
VALUES (CONCAT('Nume', CONVERT(nvarchar, #i)))
SET #i = #i +1
END
SET #end = GETDATE()
DECLARE #testID INT = (SELECT TOP 1 (TestRunID)
FROM TestRuns
ORDER BY TestRunID DESC)
DECLARE #tableID INT = (SELECT tableID
FROM Tables
WHERE Name = 'Storage')
INSERT INTO TestRunTables (TestRunID, TableID, StartAt, EndAt)
VALUES (#testID, #tableID, #start, #end)
GO
I get an error after its execution:
Msg 515, Level 16, State 2, Procedure add100*sTORAGE, Line 13
Cannot insert the value NULL into column 'TestRunID', table 'OnlineShop.dbo.TestRunTables'; column does not allow nulls. INSERT fails.
When I look in the table, it has been created 99 columns.
I have some empty tables in a relation and this are the inserts of it (maybe here is the cause):
--INSERTS--
-- insert views into "Views"
INSERT INTO Views(Name) VALUES ('View1')
INSERT INTO Views(Name) VALUES ('View2')
INSERT INTO Views(Name) VALUES ('View3')
select * from views
delete from views where ViewID>1
-- insert into "Tests"
INSERT INTO Tests(Name) VALUES ('[add100*Storage-runView1-del100*Storage]')
INSERT INTO Tests(Name) VALUES ('[add100*Product-runView2-del100*Product]')
INSERT INTO Tests(Name) VALUES ('[add100*OrderProduct-runView3- del100*OrderProduct]')
SELECT * FROM Tests
--insert into tables
INSERT INTO Tables(Name) VALUES ('Table1')
INSERT INTO Tables(Name) VALUES ('Table2')
INSERT INTO Tables(Name) VALUES ('Table3')
SELECT * from Tables
-- insert into "testTable"
INSERT INTO TestTables(TestID, TableID, NoOfRows, Position) VALUES (1,1,100,1)
INSERT INTO TestTables(TestID, TableID, NoOfRows, Position) VALUES (3,2,100,1)
INSERT INTO TestTables(TestID, TableID, NoOfRows, Position) VALUES (2,3,100,1)
SELECT * FROM TestTables
-- insert into "testViews"
INSERT INTO TestViews(TestID,ViewID) VALUES (1,1)
INSERT INTO TestViews(TestID,ViewID) VALUES (3,2)
INSERT INTO TestViews(TestID,ViewID) VALUES (2,3)
SELECT * FROM TestViews
What's wrong? Thank you.
The error tells you everything--table TestRunTables has column "TestRunID" which requires that field to have a value. You either need to be sure to insert a value into that field, or alter the column so that it will use a default value when you don't specify it.
This line:
DECLARE #testID INT = (SELECT TOP 1 (TestRunID) FROM TestRuns ORDER BY TestRunID DESC)
will set #testID to null if no records are returned from TestRuns or if the first TestRunID is null. This is probably what you need to fix.

Assign multiple values to Table variable in SQL

DECLARE #ID INT
SET #ID = (select top 1 USER_REQ_JOB_ID
from T8504_USER_REQ_JOB
where JOB_GRP_ID = 160
order by LST_UPDT_TS desc)
SELECT INPUT_PARM_VAL_TX
from TBL_RPT_JOB_INPUT_PARAM
where USER_REQ_JOB_ID = #ID
This returns these results:
USA
USCC
6
7
2
These five records what I get I want to assign to five different variables to use in stored procedure.
I was trying with table variable like this :
declare #CID table (
Region Char(3)
,Segment Char(3)
,MasterContractId int
,ctcid int
,templateid int)
insert into #CID (Region,Segment,MasterContractId,ctcid,templateid)
But how to insert that 5 rows here?
INSERT INTO #CID
select * from
(
select
'Temp' + convert(char(1), row_number() over (order by (select 0))) as columnName,
INPUT_PARM_VAL_TX as Value
from TBL_RPT_JOB_INPUT_PARAM where USER_REQ_JOB_ID = #ID
) d
pivot
(
max(value)
for columnname in (Temp1, Temp2, Temp3, Temp4, Temp5)
) piv;
See if this helps.
Take a look at this fiddle for an example.
Courtesy:
Add row number to this T-SQL query
Efficiently convert rows to columns in sql server
EDIT: The sql adds an extra column to generate row numbers to use it as an extra column, which is pivoted as column heading.
it's really gross, but one way you could probably do it is this (though you'll need to apply it to your case):
http://sqlfiddle.com/#!6/d41d8/21507
declare #table TABLE (value varchar(50))
INSERT INTO #table
VALUES ('first')
INSERT INTO #table
VALUES ('second')
INSERT INTO #table
VALUES (3)
INSERT INTO #table
VALUES (4)
DECLARE #temp TABLE (id int identity(1,1), value varchar(50))
INSERT INTO #temp
SELECT [value]
FROM #table t
SELECT *
FROM #temp
DECLARE #CID TABLE (Region varchar(50), cont varchar(50), another int, andAnother int)
INSERT INTO #CID
(
Region,
cont,
another,
andAnother
)
VALUES
(
(SELECT value FROM #temp WHERE id = 1), -- Region - varchar
(SELECT value FROM #temp WHERE id = 2), -- cont - varchar
(SELECT value FROM #temp WHERE id = 3), -- another - int
(SELECT value FROM #temp WHERE id = 4) -- andAnother - int
)
SELECT * FROM #cid
note that i assumed you're using mssql, you did not specify

SQl Constraint UNIQUE based on other column value

Table A has columns 1 and 2.
Column 1's value must be unique if column 2 is equal to x.
ALTER TABLE A
ADD UNIQUE (1) WHERE 2 = x.
But this gives me a syntax error near WHERE.
I tried to create an index, but I can't figure out how to make that do what I want either.
Create unique nonclustered index [my_index]
on [TableA]([1])
where [2] = x
Here's an alternative solution using a Function although the index is a better solution:
CREATE FUNCTION dbo.CheckConstraint
(
#col1 int,
#col2 CHAR(1)
)
RETURNS INT
AS
BEGIN
DECLARE #ret INT
SELECT #ret = COUNT(*)
FROM YourTable
WHERE col1 = #col1 AND #col2 = 'X'
RETURN #ret
END;
CREATE TABLE YourTable (col1 int, col2 char(1));
ALTER TABLE YourTable
ADD CONSTRAINT CheckForXConstraint CHECK (NOT (dbo.CheckConstraint(col1,col2) > 1));
INSERT INTO YourTable VALUES (1, 'X');
INSERT INTO YourTable VALUES (2, 'X');
INSERT INTO YourTable VALUES (2, 'Y');
INSERT INTO YourTable VALUES (2, 'X'); <-- This line fails
SQL Fiddle Demo