replace a computed column with a logic that works with INSERT - sql

I have a table called tblPacks.
CREATE TABLE [dbo].[tblPacks]
(
[ID] [int] NOT NULL,
[BatchNumber] [varchar](30) NULL,
[PackID] VARCHAR(50),
[Status] [int] NULL
)
And a stored procedure spInsertPacks.
CREATE PROCEDURE spInsertPacks
#ID INT,
#BatchNumber VARCHAR(30),
#Count INT
AS
BEGIN
INSERT INTO tblPacks
Values
(
#ID,
#BatchNumber,
CONVERT([varchar](50),
'PK'+
case
when len(#ID)<=(3) then CONVERT([varchar](20),right((0.001)*#ID,(3)),0)
else CONVERT([varchar](20),#ID,0)
end,0),0)
END
If ID of data type INT inserted in an order like 1,2,3,4,5... the above logic works fine. But there is no restriction for a user to enter random numbers. I want a stored procedure to generate PackID(PK001,PK002..) sequence in order, irrespective of #ID and ID. Cannot be an identity Column. How can I do that?
Actually This PackID is a barcode If barcode already existed for Pack then that sequence may not be same with the sequence we used and Newly generated barcodes which we are generating will be in seuquence PK001
Sample Output:-
ID BatchNumber PackID Status
1 b1 PK001 0
1 b2 Pk002 0
5 b7 ABC768 0
3 b2 PK003 0

I have simplified the logic a bit for generating PackID
Add a new column(identifier) for identifying the code and use it for PackID generation and for sequence use Identity column
CREATE TABLE [dbo].[tblPacks]
(
Iden_ID INT IDENTITY(1, 1),
[ID] [INT] NOT NULL,
[BatchNumber] [VARCHAR](30) NULL,
[Identifier] [VARCHAR](50),
[PackID] AS [Identifier]
+ CASE
WHEN Iden_ID <= 999 THEN RIGHT('00' + CONVERT(VARCHAR(3), ID), 3)
ELSE CONVERT([VARCHAR](20), ID, 0)
END,
[Status] [INT] NULL
)
To check the working
INSERT INTO [dbo].[tblPacks]
([ID],identifier,[BatchNumber],[Status])
VALUES (1,'pk','bat',1)
SELECT *
FROM [tblPacks]

Related

How to insert data in multiple tables using single query in SQL Server?

I'm trying to insert data into multiple tables if it doesn't already exist. I can't seem to figure this out at all.
Table 1:
CREATE TABLE [dbo].[search_results]
(
[company_id] [int] NULL,
[title] [text] NULL,
[link] [text] NULL,
[domain] [text] NULL,
[index] [int] NULL,
[id] [int] PRIMARY KEY IDENTITY(1,1) NOT NULL
)
Table 2:
CREATE TABLE [dbo].[statements]
(
[statement_link_id] [int] NULL,
[statement_page] [text] NULL,
[statement_text_location] [text] NULL,
[statement_description] [text] NULL,
[statement_description_html] [text] NULL,
[statement] [int] NULL,
[id] [int] PRIMARY KEY IDENTITY(1,1) NOT NULL
)
This is what I want to do:
check to see if the company_id and the link already exist in the table or not.
SELECT *
FROM search_results
WHERE company_id = 4 AND link = 'https://test.com';
If the data does not exist, insert it into two tables
INSERT INTO search_results (company_id, link, title, domain)
VALUES (4, 'https://test.com', 'title', 'test.com');
and also insert the search_result last inserted id to the following table. corporate_statement value is always 1
INSERT INTO corporate_statements (statement_link_id, corporate_statement)
VALUES (743, 1);
I'm trying this based on what I found on SO
DECLARE #result AS TABLE (id int, company_id int, link text, title text, domain text);
WITH cte AS
(
SELECT *
FROM (VALUES (4, 'https://test.com', null, null)) AS t(company_id, link, title, domain)
)
INSERT INTO #result
SELECT *
FROM
(INSERT INTO dbo.search_results (company_id, link, title, domain)
OUTPUT inserted.*
SELECT * FROM cte
WHERE NOT EXISTS (SELECT * FROM dbo.[search_results]
WHERE company_id = cte.company_id
AND CAST(link AS varchar(250)) = CAST(cte.link AS varchar(50))
)) r
SELECT * FROM #result;
Even trying with a single insert statement, I get the following error:
Msg 213, Level 16, State 1, Line 8
Column name or number of supplied values does not match table definition.
As you can see, I also tried to cast it to varchar since it was throwing error when I hadn't. How can update this?
To me - this seems a lot cleaner, and it also will be a lot simpler to understand (and maintain!) in the future:
-- check to see if your data already exists
IF NOT EXISTS (SELECT *
FROM search_results
WHERE company_id = 4 AND link = 'https://test.com')
BEGIN TRY
BEGIN TRANSACTION
-- if not -> insert into the first table
INSERT INTO search_results (company_id, link, title, domain)
VALUES (4, 'https://test.com', 'title', 'test.com');
-- grab the last identity value from that previous INSERT
DECLARE #LastId INT;
SELECT #LastId = SCOPE_IDENTITY();
-- insert into the second table
INSERT INTO corporate_statements (statement_link_id, corporate_statement)
VALUES (#LastId, 1);
COMMIT;
END TRY
BEGIN CATCH
-- in case of an error rollback the full transaction
ROLLBACK;
END CATCH;
and you're done. Or am I missing something? I think this would be doing what you're described in the intro of your post - not necessarily what you're showing in your code...

Create trigger for first letter of first name and last name in SQL Server

I want write a trigger which will change from lower case to upper case in the name and surname.In my table klient where imie = name , and nazwisko = surname. How to do it in SQL Server?
CREATE TABLE [dbo].[klient]
(
[id_klient] [int] IDENTITY(1,1) NOT NULL,
[id_silownia] [int] NOT NULL,
[imie] [varchar](50) NOT NULL,
[nazwisko] [varchar](50) NOT NULL,
[telefon] [varchar](50) NULL,
)
You'll need something like this:
-- create the trigger, on table "klient", after insert
CREATE TRIGGER trg_uppercase
ON dbo.klient
AFTER INSERT
AS
BEGIN
-- update the table dbo.Klient
UPDATE k
-- set "imie" to be first letter in UPPER(), rest as is
SET imie = UPPER(SUBSTRING(i.imie, 1, 1)) + SUBSTRING(i.imie, 2, 999),
nazwisko = UPPER(SUBSTRING(i.nazwisko, 1, 1)) + SUBSTRING(i.nazwisko, 2, 999)
FROM dbo.klient k
-- join with "pseudo-table" Inserted to update only what was freshly inserted
INNER JOIN Inserted i ON k.id_klient = i.id_klient;
END
This still has some "rough edges" to iron out - e.g. what if your name is longer than 999 characters? How will it handle NULL values being inserted?
But it should be a decent starting point to get a feeling for how to write triggers and what you can do with them.

Get Identity of destination table when using **DELETE FROM ... OUTPUT ... INTO**

I use bellow code to archive old data in ArchiveTable and delete archived data from SourceTable
DELETE FROM SourceTable
OUTPUT
DELETED.[ID],
DELETED.[Code],
DELETED.[Title]
INTO ArchiveTable([OldID], [Code], [Title])
WHERE Condition
Structure of tables:
CREATE TABLE [SourceTable](
[ID] [INT] IDENTITY(1,1) NOT NULL,
[Code] [VARCHAR](16) NULL,
[Title] [NVARCHAR](128) NULL,
CONSTRAINT [PK_SourceTable] PRIMARY KEY CLUSTERED ([ID] ASC)
)
GO
CREATE TABLE [ArchiveTable](
[ID] [INT] IDENTITY(1,1) NOT NULL,
[OldID] [INT] NOT NULL,
[Code] [VARCHAR](16) NULL,
[Title] [NVARCHAR](128) NULL,
CONSTRAINT [PK_ArchiveTable] PRIMARY KEY CLUSTERED ([ID] ASC)
)
GO
I need to return deleted records and ArchiveTable.[ID] to application. I change the code like this:
DELETE FROM SourceTable
OUTPUT
DELETED.[ID],
DELETED.[Code],
DELETED.[Title]
INTO ArchiveTable([OldID], [Code], [Title])
OUTPUT DELETED.*
WHERE Condition
This code return deleted records but I don't know how to get ID of ArchiveTable for this records. Look at ArchiveTable structure, It has OldID column that refer to SourceTable.ID and ID column that it is an Identity column of ArchiveTable. I need to ArchiveTable.ID in final result.
You can use a temporary table
CREATE TABLE #DeletedRows(
[ID] [INT] NOT NULL,
[Code] [VARCHAR](16) NULL,
[Title] [NVARCHAR](128) NULL
)
DELETE SourceTable
OUTPUT
DELETED.[ID],
DELETED.[Code],
DELETED.[Title]
INTO #DeletedRows([ID], [Code], [Title])
WHERE Condition
INSERT ArchiveTable([OldID], [Code], [Title])
OUTPUT INSERTED.*
SELECT [ID], [Code], [Title]
FROM #DeletedRows
DROP TABLE #DeletedRows
A variant with a table variable
DECLARE #DeletedRows TABLE(
[ID] [INT] NOT NULL,
[Code] [VARCHAR](16) NULL,
[Title] [NVARCHAR](128) NULL
)
DELETE SourceTable
OUTPUT
DELETED.[ID],
DELETED.[Code],
DELETED.[Title]
INTO #DeletedRows([ID], [Code], [Title])
WHERE Condition
INSERT ArchiveTable([OldID], [Code], [Title])
OUTPUT INSERTED.*
SELECT [ID], [Code], [Title]
FROM #DeletedRows
I found an interesting variant using DML with OUTPUT in SP and INSERT...EXEC... after that:
Test tables:
CREATE TABLE TestTable(
ID int NOT NULL PRIMARY KEY,
Title varchar(10) NOT NULL
)
CREATE TABLE TestTableLog(
LogID int NOT NULL IDENTITY,
OperType char(1) NOT NULL,
CHECK(OperType IN('I','U','D')),
ID int NOT NULL,
Title varchar(10) NOT NULL
)
DML procedures:
CREATE PROC InsTestTable
#ID int,
#Title varchar(10)
AS
INSERT TestTable(ID,Title)
OUTPUT inserted.ID,inserted.Title,'I' OperType
VALUES(#ID,#Title)
GO
CREATE PROC UpdTestTable
#ID int,
#Title varchar(10)
AS
UPDATE TestTable
SET
Title=#Title
OUTPUT inserted.ID,inserted.Title,'U' OperType
WHERE ID=#ID
GO
CREATE PROC DelTestTable
#ID int
AS
DELETE TestTable
OUTPUT deleted.ID,deleted.Title,'D' OperType
WHERE ID=#ID
GO
Tests:
-- insert test
INSERT TestTableLog(ID,Title,OperType)
EXEC InsTestTable 1,'A'
INSERT TestTableLog(ID,Title,OperType)
EXEC InsTestTable 2,'B'
INSERT TestTableLog(ID,Title,OperType)
EXEC InsTestTable 3,'C'
-- update test
INSERT TestTableLog(ID,Title,OperType)
EXEC UpdTestTable 2,'BBB'
-- delete test
INSERT TestTableLog(ID,Title,OperType)
EXEC DelTestTable 3
GO
-- show resutls
SELECT *
FROM TestTableLog
Maybe it'll be interesting to someone.

Table-Valued Function using IF statement in SQL Server

I have a Student table consists of following parameters
[ID] [nvarchar](50) NOT NULL,
[Firsname] [nvarchar](50) NOT NULL,
[Lastname] [nvarchar](50) NOT NULL,
[Melicode] [nchar](10) NOT NULL,
[City] [nvarchar](50) NOT NULL,
[Province] [nvarchar](50) NOT NULL,
[Active] [int] NULL
i want to write a Table-Valued Function named Show which has one parameter as number. the function will act as following
if #number = 1 , returns all columns from Student table
if #number = 2 , returns only City from Student
if #number = 3 , returns only Province from Student
i wrote the following T-SQL, but it only works for (if (#number = 1)). When the user enter #number as 2 or 3, the function does not work. Thank You
Create function Show(#number int)
RETURNS #result TABLE
(
[ID] [nvarchar](50) NOT NULL,
[Firsname] [nvarchar](50) NOT NULL,
[Lastname] [nvarchar](50) NOT NULL,
[Melicode] [nchar](10) NOT NULL,
[City] [nvarchar](50) NOT NULL,
[Province] [nvarchar](50) NOT NULL,
[Active] [int] NULL
)
AS
BEGIN
IF (#number = 1)
INSERT INTO #result SELECT * from Student
IF (#number = 2)
INSERT INTO #result (City) values ((SELECT City from Student))
IF (#number = 3)
INSERT INTO #result (Province) values ((SELECT Province from Student))
RETURN -- #Players (variable only required for Scalar functions)
END
go
select *from dbo.show(1)
This is not going to work:
INSERT INTO #result (City)
VALUES ((SELECT City from Student))
Either you have all the values as scalar SQL variables, or literals - then you can use
INSERT INTO #result (City)
VALUES ('New York')
INSERT INTO #result (City)
VALUES (#ChosenCity)
or you have a SELECT statement to fill the values - then you need this syntax:
INSERT INTO #result (City)
SELECT City
FROM Student
without the VALUES keyword. And as #GiorgiNakeuri correctly states - this will then fail because all your columns require a value (have the NOT NULL attribute), so this insert cannot succeed - you need to provide all NOT NULL values (or define a default value for each column)
CREATE FUNCTION dbo.Show
(
#number INT
)
RETURNS #result TABLE
(
ID NVARCHAR(50),
Firsname NVARCHAR(50),
Lastname NVARCHAR(50),
Melicode NCHAR(10),
City NVARCHAR(50),
Province NVARCHAR(50),
Active INT
)
AS
BEGIN
IF (#number = 1)
INSERT INTO #result
SELECT * FROM dbo.Student
IF (#number = 2)
INSERT INTO #result (City)
SELECT City FROM dbo.Student
IF (#number = 3)
INSERT INTO #result (Province)
SELECT Province FROM dbo.Student
RETURN
END
GO
SELECT * FROM dbo.Show(2)
the table returned is dictated by how the result table was declared. the query below works (in a sense) but the results include all the columns with NULLs for those columns not targeted by the #number parameter:
CREATE TABLE dbo.z_Show (str1 VARCHAR(10), str2 VARCHAR(10), str3 VARCHAR(10))
INSERT z_show
SELECT 1, 1, 1 UNION ALL
SELECT 2, 2, 2 UNION ALL
SELECT 3, 3, 3
CREATE FUNCTION dbo.Show(#number int)
RETURNS #result TABLE
(
--[ID] [nvarchar](50) NOT NULL,
--[Firsname] [nvarchar](50) NOT NULL,
--[Lastname] [nvarchar](50) NOT NULL,
--[Melicode] [nchar](10) NOT NULL,
--[City] [nvarchar](50) NOT NULL,
--[Province] [nvarchar](50) NOT NULL,
--[Active] [int] NULL
str1 VARCHAR(10), str2 VARCHAR(10), str3 VARCHAR(10)
)
AS
BEGIN
--for debugging|start
--DECLARE #number INT = 3
--DECLARE #result TABLE (str1 VARCHAR(10), str2 VARCHAR(10), str3 VARCHAR(10))
--for debugging|end
IF (#number = 1)
BEGIN
--PRINT ('IF (#number = 1)')
INSERT INTO #result SELECT * from dbo.z_Show
END
IF (#number = 2)
BEGIN
--PRINT ('IF (#number = 2)')
INSERT INTO #result (str2) SELECT str2 from dbo.z_Show
END
IF (#number = 3)
BEGIN
--PRINT ('IF (#number = 3)')
INSERT INTO #result (str3) SELECT str3 from dbo.z_Show
END
RETURN -- #Players (variable only required for Scalar functions)
END
SELECT 'number 1 was passed', *
FROM dbo.show(1)
SELECT 'number 2 was passed', *
FROM dbo.show(2)
SELECT 'number 3 was passed', *
FROM dbo.show(3)
You mentioned #result has all NOT NULL columns. If you want to insert only city into that #result, it will take remaining columns as Null so that's why an error happened. You don't mention that #result columns are NOT NULL columns and one more is. Remove VALUES keyword from the INSERT statement because it is inserting with a Select statement
The insert statements for cases 2 and 3 are incorrect. No need for VALUES keyword when inserting values coming from a select statement.

Create table and get data from another table

I have a table Cost category:
CREATE TABLE [dbo].[CostCategory](
[ID_CostCategory] [int] NOT NULL,
[Name] [varchar](150) NOT NULL,
[Plan] [money] NOT NULL,
[Realization] [money] NULL,
)
go
and I have another table with defined Costs:
CREATE TABLE [dbo].[Cost](
[ID_Cost] [int] NOT NULL,
[Name] [varchar](50) NULL,
[ID_CostCategory] [int] NULL,
[ID_Department] [int] NULL,
[ID_Project] [int] NULL,
[Value] [money] NULL,
)
go
What I want to do is to sum values from Cost table (according to ID_CostCategory) and put into Cost Category table, Realization column. So each ID_CostCategory presents automaticaly sum of Costs from Cost table (per ID_CostCategory).
How can I modify the script of Cost Category table to achieve it? Probably it's not a rocket science but I am really new to SQL.
OK. One more thing I forgot to add...
the structure looks in a way:
Cost>Cost Category>Department>Project
Right now I can easily take data from cost table and present it in Cost Category table. But Cost table includes such data as:
ID_Cost Name ID_CostCategory ID_Department ID_Project Value
1 fv 001 1 1 1 100
2 fv 002 2 1 1 500
3 fv 003 2 2 1 300
4 fv 004 3 2 2 150
5 fv 005 3 3 2 30
6 fv 006 4 3 2 15
I have also table Department which includes colums: ID_Department, Name, Plan,
So now, I want to do is to sum values from Cost table (according to ID_CostCategory and ID_Department) and put into Department table as Realization column. So each ID_Department presents automaticaly sum of Costs from Cost table (per ID_CostCategory and ID_Department).
Hope it is clear (later on will have to do it with table Project, but once I got it, it will be easy)
Try this
--create table without realization column
CREATE TABLE [dbo].[CostCategory](
[ID_CostCategory] [int] NOT NULL,
[Name] [varchar](150) NOT NULL,
[Plan] [money] NOT NULL
) go
CREATE TABLE [dbo].[Cost](
[ID_Cost] [int] NOT NULL,
[Name] [varchar](50) NULL,
[ID_CostCategory] [int] NULL,
[ID_Department] [int] NULL,
[ID_Project] [int] NULL,
[Value] [money] NULL,
) go
Create a UDF to calculate sum of the cost column:
CREATE FUNCTION [dbo].[CalculateRealization](#Id INT)
RETURNS money
AS
BEGIN
DECLARE #cost money
SELECT #cost = SUM(Value)
FROM [dbo].[Cost]
WHERE [ID_CostCategory] = #ID
return #cost
END
Now Alter your CostCategory table to add computed column:
ALTER TABLE [dbo].[CostCategory]
ADD [Realization] AS dbo.CalculateRealization(ID_CostCategory);
Now you can select Realization from Costcategory
SELECT ID_CostCategory, Realization
FROM [dbo].[CostCategory]
Answer to your comment below:
Create Another UDF
CREATE FUNCTION [dbo].[CheckValue](#Id INT, #value Money)
RETURNS INT
AS
BEGIN
DECLARE #flg INT
SELECT #flg = CASE WHEN [Plan] >= #value THEN 1 ELSE 0 END
FROM [dbo].[CostCategory]
WHERE [ID_CostCategory] = #ID
return #flg;
END
Now add Constraint on Cost Table:
ALTER TABLE ALTER TABLE [dbo].[Cost]
ADD CONSTRAINT CHK_VAL_PLAN_COSTCATG
CHECK(dbo.CheckValue(ID_CostCategory, Value) = 1)
You do not need to have a Realization column as part of the CostCategory table. Rather, you will want to use a join.
Select A.ID_CostCategory, A.Name, SUM(B.Value) As Realization from CostCategory A
JOIN Cost B ON A.ID_CostCategory = B.ID_CostCategory
Group By A.ID_CostCategory, A.Name