I have a table with fallowing columns
symbol |market| bid_price | ask_price | update_time
------- ------ ----------- ----------- ------------
ABC US 123.00 675.00 20012-09-10 4:24:32.986
CDE SG 456.00 545.00 20012-09-10 4:26:32.986
And my application calling a SP for update this table.
update price_tbl
set bid_price=#bid_price, ask_price=#ask_price, update_time = getdate()
where market = #market and symbol = #symbol
But my application calling more than 1000 updates per second. so this SP not fast enough to update the table. I checked and found getdate() function is the bottleneck. but this system running with sql server 2000.
Can I use any other method to update time other than getdate() Any
other proposal for increase performance of this update.
FYI: this price_tbl having around 2000 records.
Eddied with test result.
Table Definition
..........................................................................................
CREATE TABLE [dbo].[GLDPrice](
[Company_code] [varchar](10) NOT NULL,
[Symbol] [varchar](10) NOT NULL,
[SymbolA] [varchar](10) NULL,
[SymbolB] [varchar](10) NULL,
[Market] [char](2) NOT NULL,
[ExchangeCode] [varchar](4) NULL,
[Remark] [char](6) NULL,
[Last_done] [numeric](19, 8) NULL,
[Change] [numeric](19, 8) NOT NULL,
[Open_Price] [numeric](19, 8) NULL,
[Closing_Price] [numeric](19, 8) NULL,
[Buy_Price] [numeric](19, 8) NULL,
[Sell_Price] [numeric](19, 8) NULL,
[Day_High] [numeric](19, 8) NULL,
[Day_Low] [numeric](19, 8) NULL,
[Time_done] [char](5) NULL,
[Cumm_vol] [int] NOT NULL,
[Buy_quantity] [int] NULL,
[Sell_quantity] [int] NULL,
[Per_Change] [numeric](19, 8) NULL,
[GLDBid] [numeric](19, 8) NULL,
[GLDAsk] [numeric](19, 8) NULL,
[GlobalGLDBid] [numeric](19, 8) NULL,
[GlobalGLDAsk] [numeric](19, 8) NULL,
[GLDBuyLastDone] [numeric](19, 8) NULL,
[GLDSellLastDone] [numeric](19, 8) NULL,
[GLDBuyLDUptTime] [datetime] NULL,
[GLDSellLDUptTime] [datetime] NULL,
[UpdateTime] [datetime] NOT NULL,
CONSTRAINT [PK_GLDPrice] PRIMARY KEY CLUSTERED
(
[Company_code] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[GLDPrice] ADD CONSTRAINT [DF_GLDPrice_SymbolA] DEFAULT (' ') FOR [SymbolA]
GO
ALTER TABLE [dbo].[GLDPrice] ADD CONSTRAINT [DF_GLDPrice_Last_done] DEFAULT (0) FOR [Last_done]
GO
ALTER TABLE [dbo].[GLDPrice] ADD DEFAULT (getdate()) FOR [UpdateTime]
GO
SP with getdate()
..........................................................................................
ALTER PROCEDURE [dbo].[update_test_one]
#Symbol varchar(10),
#Market varchar(5),
#BuyPrice money,
#SellPrice money,
#LastPrice money,
#High money,
#Low money
AS
DECLARE #GLDBidPrice money
DECLARE #GLDAskPrice money
DECLARE #GlobalGLDBid money
DECLARE #GlobalGLDAsk money
DECLARE #GLDBidAdjust money
DECLARE #GLDAskAdjust money
DECLARE #GlobalBidAdjust money
DECLARE #GlobalAskAdjust money
SELECT #GLDBidPrice = #BuyPrice + 5
SELECT #GLDAskPrice = #SellPrice + 5
SELECT #GlobalGLDBid = #BuyPrice + 5
SELECT #GlobalGLDAsk = #SellPrice + 5
UPDATE dbo.GLDprice
SET Buy_price = #BuyPrice,
Sell_price = #SellPrice,
GLDBid = #GLDBidPrice,
GLDAsk = #GLDAskPrice,
Day_high = #High,
Day_Low = #Low,
GlobalGLDBid = #GlobalGLDBid,
GlobalGLDAsk = #GlobalGLDAsk,
UpdateTime=GetDate(),
Last_Done = #LastPrice
WHERE Symbol = #symbol AND Market = #Market
SP without getdata()
............................................................................................
ALTER PROCEDURE [dbo].[update_test_two]
#Symbol varchar(10),
#Market varchar(5),
#BuyPrice money,
#SellPrice money,
#LastPrice money,
#High money,
#Low money
AS
DECLARE #GLDBidPrice money
DECLARE #GLDAskPrice money
DECLARE #GlobalGLDBid money
DECLARE #GlobalGLDAsk money
DECLARE #GLDBidAdjust money
DECLARE #GLDAskAdjust money
DECLARE #GlobalBidAdjust money
DECLARE #GlobalAskAdjust money
SELECT #GLDBidPrice = #BuyPrice + 5
SELECT #GLDAskPrice = #SellPrice + 5
SELECT #GlobalGLDBid = #BuyPrice + 5
SELECT #GlobalGLDAsk = #SellPrice + 5
UPDATE dbo.GLDprice
SET Buy_price = #BuyPrice,
Sell_price = #SellPrice,
GLDBid = #GLDBidPrice,
GLDAsk = #GLDAskPrice,
Day_high = #High,
Day_Low = #Low,
GlobalGLDBid = #GlobalGLDBid,
GlobalGLDAsk = #GlobalGLDAsk,
Last_Done = #LastPrice
WHERE Symbol = #symbol AND Market = #Market
Test Script
...........................................................................................
DECLARE #return_value int
DECLARE #count int
DECLARE #start datetime
SET NOCOUNT ON
SET #count = 0;
set #start = CURRENT_TIMESTAMP
WHILE (#count < 10000)
BEGIN
SET #count = #count + 1
EXEC [dbo].[update_test_one]
#Symbol = N'I9T',
#Market = N'SG',
#BuyPrice = 0.8,
#SellPrice = 0.8,
#LastPrice = 0.8,
#High = 0.8,
#Low = 0.8
EXEC [dbo].[update_test_one]
#Symbol = N'0001.HK',
#Market = N'HK',
#BuyPrice = 112,
#SellPrice = 112,
#LastPrice = 112,
#High = 112,
#Low = 112
END
print 'Test 01 : ' + CONVERT(varchar(20),DATEDIFF(millisecond,#start,CURRENT_TIMESTAMP))
SET #count = 0;
set #start = CURRENT_TIMESTAMP
WHILE (#count < 10000)
BEGIN
SET #count = #count + 1
EXEC [dbo].[update_test_two]
#Symbol = N'I9T',
#Market = N'SG',
#BuyPrice = 0.8,
#SellPrice = 0.8,
#LastPrice = 0.8,
#High = 0.8,
#Low = 0.8
EXEC [dbo].[update_test_two]
#Symbol = N'0001.HK',
#Market = N'HK',
#BuyPrice = 112,
#SellPrice = 112,
#LastPrice = 112,
#High = 112,
#Low = 112
END
print 'Test 02 : ' + CONVERT(varchar(20),DATEDIFF(millisecond,#start,CURRENT_TIMESTAMP))
GO
Result:
Test 01 : 82310
Test 02 : 12176
Result with reverse test.
Test 02 : 15413
Test 01 : 81636
I checked and found getdate() function is the bottleneck
I can absolutely 100% guarantee that your finding is wrong. The performance problem is by no means getdate(). For the record, not only calling getdate() 1000 times is not visible in the performance, but getdate()` happens to be a runtime constant function!
If you want to troubleshoot SQL Server performance issues, follow a methodology like Waits and Queues or follow the Performance Troubleshooting Flowchart.
My bet is that the performance problem is here:
where market = #market and symbol = #symbol
You're either missing an appropriate index on (market, symbol) or the type of the parameters #market and/or #symbol are incorrect and, due to data type precedence rules, cause table scans instead of index seeks.
Since you haven't presented a benchmark, I thought I'd do one, and then everyone can poke holes in the methodology. Hence CW :-):
SET NOCOUNT ON
go
create table prices (symbol char(3) not null,market char(2) not null,
bid_price decimal(18,4) not null,ask_price decimal(18,4) not null,
update_time datetime not null,
constraint PK_prices PRIMARY KEY (symbol,market)
)
GO
create table #Digits (d int not null)
insert into #Digits (d)
select 0 union all select 1 union all select 2 union all select 3 union all select 4 union all
select 5 union all select 6 union all select 7 union all select 8 union all select 9
create table #Numbers (n int not null)
insert into #Numbers (n)
select d1.d * 10000 + d2.d * 1000 + d3.d * 100 + d4.d * 10 + d5.d
from #Digits d1,#Digits d2,#Digits d3,#Digits d4,#Digits d5
insert into prices (symbol,market,bid_price,ask_price,update_time)
select
CHAR(65+n/(26*26)) + CHAR(65+((n/26)%26)) + CHAR(65+(n%26)),m.market,n,n+5,GETDATE()
from
#Numbers nu,
(select 'US' as market union all select 'SG') m
where nu.n < (26*26*26)
go
drop table #Digits
drop table #Numbers
The above sets us up with a table with ~30000 rows (so, more than you say you have). It also warms up the table, because every row has been recently accessed. I can't remember what invocations will clear this out for SQL Server 2000, but it ought to be fair enough to now run the tests in either order.
declare #market char(2)
declare #symbol char(3)
declare #price1 decimal(18,4)
declare #price2 decimal(18,4)
declare #start datetime
declare updates cursor local static for select symbol,market,bid_price,ask_price from prices
open updates
set #start = CURRENT_TIMESTAMP
fetch next from updates into #symbol,#market,#price1,#price2
while ##FETCH_STATUS = 0
begin
update prices
set bid_price=#price2, ask_price=#price1
where market = #market and symbol = #symbol
fetch next from updates into #symbol,#market,#price1,#price2
end
close updates
deallocate updates
print '"FAST" took ' + CONVERT(varchar(20),DATEDIFF(millisecond,#start,CURRENT_TIMESTAMP))
go
declare #market char(2)
declare #symbol char(3)
declare #price1 decimal(18,4)
declare #price2 decimal(18,4)
declare #start datetime
declare updates cursor local static for select symbol,market,bid_price,ask_price from prices
open updates
set #start = CURRENT_TIMESTAMP
fetch next from updates into #symbol,#market,#price1,#price2
while ##FETCH_STATUS = 0
begin
update prices
set bid_price=#price2, ask_price=#price1,update_time = GETDATE()
where market = #market and symbol = #symbol
fetch next from updates into #symbol,#market,#price1,#price2
end
close updates
deallocate updates
print '"SLOW" took ' + CONVERT(varchar(20),DATEDIFF(millisecond,#start,CURRENT_TIMESTAMP))
go
drop table prices
The only difference between these two cursor loops is that the second one does the GETDATE() based update to update_time, whereas the first performs no update to this column (as you indicated you'd done in your testing).
On my machine(*) , I get the result:
"FAST" took 20503
"SLOW" took 20436
Which indicates that the "slow" method, using GETDATE(), was 0.1 seconds faster.
(*)
Microsoft SQL Server 2000 - 8.00.2039 (Intel X86)
May 3 2005 23:18:38
Copyright (c) 1988-2003 Microsoft Corporation
Standard Edition on Windows NT 5.2 (Build 3790: Service Pack 2)
DECLARE #CurrentDate DateTime;
SELECT #CurrentDate = getdate();
update price_tbl
set bid_price=#bid_price, ask_price=#ask_price, update_time = #CurrentDate
where market = #market and symbol = #symbol
See if this works
I had a scenario like this and i gave a solution like this,
I passed the date time as the parameter from the front end to SP. otherwise if you want to handle in the SP itself you can go for this, like
DECLARE #time DATETIME
Set #time = getdate()
and you can pass it to your argument list, but note that we wont get the exact milliseconds in some case we handle that too in front end. because all record will have same present time with only different second value.
In front end yo can pass the value as,
DateTime.Now.ToString("HH:mm:ss", System.Globalization.DateTimeFormatInfo.InvariantInfo)
DateTime.Now.ToString("HH:mm:ss.fff", System.Globalization.DateTimeFormatInfo.InvariantInfo)
This works for me Test 01 : 266
ALTER PROCEDURE [dbo].[update_test_one]
#Symbol varchar(10),
#Market varchar(5),
#BuyPrice money,
#SellPrice money,
#LastPrice money,
#High money,
#Low money
AS
SET NOCOUNT ON;
UPDATE P
SET P.Buy_price = #BuyPrice,
P.Sell_price = #SellPrice,
P.GLDBid = #BuyPrice + C.BidRate,
P.GLDAsk = #SellPrice + isnull(C.AskRate,0),
P.Day_high = #High,
P.Day_Low = #Low,
P.GlobalGLDBid = #BuyPrice+ isnull(C.BidRateGlobal,0),
P.GlobalGLDAsk = #SellPrice+isnull(C.AskRateGlobal,0),
P.UpdateTime=CONVERT(VARCHAR(20), GETDATE(), 120),
P.Last_Done = #LastPrice
FROM dbo.GLDPrice AS P
INNER JOIN DBO.GLDContract AS C
ON P.Company_code=C.Company_code
WHERE P.Symbol = #symbol AND P.Market = #Market
SET NOCOUNT OFF
Related
How would I alter the below manual DDL script in a way to instead create the table dynamically? For example I want to create 2 columns (enroll year and disenroll year) for every year between 2017 and the current year (2021) and pivot them. Currently the script will manually create the columns from 2017 until year 2021. What direction should I take to make this dynamic?
I am thinking to use while loop to accomplish this but I'm not quite sure if I'm on the right track or if there was a better way. Next year in 2022 when the script is run it should append [2022] columns. Next next year in 2023 the script should append [2023] columns and so on.
DROP TABLE IF EXISTS ##Enrollment
CREATE TABLE ##Enrollment
(
[ID] VARCHAR(10),
[First_Name] VARCHAR(255) NULL ,
[Middle_Name] VARCHAR(255) NULL,
[Last_Name] VARCHAR(255) NULL,
[Date_of_Birth] DATE NULL,
[2017_Enrollment_Date] DATE NULL,
[2017_Disenrollment_Date] DATE NULL,
[2018_Enrollment_Date] DATE NULL,
[2018_Disenrollment_Date] DATE NULL,
[2019_Enrollment_Date] DATE NULL,
[2019_Disenrollment_Date] DATE NULL,
[2020_Enrollment_Date] DATE NULL,
[2020_Disenrollment_Date] DATE NULL,
[2021_Enrollment_Date] DATE NULL,
[2021_Disenrollment_Date] DATE NULL,
[Begin_Date] DATE NULL,
[End_Date] DATE NULL
)
One way is iterating over years between a max and min, and then iterating over the 2 possible names for your dynamic columns
declare #minyear int = 2017
declare #maxyear int
declare #ddl varchar(max)
declare #i int = 0
if (#maxyear is null) select #maxyear=year(getdate())
drop table if exists ##Enrollment
set #ddl =
'CREATE TABLE ##Enrollment(
[ID] VARCHAR(10)
,[First_Name] VARCHAR (255) NULL
,[Middle_Name] VARCHAR (255) NULL
,[Last_Name] VARCHAR (255) NULL
,[Date_of_Birth] DATE NULL
,[Begin_Date] DATE NULL
,[End_Date] DATE NULL'
while #maxyear >= #minyear
begin
while #i <= 1
begin
set #ddl = #ddl + char(13) +
',[' + cast(#minyear as varchar(4)) + '_' + case when #i = 0 then 'En' else 'Disen' end + 'rollment_Date] DATE NULL'
set #i = #i + 1
end
set #minyear = #minyear + 1
set #i = 0
end
set #ddl = #ddl + char(13) + ')'
exec (#ddl)
If you set a value to #maxyear (for example in the declaration, with declare #maxyear int = 2023) you can check the columns reach the year 2023.
You can test on this db<>fiddle
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
*/
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
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'
I have modified a stored procedure and added an extra parameter to be added to a table. The problem is the value for the added parameter is being swapped with an original parameter.
ALTER PROCEDURE [dbo].[spWeb_BulkGenerateWRsDevices]
(
#WorkRequestDeviceId nvarchar(max) = NULL,
#TechnicianId int = NULL,
#RequiredBy datetime = NULL,
#Priority int = NULL,
#WRTypeId int,
#WorkRequestSummary nvarchar(255) = NULL,
#AuthorOfRequest int,
#Contact nvarchar(40) = NULL,
#PhoneNo nvarchar(20) = NULL,
#StatusId int,
#Created datetime = NULL,
#CustomerId int
)
AS
/* Update the WR record */
BEGIN
DECLARE #COUNT INT
SET #COUNT =(SELECT COUNT(*) FROM WorkRequest WHERE WorkRequestDeviceId IN(SELECT stringval FROM dbo.CSV(#WorkRequestDeviceId)))
IF #COUNT >0
BEGIN
DECLARE #InsertedRows AS TABLE (WorkRequestId int,WorkRequestDeviceId int)
INSERT INTO WorkRequest
(AllocatedTo,RequiredBy,Priority,WRTypeId,WorkRequestSummary,AuthorOfRequest,
Contact,PhoneNo,StatusId,DateOfRequest,WorkRequestDeviceId,CustomerId)
OUTPUT Inserted.WorkRequestId,Inserted.WorkRequestDeviceId INTO #InsertedRows
SELECT #TechnicianId ,#RequiredBy,#Priority,#WRTypeId,#WorkRequestSummary,#AuthorOfRequest,#Contact
,#PhoneNo,#StatusId,#Created,#CustomerId,* from dbo.fnSplit(#WorkRequestDeviceId, ',')
END
IF #COUNT = 0
BEGIN
INSERT INTO WorkRequest
(WorkRequestDeviceId)
SELECT * from dbo.fnSplit(#WorkRequestDeviceId, ',')
UPDATE WorkRequest
SET
AllocatedTo =#TechnicianId ,
RequiredBy = #RequiredBy,
Priority = #Priority,
WRTypeId = #WRTypeId,
WorkRequestSummary =#WorkRequestSummary,
AuthorOfRequest= #AuthorOfRequest,
Contact = #Contact,
PhoneNo = #PhoneNo,
StatusId = #StatusId,
DateOfRequest = #Created,
CustomerId = #CustomerId
WHERE WorkRequestDeviceId IN(SELECT stringval FROM dbo.CSV(#WorkRequestDeviceId))
END
I have added the CustomerId parameter. When I call the procedure and say set WorkRequestDeviceId = 312 and CustomerId = 148 the table WorkRequest has a new record but with WorkRequestDeviceId = 148 and CustomerId = 312. I am new to stored procedure so sorry for my ignorance, any ideas?
James
Your insert is transposing the variables in the insert statement and the select.
Change your insert to read:
INSERT INTO WorkRequest
(AllocatedTo,RequiredBy,Priority,WRTypeId,WorkRequestSummary,AuthorOfRequest,
Contact,PhoneNo,StatusId,DateOfRequest,CustomerId,WorkRequestDeviceId)
And all should be fine.