How to convert numeric to nvarchar in SQL Server? - sql

Consider the following table. I use a trigger to add to the table. In the column of converting the number to the string, it fails.
CREATE TABLE [dbo].[tblAIAgent]
(
[AgentCode] [NVARCHAR](10) NOT NULL,
[NationalCode] [BIGINT] NOT NULL
CONSTRAINT [DF_tblAIAgent_NationalCode] DEFAULT ((0)),
[FirstName] [NVARCHAR](50) NOT NULL
CONSTRAINT [DF_tblAIAgent_Name] DEFAULT (''),
[LastName] [NVARCHAR](50) NOT NULL
CONSTRAINT [DF_tblAIAgent_Family] DEFAULT (''),
[IsActive] [BIT] NOT NULL
CONSTRAINT [DF_tblAIAgent_Active] DEFAULT ((1)),
[Counter] [INT] IDENTITY(1,1) NOT NULL,
CONSTRAINT [PK_tblAIAgent]
PRIMARY KEY CLUSTERED
)
ALTER TRIGGER [dbo].[AgentInsert]
ON [dbo].[tblAIAgent]
INSTEAD OF INSERT
AS
BEGIN
DECLARE #AgentCode NVARCHAR(10)
DECLARE #NationalCode BIGINT
DECLARE #FirstName NVARCHAR(50)
DECLARE #LastName NVARCHAR(50)
DECLARE #IsActive BIT
DECLARE #CounterIs INT
SET #CounterIs = ##IDENTITY
SELECT
#AgentCode = AgentCode,
#NationalCode = NationalCode,
#FirstName = FirstName,
#LastName = LastName,
#IsActive = IsActive
FROM inserted
INSERT INTO tblAIAgent (NationalCode, FirstName, LastName, IsActive, AgentCode)
VALUES (#NationalCode, #FirstName, #LastName, #IsActive, 'Agent_' + CAST(#CounterIs AS NVARCHAR(4)))
END

You have a few problems here:
The ##IDENTITY is a system function contains the last identity value that is generated when an INSERT, SELECT INTO, or BULK COPY statement is completed. If the statement did not affect any tables with identity columns, ##IDENTITY returns NULL. If multiple rows are inserted, generating multiple identity values, ##IDENTITY returns the last identity value generated.
In your case, you have an INSTEAD OF INSERT trigger, so there is no INSERT.
This below query is completely wrong and will gives wrong results, it works as expected only if one row inserted, if there is more than 1 row, then those variables will hold just the values of one row, and you will lose the other values of the other rows, cause the pseudo INSERTED may contains 1 or more rows
select #AgentCode=AgentCode,
#NationalCode=NationalCode,
#FirstName=FirstName,
#LastName=LastName,
#IsActive=IsActive
from inserted
Now, looking to your table, you already have an IDENTITY column, so you don't need to a TRIGGER at all, you can just make a computed column as
CREATE TABLE [dbo].[tblAIAgent](
[AgentCode] AS 'Agent_' + CAST([Counter] AS VARCHAR(10)),
[NationalCode] [bigint] NOT NULL
CONSTRAINT [DF_tblAIAgent_NationalCode] DEFAULT ((0)),
[FirstName] [nvarchar](50) NOT NULL
CONSTRAINT [DF_tblAIAgent_Name] DEFAULT (''),
[LastName] [nvarchar](50) NOT NULL
CONSTRAINT [DF_tblAIAgent_Family] DEFAULT (''),
[IsActive] [bit] NOT NULL
CONSTRAINT [DF_tblAIAgent_Active] DEFAULT ((1)),
[Counter] [int] IDENTITY(1,1) NOT NULL,
CONSTRAINT [PK_tblAIAgent] PRIMARY KEY ([Counter])
);
UPDATE:
According to your comment "a computed column can no longer be selected as the PK. I want this column to be placed in other relevant tables as a FK.I wrote the trigger to get the column value instead of the computed column so that I can select the column as the primary key". You are trying to make it a PRIMARY KEY so you can do as
CREATE TABLE T(
Counter INT IDENTITY(1,1) NOT NULL,
OtherCol INT,
Computed AS CONCAT('Agent_', CAST(Counter AS VARCHAR(10))) PERSISTED,
CONSTRAINT PKT PRIMARY KEY(Computed)
);
CREATE TABLE TT(
ReferenceComputedColumn VARCHAR(16) NOT NULL,
OtherColumn INT,
CONSTRAINT FK_ReferencedComputedColumn
FOREIGN KEY(ReferenceComputedColumn)
REFERENCES T(Computed)
)
INSERT INTO T(OtherCol) VALUES
(1), (2), (3);
INSERT INTO TT(ReferenceComputedColumn, OtherColumn) VALUES
('Agent_1', 10),
('Agent_3', 20);
SELECT *
FROM T LEFT JOIN TT
ON T.Computed = TT.ReferenceComputedColumn;
See how it's working.
See also this article Properly Persisted Computed Columns by Paul White.

Try this
SELECT CONVERT(NVARCHAR(255), #AgentCode)

SELECT CAST([PictureId] AS NVARCHAR(4)) From Category

Related

Replacing a trigger with a stored procedure

I'm trying to replace a trigger statement with a stored procedure since enabled triggers are not allowed when using the tables in microsoft powerapps.
Simplified, I have to tables:
KPI_Dim (KPI_ID [PK] , KPIName, KPIGroup...)
KPICurrent_Fact (KPI_key [FK i.e KPI_Dim[KPI_ID], KPICurrent_ID, KPI_Value...)
Currently, for every new record in KPI_Dim my trigger adds a new row in KPICurrent_Fact with the FK and an autoincremented PK. The rest of the columns e.g. KPI_Value are supposed to be empty.
My simple trigger looks like this:
CREATE TRIGGER [dbo].[trg_insert_newKPI]
ON [dbo].[KPI_Dim]
FOR INSERT AS
INSERT INTO KPICurrent_Fact (KPI_key)
SELECT KPI_ID
FROM INSERTED
Now, I want to create a stored procedure that can achieve exactly the same. I have tried to find a solution myself but I'm new to stored procedures and could not find anything that would replicate a trigger.
I'm using SSMS v.18.4.
Thank you for any suggestions.
EDIT
Added Table creation and insert into statement code.
/* Create KPI_Dim table*/
CREATE TABLE [dbo].[KPI_Dim](
[KPI_ID] [int] IDENTITY(1,1) PRIMARY KEY NOT NULL,
[KPIName] [varchar](200) NOT NULL,
[KPIDescription] [varchar](500) NULL,
[KPIGroup] [varchar](100) NOT NULL,
[KPISubGroup] [varchar](100) NULL,
[KPIOwner] [varchar] (50) NOT NULL,
[DateCreated] DATETIME NULL DEFAULT(GETDATE())
)
/* Example data */
INSERT INTO [dbo].[KPI_Dim]
(
KPIName,
KPIDescription,
KPIGroup,
KPISubGroup,
KPIOwner
)
VALUES
('TestKPIName','testtest','TestGroup', 'TestSubGroup', 'TestOwner');
You can go for OUTPUT Clause and insert into table variable. From the table variable, you can insert into fact table.
CREATE TABLE [dbo].[KPI_Dim](
[KPI_ID] [int] IDENTITY(1,1) PRIMARY KEY NOT NULL,
[KPIName] [varchar](200) NOT NULL,
[KPIDescription] [varchar](500) NULL,
[KPIGroup] [varchar](100) NOT NULL,
[KPISubGroup] [varchar](100) NULL,
[KPIOwner] [varchar] (50) NOT NULL,
[DateCreated] DATETIME NULL DEFAULT(GETDATE())
)
CREATE TABLE dbo.KPI_Fact
(
[KPI_ID] [int] IDENTITY(1,1) PRIMARY KEY NOT NULL,
[KPIDIMID] INT NULL FOREIGN KEY references [dbo].[KPI_Dim]([KPI_ID])
)
DECLARE #inserted table(KPI_DIMID INT)
INSERT INTO [dbo].[KPI_Dim]
(
KPIName,
KPIDescription,
KPIGroup,
KPISubGroup,
KPIOwner
)
OUTPUT inserted.KPI_ID INTO #inserted
VALUES
('TestKPIName','testtest','TestGroup', 'TestSubGroup', 'TestOwner');
INSERT INTO dbo.KPI_Fact([KPIDIMID])
SELECT * FROM #inserted
KPI_ID
KPIDIMID
1
1

Cannot insert the value NULL into column 'Id' although Id is set

I have a strange problem.
I want to insert an item to a table from database. I use Entity Framework.
Although the Id is set, I keep getting the following error:
Cannot insert the value NULL into column 'Id', table 'project_atp.dbo.ShoppingCarts'; column does not allow nulls. INSERT fails.\r\nThe statement has been terminated."}
The table definition:
CREATE TABLE [dbo].[ShoppingCarts] (
[Id] INT NOT NULL,
[Guid] UNIQUEIDENTIFIER NULL,
[Name] NVARCHAR (255) NULL,
[Code] NVARCHAR (255) NULL,
[SupplierNo] NVARCHAR (255) NULL,
[SupplierName] NVARCHAR (255) NULL,
[Price] NVARCHAR (50) NULL,
[Quantity] INT NULL,
CONSTRAINT [PK_ShoppingCarts] PRIMARY KEY CLUSTERED ([Id] ASC)
);
Can you please advise what could be wrong here! Thanks!
By default Entity Framework assumes that an integer primary key is database generated. As the result Entity Framework would not include Primary Key field in the actual INSERT statement.
I would try to either play along and ALTER the table to auto-generate the ID (which judging by your comment you did)
or set StoreGeneratedPattern property of OnlineCarStore.Models.ShoppingCarts Id column to 'None'
or use annotation: [DatabaseGenerated(DatabaseGeneratedOption.None)].

SQL Trigger on Insert to call a Function and Manipulate Values

I am having a problem knowing set up a trigger on insert to call a function and passing an identifier SiteID to that function which then operates on variables from another table.
I have the table, where values are inserted:
CREATE TABLE [dbo].[Tests] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[SiteID] NVARCHAR (128) NOT NULL,
[Date] NVARCHAR (MAX) NULL,
[Time] NVARCHAR (MAX) NULL,
[Tub] NVARCHAR (MAX) NULL,
[FCl] FLOAT (53) NOT NULL
PRIMARY KEY CLUSTERED ([Id] ASC)
);
Every time a value is inserted in to the Tests table I would like a function to be called which uses the table Review to perform a simple calculation (see the trigger down the post below):
CREATE TABLE [dbo].[Review] (
[SiteID] NVARCHAR (128) NOT NULL,
[TubNum] INT NOT NULL,
[Supplied] INT NULL,
[Performed] INT NULL,
[Remaining] INT NULL
PRIMARY KEY CLUSTERED ([SiteID] ASC)
);
This is my sqlfiddle so far, however, I am unable to save it with the functions I am working on, which are here:
The trigger - not sure how to pass the SiteID for the insert or call the function:
CREATE TRIGGER [dbo].[TRIG_MyTable]
ON [dbo].[Tests]
AFTER INSERT
AS
CALL CalcRemaining() //how to pass it SiteID?
and the the function itself:
CREATE FUNCTION [dbo].[CalcRemaining](#ID NVARCHAR (128))
RETURNS NULL
AS
BEGIN
SELECT (Supplied, Performed FROM Review WHERE SiteID = ID);
Performed = Performed + 1;
INSERT INTO Review (Performed, Remaining) VALUES (Performed, (Supplied-Performed))
RETURN;
END
The idea is that the SiteID from the inserted line is passed to the function when called, the function then selects the values Supplied and Performed for that matching SiteID from the Review table. The Performed value is incremented by 1 and that new value along with the retrieved Supplied value are subtracted and written to the Remaining field in the Review table.
If the assumptions I've asked about in the comments are valid (that Remaining and Performed can always be calculated), here's how I'd implement your database structure, with no trigger nor function:
Base tables:
CREATE TABLE dbo.Tests (
[Id] INT IDENTITY (1, 1) NOT NULL,
[SiteID] NVARCHAR (128) NOT NULL,
[Date] NVARCHAR (MAX) NULL,
[Time] NVARCHAR (MAX) NULL,
[Tub] NVARCHAR (MAX) NULL,
[FCl] FLOAT (53) NOT NULL
PRIMARY KEY CLUSTERED ([Id] ASC)
);
GO
CREATE TABLE dbo._Review (
[SiteID] NVARCHAR (128) NOT NULL,
[TubNum] INT NOT NULL,
[Supplied] INT NULL,
PRIMARY KEY CLUSTERED ([SiteID] ASC)
);
Then a view that calculates Performed:
CREATE VIEW dbo._Tests_Count
WITH SCHEMABINDING
AS
SELECT
SiteID,
COUNT_BIG(*) as Performed
FROM
dbo.Tests t
GROUP BY SiteID
GO
CREATE UNIQUE CLUSTERED INDEX IX_Tests_Count ON dbo._Tests_Count (SiteID)
And finally a view that re-creates the original Review table:
CREATE VIEW dbo.Review
WITH SCHEMABINDING
AS
SELECT
r.SiteID,
r.TubNum,
r.Supplied,
COALESCE(tc.Performed,0) as Performed,
r.Supplied - COALESCE(tc.Performed,0) as Remaining
FROM
dbo._Review r
left join
dbo._Tests_Count tc WITH (NOEXPAND)
on
r.SiteID = tc.SiteID
GO
If needed, at this point a trigger could be created on this Review view to allow any INSERTs, DELETEs and UPDATEs to be performed against it rather than _Review, if you cannot change some calling code.
You have to be completely sure you're manipulating ONLY one row at a time!
CREATE TRIGGER [dbo].[TRIG_MyTable]
ON [dbo].[Tests]
AFTER INSERT
AS
DECLARE #SiteID NVARCHAR (128)
SELECT #SiteID = ID FROM inserted
CALL CalcRemaining(#SiteID)

If the field was essentially changed, then change the value of one of its column

I have the essence of the "Requirements". If the user has changed the field "Description" in this instance, it should automatically change the value of the field "Stability" (dropdownlist). That is, if you had to change the description of the requirements, it becomes unstable. I wrote a trigger
CREATE TRIGGER [ChangeStabilityRequirement]
ON [dbo].[Requirement] AFTER UPDATE
AS
BEGIN
if update([Definition])
begin
update [Requirement] set Stability = 2
where RequirementId in (select RequirementId from inserted)
end
end
But the problem is that the trigger is activated when any change entries in the table. A need to respond only to changes in the column "Definition".
MY TABLE
CREATE TABLE [dbo].[Requirement] (
[RequirementId] INT IDENTITY (1, 1) NOT NULL,
[Rationale] NVARCHAR (MAX) NULL,
[CreatedOn] DATE CONSTRAINT [DF__Requireme__Creat__473C8FC7] DEFAULT (getdate()) NULL,
[CurentVersion] NVARCHAR (MAX) NULL,
[State] INT NOT NULL,
[Priority] INT NOT NULL,
[Type] INT NOT NULL,
[Source_BusinessRuleId] INT NULL,
[Stability] INT NOT NULL,
[UserPart] NVARCHAR (MAX) NULL,
[CreatedBy_UserId] INT NULL,
[Responsible_UserId] INT NULL,
[ImplementationVersion] NVARCHAR (MAX) NULL,
[ResponsibleId] INT DEFAULT ((0)) NULL,
[SourceId] INT DEFAULT ((0)) NULL,
[InterfacePoint_InterfacePointId] INT NULL,
[InterfacePointId] INT DEFAULT ((0)) NULL,
[CreatedById] INT DEFAULT ((0)) NULL,
[Definition] NVARCHAR (MAX) DEFAULT ('') NOT NULL,
CONSTRAINT [PK_dbo.Requirement] PRIMARY KEY CLUSTERED ([RequirementId] ASC),
CONSTRAINT [FK_dbo.Requirement_dbo.User_CreatedById] FOREIGN KEY ([CreatedById]) REFERENCES [dbo].[User] ([UserId]),
CONSTRAINT [FK_dbo.Requirement_dbo.User_ResponsibleId] FOREIGN KEY ([ResponsibleId]) REFERENCES [dbo].[User] ([UserId]),
CONSTRAINT [FK_dbo.Requirement_dbo.BusinessRule_SourceId] FOREIGN KEY ([SourceId]) REFERENCES [dbo].[BusinessRule] ([BusinessRuleId])
);
Update table
Updating occurs in a web application interface
This is query from SQL SERVER Profile
exec [dbo].[Requirement_Update]
#RequirementId=32,
#Definition=N'Реализовать возможность выбора значения "Без пени" в поле "Тип начисления пени". Диалоговое окно АР-12',
#Rationale=N'В соответствии с изменением бизнес-правила',
#CreatedOn='2014-12-18 00:00:00',
#CurentVersion=N'2.1',
#ImplementationVersion=N'2.2',
#State=0,
#Priority=1,
#Stability=1,
#Type=0
Ok, if you need update Requirement only when Definition is updated then change your trigger as:
CREATE TRIGGER [ChangeStabilityRequirement]
ON [dbo].[Requirement] AFTER UPDATE
AS
BEGIN
if update([Definition]) AND NOT update([Rationale]) AND NOT update([CurentVersion])
--AND NOT UPDATE(.....list here all possible columns that may be updated
begin
update [Requirement] set Stability = 2
where RequirementId in (select RequirementId from inserted)
end
end

SQL Server IDENTITY column

How can I modify this command in order to have an identity column which has five digits integer like 00000 and start from 00001 ?
CREATE TABLE [dbo].[Company]
(
[CompanyId] [bigint] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](200) NOT NULL
)
An integer does not have any leading 0 by itself. It is a formatting issue to deal with when converting the integer to a string for displaying.
If you really, really need to be able to present such a string right out of SQL, you can do it with a computed column:
CREATE TABLE [dbo].[Company](
[CompanyId] [bigint] IDENTITY(1,1) NOT NULL,
[FormattedCompanyId] AS RIGHT('0000'+ CONVERT(VARCHAR,Num),5),
[Name] nvarchar NOT NULL,
I would never use that solution myself though, formatting doesn't belong in the data store.
You need to add the leading zeros yourself. As a solution you can add an other colomn named say "formatedID" and update it with an "after insert trigger" with the value from the identity column and formatted with the leading zeros you want to.
Example :
CREATE TABLE [dbo].[Company]
(
[CompanyId] [bigint] IDENTITY(1,1) NOT NULL,
[FormattedID] [VARCHAR(20)],
[Name] [nvarchar](200) NOT NULL
)
CREATE TRIGGER ON [dbo].[Company]
FOR INSERT
AS
BEGIN
UPDATE [dbo].[Company]
FROM inserted
SET FormattedID = RIGHT('0000'+ CONVERT(VARCHAR, [dbo].[Company].CompanyId),5)
WHERE dbo.Company.CompanyId = inserted.CompanyId
END