serialising rows in a table - sql-server-2008

I have a table which contains header information for transactions. The transactions belong to different projects.
In the header I have columns:
rhguid - uniqueidentifier
rhserial - int
rh_projectID - int
First I insert the row (there's more columns)
Then I calculate the serial number for that project:
update responseheader
set rhSerial = 1 + (select isnull(max(rhSerial), 0)
from responseheader
where (rhstatus = 0) AND (rh_projectID = 1234))
where
(rhGUID = <preassignedGUID>);
However when there are many transactions happening at the same time for a project I am finding duplicate rhserial values.
I'm doing this in classic ASP with SQL Server 2008.
Is there a better way?

From your example, it doesn't look like you're using a transaction. My guess is that the SELECT portion of the statement is running as READ UNCOMMITTED, otherwise you would not see duplicates. There are ways to start transactions with ADO, but I prefer using stored procedures instead.
Try implementing something like this:
CREATE PROC dbo.ResponseHeader_Insert
<more data to insert>,
#ProjectID INT,
#Status SMALLINT
as
insert responseheader (column names here)
select <param values here>, isnull(max(rhSerial), 0) + 1
from responseheader
where (rhstatus = #Status) AND (rh_projectID = #ProjectID))
If this doesn't work for ya, try creating sequence tables (one for each sequence).
create table <tablename> (
SeqID int identity(1,1) primary key,
SeqVal varchar(1)
)
Create a procedure to get the next identity:
create procedure GetNewSeqVal_<tablename>
as
begin
declare #NewSeqValue int
set NOCOUNT ON
insert into <tablename> (SeqVal) values ('a')
set #NewSeqValue = scope_identity()
delete from <tablename> WITH (READPAST)
return #NewSeqValue
end
If there are too many sequence tables that need to be created or you want to create sequences on the fly, try this approach:
Create table AllSequences (
SeqName nvarchar(255) primary key, -- name of the sequence
Seed int not null default(1), -- seed value
Incr int not null default(1), -- incremental
Currval int
)
Go
create procedure usp_CreateNewSeq
#SeqName nvarchar(255),
#seed int = 0,
#incr int = 1
as
begin
declare #currval int
if exists (
select 1 from AllSequences
where SeqName = #SeqName )
begin
print 'Sequence already exists.'
return 1
end
if #seed is null set #seed = 1
if #incr is null set #incr = 1
set #currval = #seed
insert into AllSequences (SeqName, Seed, Incr, CurrVal)
values (#SeqName, #Seed, #Incr, #CurrVal)
end
go
create procedure usp_GetNewSeqVal
#SeqName nvarchar(255)
as
begin
declare #NewSeqVal int
set NOCOUNT ON
update AllSequences
set #NewSeqVal = CurrVal = CurrVal+Incr
where SeqName = #SeqName
if ##rowcount = 0 begin
print 'Sequence does not exist'
return
end
return #NewSeqVal
end
go

Related

How to write an update statement to update columns and check for prime numbers?

I am just learning to code SQL. Can someone help me with an update statement, I have done the insert but am stuck with an update. My table prime_test has two columns number and is_prime
Here is the question:
Write one or more update statements that sets the 'is_prime' column for each associated integer. For a prime, the column should read 'prime'. For non-primes, the column should read 'composite'.
My code
CREATE TABLE `sql_inventory`.`prime_test` (
`number` INT NOT NULL,
`is_prime` VARCHAR(50) NULL,
PRIMARY KEY (`number`));
INSERT INTO `prime`.`prime_test`
(`number`)
VALUES
(rand()*(100-2+1)+2);
Function to find the prime numbers:
CREATE FUNCTION isPrime(#num int)
RETURNS VARCHAR(50)
BEGIN
DECLARE #prime_or_notPrime INT
DECLARE #counter INT
DECLARE #retVal VARCHAR(50)
SET #retVal = 'composite'
SET #prime_or_notPrime = 1
SET #counter = 2
WHILE (#counter <= #number/2 )
BEGIN
IF (( #number % #counter) = 0 )
BEGIN
set #prime_or_notPrime = 0
BREAK
END
IF (#prime_or_notPrime = 1 )
BEGIN
SET #retVal = 'prime'
END
SET #counter = #counter + 1
END
return #retVal
END
update prime_test
set is_prime = dbo.isPrime(select number From 'prime'.'prime_test')

Stored Procedure updates two rows

DELIMITER //
CREATE OR REPLACE PROCEDURE GET_USER_PNTS(USER_ID INT , PNTS INT, QNT INT)
BEGIN
DECLARE x INT DEFAULT 1;
DECLARE TEMP_GIFT_ID INT;
UPDATE USR_PNT_SUMM SET USD_PNTS = USD_PNTS + PNTS WHERE USER_ID = 1;
COMMIT;
END //
DELIMITER ;
The above stored procedure updates two rows - one for user_id = 1 and the other one for userid 0. I dont understand why!
This is how I call the stored procedure -
CALL GET_USER_PNTS(1, 1, 1)
Please let me know why the user_id 0 is also getting updated.
P.S
1. I am using MariaDB.
2. UserID 0 is what I had manually added in the table. In pratice there won't be any 0 user_id. But even then, the row should not have been updated.
Please rename your parameters:
CREATE OR REPLACE PROCEDURE GET_USER_PNTS(L_USER_ID INT , L_PNTS INT, L_QNT INT)
BEGIN
DECLARE x INT DEFAULT 1;
DECLARE TEMP_GIFT_ID INT;
UPDATE USR_PNT_SUMM SET USD_PNTS = USD_PNTS + L_PNTS WHERE USER_ID = L_USER_ID;
COMMIT;
END //
Probably USER_ID = USER_ID is treated as true.

Get String between two strings - SQL Server

I got a function that returns string between two strings:
CREATE FUNCTION dbo.udf_GetStringBetween2Chars (#String VARCHAR(50), #FirstSpecialChar VARCHAR(50), #LastSpecialChar VARCHAR(50))
RETURNS VARCHAR(50)
AS
BEGIN
DECLARE #FirstIndexOfChar INT,
#LastIndexOfChar INT,
#LengthOfStringBetweenChars INT
SET #FirstIndexOfChar = CHARINDEX(#FirstSpecialChar,#String,0)
SET #LastIndexOfChar = CHARINDEX(#LastSpecialChar,#String,#FirstIndexOfChar+1)
SET #LengthOfStringBetweenChars = #LastIndexOfChar - #FirstIndexOfChar -1
SET #String = SUBSTRING(#String,#FirstIndexOfChar+1,#LengthOfStringBetweenChars)
RETURN #String
END
However, when I try to get string between POINTDESCRIPTION and DATAPOINT, I get error:
SET ANSI_WARNINGS OFF
declare #table table
(string varchar(50))
insert into #table
select 'EVENT: ID Order Reassignment (Individual Job Specific Update)|||LOCATION: Reassign.reassign()|||DATEPOINTDESCRIPTION: Only specific ID orders were reassigned from user fatis to user blake.|||DATAPOINT: blake' union all
select 'EVENT: ID Order Reassignment (Individual Job Specific Update)|||LOCATION: Reassign.reassign()|||DATAPOINTDESCRIPTION: Only specific ID orders were reassigned from user ilevic to user manic2.|||DATAPOINT: manic2' union all
select 'EVENT: ID Order Reassignment (Individual Job Specific Update)|||LOCATION: Reassign.reassign()|||DATAPOINTDESCRIPTION: Only specific ID orders were reassigned from user links to user sepir.|||DATAPOINT: sepir'
select dbo.udf_GetStringBetween2Chars (Tab.string,'POINTDESCRIPTION: ','|||DATAPOINT')
FROM #table Tab
Msg 537, Level 16, State 2, Line 10
Invalid length parameter passed to the LEFT or SUBSTRING function.
Does anyone see why this would happen?
If anyone finds it useful, here is final function to return string between 2 strings:
CREATE FUNCTION dbo.udf_GetStringBetween2Chars (#String VARCHAR(500), #FirstSpecialChar VARCHAR(500), #LastSpecialChar VARCHAR(500))
RETURNS VARCHAR(500)
AS
BEGIN
DECLARE #FirstIndexOfChar INT,
#LastIndexOfChar INT,
#LengthOfStringBetweenChars INT
SET #FirstIndexOfChar = CHARINDEX(#FirstSpecialChar,#String,0)
SET #LastIndexOfChar = CHARINDEX(#LastSpecialChar,#String,#FirstIndexOfChar+1)
SET #LengthOfStringBetweenChars = #LastIndexOfChar - #FirstIndexOfChar -1
SET #String = SUBSTRING(#String,#FirstIndexOfChar+LEN(#FirstSpecialChar),#LengthOfStringBetweenChars)
RETURN #String
END
GO
And to call it:
select dbo.udf_GetStringBetween2Chars (tab.someString,'POINTDESCRIPTION: ','|||DATAPOINT')
FROM yourTable tab

Running a Stored Procedure against all records in a table

I'm trying to create a SSIS package where I pull data from an Oracle source, insert into a destination table and run that data against a stored procedure that manipulates the data.
The stored procedure has the following variables:
#ShiftID Varchar (50),
#ProcedureID int,
#Registered int,
#Performed int,
#Deferred int,
#Collected int,
#QNS int,
#Deleted int,
#Intent int,
#APH int,
#ResultError int output,
#ResultMessage varchar(1024) output
And the table I'd like to run the procedure against contain the following columns:
Shift_ID
ProcedureID
Registered
Performed
Deferred
Collected
QNS
Deleted
APH
Right now I have about 500 records in the table and I'd like to run this procedure against all records but I'm not sure how to go about this task.
How can I assign the variables to specific columns in the table and then have it loop or cursor through the entire table?
Thanks,
You can use after Insert trigger for that on the Staging table where you insert rows from your ssis package .
Like this --
CREATE TRIGGER [dbo].[TriggerName] ON [dbo].[yourTableName]
AFTER INSERT
AS
DECLARE #ShiftID VARCHAR(50)
,#ProcedureID INT
,#Registered INT
,#Performed INT
,#Deferred INT
,#Collected INT
,#QNS INT
,#Deleted INT
,#Intent INT
,#APH INT
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SELECT #ShiftID = ins.ShiftID
,#ProcedureID = ins.ProcedureID
,#Registered = ins.Registered
,#Performed = ins.Performed
,#Deferred = ins.Deferred
,#Collected = ins.Collected
,#Pincode = ins.Pincode
,#QNS = ins.QNS
,#Deleted = ins.Deleted
,#Intent = ins.Intent
,#APH = ins.APH
FROM inserted ins
-- TODO: Set parameter values here.
EXECUTE [your Proc Name] #ShiftID
,#ProcedureID
,#Registered
,#Performed
,#Deferred
,#Collected
,#QNS
,#Deleted
,#Intent
,#APH
-- Insert statements for trigger here
END
GO

Loop items in list of user defined table types

I have simple table with 2 columns CarId int, primary key and CarName, varchar.
I need to create a stored procedure which accepts a list of cars. If car with CarId doesn't exist, I want to insert that car, and if already exists, I want to update it.
I created a user-defined table type CarType:
CREATE TYPE dbo.CarType
AS TABLE
(
CARID int null,
CARNAME varchar(800) not null,
);
and stored procedure InsertCars:
CREATE PROCEDURE dbo.InsertCars
#Cars AS CarType READONLY
AS
DECLARE #CarCount INT = 0;
DECLARE #Counter INT = 0;
BEGIN
SET #CarsCount = (SELECT COUNT(CarId) FROM #Cars);
WHILE(#Counter < #CarsCount)
BEGIN TRY
--how get item from list Cars?
#CurrentCar = Cars(#Counter)
IF EXISTS(SELECT 1 FROM Cars WHERE CarsId = CurrentCar.CarId)
--if doesn’t exist insert
BEGIN
INSERT INTO CARS(CARID, CARNAME)
SELECT * FROM #CurrentCar;
END
ELSE
BEGIN
--if exist update
END
END
SET #Counter= #Counter + 1;
END TRY
BEGIN CATCH
Print (ERROR_MESSAGE());
END CATCH
END
I don't know how get current car in loop from list of cars (parameter Cars in the stored procedure).
Or some elegant solution for this problem.
It seems you may get rid of loop here:
CREATE PROCEDURE dbo.InsertCars
#Cars AS CarType READONLY
AS
BEGIN
SET NOCOUNT ON;
UPDATE c
SET c.CARNAME = c2.CARNAME
FROM Cars c
JOIN #Cars c2 on c2.CARID = c.CARID;
INSERT INTO Cars(CARID, CARNAME)
SELECT c.CARID, c.CARNAME
FROM #Cars c
WHERE NOT EXISTS (SELECT 1 FROM Cars WHERE CARID = c.CARID);
END
or (using merge construct):
CREATE PROCEDURE dbo.InsertCars
#Cars AS CarType READONLY
AS
BEGIN
SET NOCOUNT ON;
MERGE Cars AS target
USING (SELECT CARID, CARNAME FROM #Cars) AS source (CARID, CARNAME)
ON (target.CARID = source.CARID)
WHEN MATCHED THEN
UPDATE SET CARNAME = source.CARNAME
WHEN NOT MATCHED THEN
INSERT (CARID, CARNAME)
VALUES (source.CARID, source.CARNAME);
END