Syntax issue using ON in a MERGE INTO statement - sql-server

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

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);

Snowflake Merge using streams

Merge statement throws:
"Boolean value is not recognized"
I'm reading all varchar values from streams and writing to a master table. I dont have any Boolean in the source or destination table. Unable to find out why I'm getting: "Boolean value is not recognized" Error."
create table "EMP_TEST" (EMPID integer, EMPNAME VARCHAR(500), EMPADD VARCHAR(500), EMPSALARY INTEGER);
create table "EMP_TEST_MAIN" (EMPID integer, EMPNAME VARCHAR(500), EMPADD VARCHAR(500), EMPSALARY INTEGER);
create or replace stream ST_EMP_TEST on table "EMP_TEST";
insert into "EMP_TEST"
select 1, 'AAA','PLACE 1', 100 UNION
select 2, 'BBB','PLACE 2', 200 UNION
select 3, 'CCC','PLACE 3', 300;
MERGE INTO "EMP_TEST_MAIN" AS T USING (select * from ST_EMP_TEST where NOT (METADATA$ACTION ='DELETE' AND METADATA$ISUPDATE = TRUE)) AS S ON T.EMPID = S.EMPID WHEN MATCHED AND S.METADATA$ACTION = 'INSERT' AND S.METADATA$ISUPDATE THEN UPDATE SET T.EMPNAME = S.EMPNAME AND T.EMPADD = S.EMPADD AND T.EMPSALARY = S.EMPSALARY WHEN MATCHED AND S.METADATA$ACTION = 'DELETE' THEN
DELETE WHEN NOT MATCHED AND S.METADATA$ACTION = 'INSERT' THEN
INSERT (T.EMPID, T.EMPNAME, T.EMPADD, T.EMPSALARY) VALUES (S.EMPID, S.EMPNAME, S.EMPADD, S.EMPSALARY);
The columns in UPDATE part are separated with AND:
WHEN MATCHED AND S.METADATA$ACTION = 'INSERT' AND S.METADATA$ISUPDATE
THEN UPDATE SET T.EMPNAME = S.EMPNAME
AND T.EMPADD = S.EMPADD
AND T.EMPSALARY = S.EMPSALARY
-- AND is incorrect in this context
Should be ,:
WHEN MATCHED AND S.METADATA$ACTION = 'INSERT' AND S.METADATA$ISUPDATE
THEN UPDATE SET T.EMPNAME = S.EMPNAME
,T.EMPADD = S.EMPADD
,T.EMPSALARY = S.EMPSALARY

t-sql select count(*) to integer variable creates exception for can't be varchar type can't be converted to int?

