Creating SP in mssql gives "conversation" error - sql-server

I am rewriting my database system from MySql to MSSQL. In my current piece of code i get the error:
SQL80001: Incorrect syntax near ';'. Expecting CONVERSATION.
CREATE PROCEDURE [dbo].[addAnime]
#$animeID INT,
#$animeName VARCHAR(100),
#$animeEpisodes INT,
#$animeSeasons INT,
#$animeGenre1 VARCHAR(100),
#$animeGenre2 VARCHAR(100),
#$animeGenre3 VARCHAR(100),
#$animeArtist VARCHAR(100),
#$animeStudio VARCHAR(100),
#$animeReleaseDate VARCHAR(100),
#$animeDescription VARCHAR(8000),
#$animeImageLocation VARCHAR(1000)
AS
BEGIN
IF NOT EXISTS (SELECT * FROM animedatabase.anime WHERE animeName LIKE #$animeName)
INSERT INTO animedatabase.anime(animeID, animeName, episodes, seasons, genre1, genre2, genre3, artist, studio, releaseDate, animeDescription, imageLocation)
VALUES(#$animeID, #$animeName, #$animeEpisodes, #$animeSeasons, #$animeGenre1, #$animeGenre2, #$animeGenre3, #$animeArtist, #$animeStudio, #$animeReleaseDate, #$animeDescription, #$animeImageLocation);
ELSE IF ((SELECT COUNT(*) FROM animedatabase.anime WHERE animeName LIKE #$animeName) = 1)
UPDATE
animedatabase.anime
SET
animeID = #$animeID,
animeName = #$animeName,
episodes = #$animeEpisodes,
seasons = #$animeSeasons,
genre1 = #$animeGenre1,
genre2 = #$animeGenre2,
genre3 = #$animeGenre3,
artist = #$animeArtist,
studio = #$animeStudio,
releaseDate = #$animeReleaseDate,
animeDescription = #$animeDescription,
imageLocation = #$animeImageLocation
WHERE animedatabase.anime.animename LIKE #$animeName;
END
END
END;
I've tried quite a lot of changes in structure and naming, but none solved the problem. I can't seem to find what I'm doing wrong. Any help would be much appreciated.
Thanks in advance.

Related

SQL Server query with a scalar variable that can be a varchar or uniqueidentifier

The query below has a scalar variable #LocationID that can either be a varchar or a uniqueidentifier depending on the value of the scalar variable #LimitTo.
This query worked before the addition of the addition of the lines
OR ((#LimitTo = 'Drawing') AND (ba.DrawingGuid = ...
DrawingGuid is a Guid and not text like the other OR statements.
This suggests that the SQL is analyzed and selects a single conversion method ahead of running the query, and seeing that there are two possibilities, throws the following error when I use #LocationID as a Varchar (it works fine if #LocationID is a uniqueidentifier)
Conversion failed when converting from a character string to uniqueidentifier
Though, I'm not sure if this theory is correct. Is there a way to have the #LocationID variable either be a varchar or uniqueidentifier for this query?
Here's the query:
DECLARE #Contract VARCHAR(60);
SET #Contract = 'F8C018CA-A00C-4BB1-B920-D460786F6820';
DECLARE #LimitTo VARCHAR(30);
SET #LimitTo = 'WorkZone';--'Drawing'; 'WorkZone'
DECLARE #LocationID VARCHAR(60);
SET #LocationID = 'North'; --'2FB87868-D5D7-4A84-916F-F1DEE871A085'; 'North'
SELECT DISTINCT
asm.AssemblyCode,
asm.AssemblyRestorationDesc,
asm.AssemblyUnit,
asm.AssemblyGuid,
(SELECT SUM(m.MarkerQuantity)
FROM Marker m
WHERE m.AssemblyGuid = asm.AssemblyGuid
AND m.MarkerExcludeFromScope = 'False'
AND m.ContractGuid = #Contract
AND (((#LimitTo = 'WorkZone') AND (m.MarkerWorkZone = #LocationID))
OR ((#LimitTo = 'WorkRegion') AND (m.MarkerWorkRegion = #LocationID))
OR ((#LimitTo = 'Drawing') AND (m.DrawingGuid = #LocationID)))
AND m.Deleted = 0) AS Quantity,
(SELECT SUM(bm.MarkerQuantity)
FROM BaselineMarker bm
WHERE bm.AssemblyCode = asm.AssemblyCode
AND bm.MarkerExcludeFromScope = 'False'
AND (((#LimitTo = 'WorkZone') AND (bm.MarkerWorkZone = #LocationID))
OR ((#LimitTo = 'WorkRegion') AND (bm.MarkerWorkRegion = #LocationID))
OR ((#LimitTo = 'Drawing') AND (bm.DrawingGuid = #LocationID)))
AND bm.Deleted = 0) AS BaselineQuantity,
(SELECT SUM(ba.AllowanceQuantity)
FROM BaselineAllowance ba
WHERE ba.AssemblyCode = asm.AssemblyCode
AND (((#LimitTo = 'WorkZone') AND (ba.AllowanceWorkZone = #LocationID))
OR ((#LimitTo = 'WorkRegion') AND (ba.AllowanceWorkRegion = #LocationID))
OR ((#LimitTo = 'Drawing') AND (ba.DrawingGuid = CONVERT(uniqueidentifier, #LocationID))))
AND ba.Deleted = 0) AS AllowanceQuantity
FROM
Assembly asm
WHERE
asm.Deleted = 0
ORDER BY
asm.AssemblyCode, asm.AssemblyRestorationDesc,
asm.AssemblyUnit, asm.AssemblyGuid
I think I understand what you are trying to do.
You are trying to compare different columns to the #locationId, and the column you want to compare depends on the value of #limitTo. The other columns (like AllowanceWorkZone) are presumably all varchar, but in cases where #limitTo = 'Drawing' you instead want to compare #LocationId with the DrawingGuid column, which is a uniqueidentifier.
No, you can't do this.
Just declare a second variable which is a uniqueidentifier, and use that in the comparison against the DrawingGuid column...
declare #LocationVarchar varchar(36) = 'North';
declare #LocationGuid uniqueidentifier = try_cast(#LocationVarchar as uniqueidentifier);
declare #LimitTo varchar(30) = 'foo';
select ...
from ...
where (#LimitTo = 'foo' and MyVarcharColumn = #locationVarchar)
or (#LimitTo = 'bar' and MyGuidColumn = #locationGuid);
In the above code, if the incoming filter value is not a valid uniqueidentifier, then #locationGuid will be null, but it won't matter, because the comparison of MyVarcharColumn against #LocationVarchar is the relevant predicate.
You could of course also use try_cast directly in the predicate, but I separated it out to a separate variable just to make it clearer what was going on.
Edit: Without the second variable, it would be...
where (#limitTo = 'foo' and MyVarcharColumn = #locationVarchar)
or (#limitTo = 'bar' and MyGuidColumn = try_cast(#locationVarchar as uniqueidentifier);

Querying into a memory table

SQL Server lets you create in-memory tables. But how do you do insert into operation on that?
So for example, I used this code to create my type:
CREATE TYPE dbo.typeTableDelimetedFileSpec
AS TABLE
(
TemplateId INT NOT NULL,
FieldName VARCHAR(50) NOT NUL,
FieldPosition SMALLINT NOT NULL INDEX fpos
)
WITH (MEMORY_OPTIMIZED = ON);
Then, I tried to do this:
DECLARE #T [dbo].[typeTableDelimetedFileSpec]
SELECT *
INTO #T
FROM [dbo].[_DelimetedFileSpec]
WHERE TemplateId = 1
I know the structures match (_DelimetedFileSpec does not have index fpos, but other than that there are no differences).
I get:
Incorrect syntax near '#T'.
Also, just to check out that there are no other errors, I confirmed that the following works fine:
SELECT *
INTO #x
FROM [dbo].[_DelimetedFileSpec]
WHERE TemplateId = 1
Is it possible to somehow insert directly into the memory-table, like this?
I found a way to do it efficiently!
Declare #DeliSpecs [dbo].[typeTableDelimetedFileSpec]
Insert into #DeliSpecs (TemplateId, FieldName, FieldPosition) Select TemplateId, FieldName, FieldPosition from _DelimetedFileSpec where TemplateId = #Id

Syntax issue using ON in a MERGE INTO statement

Intellisense error:
Msg 156, Level 15, State 1, Procedure Location_CVT_Insert, Line 19 [Batch Start Line 2]
Incorrect syntax near the keyword 'ON'.
Stored procedure code:
DROP PROCEDURE IF EXISTS [dbo].[Location_CVT_Insert]
GO
CREATE PROCEDURE [dbo].[Location_CVT_Insert]
(#Location_NotificationJson NVARCHAR(MAX))
AS
BEGIN
MERGE INTO Location_CVT AS C
USING (SELECT
deviceID, lastSeen, locationMapHierarchy, locationCoordinateX,
locationCoordinateY, locationCoordinateUnit, geoCoordinateLat,
geoCoordinateLong, geoCoordinateUnit
FROM
OPENJSON(#Location_NotificationJson)
WITH
(deviceId nchar(17),
lastSeen varchar(128),
locationMapHierarchy nvarchar(256),
locationCoordinateX float,
locationCoordinateY float,
locationCoordinateUnit nvarchar(64),
geoCoordinateLat float,
geoCoordinateLong float,
geoCoordinateUnit nvarchar(64)) AS InputJSON) ON (C.deviceId = InputJSON.deviceId)
WHEN MATCHED THEN
UPDATE
SET C.deviceId = InputJSON.deviceId,
C.lastSeen = InputJSON.lastSeen,
C.locationMapHierarchy = InputJSON.locationMapHierarchy,
C.locationCoordinateX = InputJSON.locationCoordinateX,
C.locationCoordinateY = InputJSON.locationCoordinateY,
C.locationCoordinateUnit = InputJSON.locationCoordinateUnit,
C.geoCoordinateLat = InputJSON.geoCoordinateLat,
C.geoCoordinateLong = InputJSON.geoCoordinateLong,
C.geoCoordinateUnit = InputJSON.geoCoordinateUnit
WHEN NOT MATCHED THEN
INSERT (deviceId, lastSeen, locationMapHierarchy,
locationCoordinateX, locationCoordinateY,
locationCoordinateUnit, geoCoordinateLat,
geoCoordinateLong, geoCoordinateUnit)
VALUES (InputJSON.deviceId, InputJSON.lastSeen, InputJSON.locationMapHierarchy,
InputJSON.locationCoordinateX, InputJSON.locationCoordinateY,
InputJSON.locationCoordinateUnit, InputJSON.geoCoordinateLat,
InputJSON.geoCoordinateLong, InputJSON.geoCoordinateUnit);
END
It seems that the ON statement is the problem. I'd like to select from a JSON object (SQL Server 2016) and if there is a match in my Location_CVT table on Device ID, update this entry, else insert a new record.
I am attempting to do something similar to the following example.
MERGE INTO Person AS P
USING (
SELECT *
FROM OPENJSON(#json)
WITH (id int, firstName nvarchar(50), lastName nvarchar(50),
age int, dateOfBirth datetime2) InputJSON
ON (P.id = InputJSON.id)
WHEN MATCHED THEN
UPDATE SET P.firstName = InputJSON.firstName,
P.lastName = InputJSON.lastName,
P.age = InputJSON.age,
P.dateOfBirth = InputJSON.dateOfBirth
WHEN NOT MATCHED THEN
INSERT (firstName, lastName, age, dateOfBirth)
VALUES (InputJSON.firstName, InputJSON.lastName, InputJSON.age,
InputJSON.dateOfBirth);
Source: https://www.codeproject.com/Articles/1087995/Inserting-JSON-Text-into-SQL-Server-Table
If, when you have parentheses that span multiple lines, you ensure that your open and close parentheses have consistent indentation, the issue becomes more clear (at least it does to me).
MERGE INTO Location_CVT AS C
USING
( SELECT
deviceID, lastSeen, locationMapHierarchy, locationCoordinateX,
locationCoordinateY, locationCoordinateUnit, geoCoordinateLat,
geoCoordinateLong, geoCoordinateUnit
FROM
OPENJSON(#Location_NotificationJson)
WITH
( deviceId nchar(17),
lastSeen varchar(128),
locationMapHierarchy nvarchar(256),
locationCoordinateX float,
locationCoordinateY float,
locationCoordinateUnit nvarchar(64),
geoCoordinateLat float,
geoCoordinateLong float,
geoCoordinateUnit nvarchar(64)
) AS InputJSON
) ------------------------------------------------- ISSUE IS HERE
ON (C.deviceId = InputJSON.deviceId)
WHEN MATCHED THEN
UPDATE
SET C.deviceId = InputJSON.deviceId,
C.lastSeen = InputJSON.lastSeen,
C.locationMapHierarchy = InputJSON.locationMapHierarchy,
C.locationCoordinateX = InputJSON.locationCoordinateX,
C.locationCoordinateY = InputJSON.locationCoordinateY,
C.locationCoordinateUnit = InputJSON.locationCoordinateUnit,
C.geoCoordinateLat = InputJSON.geoCoordinateLat,
C.geoCoordinateLong = InputJSON.geoCoordinateLong,
C.geoCoordinateUnit = InputJSON.geoCoordinateUnit
As you may now be able to see, your subquery that you are using for the source has no alias. It should be:
MERGE INTO Location_CVT AS C
USING
( SELECT
deviceID, lastSeen, locationMapHierarchy, locationCoordinateX,
locationCoordinateY, locationCoordinateUnit, geoCoordinateLat,
geoCoordinateLong, geoCoordinateUnit
FROM
OPENJSON(#Location_NotificationJson)
WITH
( deviceId nchar(17),
lastSeen varchar(128),
locationMapHierarchy nvarchar(256),
locationCoordinateX float,
locationCoordinateY float,
locationCoordinateUnit nvarchar(64),
geoCoordinateLat float,
geoCoordinateLong float,
geoCoordinateUnit nvarchar(64)
) AS InputJSON
) AS InputJSON -- ALIAS ADDED HERE
ON (C.deviceId = InputJSON.deviceId)
WHEN MATCHED THEN
UPDATE
SET C.deviceId = InputJSON.deviceId,
C.lastSeen = InputJSON.lastSeen,
C.locationMapHierarchy = InputJSON.locationMapHierarchy,
C.locationCoordinateX = InputJSON.locationCoordinateX,
C.locationCoordinateY = InputJSON.locationCoordinateY,
C.locationCoordinateUnit = InputJSON.locationCoordinateUnit,
C.geoCoordinateLat = InputJSON.geoCoordinateLat,
C.geoCoordinateLong = InputJSON.geoCoordinateLong,
C.geoCoordinateUnit = InputJSON.geoCoordinateUnit

Need help making my stored procedure more efficient

I would like some help making my SQL Server 2016 stored procedure more efficient. I got it to work and that is 50% of my battle but I know that many (if not most) of you folks have much more experience with SQL Server stored procedures than I do.
My code so far:
DECLARE #U1A nvarchar(50), #U2A nvarchar(50),
#U3A nvarchar(50), #U4A nvarchar(50),
#U5A nvarchar(50), #U6A nvarchar(50),
#U7A nvarchar(50), #U8A nvarchar(50),
#U9A nvarchar(50)
DECLARE #Jsonstring nvarchar(max)
DECLARE #recCount int
SELECT
#recCount = COUNT(*)
FROM
[dbo].[Staging_PersonalInformation]
WHERE
jsondata IS NULL
WHILE #recCount > 0
BEGIN
SELECT TOP 1
#U1A = [FirstName], #U2A = [MiddleName],
#U3A = [LastName], #U4A = [EmailAddress],
#U5A = eraCommons, #U6A = [PositionTitle],
#U7A = [MyNCBILink], #U8A = [UniqueID],
#U9A = [ReferenceID]
FROM
[dbo].[Staging_PersonalInformation]
WHERE
jsondata IS NULL
SET #Jsonstring = '[{"name":"FirstName","value":"'+isnull(#U1A, '')+'"},{"name":"Middlename","value":"'+ISNULL(#U2A, '')+'"},{"name":"LastName","value":"'+isnull(#U3A, '')+'"},{"name":"emailaddress","value":"'+isnull(#U4A, '')+'"},{"name":"eRACommons","value":"'+ISNULL(#U5A, '')+'"},{"name":"positionTitle","value":"'+ISNULL(#U6A, '')+'"},{"name":"MyNCBILink","value":"'+ISNULL(#U7A, '')+'"},{"name":" uniqueid","value":"'+ISNULL(#U8A, '')+'"},{"name":"ReferenceID","value":"'+ISNULL(#U9A, '')+'"}]'
UPDATE Staging_PersonalInformation
SET JsonData = #Jsonstring
WHERE (EmailAddress = #U4A);
SET #recCount = #recCount - 1
END
The purpose of this is to take the individual column values and make a string that my sterilized JavaScript form can repopulate. I would rather store the string than to make it on the fly each time.
Thanks for your help
Well the biggest issue is that looping is horribly inefficient. And since you are always going to update this column based on values already in the table you could use a computed column and avoid all this work entirely.
I would suggest that in the future you give your variable names something meaningful instead of just numbering them.
Here is how you could make this a computed column. You can read more about computed columns here. https://technet.microsoft.com/en-us/library/ms191250.aspx
alter table [dbo].[Staging_PersonalInformation]
add jsondata as '[{"name":"FirstName","value":"' + isnull(FirstName, '')
+ '"},{"name":"Middlename","value":"' + ISNULL(MiddleName, '')
+ '"},{"name":"LastName","value":"' + isnull(LastName, '')
+ '"},{"name":"emailaddress","value":"'+isnull(EmailAddress, '')
+ '"},{"name":"eRACommons","value":"'+ISNULL(eraCommons, '')
+ '"},{"name":"positionTitle","value":"'+ISNULL(PositionTitle, '')
+ '"},{"name":"MyNCBILink","value":"'+ISNULL(MyNCBILink, '')
+ '"},{"name":" uniqueid","value":"'+ISNULL(UniqueID, '')
+ '"},{"name":"ReferenceID","value":"'+ISNULL(ReferenceID, '')
+ '"}]'
The answer by Sean Lange is a great answer, but I am curious as to why you are not taking advantage of SQL Server 2016 support of for json.
I realize that the format is not the same as you specified, so I suppose that could be the reason. Perhaps this format would also work?:
select *
from Staging_PersonalInformation
for json auto, include_null_values
dbfiddle.uk demo: http://dbfiddle.uk/?rdbms=sqlserver_2016&fiddle=d533c6d3b82fdd7865b3817fba94037d
returns:
[{"Id":1,"FirstName":"Sean","MiddleName":null,"LastName":"Lange","EmailAddress":null,"PositionTitle":null,"MyNCBILink":null,"UniqueID":"6E6732A9-9FC9-4B6E-8695-AF6BB2DA2152","ReferenceID":0}
,{"Id":2,"FirstName":"Sql","MiddleName":null,"LastName":"Zim","EmailAddress":null,"PositionTitle":null,"MyNCBILink":null,"UniqueID":"FA33808B-E8BE-41B5-AA89-DA8A37503F8F","ReferenceID":0}]
Reference:
JSON support in SQL Server 2016 - Robert Sheldon
JSON Data
Include Null Values in JSON - include_null_values Option

better way to assign stored procedures output values from a table select

Due to programming language restraints, my ERP system does not allow me to make advanced select queries, that´s why I need to rely on making a stored procedure on SQL Server, calling it from the ERP system and getting the result through an array.
The code belows works ok, but I think it´s not the correct way to assign the values to the output variables... I wanted to assign the output variables directly from the select, without need to make a #temp table... is it possible? or did I make it right?
If the code can be enhanced, I would gracefully accept any suggestions. The objective of the code is call a stored procedure with a RFID tag (read by a RFID card reader) and then get some employee info from another database, from another ERP, on another server (linked through SQL "linked servers")
ALTER procedure [dbo].[KSBValTag]
(
#rfid varchar(20),
#OUT_NUMCAD varchar(10) OUTPUT,
#OUT_NOMFUN varchar(50) OUTPUT,
#OUT_SIT varchar(2) OUTPUT,
#OUT_CODCCU varchar(5) OUTPUT,
#OUT_NOMCCU varchar(30) OUTPUT
) as
Begin
set #rfid = SUBSTRING(#rfid, PATINDEX('%[^0]%', #rfid+'.'), LEN(#rfid))
select fun.numcad as Numcad,
fun.nomfun as Nomefun,
fun.sitafa as Situacao,
fun.codccu as CodCCU,
ccu.nomccu as NomeCCU
into #temp
from [vetorh].vetorh.r034fun as FUN
inner join
[vetorh].vetorh.r018ccu CCU
on fun.codccu = ccu.codccu
where numcad = (select num_cartao from [ksb-app01].topacesso.dbo.Cartoes where CodigoDeBarras = #rfid)
and tipcol = '1'
set #OUT_NUMCAD = (select Numcad from #temp)
set #OUT_NOMFUN = (select Nomefun from #temp)
set #OUT_SIT = (select Situacao from #temp)
set #OUT_CODCCU = (select CodCCU from #temp)
set #OUT_NOMCCU = (select NomeCCU from #temp)
End
select
#OUT_NUMCAD = fun.numcad,
#OUT_NOMFUN = fun.nomfun,
#OUT_SIT = fun.sitafa,
#OUT_CODCCU = fun.codccu,
#OUT_NOMCCU = ccu.nomccu
from [vetorh].vetorh.r034fun as FUN
inner join
[vetorh].vetorh.r018ccu CCU
on fun.codccu = ccu.codccu
where numcad = (select num_cartao from [ksb-app01].topacesso.dbo.Cartoes where CodigoDeBarras = #rfid)
and tipcol = '1'

Resources