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
>Solution :
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 uniquidentifier);
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);