I am not new to SQL technologies and have some experience with PL/SQL but T-SQL is really working way different than PL/SQL... I am building a database trigger that i am trying to calculate/sum two types of values and check how many times that they do exist in my table, then will simply implement a business logic over this calculation. All type declarations are done at the beginning:
USE [DB_KD_Benchmarking_SQL]
GO
/****** Object: Trigger [dbo].[InsertTrigger_int_ext_mix_2] Script Date: 12/2/2020 3:36:51 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[InsertTrigger_int_ext_mix_2] ON [dbo].[Ext_trial_2]
AFTER INSERT
AS DECLARE #Order_id_var_2 varchar(255),
#Country_var_2 varchar(50),
#Panel_brand_var_2 varchar(255),
#Int_ext_mix_var_2 char(10),
#Business_field_var_2 char(10),
#RC_Code_var_2 char(10),
#FC_Code_var_2 char(10),
#F_ident_number_var_2 varchar(255),
#Repeated_repairs_var_2 varchar(255),
#Same_order_ID_int int,
#Same_order_ID_ext int,
#Total_Same_order_ID int,
#Total_Same_order_ID_integer int;
DECLARE new_order CURSOR FOR
SELECT Country, Order_id, Panel_Brand, Internal_Ext_Mix, Business_Field, RC_Code, FC_Code, F_ident_Number, Repeated_Repairs FROM inserted
OPEN new_order;
FETCH NEXT FROM new_order INTO #Country_var_2, #Order_id_var_2, #Panel_brand_var_2, #Int_ext_mix_var_2, #Business_field_var_2, #RC_Code_var_2, #FC_Code_var_2, #F_ident_number_var_2, #Repeated_repairs_var_2;
WHILE ##FETCH_STATUS=0
BEGIN
select #Same_order_ID_int = count(*) from Ext_trial_2 where Order_id = #Order_id_var_2 and Internal_Ext_Mix = 'Int';
Print '#Same_order_ID_int ' + #Same_order_ID_int;
select #Same_order_ID_ext = count(*) from Ext_trial_2 where Order_id = #Order_id_var_2 and Internal_Ext_Mix = 'Ext';
Print '#Same_order_ID_ext ' + #Same_order_ID_ext;
set #Total_Same_order_ID = #Same_order_ID_int + #Same_order_ID_ext;
Print '#Total_Same_order_ID ' + #Total_Same_order_ID ;
IF #Total_Same_order_ID > 1 and (#Int_ext_mix_var_2 = 'Int' or #Int_ext_mix_var_2 = 'Ext')
BEGIN
Insert into Ext_trial_2 (Country, Order_id, Panel_Brand, Internal_Ext_Mix, Business_Field, RC_Code, FC_Code, F_ident_Number, Repeated_Repairs)
Values (#Country_var_2, #Order_id_var_2, #Panel_brand_var_2, 'Mix', #Business_field_var_2, #RC_Code_var_2, #FC_Code_var_2, #F_ident_number_var_2, #Repeated_repairs_var_2);
Delete from Ext_trial_2 where Order_id = #Order_id_var_2 and Internal_Ext_Mix = 'Int';
END
FETCH NEXT FROM new_order INTO #Country_var_2, #Order_id_var_2, #Panel_brand_var_2, #Int_ext_mix_var_2, #Business_field_var_2, #RC_Code_var_2, #FC_Code_var_2, #F_ident_number_var_2, #Repeated_repairs_var_2;
END
CLOSE new_order
DEALLOCATE new_order
As you might see that #Same_order_ID_int & #Same_order_ID_ext & #Total_Same_order_ID is defined as type int. Here i try to assign values to them :
select #Same_order_ID_int = count(*) from Ext_trial_2 where Order_id = #Order_id_var_2 and Internal_Ext_Mix = 'Int';
Print '#Same_order_ID_int ' + #Same_order_ID_int;
select #Same_order_ID_ext = count(*) from Ext_trial_2 where Order_id = #Order_id_var_2 and Internal_Ext_Mix = 'Ext';
Print '#Same_order_ID_ext ' + #Same_order_ID_ext;
set #Total_Same_order_ID = #Same_order_ID_int + #Same_order_ID_ext;
Print '#Total_Same_order_ID ' + #Total_Same_order_ID ;
This part of the trigger always fires an exception and creates an error and fires exception why i try to insert row for table which I have defined :
Conversion failed when converting the varchar value to datatype int for variable #Same_order_ID_int. When i change type of Same_order_ID_int to varchar(255), it works fine and return some results and assign to variable with string '1' or '0'.
Then this leads to an error on calculation method to sum these variables :
set #Total_Same_order_ID = #Same_order_ID_int + #Same_order_ID_ext;
returns string "10" or "01" or "11" which is definitely not a proper integer values. Can someone please explain me where could be problem on my calculation ?
I am using SQL Server 2016 build version 13.0.5.
Thanks for your answers in advance.
Here's your problem
declare #Same_order_ID_int int = 2
Print '#Same_order_ID_int ' + #Same_order_ID_int;
fails with
Msg 245, Level 16, State 1, Line 3
Conversion failed when converting the varchar value '#Same_order_ID_int ' to data type int.
Because you're trying to use the '+' operator with a varchar and an int. Instead use CONCAT which handles string conversion and null-to-empty string conversion for you.
Print concat('#Same_order_ID_int ', #Same_order_ID_int);

How to check if a list of comma separated string values match an integer?

Below I've added the SQL query.
I wanted retrieve the list of records that match a condition. I pass integer values into #ClassID and #SectionID parameters, The problem is ce.Class_ID and ce.Section_ID are lists of comma-separated string values.
SELECT ce.ID AS CircularEntryCount
FROM dbo.CircularEntry ce
WHERE ce.AcademicYearID = 1
AND (ce.Circular_Date = #CurrentDate OR CAST(ce.Created_Date AS date) = #CurrentDate)
AND (ce.CircularApplicableForID = 1 OR ce.CircularApplicableForID = 3)
AND (ce.Class_ID = #ClassID OR ce.Class_ID = '0')
AND (ce.Section_ID = #SectionID OR ce.Section_ID = '0')
PS: I used split string function to split the values into individual columns and compared the same with the parameters, but it shows.
Error converting data type nvarchar to bigint
(
#List nvarchar(2000),
#SplitOn nvarchar(1)
)
RETURNS #RtnValue table (
Id int identity(1,1),
Value nvarchar(100)
)
AS
BEGIN
While (Charindex(#SplitOn,#List)>0)
Begin
Insert Into #RtnValue (value)
Select
Value = ltrim(rtrim(Substring(#List,1,Charindex(#SplitOn,#List)-1)))
Set #List = Substring(#List,Charindex(#SplitOn,#List)+len(#SplitOn),len(#List))
End
Insert Into #RtnValue (Value)
Select Value = ltrim(rtrim(#List))
Return
EN
The correct solution is to fix the problem - which means changing the structure of the database to not store delimited strings at all, but instead normalize the data and use foreign keys.
For more information, read Is storing a delimited list in a database column really that bad?, and not only the accepted answer by Bill Karwin, but other answers as well.
In case you can't change the database structure, you can use a workaround using like:
SELECT ce.ID AS CircularEntryCount
FROM dbo.CircularEntry ce
WHERE ce.AcademicYearID = 1
AND (ce.Circular_Date = #CurrentDate OR CAST(ce.Created_Date AS date) = #CurrentDate)
AND (ce.CircularApplicableForID = 1 OR ce.CircularApplicableForID = 3)
AND (','+ ce.Class_ID +',' LIKE '%,'+ CAST(#ClassID as varchar(20)) +'%,' OR ce.Class_ID = '0')
AND (','+ ce.Section_ID +',' LIKE '%,'+ CAST(#SectionID as varchar(20)) +'%,' OR ce.Section_ID = '0')
Note the cast to varchar(20) - bigint's min value contains a minus sign and 19 digits. If the data type of #ClassID or #SectionID is int, you can cast to varchar(11) instead.

Creating SP in mssql gives "conversation" error

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.

Resources