Stored Procedure not pulling SubQuestions (LimeSurvey) SSMS - sql-server

Using LimeSurvey to query data from stored procedure, however I can't get type T to pull correctly. Something isn't working with the multiple choice questions with multiple answers (type T). Looking for the answer for it to display results.
Using LimeSurvey to query data from stored procedure, however I can't get type T to pull correctly. Something isn't working with the multiple choice questions with multiple answers (type T). Looking for the answer for it to display results.
USE [LimeSurvey]
GO
/****** Object: StoredProcedure [dbo].[p_CreateFieldMapBailey2] Script Date: 8/15/2018 8:21:29 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
/*
Types "L", "!", "O", "D", "G", "N", "X", "Y", "5", "S", "T", "U"
Description Class Prefix Legacy Character Code Parent (if blank assume QuestionModule)
5 point choice FiveList 5
List (dropdown) Select ! List
List (radio) List L
List with comment CommentList O List
Array RadioArray F Array
Array(10 point choice) TenRadioArray B RadioArray
Array (5 point choice) FiveRadioArray A RadioArray
Array (Increase/Same/Decrease) IDRadioArray E RadioArray
Array (Numbers) NumberArray : Array
Array (Texts) TextArray ; Array
Array (Yes/No/Uncertain) YNRadioArray C RadioArray
Array by column ColumnRadioArray H RadioArray
Array dual scale DualRadioArray 1 RadioArray
Date/Time Date D
Equation Equation *
File upload File pipe character
Gender Gender G
Language switch Language I
Multiple numerical input Multinumerical K
Numerical input Numerical N
Ranking Ranking R
Text display Display X
Yes/No YN Y
Huge free text HugeText U Text
Long free text LongText T Text
Multiple short text Multitext Q
Short free text ShortText S Text
Multiple choice Check M
Multiple choice with comments CommentCheck P Check
*/
/*
Author: Thoraya Zedan
Date: 3/18/2018
Store procedure to map Lime Survey Questions and Answers.
Tables used:
* dbo.lime_questions
* dbo.lime_groups
* dbo.lime_answers
* dbo.lime_survey<sid>
*/
ALTER Procedure [dbo].[p_CreateFieldMapBailey2](#surveryid int = '883877')
as
BEGIN
SET NOCOUNT ON;
/* Check if $sLanguage is a survey valid language (else $fieldmap is empty) */
Declare #qid int, --Questiond ID
#sid int, -- Survey ID
#sqid int, -- Subquestion ID
#gid int, -- Group ID
#scale_id int,
#specialtype nvarchar(10),
#defaultvalue nvarchar(10),
#parent_qid int, -- Parent Question ID for SubQuestions
#type varchar(1), -- Question Type
#title nvarchar(max), -- Question Title
#question nvarchar(max), -- Question Text
#preg nvarchar(max) ,
#help nvarchar(max),
#other varchar(1),
#mandatory varchar(1),
#question_order int,
#language varchar(20),
#same_default int,
#relevance varchar(max),
#modulename nvarchar(255),
#SubQuestionsFound bit,
#SQL nvarchar(max), -- for dynamic SQL
#TableName nvarchar(100), -- Table name for survey responses
#SurveyIDChar nvarchar(100), -- Convert Survey ID from int to varchar
#column_name nvarchar(100), -- From data dictionary
#DerivedColumnName nvarchar(100), -- Derived from code
#ParentQuestion nvarchar(max), -- Parent Question is the main question for the subquestion group
#Answer_Value nvarchar(max), -- Answer value, not the code
#Answer_ValueFromCode nvarchar(max), -- Answer value for code based answers
#stitle nvarchar(max), -- Subquestion Title
#squestion nvarchar(max), -- Subquestion Text
#squestion_order int, -- Subquestion Order
#SubQuestionPaddedNumber nvarchar(10), -- Zero Padded number
#AnswerColumnName nvarchar(100), -- The response table column name
#id int, -- reponse ID from the response table
#ParmDefinition nvarchar(1000) -- Parameter list
-- Temp Table
IF OBJECT_ID('tempdb..##TempLimeQuestionAnswers') IS NOT NULL
DROP Table ##TempLimeQuestionAnswers
IF OBJECT_ID('tempdb..##TempLimeReponse') IS NOT NULL
DROP Table ##TempLimeReponse
Set #DerivedColumnName = '';
Set #SurveyIDChar = CAST(#surveryid as varchar(100));
Set #TableName = 'lime_survey_' + #SurveyIDChar
-- Prep Temp Answer Table
Set #SQL='Select * into ##TempLimeReponse from '+ #TableName;
exec sp_executesql #SQL
-- Answer Table Cursor
declare curResponseTable cursor for
Select id from ##TempLimeReponse;
-- Prep Temp Q&A Table
CREATE TABLE ##TempLimeQuestionAnswers(
[id] int null,
[qid] [int] NULL,
[Column_Name] [nvarchar](max) NULL,
[parent_qid] [int] NULL,
[sid] [int] NULL,
[gid] [int] NULL,
[type] [varchar](1) NULL,
[title] [nvarchar](20) NULL,
[question] [nvarchar](max) NULL,
[subquestion] [nvarchar](max) NULL,
[Answer_Value] [nvarchar](max) NULL,
[preg] [nvarchar](max) NULL,
[help] [nvarchar](max) NULL,
[other] [varchar](1) NULL,
[mandatory] [varchar](1) NULL,
[question_order] [int] NULL,
[language] [varchar](20) NULL,
[scale_id] [int] NULL,
[same_default] [int] NULL,
[relevance] [varchar](max) NULL,
[modulename] [nvarchar](255) NULL
);
Open curResponseTable
Fetch Next from curResponseTable into #id
While ##FETCH_STATUS = 0
BEGIN
-- Main Query for Mapping
declare curMainQuery cursor for
SELECT [qid]
,[parent_qid]
,questions.[sid]
,questions.[gid]
,[type]
,[title]
,[question]
,[preg]
,[help]
,[other]
,[mandatory]
,[question_order]
,questions.[language]
,[scale_id]
,[same_default]
,[relevance]
,[modulename]
FROM dbo.lime_questions as questions, dbo.lime_groups as groups
WHERE questions.gid=groups.gid AND
questions.language=groups.language AND
questions.parent_qid=0 AND
questions.sid = #surveryid
ORDER BY group_order, question_order;
Open curMainQuery
Fetch Next from curMainQuery into #qid,#parent_qid,#sid,#gid,#type,#title,#question,#preg,#help,#other,#mandatory,#question_order,#language,#scale_id,#same_default,#relevance,#modulename
WHILE ##FETCH_STATUS = 0
BEGIN
Set #SubQuestionsFound = 0; -- init value
-- Derive the column name
Set #DerivedColumnName = #SurveyIDChar + 'X' + Cast(#gid as nvarchar(100)) + 'X' + Cast(#qid as nvarchar(100)) ; --"{$arow['sid']}X{$arow['gid']}X{$arow['qid']}
IF #other = 'Y'
Set #DerivedColumnName = #DerivedColumnName + 'other'
-- Is this a comment field?
IF #type = 'O'
Set #DerivedColumnName = #DerivedColumnName + 'comment'
-- Multiple Comment
IF #type = 'P' --$fieldname="{$arow['sid']}X{$arow['gid']}X{$arow['qid']}{$abrow['title']}comment";
Set #DerivedColumnName = #DerivedColumnName + #title + 'comment'
-- Multiple Choice
IF #type = 'T'
Set #DerivedColumnName = #SurveyIDChar + 'X' + Cast(#parent_qid as nvarchar(100)) + 'X' + Cast(#qid as nvarchar(100)) + 'SQ' + ''
-- is this a subquestion?
IF (select count(*) from dbo.lime_questions where parent_qid = #qid) > 0 -- We have subquestions. Use the count not the question type
Begin
Set #DerivedColumnName = #SurveyIDChar + 'X' + Cast(#parent_qid as nvarchar(100)) + 'X' + Cast(#qid as nvarchar(100)) + 'SQ' + '';
Set #SubQuestionsFound = 1;
Set #ParentQuestion = #question; -- Since the cursor has parent_id=0 we know this is the parent question.
--Find all subquestions
-- Cursor for parent_id
declare curSubQuestion cursor for
select qid, title, question, question_order from dbo.lime_questions
where [sid] = #sid
and gid = #gid
and parent_qid = #qid
order by question_order;
Open curSubQuestion
Fetch Next From curSubQuestion into #sqid, #stitle, #squestion, #squestion_order
WHILE ##FETCH_STATUS = 0
BEGIN
-- Loop
Set #SubQuestionPaddedNumber = Replace(STR(#squestion_order,3), '', ''); -- padd with zeros
Set #AnswerColumnName = #SurveyIDChar + 'X' + Cast(#parent_qid as nvarchar(100)) + 'X' + Cast(#qid as nvarchar(100)) + 'SQ' + '';
-- Get the answer
SET #ParmDefinition = N'#Answer_Value nvarchar(100) output'
Set #SQL = 'Select #Answer_Value= ['+ #AnswerColumnName +'] From ##TempLimeReponse where id =' + CAST(#id as nvarchar(100))
exec sp_executesql #SQL, #ParmDefinition, #Answer_Value=#Answer_Value output
Insert into ##TempLimeQuestionAnswers(
[id],[qid],[Column_Name],[parent_qid],[sid],[gid],[type],[title],[question],[SubQuestion],[Answer_Value],[preg],[help],[other],[mandatory],[question_order],[language],[scale_id],[same_default],[relevance],[modulename])
Values(#id, #sqid,#AnswerColumnName, #parent_qid, #sid, #gid, #type, #stitle, #question, #squestion, #Answer_Value,#preg, #help, #other, #mandatory, #squestion_order, #language, #scale_id, #same_default, #relevance, #modulename );
Fetch Next From curSubQuestion into #sqid, #stitle, #squestion, #squestion_order
END -- curSubQuestions
CLOSE curSubQuestion
DEALLOCATE curSubQuestion
End -- Array Questions
IF #SubQuestionsFound = 0
Begin
-- Get the answer
SET #ParmDefinition = N'#Answer_Value nvarchar(100) output'
Set #SQL = 'Select #Answer_Value= ['+ #DerivedColumnName +'] From ##TempLimeReponse where id =' + CAST(#id as nvarchar(100))
exec sp_executesql #SQL, #ParmDefinition, #Answer_Value=#Answer_Value output
-- Check if this is a code
IF (select count(*) from dbo.lime_answers where qid = #qid) > 0
BEGIN
Select #Answer_ValueFromCode = [answer] From dbo.lime_answers where qid = #qid and code = #Answer_Value;
Set #Answer_Value = #Answer_ValueFromCode;
END
Insert into ##TempLimeQuestionAnswers(
[id],[qid],[Column_Name],[parent_qid],[sid],[gid],[type],[title],[question],[Answer_Value], [preg],[help],[other],[mandatory],[question_order],[language],[scale_id],[same_default],[relevance],[modulename])
Values(#id,#qid,#DerivedColumnName, #parent_qid, #sid, #gid, #type, #title, #question, #Answer_Value, #preg, #help, #other, #mandatory, #question_order, #language, #scale_id, #same_default, #relevance, #modulename );
End
Fetch Next from curMainQuery into #qid,#parent_qid,#sid,#gid,#type,#title,#question,#preg,#help,#other,#mandatory,#question_order,#language,#scale_id,#same_default,#relevance,#modulename
END -- curMainQuery
Close curMainQuery;
Deallocate curMainQuery;
Fetch Next from curResponseTable into #id
End --curResponseTable
Close curResponseTable;
Deallocate curResponseTable;
-- Replace the Select with an insert statement to place in a table.
Select * from ##TempLimeQuestionAnswers;
-- Temp Table
IF OBJECT_ID('tempdb..##TempLimeQuestionAnswers') IS NOT NULL
DROP Table ##TempLimeQuestionAnswers
IF OBJECT_ID('tempdb..##TempLimeReponse') IS NOT NULL
DROP Table ##TempLimeReponse
END

Related

Cursorfetch Error: Datatypes and number of variables match the select query

I'm facing issue When I calling this procedure from my main procedure. Error: Cursorfetch: The number of variables declared in the INTO list must match that of selected columns.
ALTER PROCEDURE [ABC].[SKIPPED_EMAILS] (#BatchID INT, #Record VARCHAR(20), #SkippedReason VARCHAR(100), #RecordID INT, #TaskID INT, #TableID INT, #EmailCount INT OUTPUT) AS
BEGIN
DECLARE #EmailBody VARCHAR(4000)
DECLARE #Subject VARCHAR(30)
DECLARE #PersonObj INT
DECLARE #PersonID VARCHAR(10)
DECLARE #PersonName VARCHAR(40)
DECLARE #EmailAddr VARCHAR(50)
DECLARE #PhoneNo VARCHAR(20)
DECLARE #EmailStatus VARCHAR(1) = 'H'
SET #Subject = 'Skipped Emails'
SELECT #EmailBody = Text FROM FLT.EMAIL_TEMP WHERE InformationID = 'SKIPPED_EMAIL'
SELECT #EmailBody = replace(replace(#EmailBody, '[Reason]', #SkippedReason), '[Record]', #Record);
SET #EmailCount = 0;
DECLARE EMAILREC CURSOR LOCAL FOR
SELECT person.PersonObj, person.PersonID, person.PersonName, person.EmailAddr, person.PhoneNo
FROM [ABC].[PERSON] person
LEFT JOIN [ABC].[School] school ON (person.PersonObj = school.StudentObj)
LEFT JOIN [ABC].[Class] class ON (school.StudentObj = class.StudentObj)
WHERE person.Status = 'A'
AND school.StudentType = 'STU'
AND class.AttendeeID= 'ATTENDEE'
AND class.Status = 'A'
OPEN EMAILREC
FETCH NEXT FROM EMAILREC INTO #PersonObj, #PersonID, #PersonName, #EmailAddr, #PhoneNo
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO FLT.LOG_EMAILOG VALUES(0, #BatchID, 0, #TaskID, #TableID, #RecordID, GETDATE(), 'MBX', #Subject, #EmailBody,NULL,'',1, 'PER',#PersonObj, #PersonID, #PersonName, #EmailAddr, #PhoneNo, 'N', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, #EmailStatus);
SET #EmailCount = #EmailCount + 1;
FETCH NEXT FROM EMAILREC INTO #PersonObj, #PersonID, #PersonName, #EmailAddr, #PhoneNo
END
CLOSE EMAILREC
DEALLOCATE EMAILREC
END
GO
This does not need to be a cursor at all. Just use INSERT...SELECT...
First rule of cursors in SQL: Don't use them unless you have no other option. SQL likes things to be set-based, do batches as one command, not WHILE loops and cursors.
ALTER PROCEDURE [ABC].[SKIPPED_EMAILS]
(#BatchID INT, #Record VARCHAR(20), #SkippedReason VARCHAR(100), #RecordID INT,
#TaskID INT, #TableID INT, #EmailCount INT OUTPUT)
AS
DECLARE #EmailBody VARCHAR(4000);
SELECT #EmailBody = Text FROM FLT.EMAIL_TEMP WHERE InformationID = 'SKIPPED_EMAIL';
SET #EmailBody = replace(replace(#EmailBody, '[Reason]', #SkippedReason), '[Record]', #Record);
INSERT INTO FLT.LOG_EMAILOG
SELECT
0, #BatchID, 0, #TaskID, #TableID, #RecordID, GETDATE(), 'MBX', 'Skipped Emails', #EmailBody,
NULL,'',1, 'PER',p.PersonObj, p.PersonID, p.PersonName, p.EmailAddr, p.PhoneNo, 'N',
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'H'
FROM [ABC].[PERSON] person p
LEFT JOIN [ABC].[School] school ON (person.PersonObj = school.StudentObj)
LEFT JOIN [ABC].[Class] class ON (school.StudentObj = class.StudentObj)
WHERE person.Status = 'A'
AND school.StudentType = 'STU'
AND class.AttendeeID= 'ATTENDEE'
AND class.Status = 'A';
SET #EmailCount = ##ROWCOUNT;
GO
I strongly suggest you declare the exact column names you are inserting into.
I don't know the structure of FLT.LOG_EMAILOG but I would suggest that you specify the columns of this table in your insert statement like this:
INSERT INTO FLT.LOG_EMAILOG (f1,f2...,f30) VALUES(0, #BatchID, 0, #TaskID, #TableID, #RecordID, GETDATE(), 'MBX', #Subject, #EmailBody,NULL,'',1, 'PER',#PersonObj, #PersonID, #PersonName, #EmailAddr, #PhoneNo, 'N', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, #EmailStatus);

Workaround for executing dynamic sql in function

I'm trying to create a view which dynamically generates text from input parameters. But executing dynamic sql or stored procedure is not allowed in functions. Is there a work around for this?
I'm working on SQL Server 2014
Windows 10
Below is sample script for:
Create tables
Emp_info holds the details of the employee.
Table_list holds the details of the tables with column table_template_id.
table_template_id holds the format of the data to be displayed in the view. This can be customized.
Create function
Accepts 2 input parameters (table_key and emp_key to get the formatted table_template_id)
Create view
Expected output
----------------1. TABLES-------------------------
IF OBJECT_ID('dbo.emp_info', 'U') IS NOT NULL
DROP TABLE dbo.emp_info;
CREATE TABLE emp_info (emp_key INT, table_key INT, emp_fname NVARCHAR(100), emp_lname NVARCHAR(100), city_name NVARCHAR(100), city_zip BIGINT)
INSERT INTO emp_info (emp_key, table_key, emp_fname, emp_lname, city_name, city_zip) VALUES (1, 3, 'Brad', 'Pitt', 'San Jose', 95670)
INSERT INTO emp_info (emp_key, table_key, emp_fname, emp_lname, city_name, city_zip) VALUES (2, 3, 'Will', 'Smith', 'Kansas', 65870)
IF OBJECT_ID('dbo.table_list', 'U') IS NOT NULL
DROP TABLE dbo.table_list;
CREATE TABLE table_list (table_key int, table_name NVARCHAR(100), table_template_id NVARCHAR(100))
INSERT INTO table_list (table_key, table_name, table_template_id) VALUES (3, 'emp_info', '[emp_lname],[emp_fname]-[empkey]')
----------------TABLES-------------------------
----------------2. FUNCTION-------------------------
IF OBJECT_ID(N'crt_emp_temp_id', N'FN') IS NOT NULL
DROP FUNCTION crt_emp_temp_id
GO
CREATE FUNCTION crt_emp_temp_id (#table_key INT, #emp_key INT)
RETURNS NVARCHAR(1000)
AS
BEGIN
DECLARE #mi_table_id_tmp TABLE (
table_key BIGINT,
table_fld NVARCHAR(100),
fld_order BIGINT
)
DECLARE #table_template_id NVARCHAR(100), #table_fld NVARCHAR(100), #table_flds NVARCHAR(100),
#count INT = 0, #max_fld_order INT, #table_name NVARCHAR(100),
#fld_order INT, #sql_stmt NVARCHAR(MAX), #out_value NVARCHAR(1000)
SELECT #table_template_id = table_template_id
,#table_name = table_name
FROM table_list
WHERE table_key = #table_key
SET #table_flds = #table_template_id
WHILE (LEN(#table_flds) > 0)
BEGIN
SET #table_fld = SUBSTRING(#table_flds, CHARINDEX('[', #table_flds)+1, CHARINDEX(']', #table_flds)-2)
SET #table_flds = SUBSTRING(#table_flds, CHARINDEX(#table_fld, #table_flds)+LEN(#table_fld)+1, LEN(#table_flds));
SET #table_flds = SUBSTRING(#table_flds, CHARINDEX('[', #table_flds ), LEN(#table_flds))
SET #count = #count + 1;
INSERT INTO #mi_table_id_tmp (table_key, table_fld, fld_order) VALUES (#table_key, #table_fld, #count);
END
SELECT #max_fld_order = MAX(fld_order) FROM #mi_table_id_tmp
SET #count = 1
WHILE (#count <= #max_fld_order)
BEGIN
SELECT top(1) #table_fld = table_fld
FROM #mi_table_id_tmp
WHERE fld_order = #count
IF (#count = 1)
SET #table_template_id = REPLACE(#table_template_id, '['+#table_fld+']', #table_fld+'+''')
ELSE IF (#count = #max_fld_order)
SET #table_template_id = REPLACE(#table_template_id, '['+#table_fld+']', '''+'+#table_fld);
ELSE
SET #table_template_id = REPLACE(#table_template_id, '['+#table_fld+']', '''+'+#table_fld+'+''');
SET #count = #count + 1
END
SET #sql_stmt = 'select #out = '+#table_template_id+' from '+#table_name+' where emp_key = #e_k'
------------This is where I'm stuck-------------------
EXECUTE sp_executesql #sql_stmt, N'#e_k int,#out NVARCHAR(1000) OUTPUT', #e_k = #emp_key, #out = #out_value OUTPUT
RETURN #out_value
------------This is where I'm stuck-------------------
END
GO
----------------FUNCTION-------------------------
----------------3. VIEW-------------------------
IF object_id(N'emp_info_vw', N'V') IS NOT NULL
DROP FUNCTION emp_info_vw
GO
CREATE VIEW emp_info_vw AS
SELECT crt_emp_temp_id (table_key, emp_key) emp_id,
city_name,
city_zip
FROM emp_info
GO
----------------VIEW-------------------------
--------------4. Expected Output----------------
/*
I'd like to have my output of the view as below
Pitt,Brad-1 San Jose 95670
Smith,Will-2 Kansas 65870
*/
--------------Expected Output----------------

Stored procedure in that needs to return unique values in a custom format but seems to return duplicates

I have a stored procedure in Microsoft SQL Server that should return unique values based on a custom format: SSSSTT99999 where SSSS and TT is based on a parameter and 99999 is a unique sequence based on the values of SSSS and TT. I need to store the last sequence based on SSSS and TT on table so I can retrieve the next sequence the next time. The problem with this code is that in a multi-user environment, at least two simultaneous calls may generate the same value. How can I make sure that each call to this stored procedure gets a unique value?
CREATE PROCEDURE GenRef
#TT nvarchar(30),
#SSSS nvarchar(50)
AS
declare #curseq as integer
set #curseq=(select sequence from DocSequence where
docsequence.TT=#TT and
DocSequence.SSSS=#SSSS)
if #curseq is null
begin
set #curseq=1
insert docsequence (id,TT,SSSS,sequence) values
(newid(),#TT,#SSSS,1)
end
else
begin
update DocSequence set Sequence=#curseq+1 where
docsequence.TT=#TT and
DocSequence.SSSS=#SSSS
end
declare #curtr varchar(30)
set #curtr=RIGHT('0000' + #SSSS,4)
+ #TT
+ RIGHT('00000' + #curseq,5)
select #curtr
GO
updated code with transactions:
ALTER PROCEDURE [dbo].[GenTRNum]
#TRType nvarchar(50),
#BranchCode nvarchar(50)
AS
declare #curseq as integer
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
begin transaction
if not exists (select top 1 sequence from DocSequence where
docsequence.DocType=#trtype and
DocSequence.BranchCode=#BranchCode)
begin
insert docsequence (id,doctype,sequence,branchcode) values
(newid(),#trtype,1,#BranchCode)
end
else
begin
update DocSequence set Sequence=sequence+1 where
docsequence.DocType=#trtype and
DocSequence.BranchCode=#BranchCode
end
commit
set #curseq=(select top 1 sequence from DocSequence where
docsequence.DocType=#trtype and
DocSequence.BranchCode=#BranchCode)
declare #curtr varchar(30)
set #curtr=RIGHT('0000' + #BranchCode,4)
+ #TRType
+ RIGHT('00000' + convert(varchar(5),#curseq),5)
select #curtr
You can handle this on application level by using threading assuming you have single application Server.
Suppose you have method GetUniqueVlaue Which Executes this SP.
What you should do is use threading. that method use database transactions with readcommited. Now for example if two users have made the call to GetUniqueVlaue method at exactly 2019-08-30 10:59:38.173 time your application will make threads and Each thread will try to open transaction. only one will open that transaction on that SP and other will go on wait.
Here is how I would solve this task:
Table structure, unique indexes are important
--DROP TABLE IF EXISTS dbo.DocSequence;
CREATE TABLE dbo.DocSequence (
RowID INT NOT NULL IDENTITY(1,1)
CONSTRAINT PK_DocSequence PRIMARY KEY CLUSTERED,
BranchCode CHAR(4) NOT NULL,
DocType CHAR(2) NOT NULL,
SequenceID INT NOT NULL
CONSTRAINT DF_DocSequence_SequenceID DEFAULT(1)
CONSTRAINT CH_DocSequence_SequenceID CHECK (SequenceID BETWEEN 1 AND 999999),
)
CREATE UNIQUE INDEX UQ_DocSequence_BranchCode_DocType
ON dbo.DocSequence (BranchCode,DocType) INCLUDE(SequenceID);
GO
Procedure:
CREATE OR ALTER PROCEDURE dbo.GenTRNum
#BranchCode VARCHAR(4),
#DocType VARCHAR(2),
--
#curseq INT = NULL OUTPUT,
#curtr VARCHAR(30) = NULL OUTPUT
AS
SELECT #curseq = NULL,
#curtr = NULL,
#BranchCode = RIGHT(CONCAT('0000',#BranchCode),4),
#DocType = RIGHT(CONCAT('00',#DocType),2)
-- Atomic operation, no transaction needed
UPDATE dbo.DocSequence
SET #curseq = SequenceID += 1
WHERE DocType = #DocType
AND BranchCode = #BranchCode;
IF #curseq IS NULL -- Not found, create new one
BEGIN
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRAN
INSERT dbo.docsequence (doctype,branchcode)
SELECT #DocType, #BranchCode
WHERE NOT EXISTS(SELECT 1 FROM dbo.DocSequence WHERE DocType = #DocType AND BranchCode = #BranchCode)
IF ##ROWCOUNT = 1
BEGIN
COMMIT;
SET #curseq = 1
END
ELSE
BEGIN
ROLLBACK;
UPDATE dbo.DocSequence
SET #curseq = SequenceID += 1
WHERE DocType = #DocType
AND BranchCode = #BranchCode;
END
END
SET #curtr = #BranchCode + #DocType + RIGHT(CONCAT('00000',#curseq),5)
RETURN
GO
I did some tests to make sure is it works as described. You can use it if you need
-- Log table just for test
-- DROP TABLE IF EXISTS dbo.GenTRNumLog;
CREATE TABLE dbo.GenTRNumLog(
RowID INT NOT NULL IDENTITY(1,1) PRIMARY KEY CLUSTERED,
SPID SMALLINT NOT NULL,
Cycle INT NULL,
dt DATETIME NULL,
sq INT NULL,
tr VARCHAR(30) NULL,
DurationMS INT NULL
)
This script should be opened in several separate MS SQL Management Studio windows and run they almost simultaneously
-- Competitive insertion test, run it in 5 threads simultaneously
SET NOCOUNT ON
DECLARE
#dt DATETIME,
#start DATETIME,
#DurationMS INT,
#Cycle INT,
#BC VARCHAR(4),
#DC VARCHAR(2),
#SQ INT,
#TR VARCHAR(30);
SELECT #Cycle = 0,
#start = GETDATE();
WHILE DATEADD(SECOND, 60, #start) > GETDATE() -- one minute test, new #DocType every second
BEGIN
SET #dt = GETDATE();
SELECT #BC = FORMAT(#dt,'HHmm'), -- Hours + Minuts as #BranchCode
#DC = FORMAT(#dt,'ss'), -- seconds as #DocType
#Cycle += 1
EXEC dbo.GenTRNum #BranchCode = #BC, #DocType = #Dc, #curseq = #SQ OUTPUT, #curtr = #TR OUTPUT
SET #DurationMS = DATEDIFF(ms, #dt, GETDATE());
INSERT INTO dbo.GenTRNumLog (SPID, Cycle , dt, sq, tr, DurationMS)
SELECT SPID = ##SPID, Cycle = #Cycle, dt = #dt, sq = #SQ, tr = #TR, DurationMS = #DurationMS
END
/*
Check test results
SELECT *
FROM dbo.DocSequence
SELECT sq = MAX(sq), DurationMS = MAX(DurationMS)
FROM dbo.GenTRNumLog
SELECT * FROM dbo.GenTRNumLog
ORDER BY tr
*/

"String or binary data would be truncated" error while updating but not when inserting new record

I understand that this error comes when we try to insert characters more than the capacity of column.
But what I am not able to understand is a recent output in which I was getting the error when updating in the table but not when a new record was getting inserted in the table with the same parameter size.
Table structrue:
Entity
(
#EntityID int,
#Name varchar(200),
#FullName varchar(50),
#ParentEntityID int,
#ShortDesc varchar(255),
#LongDesc varchar(2000)
)
When I am trying to insert a new row, it simply truncates the parameter who has value more than its size (here #FullName), but when I try to update the same values on an existing record, it simply throws the exception.
Why can't it just truncate here also?
Edit::
Procedure for update.
ALTER PROCEDURE [dbo].[Entity_Update]
#EntityID int,
#Name varchar(200),
#FullName varchar(410),
#ParentEntityID int,
#ShortDesc varchar(255),
#LongDesc varchar(2000)
AS
set nocount on;
update dbo.Entity set
Name = #Name,
FullName = #FullName,
ParentEntityID = #ParentEntityID,
ShortDesc = #ShortDesc,
LongDesc = #LongDesc,
DateUpdated = GETUTCDATE()
where EntityID = #EntityID;
return 0;
INSERT Procedure is :
ALTER PROCEDURE [dbo].[Entity_Insert]
#Name varchar(200),
#FullName varchar(410),
#ParentEntityID int,
#ShortDesc varchar(255),
#LongDesc varchar(2000),
#UserID int,
#EntityID int OUTPUT
AS
set nocount on;
set xact_abort on;
begin tran
insert dbo.Entity (
Name,
FullName,
ParentEntityID,
ShortDesc,
LongDesc,
RecordStatus,
DateUpdated)
values (#Name,
#FullName,
#ParentEntityID,
#ShortDesc,
#LongDesc,
1,
GETUTCDATE());
select #EntityID = SCOPE_IDENTITY();
CREATE TABLE Entity
(
EntityID INT IDENTITY
NOT NULL
PRIMARY KEY ,
Name VARCHAR(200) ,
FullName VARCHAR(50) ,
ParentEntityID INT ,
ShortDesc VARCHAR(255) ,
LongDesc VARCHAR(2000)
);
INSERT [Entity] ( [Name], [FullName], [ParentEntityID], [ShortDesc],
[LongDesc] )
VALUES ( 'Name', 'FullName', 0, 'Short', 'Long' );
UPDATE [Entity]
SET [LongDesc] = 'Here is an updated value'
WHERE [FullName] = 'FullName';
SELECT *
FROM [Entity] AS [e];
UPDATE [Entity]
SET [LongDesc] = LEFT('Yet another update ' + REPLICATE('blah ', 1000),
2000)
WHERE [FullName] = 'FullName';
SELECT *
FROM [Entity] AS [e];
Works perfectly well for me.

Error Msg 213: Column name or number of supplied values does not match table definition

I created a table with a stored procedure and I'm trying to insert data into it, but I'm getting this error
Msg 137, Level 15, State 2, Line 49
Must declare the scalar variable "#EmployeeID")
Please help!
Create Table Humanresource.Employee
(
EmployeeID char(3) constraint pkemployeeid primary key,
FirstName varchar(20) not null,
LastName varchar(20) not null,
Emp_Address varchar(30) not null,
Title char(30) constraint ckTitle check(Title in ('Executive','Senior Executive','Management Trainee','Event Manager','Senior Event Manager')),
Phone varchar(20) check(Phone like '[0-9][0-9]-[0-9][0-9][0-9][0-9]-[0-9][0-9][0-9]-[0-9][0-9][0-9]')
)
Stored procedure:
create procedure Auto_EmpID
#FirstName varchar(20),
#LastName varchar(20),
#Address varchar(30),
#Title varchar(30),
#Phone varchar(20)
AS
Declare #EmployeeID char(3)
if exists (select EmployeeID From HumanResource.Employee Where EmployeeID = 'E101')
BEGIN
Select
#EmployeeID = Max(right(EmployeeID, 2))
from HumanResource.Employee
Select
#EmployeeID = CASE
When #EmployeeID >= 0 AND #Employeeid < 9
Then 'E0' + convert(char(3), #EmployeeID + 1)
When #EmployeeID >= 9 AND #EmployeeID < 99
Then 'E' + convert(char(3), #EmployeeID + 1)
END
END
Else
set #Employeeid = 'E101'
insert into HumanResource.Employee
Values(#EmployeeID, #FirstName, #LastName, #Address, #Title, #Phone)
Return
Exec Auto_EmpID 'Bamidele','Ajose','Lagos','11-1111-111-111'
exec Auto_Empid 'Kunle','Awele','Ikeja','22-3332-655-897'
exec auto_empid 'John','George','Benin','33-5555-7654-443'
select * from humanresource.employee
insert into humanresource.employee(#EmployeeID, #FirstName, #LastName, #Address, #Title, #Phone)
values(#EmployeeID = 'e101', 'Baley', 'Carles', 'Los Angeles', '23-3445-434-344')
These 2 lines have a lot of problems:
insert into humanresource.employee(#EmployeeID,#FirstName,#LastName,#Address,#Title,#Phone)
values(#EmployeeID='e101','Baley','Carles','Los Angeles','23-3445-434-344')
You don't need to prefix the # symbol to column names. That should only be used with variables.
The column list contains 6 columns, but you are only supplying 5 values in the values clause. Please add the 6th value or remove the column where you don't want to insert.
If you want to use the value 'e101', you can directly insert that. If you want to use the variable #employeeID, just pass that. The assignment #employeeID = 'e101' is wrong inside the values clause. You could just do set #employeeID = 'e101 to assign the value before the insert statement.
#employeeID is declared as char(3), so even if you wanted to assign the value 'e101' to it before the insert statement, the value would get truncated to 'e10'. So, you must declare it as char(4) at least.
Another thing is that your stored procedure takes 5 input parameters, but you pass only 4 when calling it. If you want to pass only some parameters instead of all, you need to specify default values for each parameter while creating the procedure, something like this:
create procedure Auto_EmpID
#FirstName varchar(20) = 'somefirstname',
#LastName varchar(20) = 'somelastname',
#Address varchar(30) = 'somecity',
#Title varchar(30) = 'sometitle',
#Phone varchar(20) = '111-111-111'

Resources